OpenGL ES 2.0:使用 GL_STREAM_DRAW 的 VBO 最有效的设置?
我在 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.
但是,这会破坏 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
,这显然是错误的。
I'm using a Vertex Buffer Object (VBO) in OpenGL ES 2.0.
I have a set of vertex data which is permanently stored in normal RAM. The reason is that calculating the vertex positions from scratch is costly, but a delta can be added to the last position to cheaply update it.
The actual number of vertices to be drawn changes rapidly over time. In one frame I may have 1000 and in the next 2500. Following advice received here earlier, I now specify integer UPPER
as an upper bound on the number of vertices which will ever be drawn. I malloc
my vertex and index data arrays only once at startup based on this value.
I pass the GL_STREAM_DRAW
usage hint to each glBindBuffer
call to indicate the data changes each frame.
Attempting to be as efficient as possible, I created the following setup:
// 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.
However this breaks with an EXC_BAD_ACCESS
on glDrawElements
, and I know it's due to my ordering of gl
commands.
I had a similar setup earlier which worked:
// 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, ...);
However, this setup requires much more work per frame, and as you can see involves changing the VBO sizes (since it uses actual size, not UPPER
), which I've been told is a big performance drain.
Could someone please explain to me any obvious problems with my new setup, and most importantly which commands I have to call every frame before glDrawElements
? My assumption that I can prepare all possible indices in advance and then pass the actual vertex count to glDrawElements
is clearly wrong.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
为了回答您在问题标题中提出的问题,没有用于流式传输顶点数据的“最有效”缓冲区对象设置。尤其是 ES 2.0,它涵盖了各种不同的硬件,每种硬件都有自己的特点。
要回答有关代码为何停止工作的问题,很可能是因为您没有尊重这些函数的实际用途。
例如,
glUseProgram
导致给定的程序对象成为任何后续glDraw*
调用都将使用的程序对象,直到您调用glUseProgram
再次。 将大多数 OpenGL 函数视为对全局状态的探索,因为这就是它的工作原理。glUseProgram
正在设置一个全局变量,glDraw*
会读取该变量以找出要使用的着色器。因此,如果您想确保特定的绘制调用使用特定的着色器,则必须事先
glUseProgram
该着色器。或者至少最近你知道你没有在其他地方改变它。通常,对象的渲染如下所示:第一步使用
glEnableVertexAttribArray
、glBindBuffer
和glVertexAttribPointer
。这些函数,就像glUseProgram
设置全局状态一样。在使用对象进行渲染后,您应该使用glDisableVertexAttribArray,并且您应该取消绑定可能已使用的任何缓冲区。To answer the question you asked in your question title, there is no "most efficient" buffer object setup for streaming vertex data. Especially not on ES 2.0, which covers a wide range of different hardware, each with its own peculiarities.
To answer your question about why your code stopped working, it's likely because you're not respecting what these functions actually do.
For example,
glUseProgram
causes the given program object to become the program object that any subsequentglDraw*
calls will use until you callglUseProgram
again. Think of most OpenGL functions as poking at global state, because that's how it works.glUseProgram
is setting a global variable thatglDraw*
reads to find out what shader to use.Therefore, if you want to ensure that a particular draw call uses a particular shader, then you must
glUseProgram
that shader immediately beforehand. Or at least recently enough that you know that you didn't change it somewhere else. Generally, rendering for an object looks like this:The first step uses
glEnableVertexAttribArray
,glBindBuffer
, andglVertexAttribPointer
. These functions, just likeglUseProgram
set global state. You should useglDisableVertexAttribArray
after having rendered with the object, and you should unbind any buffers you may have used.