2
votes

I'm trying to figure out how to put different textures into different texture units and choose which texture to draw with. I have the following code in my onDrawFrame() method

    int[] texture = new int[7];
    texture[0] =TextureHelper.loadTexture(mActivityContext,R.drawable.texture1);
    texture[1] =TextureHelper.loadTexture(mActivityContext,R.drawable.texture2);
    texture[2] =TextureHelper.loadTexture(mActivityContext,R.drawable.texture3);
    texture[3] =TextureHelper.loadTexture(mActivityContext,R.drawable.texture4);
    texture[4] =TextureHelper.loadTexture(mActivityContext,R.drawable.texture5);
    texture[5] =TextureHelper.loadTexture(mActivityContext,R.drawable.texture6);
    texture[6] =TextureHelper.loadTexture(mActivityContext,R.drawable.texture7);

    for (int i = 0; i < 7; i ++) {
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + i);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture[i]);
        GLES20.glUniform1i(mTextureUniformHandle, i);
        Matrix.setIdentityM(mModelMatrix, 0);
        Matrix.translateM(mModelMatrix, 0, -0.60f + 0.2f * i, 0.0f, 0.0f);
        draw();
    }

What this is supposed to do is load seven different textures into separate texture units and draw cubes, each cube with a different texture. However, what ends up happening is that all of the cubes end up being drawn with the first texture.

It works correctly if I change GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + i) to GLES20.glActiveTexture(GLES20.GL_TEXTURE0) and GLES20.glUniform1i(mTextureUniformHandle, i) to GLES20.glUniform1i(mTextureUniformHandle, 0), but that just uses a single texture unit and replaces the texture in that unit every time, which is not what I want to do.

What am I doing wrong? Thanks in advance.

EDIT:

Vertex shader:

            "uniform mat4 u_MVPMatrix;" + // A constant representing the
                                            // combined
                                            // model/view/projection matrix.
            "uniform mat4 u_MVMatrix;" + // A constant representing the
                                            // combined model/view matrix.

            "attribute vec4 a_Position;" + // Per-vertex position
                                            // information we will pass in.
            "attribute vec4 a_Color;" + // Per-vertex color information we
                                        // will pass in.
            "attribute vec2 a_TexCoordinate;" + // Per-vertex texture
                                                // coordinate information we
                                                // will pass in.

            "varying vec3 v_Position;" + // This will be passed into the
                                            // fragment shader.
            "varying vec4 v_Color;" + // This will be passed into the
                                        // fragment shader.
            "varying vec2 v_TexCoordinate;" + // This will be passed into
                                                // the fragment shader.

            // The entry point for our vertex shader.
            "void main()" + "{" +
            // Transform the vertex into eye space.
            "v_Position = vec3(u_MVMatrix * a_Position);" +

            // Pass through the color.
            "v_Color = a_Color;" +

            // Pass through the texture coordinate.
            "v_TexCoordinate = a_TexCoordinate;" +

            // gl_Position is a special variable used to store the final
            // position.
            // Multiply the vertex by the matrix to get the final point in
            // normalized screen coordinates.
            "gl_Position = u_MVPMatrix * a_Position;" + "} ";

Fragment shader:

            "precision mediump float;" + // Set the default precision to medium. We don't need as high of a
            // precision in the fragment shader.
            "uniform sampler2D u_Texture;" + // The input texture.

            "varying vec3 v_Position;" + // Interpolated position for this fragment.
            "varying vec4 v_Color;" + // This is the color from the vertex shader interpolated across the
            // triangle per fragment.
            "varying vec2 v_TexCoordinate;" + // Interpolated texture coordinate per fragment.

            // The entry point for our fragment shader.
            "void main()" +
            "{" +
                // Multiply the color by the diffuse illumination level and texture value to get final output color.
                "gl_FragColor = (v_Color * texture2D(u_Texture, v_TexCoordinate));" +
            "}";

draw() method:

public void draw() {
// Pass in the position information
    mCubePositions.position(0);
    GLES20.glVertexAttribPointer(mPositionHandle, mPositionDataSize, GLES20.GL_FLOAT, false, 0, mCubePositions);
    GLES20.glEnableVertexAttribArray(mPositionHandle);

    // Pass in the color information
    mCubeColors.position(0);
    GLES20.glVertexAttribPointer(mColorHandle, mColorDataSize, GLES20.GL_FLOAT, false, 0, mCubeColors);
    GLES20.glEnableVertexAttribArray(mColorHandle);

    // Pass in the texture coordinate information
    mCubeTextureCoordinates.position(0);
    GLES20.glVertexAttribPointer(mTextureCoordinateHandle, mTextureCoordinateDataSize, GLES20.GL_FLOAT, false, 0, mCubeTextureCoordinates);
    GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle);

    // This multiplies the view matrix by the model matrix, and stores the
    // result in the MVP matrix
    // (which currently contains model * view).
    Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mModelMatrix, 0);

    // Pass in the modelview matrix.
    GLES20.glUniformMatrix4fv(mMVMatrixHandle, 1, false, mMVPMatrix, 0);

    // This multiplies the modelview matrix by the projection matrix, and
    // stores the result in the MVP matrix
    // (which now contains model * view * projection).
    Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0);

    // Pass in the combined matrix.
    GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0);

    // Draw the cube.
    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 6);
}

Assigning mTextureUniformHandle :

mTextureUniformHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_Texture");

1
That code looks correct to me. Can you make sure that you don't have any GL errors w/ glGetError? The only thing I can think of is that mTextureUniformHandle is not linked properly. Does it have a valid uniform ID?Tim
glGetError(), called right at the end of the for loop, returns 0. How do I check the uniform ID?Jon W
If error is zero than I guess your uniform is fine. When I said to check the ID I just meant what is the value of mTextureUniformHandle. Maybe post your shaders just to be sure?Tim
Also please add the draw function.Tim
I've posted the shaders, draw(), and the value for mTextureUniformHandle.Jon W

1 Answers

0
votes

Lately I've been searching for multiple textures in fragment shader and came across this Binding textures to samplers

from which I got the following to work:

In onSurfaceCreated or onSurfaceChanged:

  • Load shaders (attach and link) and get uniform locations for sampler2D (and other variables):

normalMapLoc = GLES20.glGetUniformLocation(shaderProgram, "normalMap"); shadowMapLoc = GLES20.glGetUniformLocation(shaderProgram, "shadowMap");

  • Load textures:

GLES20.glGenTextures(2, textures, 0); GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST); GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0); bitmap.recycle(); GLES20.glActiveTexture(GLES20.GL_TEXTURE1); GLES20.glBindTexture(GL10.GL_TEXTURE_COORD_ARRAY, textures[1]); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST); GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width, height, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, mColorBuffer); GLES20.glUniform1i(normalMapLoc, 0); // Texture unit 0 is for normal images. GLES20.glUniform1i(shadowMapLoc, 1); // Texture unit 1 is for shadow maps.

In onDrawFrame:

GLES20.glClearColor(0f, 0f, 0f, 0f); GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); // pass variables to the fragment shader ... // get handle to vertex shader's Position member, etcetera int mPositionHandle = GLES20.glGetAttribLocation(shaderProgram, "vPosition"); GLES20.glEnableVertexAttribArray(mPositionHandle); GLES20.glVertexAttribPointer(mPositionHandle, 3, GLES20.GL_FLOAT, false, 0, mVertexBuffer); GLES20.glDrawElements(GLES20.GL_TRIANGLE_STRIP, 4, GLES20.GL_UNSIGNED_SHORT, mIndexBuffer);

and finally the fragment shader looks like this (only relevant portion of code):

    uniform sampler2D normalMap, shadowMap;
    varying vec2 pos;

    void main() {
      vec4 color = texture2D(normalMap, pos);
      vec4 shadow = texture2D(shadowMap, pos);
      // do stuff with the colors
      ...
      gl_FragColor = ...;
    }

This way i was finally able to access both textures !

Hope this helps.