OpenGL ES 2.0:使用GL_STREAM_DRAW设置VBO的最高效配置?

6

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

我有一组顶点数据,它被永久地存储在正常的RAM中。原因是从头计算顶点位置代价很高,但可以添加一个增量到上一个位置来便宜地更新它。

实际要绘制的顶点数量会随时间迅速变化。在一帧中,可能有1000个,而在下一帧中有2500个。根据此前收到的建议,现在指定整数UPPER作为最多可绘制的顶点上限。我只在启动时基于这个值malloc我的顶点和索引数据数组一次。

每次调用glBindBuffer时,我传递GL_STREAM_DRAW使用提示以指示数据每帧都会更改。

为了尽可能高效,我创建了以下设置:

// 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.

然而,这会在 glDrawElements 上出现 EXC_BAD_ACCESS 错误,我知道这是由于我的 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中,但显然是错误的。

1
可能是重复的问题:OpenGL ES 2.0:在使用VBO的glDrawElements之前需要的命令。尽管标题提出了一个完全不同的问题,但实际上的文字并不关心性能或效率。它正在询问他昨天提出的相同问题。 - Nicol Bolas
你应该将那个细节添加到你的原始问题中,而不是创建一个新的问题。 - Nicol Bolas
我没有好好休息,情绪很差,对此我深表歉意。但实话说,你的某些评论听起来非常唐突。尽管如此,你的回答让我解决了问题,我非常感激。谢谢你。 - KomodoDave
1个回答

9
回答你在问题标题中提出的问题,没有针对流式顶点数据设置“最有效”的缓冲对象。特别是在覆盖了各种不同硬件的ES 2.0上,每个硬件都有其自己的特点。
回答你关于为什么你的代码停止工作的问题,很可能是因为你没有尊重这些函数实际所做的事情。
例如,glUseProgram会导致给定的程序对象成为任何后续glDraw*调用将使用的程序对象,直到你再次调用glUseProgram。把大多数OpenGL函数看作 poking at global state,因为它就是这样工作的。 glUseProgram正在设置一个全局变量,glDraw*读取该变量以找出要使用哪个着色器。
因此,如果你想确保特定的绘图调用使用特定的着色器,那么必须立即在其前面使用glUseProgram该着色器。或者至少最近使用过,以便你知道你没有在其他地方更改它。通常,为对象进行渲染如下:
1. 建立顶点属性以渲染对象。 2. 设置当前程序,并更改任何每个对象的uniforms(矩阵等)。 3. 如果有,则绑定程序的纹理。 4. 如果有,则绑定程序的其他状态。 5. 渲染。 6. 取消激活属性。
第一步使用glEnableVertexAttribArray,glBindBuffer和glVertexAttribPointer。这些函数就像glUseProgram一样设置全局状态。在渲染对象后应该使用glDisableVertexAttribArray,并解绑任何你可能使用的缓冲区。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接