【问题标题】:Charging multiple textures OpenGL-ES Android为多个纹理充电 OpenGL-ES Android
【发布时间】:2015-08-27 17:22:59
【问题描述】:

我正在按照 Nehe 的教程使用 OpenGL-Es 为 Android 制作应用程序。我有一个带有纹理的立方体,我的问题是我想通过按一个按钮来更改它(我在文件夹“raw”中有 2 个纹理)。我认为这是由于我的变量 imagenes 在其中保存了我的图像路径(MainActivity.java 上的 R.raw.imagen 和 R.raw.imagen2)只对图像充电一次应用程序的启动,所以即使我稍后在我的函数onClick() 中更改变量,纹理仍然保持不变。

我试图做的是在 TextureCube.java 类中进行切换,在加载纹理的函数中查看我的变量 imagenes,因此它应该在开始时为第一张图像充电应用程序,然后如果我按下按钮将其更改为另一个图像,因为代码 onClick()

图像永远不会改变,因为我打印了变量 imagenes。我不知道我做错了什么。

主活动:

 b.setOnClickListener(new OnClickListener() {
           @Override
           public void onClick(View v) {
               if (get_imagenes() == R.raw.imagen) {
                   imagenes = R.raw.imagen2;
                   b.setText("image2");
               } else if (get_imagenes() == R.raw.imagen2) {
                   imagenes = R.raw.imagen;
                   b.setText("image1");
               }
           }
       });

纹理立方体:

 // Construct an input stream to texture image
                switch (main.imagenes) {
                    case R.raw.imagen:
                      is = context.getResources().openRawResource(R.raw.imagen);
                        break;
                    case R.raw.imagen2:
                      is = context.getResources().openRawResource(R.raw.imagen2);
                        break;
                }

我将应用程序的其余代码留在这里。这是我的 MainActivity 代码:

public class MainActivity extends AppCompatActivity {
    private GLSurfaceView glView;
    private TextureCube cube;
    int imagenes = R.raw.imagen;
    Button b;
    Bitmap bitmap;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //GLSurfaceView
        glView = new MyGLSurfaceView(this);
        cube = new TextureCube();
        setContentView(glView);
        createButtons();
    }

    public void createButtons() {
        //ButtonB
        LinearLayout ll = new LinearLayout(this);
        b = new Button(this);
        b.setText("Change Texture");
        ll.addView(b);
        ll.setGravity(Gravity.BOTTOM | Gravity.CENTER);
        this.addContentView(ll, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));

       b.setOnClickListener(new OnClickListener() {
           @Override
           public void onClick(View v) {
               if (get_imagenes() == R.raw.imagen) {
                   imagenes = R.raw.imagen2;
                   b.setText("image2");
               } else if (get_imagenes() == R.raw.imagen2) {
                   imagenes = R.raw.imagen;
                   b.setText("image1");
               }
           }
       });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    // Call back when the activity is going into the background
    @Override
    protected void onPause() {
        super.onPause();
        glView.onPause();
    }

    // Call back after onPause()
    @Override
    protected void onResume() {
        super.onResume();
        glView.onResume();
    }

    public int get_imagenes() {
        return imagenes;
    }
}

我的渲染器代码:

public class MyGLRenderer implements GLSurfaceView.Renderer {

    private Context context;
    private TextureCube cube;
    private MainActivity main;
    // For controlling cube's z-position, x and y angles and speeds
    float angleX = 0;
    float angleY = 0;
    float speedX = 0;
    float speedY = 0;
    float z = -6.0f;

    int currentTextureFilter = 0;  // Texture filter

    // Lighting (NEW)
    boolean lightingEnabled = false;   // Is lighting on? (NEW)
    private float[] lightAmbient = {0.5f, 0.5f, 0.5f, 1.0f};
    private float[] lightDiffuse = {1.0f, 1.0f, 1.0f, 1.0f};
    private float[] lightPosition = {0.0f, 0.0f, 2.0f, 1.0f};
    // Blending (NEW)
    boolean blendingEnabled = false;  // Is blending on? (NEW)
    // Constructor
    public MyGLRenderer(Context context) {
        this.context = context;   // Get the application context (NEW)
        cube = new TextureCube();
    }

    // Call back when the surface is first created or re-created.
    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        gl.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);  // Set color's clear-value to black
        //gl.glClearColor(0f, 0f, 0f, 1.0f);
        gl.glClearDepthf(1.0f);            // Set depth's clear-value to farthest
        gl.glEnable(GL10.GL_DEPTH_TEST);   // Enables depth-buffer for hidden surface removal
        gl.glDepthFunc(GL10.GL_LEQUAL);    // The type of depth testing to do
        gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);  // nice perspective view
        gl.glShadeModel(GL10.GL_SMOOTH);   // Enable smooth shading of color
        gl.glDisable(GL10.GL_DITHER);      // Disable dithering for better performance

        // Setup Texture, each time the surface is created (NEW)
        cube.loadTexture(gl, context);    // Load image into Texture (NEW)
        gl.glEnable(GL10.GL_TEXTURE_2D);  // Enable texture (NEW)

        // Setup lighting GL_LIGHT1 with ambient and diffuse lights (NEW)
        gl.glLightfv(GL10.GL_LIGHT1, GL10.GL_AMBIENT, lightAmbient, 0);
        gl.glLightfv(GL10.GL_LIGHT1, GL10.GL_DIFFUSE, lightDiffuse, 0);
        gl.glLightfv(GL10.GL_LIGHT1, GL10.GL_POSITION, lightPosition, 0);
        gl.glEnable(GL10.GL_LIGHT1);   // Enable Light 1 (NEW)
        gl.glEnable(GL10.GL_LIGHT0);   // Enable the default Light 0 (NEW)

        // Setup Blending (NEW)
        gl.glColor4f(1.0f, 1.0f, 1.0f, 0.5f);           // Full brightness, 50% alpha (NEW)
        gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE); // Select blending function (NEW)
    }


    // Call back after onSurfaceCreated() or whenever the window's size changes.
    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        if (height == 0) height = 1;   // To prevent divide by zero
        float aspect = (float)width / height;

        // Set the viewport (display area) to cover the entire window
        gl.glViewport(0, 0, width, height);

        // Setup perspective projection, with aspect ratio matches viewport
        gl.glMatrixMode(GL10.GL_PROJECTION); // Select projection matrix
        gl.glLoadIdentity();                 // Reset projection matrix
        // Use perspective projection
        GLU.gluPerspective(gl, 45, aspect, 0.1f, 100.f);

        gl.glMatrixMode(GL10.GL_MODELVIEW);  // Select model-view matrix
        gl.glLoadIdentity();                 // Reset
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        // Clear color and depth buffers
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

        if (lightingEnabled) { //Enable lighting
            gl.glEnable(GL10.GL_LIGHTING);
        } else {
            gl.glDisable(GL10.GL_LIGHTING);
        }

        if (blendingEnabled) { //Enable blending
            gl.glEnable(GL10.GL_BLEND);       // Turn blending on (NEW)
            gl.glDisable(GL10.GL_DEPTH_TEST); // Turn depth testing off (NEW)
        } else {
            gl.glDisable(GL10.GL_BLEND);      // Turn blending off (NEW)
            gl.glEnable(GL10.GL_DEPTH_TEST);  // Turn depth testing on (NEW)
        }

        // ----- Render the Cube ----- //
        gl.glLoadIdentity();              // Reset the model-view matrix
        gl.glTranslatef(0.0f, 0.0f, z);   // Translate into the screen (NEW)
        gl.glRotatef(angleX, 1.0f, 0.0f, 0.0f); // Rotate (NEW)
        gl.glRotatef(angleY, 0.0f, 1.0f, 0.0f); // Rotate (NEW)
        cube.draw(gl);

        // Update the rotational angle after each refresh (NEW)
        angleX += speedX;  // (NEW)
        angleY += speedY;  // (NEW)

    }
}

GLSurfaceView:

public class MyGLSurfaceView extends GLSurfaceView {
    MyGLRenderer renderer;
    MainActivity main;

    private final float TOUCH_SCALE_FACTOR = 180.0f / 320.0f;
    private float previousX;
    private float previousY;

    //Allocate and set the renderer
    public MyGLSurfaceView(Context context) {
        super(context);
        renderer = new MyGLRenderer(context);
        this.setRenderer(renderer);
        this.requestFocus();
        this.setFocusableInTouchMode(true);
    }

    // Handler for key event
    @Override
    public boolean onKeyUp(int keyCode, KeyEvent evt) {
        switch(keyCode) {
            case KeyEvent.KEYCODE_DPAD_LEFT:   // Decrease Y-rotational speed
                renderer.speedY += 0.1f;
                break;
            case KeyEvent.KEYCODE_DPAD_RIGHT:  // Increase Y-rotational speed
                renderer.speedY -= 0.1f;
                break;
            case KeyEvent.KEYCODE_DPAD_UP:     // Decrease X-rotational speed
                renderer.speedX += 0.1f;
                break;
            case KeyEvent.KEYCODE_DPAD_DOWN:   // Increase X-rotational speed
                renderer.speedX -= 0.1f;
                break;
            case KeyEvent.KEYCODE_A:           // Zoom out (decrease z)
                renderer.z -= 0.2f;
                break;
            case KeyEvent.KEYCODE_Z:           // Zoom in (increase z)
                renderer.z += 0.2f;
                break;
            case KeyEvent.KEYCODE_B:  // Toggle Blending on/off (NEW)
                renderer.blendingEnabled = !renderer.blendingEnabled;
                break;
            case KeyEvent.KEYCODE_L:  // Toggle lighting on/off (NEW)
                renderer.lightingEnabled = !renderer.lightingEnabled;
                break;
        }
        return true;
    }

    // Handler for touch event
    @Override
    public boolean onTouchEvent(final MotionEvent event) {
        float currentX = event.getX();
        float currentY = event.getY();
        float deltaX, deltaY;
        switch (event.getAction()) {
            case MotionEvent.ACTION_MOVE:
                // Modify rotational angles according to movement
                deltaX = currentX - previousX;
                deltaY = currentY - previousY;
                renderer.angleX += deltaY * TOUCH_SCALE_FACTOR;
                renderer.angleY += deltaX * TOUCH_SCALE_FACTOR;
                break;
        }
        // Save current x, y
        previousX = currentX;
        previousY = currentY;
        return true;  // Event handled
    }
}

还有TextureCube:

public class TextureCube {
    private FloatBuffer vertexBuffer; //Buffer for vertex-array
    private FloatBuffer texBuffer;    //Buffer for texture-coords-array (NEW)
    private MainActivity main = new MainActivity();

    private float[] vertices = { //Vertices for a face
            -1.0f, -1.0f, 0.0f,  //left-bottom-front
            1.0f, -1.0f, 0.0f,  //right-bottom-front
            -1.0f, 1.0f, 0.0f,  //left-top-front
            1.0f, 1.0f, 0.0f   //right-top-front
    };

    float[] texCoords = { // Texture coords
            0.0f, 1.0f,  //left-bottom
            1.0f, 1.0f,  //right-bottom
            0.0f, 0.0f,  //left-top
            1.0f, 0.0f   //right-top (
    };
    int[] textureIDs = new int[1]; //new


    public TextureCube() {
        // Setup vertex-array buffer
        ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);
        vbb.order(ByteOrder.nativeOrder()); // Use native byte order
        vertexBuffer = vbb.asFloatBuffer(); // Convert from byte to float
        vertexBuffer.put(vertices);         // Copy data into buffer
        vertexBuffer.position(0);           // Rewind

        // Setup texture-array buffer
        ByteBuffer tbb = ByteBuffer.allocateDirect(texCoords.length * 4);
        tbb.order(ByteOrder.nativeOrder());
        texBuffer = tbb.asFloatBuffer();
        texBuffer.put(texCoords);
        texBuffer.position(0);
    }

    // Draw the cube
    public void draw(GL10 gl) {
        gl.glFrontFace(GL10.GL_CCW);    // Front face in counter-clockwise orientation
        gl.glEnable(GL10.GL_CULL_FACE); // Enable cull face
        gl.glCullFace(GL10.GL_BACK);    // Cull the back face (don't display)

        //Enable vertex and texture client
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
        gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);  // Enable texture-coords-array (NEW)
        gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, texBuffer); // Define texture-coords buffer (NEW)

        //Draw all the faces
        //Front
        gl.glPushMatrix();
        gl.glTranslatef(0.0f, 0.0f, 1.0f);
        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
        gl.glPopMatrix();

        //Left
        gl.glPushMatrix();
        gl.glRotatef(270.0f, 0.0f, 1.0f, 0.0f);
        gl.glTranslatef(0.0f, 0.0f, 1.0f);
        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
        gl.glPopMatrix();

        //Back
        gl.glPushMatrix();
        gl.glRotatef(180.0f, 0.0f, 1.0f, 0.0f);
        gl.glTranslatef(0.0f, 0.0f, 1.0f);
        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
        gl.glPopMatrix();

        //Right
        gl.glPushMatrix();
        gl.glRotatef(90.0f, 0.0f, 1.0f, 0.0f);
        gl.glTranslatef(0.0f, 0.0f, 1.0f);
        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
        gl.glPopMatrix();

        //Top
        gl.glPushMatrix();
        gl.glRotatef(270.0f, 1.0f, 0.0f, 0.0f);
        gl.glTranslatef(0.0f, 0.0f, 1.0f);
        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
        gl.glPopMatrix();

        //Bottom
        gl.glPushMatrix();
        gl.glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
        gl.glTranslatef(0.0f, 0.0f, 1.0f);
        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
        gl.glPopMatrix();

        gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);  // Disable texture-coords-array (NEW)
        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glDisable(GL10.GL_CULL_FACE);
    }

    // Load an image into GL texture
    public void loadTexture(GL10 gl, Context context) {

        gl.glGenTextures(1, textureIDs, 0); // Generate texture-ID array new

        gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[0]);   // Bind to texture ID

        // Set up texture filters
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);


        InputStream is = new InputStream() {
            @Override
            public int read() throws IOException {
                return 0;
            }
        };
        // Construct an input stream to texture image
        switch (main.imagenes) {
            case R.raw.imagen:
              is = context.getResources().openRawResource(R.raw.imagen);
                break;
            case R.raw.imagen2:
              is = context.getResources().openRawResource(R.raw.imagen2);
                break;
        }

        Log.d("prueba","imagenes"+main.imagenes);

        Bitmap bitmap;
        try {
            // Read and decode input as bitmap
                bitmap = BitmapFactory.decodeStream(is);
        } finally {
            try {
                    is.close();
            } catch (IOException e) {
            }
        }

        // Build Texture from loaded bitmap for the currently-bind texture ID
        GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
        bitmap.recycle();
    }
}

【问题讨论】:

    标签: java android opengl-es textures cube


    【解决方案1】:

    在程序初始化时,您需要加载 2 个位图并将它们作为 2 个新纹理上传到 GPU (glGenTextures/glBindTexture/GLUtils.texImage2D),这应该会给您两个不同的纹理 ID:textureIDs[0]textureIDs[1].

    然后,当您在public void draw(GL10 gl) 中绘制立方体时,您需要根据您的按钮状态使用textureIDs[0]textureIDs[1] 添加对glBindTexture 的调用。

    您当前的代码仅将 2 个纹理之一加载到 GPU,第二个纹理仅在单击按钮时加载到 RAM。而且你忘了在立方体的绘制函数中调用glBindTexture

    -- 编辑--

    试着用一些来更好地解释:

    首先你需要在程序初始化时将两个图像加载到OpenGL纹理中,而不是仅在按下按钮时才加载第二个。

    这将使事情更容易处理,并避免造成任何内存泄漏。所以我创建了一个新的loadTextures 函数来做到这一点:

    // you need 2 texture IDs now ...
    int NB_GL_TEXTURES = 2;
    int[] textureIDs = new int[NB_GL_TEXTURES];
    
    // tool function to load a texture to OpenGL
    public void loadTexture(GL10 gl, Context context, InputStream is, int GL_id_slot) {
    
        // decode is to a Bitmap
        Bitmap bitmap;
        try {
            bitmap = BitmapFactory.decodeStream(is);
        } finally {
            try {
                is.close();
            } catch (IOException e) {
            }
        }
    
        // tell OpenGL what is the current GL texture
        gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[GL_id_slot]);
    
        // Set up texture filters for current GL texture
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
    
        // load the bitmap into current GL texture
        GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
    
        // destroy the bitmap
        bitmap.recycle();
    }
    
    // Loads the two images into two OpenGL textures
    public void loadTextures(GL10 gl, Context context) {
    
        // generate 2 GL textures IDs => textureIDs[0], textureIDs[1]
        gl.glGenTextures(NB_GL_TEXTURES, textureIDs, 0);
    
        // load imagen into GL tex of id textureIDs[0]
        InputStream is_bitmap_0 = context.getResources().openRawResource(R.raw.imagen);
        loadTexture(gl, context, is_bitmap_0, 0);
    
        // load imagen2 into GL tex of id textureIDs[1]
        InputStream is_bitmap_1 = context.getResources().openRawResource(R.raw.imagen2);
        loadTexture(gl, context, is_bitmap_1, 1);
    }
    
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        // ...
    
        // Setup the 2 GL Textures, each time the surface is created
        gl.glEnable(GL10.GL_TEXTURE_2D);
        cube.loadTextures(gl, context);
    
        // ...
    }
    

    下一步是将立方体渲染代码更改为在每一帧调用 glBindTexture,并传递正确的 GL 纹理 ID:

    // Draw the cube
    public void draw(GL10 gl) { 
        // ...
    
        //Enable vertex and texture client
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
        gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);  // Enable texture-coords-array (NEW)
        gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, texBuffer); // Define texture-coords buffer (NEW)
    
        // choose which texture to use on the cube
        int GL_id_slot = 0;
        if (main.imagenes == R.raw.imagen)
            GL_id_slot = 0;
        else if (main.imagenes == R.raw.imagen2)
            GL_id_slot = 1;
        gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[GL_id_slot]);
    
        //Draw all the faces
        // ...
    }
    

    【讨论】:

    • 什么意思?因为如果我添加 gl.glBindTexture(GL10.GL_TEXTURE_2D, main.get_imagenes()); ,为了知道当我点击 public void draw(GL10 gl) 时我想要放置的图像是什么,会发生什么是立方体是白色的,因为它不渲染任何纹理。你首先说的关于glGenTextures, glBindTexture, GLUtils.texImage2D 的内容在我的函数loadTexture(GL10 gl, Context context) 中,我在渲染器中调用了它。所以我根本不明白,如果你能解释一下,那就太好了。不过还是谢谢你的回答:)
    • 我编辑了我的答案,添加了一些代码,我希望这会有所帮助
    • 它爆炸了,我不知道原因,因为所有代码都有意义。我将继续寻找如何处理错误,非常感谢您的努力!
    • @Sara:我刚刚发现我在代码中犯了一个错误,在加载第二个纹理时,应该是loadTexture(gl, context, is_bitmap_1, 1);而不是loadTexture(gl, context, is_bitmap_0, 0);,否则会崩溃...因为is_bitmap_0已经被摧毁了。我也编辑了答案的代码。
    • 不,我注意到了这一点,并在您在这里上传代码时更正了它。我的应用程序不会崩溃,但它也不会对其他纹理收费。这没有多大意义,因为它应该完美地工作。但是感谢您的纠正。
    猜你喜欢
    • 2012-04-08
    • 1970-01-01
    • 1970-01-01
    • 2013-09-16
    • 1970-01-01
    • 2011-04-27
    • 2011-11-12
    • 1970-01-01
    • 2016-08-26
    相关资源
    最近更新 更多