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

发布于 2024-12-02 22:53:08 字数 2269 浏览 0 评论 0原文

我在 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

月光色 2024-12-09 22:53:08

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

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

例如,glUseProgram 导致给定的程序对象成为任何后续 glDraw* 调用都将使用的程序对象,直到您调用 glUseProgram再次。 将大多数 OpenGL 函数视为对全局状态的探索,因为这就是它的工作原理。 glUseProgram 正在设置一个全局变量,glDraw* 会读取该变量以找出要使用的着色器。

因此,如果您想确保特定的绘制调用使用特定的着色器,则必须事先glUseProgram 该着色器。或者至少最近你知道你没有在其他地方改变它。通常,对象的渲染如下所示:

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

第一步使用 glEnableVertexAttribArrayglBindBufferglVertexAttribPointer。这些函数,就像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 subsequent glDraw* calls will use until you call glUseProgram again. Think of most OpenGL functions as poking at global state, because that's how it works. glUseProgram is setting a global variable that glDraw* 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:

  • Establish the vertex attributes for rendering the object.
  • Set the current program, and change any per-object uniforms (matrices, etc).
  • Bind textures for the program, if any.
  • Bind other state for the program, if any.
  • Render.
  • Deactivate attributes

The first step uses glEnableVertexAttribArray, glBindBuffer, and glVertexAttribPointer. These functions, just like glUseProgram set global state. You should use glDisableVertexAttribArray after having rendered with the object, and you should unbind any buffers you may have used.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文