c++opengl-esglslshaderopengl-es-3.0

How do multiple buffers for attributes work in openGL (ES) shaders and C++ api


I have the proverbial cube sample and have been writing some code to test it all and learn more about shaders. My question is about how multiple buffers are assigned to access within the shader and how one writes shader code to reference them. The samples out there seem to rely on some sort of implicit referencing defaults that hide what is really happening and how one manipulates it in the samples.

My buffer setup is as follows ( data omitted )

        glGenBuffers(1, &vertexPosObject);
        glBindBuffer(GL_ARRAY_BUFFER, vertexPosObject);
        glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE,
            sizeof(Vec3f), (void*)0);

        /* load index buffer with array of indices */
        glGenBuffers(1, &indexBufferObject);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferObject);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

        /* load color buffer with array of colors */
        glGenBuffers(1, &colorBufferObject);
        glBindBuffer(GL_ARRAY_BUFFER, colorBufferObject);
        glBufferData(GL_ARRAY_BUFFER, sizeof(vertColors), vertColors, GL_STATIC_DRAW);

        glEnableVertexAttribArray(1);
        glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE,
            sizeof(Vec4f), 0);

With this I get a cube drawing and can rotate it. with.

        Matrix4d modelView;
        double rot = rc.timing().frameTime()*.3;
        rot = fmod(rot, ARTD_M_PI * 2);
        modelView.setRotation(Vec3d(0, 1, 1), rot);
        artdGlUniformMatrix4(modelViewId, modelView); // convenience call

then I can draw it below

        glUseProgram(cubeShader);

        glBindBuffer(GL_ARRAY_BUFFER, vertexPosObject);
        glEnableVertexAttribArray(0);
        glBindBuffer(GL_ARRAY_BUFFER, colorBufferObject);
        glEnableVertexAttribArray(1);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferObject);

        const int vertexCount = 36;
        const int type = GL_UNSIGNED_INT; // unsigned short ??
        const int firstIndex = 0;

        // vertexCount is the number of verts in your vertex array object
        // firstIndex is the first index to use in an array of offsets (element index) into the vertex data.

        glDrawElements(GL_TRIANGLES, vertexCount, type, (void *)firstIndex);

        glDisableVertexAttribArray(0);
        glDisableVertexAttribArray(1);

I have been assuming the vertex array is assigned a "slot" with

        glEnableVertexAttribArray(0);

And that slot "0" is implicitly what the shader takes as its input vertices. (???)

So I figured I'd try assigning the color array to slot "1", so the index array handle is passed directly into glDrawElements() and does retrieve the vertices for input in the shader using the index array addressing the buffer assigned to slot "0".

I general, I assumed one might want multiple buffers for all sorts of stuff like normals, colors, etc. and one would need to access the values of these buffers addressing them with the indices. So a general case answer would be desirable.

My question is, how does one access the color array buffer in the shader as well as accessing other buffers as indexable arrays in the vertex shader?

How does one bind buffers to various shader accessible identifiers and how does one access them from these identifiers within shader code. Understanding these relationships is key.

Currently this shader pair works for drawing the cube with calculated colors.

    "uniform mat4 Projection;\n"
    "uniform mat4 Model;\n"

    "attribute vec4 vPosition;\n"
    "varying vec3 color;\n"
    "void main(){\n"
        "mat4 MVP = Model * Projection;\n"
        "gl_Position = vPosition * MVP;\n"
        "color = gl_Position.xyz + vec3(0.5);\n"
    "}\n"

Fragment shader

   "precision mediump float;\n"
    "varying vec3 color;\n"
    "void main()\n"
    "{\n"
        "gl_FragColor = vec4 ( color, 1.0 );\n"
    "}\n"

Pardon C++ strings :)


Solution

  • If you want to use the color attribute, the you have to add a new attribute to the shader:

    Vertex shader

    attribute vec4 vPosition;
    attribute vec4 vColor;
    
    varying vec4 color;
    
    uniform mat4 Projection;
    uniform mat4 Model;
    
    void main()
    {
        mat4 MVP    = Model * Projection;
        gl_Position = vPosition * MVP;
        color       = vColor;
    }
    

    After the shader program is linked by glLinkProgram, the attribute index can be retrieved through glGetAttribLocation:

    GLuint cubeShader = ....;
    
    glLinkProgram(cubeShader);
    
    GLint pos_attr_i   = glGetAttribLocation(cubeShader, "vPosition");
    GLint color_attr_i = glGetAttribLocation(cubeShader, "vColor");
    

    This are the attribute indices which have to be use in glVertexAttribPointer respectively glEnableVertexAttribArray:

    glBindBuffer(GL_ARRAY_BUFFER, vertexPosObject);
    glVertexAttribPointer(pos_attr_i, 3, GL_FLOAT, GL_FALSE, sizeof(Vec3f), (void*)0);
    
    glBindBuffer(GL_ARRAY_BUFFER, colorBufferObject);
    glVertexAttribPointer(color_attr_i, 4, GL_FLOAT, GL_FALSE, sizeof(Vec4f), 0);
    
    glEnableVertexAttribArray(pos_attr_i);
    glEnableVertexAttribArray(color_attr_i);