Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the scope of glVertexAttribPointer and glEnableVertexAttribArray?

I'm using OpenGL ES 2.0 on Android, but I assume this question is a general OpenGL question.

I'm learning OpenGL and am trying to understand exactly which APIs have to be called for every frame, versus only once. I was initially calling glVertexAttribPointer and glEnableVertexAttribArray every frame, but when I changed my test program to only call them once for each Shader program, I get behavior that isn't what I expect.

It seems like glVertexAttribPointer and glEnableVertexAttribArray should only have to be called once, rather than every frame, because they only make sense within the context of a particular Shader program. I say this because glVertexAttribPointer takes as it's first parameter GLuint index which was returned from glGetAttribLocation which specifies a string that corresponds to a variable name in the shader program. So it seems like the data is tied to a particular "attribute" variable in a particular shader program.

My test program renders two disjoint shapes (triangles), using two entirely different programs - different fragment shader source, different vertex shader source, separate calls to glLinkProgram etc. I initially ran the following code for every frame, and everything works as expected - I see both shapes rendered correctly.

m_glhook.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);

// Shape/Program 1          
m_glhook.glUseProgram(m_iGlProgramId);
int iPositionLocation = m_glhook.glGetAttribLocation(m_iGlProgramId, "a_Position");
m_bfVertices.rewind();
m_glhook.glVertexAttribPointer(iPositionLocation, 4, GLES20.GL_FLOAT, false, STRIDE, m_bfVertices);
m_glhook.glEnableVertexAttribArray(iPositionLocation);    
gl.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);

// Shape/Program 2
m_glhook.glUseProgram(m_iGlProgramId2);
int iPositionLocation2 = m_glhook.glGetAttribLocation(m_iGlProgramId2, "a_Position");
m_bfVertices2.rewind();
m_glhook.glVertexAttribPointer(iPositionLocation2, 4, GLES20.GL_FLOAT, false, STRIDE, m_bfVertices2);
m_glhook.glEnableVertexAttribArray(iPositionLocation2);
gl.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);

Then I modified the calls made for every frame as shown below. m_bFirstDraw is true for the first frame, and false for successive frames.

m_glhook.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);

// Shape/Program 1          
m_glhook.glUseProgram(m_iGlProgramId);
if (m_bFirstDraw)
{
    int iPositionLocation = m_glhook.glGetAttribLocation(m_iGlProgramId, "a_Position");
    m_bfVertices.rewind();
    m_glhook.glVertexAttribPointer(iPositionLocation, 4, GLES20.GL_FLOAT, false, STRIDE, m_bfVertices);
    m_glhook.glEnableVertexAttribArray(iPositionLocation);
}
gl.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);

// Shape/Program 2
m_glhook.glUseProgram(m_iGlProgramId2);
if (m_bFirstDraw)
{           
    int iPositionLocation2 = m_glhook.glGetAttribLocation(m_iGlProgramId2, "a_Position");
    m_bfVertices2.rewind();
    m_glhook.glVertexAttribPointer(iPositionLocation2, 4, GLES20.GL_FLOAT, false, STRIDE, m_bfVertices2);
    m_glhook.glEnableVertexAttribArray(iPositionLocation2);
}
gl.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
m_bFirstDraw = false;

The behavior I see with the above code is that for the first frame everything is still fine (which makes sense since for the first frame the call sequences are exactly the same). But for successive frames Shape1 gets drawn with Shape2's vertices. So I only see Shape2 since it gets drawn exactly on top of Shape1.

Please help me understand this...why does setting the data (shape vertices) for one program affect the data used by another program in a successive frame? I'm missing something...

like image 234
David Stone Avatar asked Sep 05 '13 20:09

David Stone


1 Answers

Although GLSL programs (vertex shaders in particular) use vertex attributes based on generic slot numbers, you do not actually supply the GLSL program with the vertex pointers.

These pointers are part of the Vertex Array state, which in newer versions of OpenGL is handled completely by Vertex Array Objects. If you want each program to have its own set of pointers, what I would suggest is that you create a VAO that stores the state and bind the VAO when you bind your program.

If VAOs are not available you will have to setup your pointers by hand whenever you switch GLSL programs. Note that in this case there is a global context in which the vertex array pointers, bound vertex buffers, enabled/disabled arrays, etc. are stored. So you have to be careful to disable vertex attrib arrays you are not using or you can wind up making GL read from invalid memory.

VAOs are a much more elegant solution, and you should favor them whenever they are available.

like image 180
Andon M. Coleman Avatar answered Sep 17 '22 18:09

Andon M. Coleman