3
votes

I have a packed vertex buffer containing postion coordinates aswell as color values for a vertex in the format {X, Y, Z, R, G, B, A}.

I am able to display the rectangle properly with a hardcoded color when I alter the fragment shader by taking out the a_Color attribute and hard coding a vec4 value for gl_FragColor but I am not able to pass the color vec4 attribute into the fragment shader (the rectangle won't display in that scenario).

What is the correct way to use glVertexAttribPointer(...) and glDrawElements(...) to draw from a packed vertex buffer in OpenGL ES 2.0?

See my code below:

public class GameRenderer implements Renderer {
    public static final int POS_SZ = 3;
    public static final int COL_SZ = 4;
    public static final int FLOAT_SZ = 4;
    public static final int SHORT_SZ = 2;

    private FloatBuffer gridVB;
    private ShortBuffer gridIndices;
    int programCode, paPositionHandle, paColorHandle, puMVPMatrixHandle;

    private final String vertexShaderCode =
        "uniform mat4 u_MVPMatrix;  \n" +
        "attribute vec4 a_Position; \n" +
        "void main(){               \n" +
        " gl_Position = u_MVPMatrix * a_Position;   \n" +
        "} \n";

    private String fragmentShaderCode = 
        "precision mediump float;   \n" +
        "attribute vec4 a_Color; \n" +
        "void main(){               \n" +
        " gl_FragColor = a_Color;   \n" +
        "}                          \n";

    public void staticGrid() {
        float vertexArray[] = {
            -0.75f, 0.75f, 0.0f,        // position
            0.0f, 0.0f, 1.0f, 1.0f,     // colour
            0.75f, 0.75f, 0.0f,
            0.0f, 0.0f, 1.0f, 1.0f,
            0.75f, -0.75f, 0.0f,
            0.0f, 0.0f, 1.0f, 1.0f,
            -0.75f, -0.75f, 0.0f,
            0.0f, 0.0f, 1.0f, 1.0f
        };

        short indicesArray[] = {
            0, 1, 2, 0, 2, 3
        };

        ByteBuffer vbb = ByteBuffer.allocateDirect(vertexArray.length * FLOAT_SZ);
        vbb.order(ByteOrder.nativeOrder());
        gridVB = vbb.asFloatBuffer();
        gridVB.put(vertexArray);
        gridVB.position(0);

        ByteBuffer ibb = ByteBuffer.allocateDirect(indicesArray.length * SHORT_SZ);
        ibb.order(ByteOrder.nativeOrder());
        gridIndices = ibb.asShortBuffer();
        gridIndices.put(indicesArray);
        gridIndices.position(0);
    }

    public void onSurfaceCreated(GL10 unused, EGLConfig arg1) {
        GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
        staticGrid();
        int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
        int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
        programCode = GLES20.glCreateProgram();
        GLES20.glAttachShader(programCode, vertexShader);
        GLES20.glAttachShader(programCode, fragmentShader);
        GLES20.glLinkProgram(programCode);

        paPositionHandle = GLES20.glGetAttribLocation(programCode, "a_Position");
        paColorHandle = GLES20.glGetAttribLocation(programCode, "a_Color");
        puMVPMatrixHandle = GLES20.glGetUniformLocation(programCode, "u_MVPMatrix");
    }

    public void onDrawFrame(GL10 unused) {
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
        GLES20.glUseProgram(programCode);

        int stride = (POS_SZ + COL_SZ) * FLOAT_SZ;
        int indices_cnt = 6;

        gridVB.position(0);
        GLES20.glEnableVertexAttribArray(paPositionHandle);
        GLES20.glVertexAttribPointer(paPositionHandle, POS_SZ, GLES20.GL_FLOAT, false, stride, gridVB);

        gridVB.position(POS_SZ);
        GLES20.glEnableVertexAttribArray(paColorHandle);
        GLES20.glVertexAttribPointer(paColorHandle, COL_SZ, GLES20.GL_FLOAT, false, stride, gridVB);

        // matrix manipulation ...

        GLES20.glDrawElements(GLES20.GL_TRIANGLES, indices_cnt, GLES20.GL_UNSIGNED_SHORT, gridIndices);
    }
}
1

1 Answers

6
votes

Fragment shaders don't use attributes. Attributes are per-vertex values, which a fragment shader wouldn't know anything about. What you want to do is to take the color as an attribute to the vertex shader, and then use a varying to pass the color to the fragment shader.

Also please start using error checking for your shaders, it will tell you when you make mistakes like this:

http://www.khronos.org/opengles/sdk/docs/man/xhtml/glGetProgramiv.xml (check for GL_LINK_STATUS)

http://www.khronos.org/opengles/sdk/docs/man/xhtml/glGetShaderiv.xml (check for GL_COMPILE_STATUS)

http://www.khronos.org/opengles/sdk/docs/man/xhtml/glGetProgramInfoLog.xml

http://www.khronos.org/opengles/sdk/docs/man/xhtml/glGetShaderInfoLog.xml