7

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

我正在学习 OpenGL,并试图准确了解每一帧必须调用哪些 API,而不是只调用一次。我最初在每一帧都调用glVertexAttribPointerglEnableVertexAttribArray但是当我将测试程序更改为每个着色器程序只调用一次时,我得到的行为不是我所期望的。

它似乎glVertexAttribPointer并且glEnableVertexAttribArray应该只需要调用一次,而不是每一帧,因为它们仅在特定着色器程序的上下文中才有意义。我这样说是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 之上。

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

4

1 回答 1

6

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

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

如果 VAO 不可用,则每次切换 GLSL 程序时都必须手动设置指针。请注意,在这种情况下,有一个全局上下文,其中存储了顶点数组指针、绑定顶点缓冲区、启用/禁用数组等。因此,您必须小心禁用您不使用的顶点属性数组,否则您可能会导致 GL 从无效内存中读取。

VAO 是一个更优雅的解决方案,只要它们可用,您就应该喜欢它们。

于 2013-09-05T20:30:03.720 回答