GL_ARB_vertex_attrib_binding 和索引绘图
GL_ARB_vertex_attrib_binding and indexed drawing
我正在尝试在 OpenGL 中渲染类似 Minecraft 的世界。世界被分成块(每个块包含 N^3 个块)并且这些块在每一帧渲染。
目前我每个块使用一个 VAO。我一直认为这有点奇怪,因为每个块都使用相同的顶点格式 - 为什么我不能使用单个 VAO,然后通过更改索引和顶点缓冲区绑定来绘制每个块?
最近我遇到了核心 4.3 中的 GL_ARB_vertex_attrib_binding 扩展,它似乎解决了这个问题——VAO 和 VBO 绑定现在可以分开了。但是没有提到索引缓冲区绑定,它们仍然是 VAO 状态的一部分,使得扩展对索引渲染毫无用处。我唯一能想到的是在渲染时为每个块和顶点缓冲区绑定更改更新绑定索引缓冲区的内容,但这没有什么意义。
我的问题是,我是否正确理解扩展程序的作用?如果我这样做,是否有另一种方法可以用来避免每块 VAO,而无需将每块索引缓冲区合并到一个大 IBO(因为它会使块流管理不必要地复杂化)?
您有...部分误解了顶点属性绑定的目的。
是的,它创建了 separation between the vertex format and the buffer bindings。然而,这种分离只是在API的层面。也就是说,您具有设置格式的 glVertexAttribFormat 和设置缓冲区的 glBindVertexBuffer 等函数。
但是所有这些函数仍然修改 VAO 状态。
但是,无需更改 VAO 只是因为您要更改 个 VAO 中的状态。顶点属性绑定存在的原因是因为改变顶点格式状态是昂贵的。而更改缓冲区绑定状态则不是(或者至少,它不像 那样 昂贵)。所以调用 glBindVertexBuffer
比调用 glVertexAttribFormat
.
更便宜
这就是该功能存在的原因:这样您就不会为了执行廉价操作而进行昂贵的调用。
那么你想做的就可以了;您可以只绑定 VAO,然后根据需要进行 glBindVertexBuffer
和 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER)
调用。是的,这些将修改 VAO 中的状态,但那又怎样?只要您不触及 VAO 中的顶点格式,就可以了。
也就是说,如果您的目标是性能,您可能无论如何都不应该更改每个块的缓冲区绑定状态。将所有块(或至少是大组块)放入同一缓冲区的不同区域。块组的所有索引同样应该存在于单个缓冲区中。索引应该相对于该块数据的开始,因此您应该能够使用 16 位 GLshort 索引。
当需要渲染特定块时,使用 BaseVertex-based rendering 到 select 您想要渲染的单个块。 base_vertex
用于提供索引的偏移量。此偏移量应基于块的最大大小。
这样,要用一组块进行渲染,您可以最大限度地减少缓冲区绑定操作的数量。从而最大限度地减少绘制调用之间的状态更改次数。
我正在尝试在 OpenGL 中渲染类似 Minecraft 的世界。世界被分成块(每个块包含 N^3 个块)并且这些块在每一帧渲染。
目前我每个块使用一个 VAO。我一直认为这有点奇怪,因为每个块都使用相同的顶点格式 - 为什么我不能使用单个 VAO,然后通过更改索引和顶点缓冲区绑定来绘制每个块?
最近我遇到了核心 4.3 中的 GL_ARB_vertex_attrib_binding 扩展,它似乎解决了这个问题——VAO 和 VBO 绑定现在可以分开了。但是没有提到索引缓冲区绑定,它们仍然是 VAO 状态的一部分,使得扩展对索引渲染毫无用处。我唯一能想到的是在渲染时为每个块和顶点缓冲区绑定更改更新绑定索引缓冲区的内容,但这没有什么意义。
我的问题是,我是否正确理解扩展程序的作用?如果我这样做,是否有另一种方法可以用来避免每块 VAO,而无需将每块索引缓冲区合并到一个大 IBO(因为它会使块流管理不必要地复杂化)?
您有...部分误解了顶点属性绑定的目的。
是的,它创建了 separation between the vertex format and the buffer bindings。然而,这种分离只是在API的层面。也就是说,您具有设置格式的 glVertexAttribFormat 和设置缓冲区的 glBindVertexBuffer 等函数。
但是所有这些函数仍然修改 VAO 状态。
但是,无需更改 VAO 只是因为您要更改 个 VAO 中的状态。顶点属性绑定存在的原因是因为改变顶点格式状态是昂贵的。而更改缓冲区绑定状态则不是(或者至少,它不像 那样 昂贵)。所以调用 glBindVertexBuffer
比调用 glVertexAttribFormat
.
这就是该功能存在的原因:这样您就不会为了执行廉价操作而进行昂贵的调用。
那么你想做的就可以了;您可以只绑定 VAO,然后根据需要进行 glBindVertexBuffer
和 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER)
调用。是的,这些将修改 VAO 中的状态,但那又怎样?只要您不触及 VAO 中的顶点格式,就可以了。
也就是说,如果您的目标是性能,您可能无论如何都不应该更改每个块的缓冲区绑定状态。将所有块(或至少是大组块)放入同一缓冲区的不同区域。块组的所有索引同样应该存在于单个缓冲区中。索引应该相对于该块数据的开始,因此您应该能够使用 16 位 GLshort 索引。
当需要渲染特定块时,使用 BaseVertex-based rendering 到 select 您想要渲染的单个块。 base_vertex
用于提供索引的偏移量。此偏移量应基于块的最大大小。
这样,要用一组块进行渲染,您可以最大限度地减少缓冲区绑定操作的数量。从而最大限度地减少绘制调用之间的状态更改次数。