glVertexAttribPointer和glEnableVertexAttribArray的范围是什么?

时间:2013-09-05 20:16:48

标签: opengl opengl-es opengl-es-2.0

我在Android上使用OpenGL ES 2.0,但我认为这个问题是一般的OpenGL问题。

我正在学习OpenGL,并试图准确理解每帧必须调用哪些API,而不是只调用一次。我最初每帧调用glVertexAttribPointerglEnableVertexAttribArray,但是当我改变我的测试程序只为每个Shader程序调用它们时,我得到的行为不是我期望的。

似乎glVertexAttribPointerglEnableVertexAttribArray只需要调用一次,而不是每一帧,因为它们只在特定Shader程序的上下文中才有意义。我这样说是因为glVertexAttribPointer采用了从GLuint index返回的第一个参数glGetAttribLocation,它指定了一个与着色器程序中的变量名对应的字符串。因此,似乎数据与特定着色器程序中的特定“属性”变量相关联。

我的测试程序渲染两个不相交的形状(三角形),使用两个完全不同的程序 - 不同的片段着色器源,不同的顶点着色器源,对glLinkProgram的单独调用等。我最初为每个帧运行以下代码,一切都按预期工作 - 我看到两个形状都正确呈现。

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);

然后我修改了为每个帧进行的调用,如下所示。 m_bFirstDraw对于第一帧是正确的,对于连续的帧是假的。

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;

我用上面的代码看到的行为是,对于第一帧,一切都很好(这是有道理的,因为对于第一帧,调用序列完全相同)。但是对于连续的帧,Shape1会被Shape2的顶点绘制。所以我只看到Shape2,因为它完全被绘制在Shape1之上。

请帮助我理解这一点......为什么设置一个程序的数据(形状顶点)会影响连续帧中另一个程序使用的数据?我错过了什么......

1 个答案:

答案 0 :(得分:6)

尽管GLSL程序(特别是顶点着色器)使用基于通用槽号的顶点属性,但实际上并没有为GLSL程序提供顶点指针。

这些指针是Vertex Array状态的一部分,在较新版本的OpenGL中,Vertex Array Objects完全处理。如果你希望每个程序都有自己的指针集,我建议你创建一个存储状态的VAO,并在绑定程序时绑定VAO。

如果VAO不可用,则无论何时切换GLSL程序,都必须手动设置指针。注意,在这种情况下,存在全局上下文,其中存储顶点阵列指针,绑定顶点缓冲区,启用/禁用阵列等。因此,您必须小心禁用未使用的顶点属性数组,否则最终会从无效内存中读取GL。

VAO是一种更优雅的解决方案,只要它们可用,您就应该支持它们。