为多个纹理 OpenGL-ES 充电 Android

Charging multiple textures OpenGL-ES Android

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

我试图做的是在 class 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;
                }

我留下了应用程序的其余代码here.Here是我的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();
    }
}

在程序初始化时,你需要加载 2 个位图并将它们作为 2 个新纹理上传到 GPU (glGenTextures / glBindTexture / GLUtils.texImage2D),这应该给你两个不同的 textureID: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
    // ...
}