我在OpenGL ES 2.0中使用了顶点缓冲对象(VBO)。

我有一组顶点数据,这些数据永久存储在普通RAM中。原因是从头开始计算顶点位置成本很高,但是可以在最后一个位置添加一个增量以廉价地更新它。

实际绘制的顶点数会随时间快速变化。在一个帧中,我可能有1000个,在下一帧中,我可能有2500个。按照前面早些时候收到的建议,我现在将整数UPPER指定为将要绘制的顶点数量的上限。我仅在启动时根据此值对顶点和索引数据数组malloc一次。

我将GL_STREAM_DRAW用法提示传递给每个glBindBuffer调用,以指示每帧数据发生了变化。

为了提高效率,我创建了以下设置:

// SETUP: Called only once.
glBindBuffer(GL_ARRAY_BUFFER,...);
glBufferData(GL_ARRAY_BUFFER,...); // Pass vertex data for UPPER vertices.
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,...);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,...); // Pass index values 0 - (UPPER-1).
glEnableVertexAttribArray(...); // Setup vertex attributes.
glVertexAttribPointer(...);
glUseProgram(...); // Use program with custom shaders.
glUniformMatrix4fv(...); // Identify shader uniforms.

// UPDATE: Called when vertex data changes (on each frame).
glBindBuffer(GL_ARRAY_BUFFER,...);
glBufferSubData(GL_ARRAY_BUFFER,...); // Update VBO data.

// RENDER: Called on each frame.
glDrawElements(GL_TRIANGLES, numberOfVerticesThisFrame, ...); // Number of vertices and indices to be used is inherently specified in here.

但是,这与EXC_BAD_ACCESS上的glDrawElements中断,并且我知道这是由于我对gl命令的排序所致。

我之前有一个类似的设置可以正常工作:
// UPDATE: Called when vertex data changes (on each frame).
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,...);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,...); // Pass index values 0 - (actual number of vertices to draw - 1)

// RENDER: Called on each frame.
glBindBuffer(GL_ARRAY_BUFFER,...);
glBufferData(GL_ARRAY_BUFFER,...); // Pass vertex data for actual number of vertices (not UPPER).
glEnableVertexAttribArray(...); // Setup vertex attributes.
glVertexAttribPointer(...);
glUseProgram(...); // Use program with custom shaders.
glUniformMatrix4fv(...); // Identify shader uniforms.
glDrawElements(GL_TRIANGLES, numberOfVerticesThisFrame, ...);

但是,此设置每帧需要做更多的工作,并且您可以看到涉及更改VBO大小(因为它使用实际大小,而不是UPPER),据我所知,这是很大的性能消耗。

有人可以向我解释一下我的新设置是否存在任何明显的问题,最重要的是,我必须在glDrawElements之前的每一帧中调用哪些命令?我可以事先准备所有可能的索引,然后将实际的顶点数传递给glDrawElements的假设显然是错误的。

最佳答案

为了回答您在问题标题中提出的问题,没有用于流传输顶点数据的“最有效”缓冲区对象设置。特别是在ES 2.0上,尤其是在涵盖各种不同硬件的ES 2.0上,每种特性都有其独特之处。

要回答有关代码为何停止工作的问题,可能是因为您不尊重这些功能的实际作用。

例如,glUseProgram导致给定的程序对象成为以后所有glDraw*调用将使用的程序对象,直到再次调用glUseProgram。可以将大多数OpenGL函数视为戳全局状态,因为这就是它的工作方式。 glUseProgram设置glDraw*读取的全局变量,以找出要使用的着色器。

因此,如果要确保特定的绘图调用使用特定的着色器,则必须事先预先对该着色器进行glUseProgram。或至少到最近才知道您没有在其他地方进行更改。通常,对象的渲染如下所示:

  • 建立用于渲染对象的顶点属性。
  • 设置当前程序,并更改任何基于对象的制服(矩阵等)。
  • 绑定(bind)程序的纹理(如果有)。
  • 绑定(bind)程序的其他状态(如果有)。
  • 渲染。
  • 停用属性

  • 第一步使用glEnableVertexAttribArrayglBindBufferglVertexAttribPointer。这些功能就像glUseProgram设置全局状态一样。与对象一起渲染后,应使用glDisableVertexAttribArray,并且应取消绑定(bind)可能使用过的所有缓冲区。

    关于performance - OpenGL ES 2.0 : The most efficient setup for a VBO with GL_STREAM_DRAW?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/7303000/

    10-12 22:34