Android OpenGL|ES 2.0 Texturing

2019-06-05 16:19发布

问题:

I am using OpenGL|ES 2.0 to create a simple 2D square. I am struggling to get the textures working. Please help me in this regard. I have attached the code below:

GFXUtils:

public class GFXUtils {
    public static final String TAG = "GFXUtils";    
    public static final int COORDS_PER_VERTEX = 3;
    public static final int COORDS_PER_TEXTURE = 2;
    public static int vertexStride = COORDS_PER_VERTEX * 4; // bytes per vertex
    public static int textureStride = COORDS_PER_TEXTURE * 4; // bytes per vertex

    public static Context Context = null;
    public static SparseIntArray textures = new SparseIntArray();

    public static int loadShader(int type, String shaderCode){

        // create a vertex shader type (GLES20.GL_VERTEX_SHADER)
        // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
        int shader = GLES20.glCreateShader(type);

        // add the source code to the shader and compile it
        GLES20.glShaderSource(shader, shaderCode);
        GLES20.glCompileShader(shader);

        return shader;
    }

    public static void checkGlError(String glOperation) {
        int error;
        while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
            Log.e(TAG, glOperation + ": glError " + error);
            throw new RuntimeException(glOperation + ": glError " + error);
        }
    }

    public static void loadTexture(final int resourceId)
    {
        final int[] textureHandle = new int[1];

        GLES20.glGenTextures(1, textureHandle, 0);

        if (textureHandle[0] != 0)
        {
            final BitmapFactory.Options options = new BitmapFactory.Options();
            options.inScaled = false;   // No pre-scaling

            // Read in the resource
            final Bitmap bitmap = BitmapFactory.decodeResource(Context.getResources(), resourceId, options);

            // Bind to the texture in OpenGL
            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[0]);

            // Set filtering
            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.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); 
            GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);

            // Load the bitmap into the bound texture.
            GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);

            // Recycle the bitmap, since its data has been loaded into OpenGL.
            bitmap.recycle();
        }

        if (textureHandle[0] == 0)
        {
            throw new RuntimeException("Error loading texture.");
        }

        textures.append(resourceId, textureHandle[0]);      
    }
}

Vertex:

public class Vertex
{
    public FloatBuffer floatBuffer; // buffer holding the vertices
    public ShortBuffer indexBuffer;
    public int numVertices;
    public int numIndeces;

    //public float vertex[];

    public Vertex (float[] vertex, int coordsPerVertex)
    {
        //this.vertex = vertex;
        this.setVertices(vertex, coordsPerVertex);
    }

    public Vertex (float[] vertex, short[] indices, int coordsPerVertex)
    {
        //this.vertex = vertex;
        this.setVertices(vertex, coordsPerVertex);
        this.setIndices(indices);
    }

    private void setVertices(float vertex[], int coordsPerVertex)
    {
        // a float has 4 bytes so we allocate for each coordinate 4 bytes
        ByteBuffer factory = ByteBuffer.allocateDirect (vertex.length * 4);
        factory.order (ByteOrder.nativeOrder ());
        // allocates the memory from the byte buffer
        floatBuffer = factory.asFloatBuffer ();
        // fill the vertexBuffer with the vertices
        floatBuffer.put (vertex);
        // set the cursor position to the beginning of the buffer
        floatBuffer.position (0);
        numVertices = vertex.length / coordsPerVertex;
    }

    protected void setIndices(short[] indices) {
        ByteBuffer ibb = ByteBuffer.allocateDirect(indices.length * 2);
        ibb.order(ByteOrder.nativeOrder());
        indexBuffer = ibb.asShortBuffer();
        indexBuffer.put(indices);
        indexBuffer.position(0);
        numIndeces = indices.length;
    }
}

UPDATE Square:

public class Square{

    private static final String TAG = "Square";

    public float[] rotation = {0.0f,0.0f,45.0f};
    public float[] scale = {100.0f,100f,100f};
    public float[] position = {0.0f,0.0f,100f};
    public float[] color = { 0.0f, 0.0f, 1.0f, 1.0f };

    private int textureRef = -1;

    private int mMVPMatrixHandle;

    protected int DRAW_MODE = GLES20.GL_TRIANGLES;
    protected int mProgram; 
    protected int mPositionHandle;
    protected Vertex vertices;
    protected Vertex texture;

    private int mColorHandle;

    private int vsTextureCoord;
    private int fsTexture;  

    protected float[] result_matrix = new float[16];    

    private final String vertexShaderCode =
            "uniform mat4 uMVPMatrix;" +
                    "attribute vec4 vPosition;" +
                    "attribute vec2 TexCoordIn;" +
                    "varying vec2 TexCoordOut;" +
                    "void main() {" +
                    //the matrix must be included as a modifier of gl_Position
                    "  gl_Position = uMVPMatrix * vPosition;" +
                    "  TexCoordOut = TexCoordIn;" +
                    "}";

    private final String fragmentShaderCode =
            "precision mediump float;" +
                    "uniform vec4 vColor;" +
                    "uniform sampler2D Texture;" +
                    "varying lowp vec2 TexCoordOut;" +                             
                    "void main() {" +                   
                    "  gl_FragColor = vColor;" +
                    "}";    
    //I am fully aware that I am not using the texture by assigning the colour, but until I can actually SEND the texture through, there would be no point.
    static float squareCoords[] = { -0.5f,  0.5f, 0.0f,   // top left
                                    -0.5f, -0.5f, 0.0f,   // bottom left
                                     0.5f, -0.5f, 0.0f,   // bottom right
                                     0.5f,  0.5f, 0.0f }; // top right


    private short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices

    public Square(int textureResourceId) {

        int vertexShader = GFXUtils.loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
        int fragmentShader = GFXUtils.loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);

        mProgram = GLES20.glCreateProgram();             // create empty OpenGL ES Program
        GLES20.glAttachShader(mProgram, vertexShader);   // add the vertex shader to program
        GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
        GLES20.glLinkProgram(mProgram);                  // creates OpenGL ES program executables
        textureRef = GFXUtils.textures.get(textureResourceId);

        // initialize vertex byte buffer for shape coordinates
        vertices = new Vertex(squareCoords, drawOrder, GFXUtils.COORDS_PER_VERTEX);

        texture = new Vertex (new float[]
                {               
                    1.0f, 0.0f,
                    0.0f, 0.0f,
                    1.0f, 1.0f,
                    0.0f, 1.0f,
                }, GFXUtils.COORDS_PER_TEXTURE);   

        DRAW_MODE = GLES20.GL_TRIANGLE_FAN;
    }

    private void getHandles()
    {

        //get handle to vertex shader's vPosition member
        mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
        if (mPositionHandle == -1) Log.e(TAG, "vPosition not found");
        //get handle to fragment shader's vColor member
        mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
        if (mColorHandle == -1) Log.e(TAG, "vColor not found");

        //get handle to shape's transformation matrix
        mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
        if (mMVPMatrixHandle == -1) Log.e(TAG, "uMVPMatrix not found");

        //get handle to texture coordinate variable
        vsTextureCoord = GLES20.glGetAttribLocation(mProgram, "TexCoordIn");
        if (vsTextureCoord == -1) Log.e(TAG, "TexCoordIn not found");

        //get handle to shape's texture reference
        fsTexture = GLES20.glGetUniformLocation(mProgram, "Texture");
        if (fsTexture == -1) Log.e(TAG, "Texture not found");

    }

    private void translateRotateScale(float[] matrix, float[] perspectiveMatrix)
    {       
        for (int i= 0; i < perspectiveMatrix.length;i++)
            matrix[i] = perspectiveMatrix[i];

        Matrix.translateM(matrix, 0, position[0], position[1], position[2]);
        Matrix.rotateM(matrix, 0, rotation[0], 1.0f, 0.0f, 0.0f);
        Matrix.rotateM(matrix, 0, rotation[1], 0.0f, 1.0f, 0.0f);
        Matrix.rotateM(matrix, 0, rotation[2], 0.0f, 0.0f, 1.0f);
        Matrix.scaleM(matrix, 0, scale[0], scale[1], scale[2]);            
    }

    public void draw(float[] mvpMatrix) {
        rotation[2]+=0.5;
        // Add program to OpenGL ES environment
        GLES20.glUseProgram(mProgram);
        GFXUtils.checkGlError("using program");

        //Housekeeping
        getHandles();
        translateRotateScale(result_matrix, mvpMatrix);
        //end housekeeping

        // Set color for drawing the shape
        GLES20.glUniform4fv(mColorHandle, 1, color, 0);     

        // Apply the projection and view transformation
        GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, result_matrix, 0);
        GFXUtils.checkGlError("glUniformMatrix4fv");

        // Prepare the shape coordinate data
        GLES20.glVertexAttribPointer(mPositionHandle, GFXUtils.COORDS_PER_VERTEX,
                GLES20.GL_FLOAT, false,
                GFXUtils.vertexStride, vertices.floatBuffer);
        GFXUtils.checkGlError("load vertex buffer");

        GLES20.glVertexAttribPointer(vsTextureCoord, GFXUtils.COORDS_PER_TEXTURE,
                GLES20.GL_FLOAT, false, 
                GFXUtils.textureStride, texture.floatBuffer);
        GFXUtils.checkGlError("load texture buffer - " + vsTextureCoord);

        // Enable a handle to the shape vertices
        GLES20.glEnableVertexAttribArray(mPositionHandle);
        GFXUtils.checkGlError("enable position handle");
        GLES20.glEnableVertexAttribArray(vsTextureCoord);
        GFXUtils.checkGlError("enable texture handle");

        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GFXUtils.checkGlError("activtexture");
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureRef);
        GFXUtils.checkGlError("bindtexture");
        GLES20.glUniform1i(fsTexture, 0);
        GFXUtils.checkGlError("uniformi");
        //Draw the shape
        GLES20.glDrawElements(DRAW_MODE, vertices.numIndeces, GLES20.GL_UNSIGNED_SHORT, vertices.indexBuffer);
        GFXUtils.checkGlError("glDrawArrays with " + vertices.numVertices + " vertices");

        //Disable vertex array
        GLES20.glDisableVertexAttribArray(vsTextureCoord);
        GLES20.glDisableVertexAttribArray(mPositionHandle);
        GFXUtils.checkGlError("glDisableVertexAttribArray for position");

    }
}

UPDATE But it now cannot find the 'Texture' sampler2D parameter for fsTexture. The image is here:

I have looked at the following:

Android: OpenGL ES 2.0 - Texture always black
Draw multiple objects with textures
Android OpenGLES 2.0 Texture Mapping Does Not Work
Textures in OpenGL ES 2.0 for Android
http://www.learnopengles.com/android-lesson-six-an-introduction-to-texture-filtering/
http://www.learnopengles.com/android-lesson-four-introducing-basic-texturing/
http://blog.shayanjaved.com/2011/05/13/android-opengl-es-2-0-render-to-texture/
http://www.raywenderlich.com/4404/opengl-es-2-0-for-iphone-tutorial-part-2-textures
http://androgeek.info/?p=167

回答1:

ok then here is my guess at a solution:

  • the glVertexAttribPointer() calls seems to be fine

  • my guess:

    vsTextureCoord == -1

    glGetAttribLocation() does not generate an error if the attribute was not found. you have to check if the result is not -1 to make sure everything went fine.

    source for the problem could be that the attribute TexCoordIn in your shader is a vec4!



回答2:

floatBuffer should contain the address of the first element in that array. you have errors after glVertexAttribPointer because the last parameter doesn't point to the vertex data. straight forward solution is to make floatbuffer = &vertexdata

my suggestion is to put the vertex data to the gl_array_buffer using glGenBuffer,glBindBuffer,glBufferData. after that, just bind the buffer before glVertexAttribPointer using 0 as the last parameter. this is faster than using the address of the vertex