如何使用OpenGL 3.x的VBO来渲染动态世界?

9
尽管似乎OpenGL 3.x本身的最新参考资料很少,但实际的OpenGL低级操作相对简单。然而,我正在尝试概念化如何操作VBO以渲染动态世界,这让我遇到了严重的困难。
显然,旧的立即模式方式不适用了,但是从那里我应该怎么做呢?我是否需要编写某种场景结构,然后将其转换为一组顶点并将其流式传输到VBO中?我如何存储平移数据?如果是这样,代码上会是什么样子?
基本上,我真的不确定该怎么继续下去。
3个回答

9
如果你的整个世界都是真正动态的,你可以使用GL_STREAM_DRAW_ARB使用标志并在每帧重置数据。不要费力去操纵它,只需尽可能高效地进行流式传输即可。
然而,我假设您有一个由多个刚体对象组成的场景,这些对象相对移动。在这种情况下,为每个对象使用一个VBO,并指定GL_STATIC_DRAW_ARB使用标志。然后,您可以为每个对象实例设置模型视图变换,并使用每个实例的一个绘制调用进行渲染。
在PC上的一个经验法则是每MHz CPU发出不超过一个绘制调用。这是一个粗略的估计,但其中有些真理。如果您保持在此限制以下,则不必担心将多个独立对象放入单个VBO或其他性能技巧。

1
“+1” 因为批处理在当前图形编程中是最大的问题。尽管在 OpenGL ES iPhone 编码中,每 MHz 的绘制调用似乎对我来说过于乐观了 - 我发现为了保持不错的帧率,我只能使用 30-40 批处理! - tsalter
好的,我应该补充一下,这个规则可能不适用于移动设备。我几年前在一些GDC幻灯片上看到过它,而且在Windows PC上它对我很有帮助。 - Malte Clasen
顺便说一下,我刚刚在这里找到了幻灯片:http://ati.amd.com/developer/gdc/D3DTutorial3_Pipeline_Performance.pdf(主要是D3D9,第28页上有OpenGL) - Malte Clasen

9

简短回答:

使用glMapBufferRange,仅更新需要修改的子范围。

详细回答:

技巧在于使用glMapBufferRange映射已经存在的缓冲区,然后只映射需要的范围。基于以下假设:

  • 您的几何体使用每个顶点动画变形
  • 模型的顶点数量在动画期间保持不变。

那么您可以使用glMapBufferRange仅更新发生变化的部分,而将其余数据保留不变。使用glBufferData进行完整上传速度很慢,因为它会删除旧的存储器并分配新的存储器。这还加上了上传新数据。 glMapBufferRange仅允许您读取/写入现有数据,它不进行任何分配或解除分配。

但是,如果您使用骨架动画,则应该将每个顶点的变换作为4x4矩阵传递到顶点着色器中,并在那里进行计算。每个顶点的数据当然使用glVertexAttribPointer指定。

此外,请记住,您可以在顶点着色器中读取纹理数据,并且OpenGL 3.1引入了一些新的实例绘制调用;glDrawArraysInstancedglDrawElementsInstanced。这两个结合可以用于特定实例的查找。也就是说,您可以使用相同的几何数据绑定进行实例绘制调用,但将位置或任何需要的每个顶点数据作为纹理或纹理数组发送。这可以避免混合和匹配不同的顶点数组数据集。

想象一下,如果您想要呈现100个相同模型的实例,但具有不同的位置或颜色方案。甚至是纹理贴图。


5
使用VBO并不意味着您必须仅使用单个绘制调用渲染整个场景。您仍然可以发出多个绘制调用,并在途中设置不同的变换矩阵。
例如,如果您正在使用场景图,则场景图中的每个模型都可以对应一个单独的绘制调用。在这种情况下,使用VBO的最简单方法是为每个模型创建一个单独的VBO。
作为一种优化,您可能能够将几个模型合并到单个VBO中,然后在进行绘制调用时传入非零偏移量;这会从VBO中取出正确的模型。将多个绘制调用合并到单个绘制调用中也是可取的,但如果它们需要具有独立的变换,则不可能实现。(实际上,在某些情况下,如果使用实例化或顶点混合,则可以实现,但我建议先了解基础知识。)

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