我在 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 上,它涵盖了广泛的不同硬件,每个都有自己的特点。
要回答有关您的代码为何停止工作的问题,很可能是因为您不尊重这些函数的实际作用。
例如,glUseProgram
使给定的程序对象成为任何后续 glDraw*
的程序对象。在您调用 glUseProgram
之前,电话将一直使用再次。将大多数 OpenGL 函数视为在全局状态下进行操作,因为它就是这样工作的。 glUseProgram
正在设置一个全局变量 glDraw*
阅读以找出要使用的着色器。
因此,如果您想确保特定的绘制调用使用特定的着色器,那么您必须 glUseProgram
事先立即使用该着色器。或者至少最近足够你知道你没有在其他地方改变它。通常,对象的渲染如下所示:
第一步使用
glEnableVertexAttribArray
, glBindBuffer
, 和 glVertexAttribPointer
.这些函数,就像 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/