如何使用 OpenGL 3.x VBO 渲染动态世界?
Posted
技术标签:
【中文标题】如何使用 OpenGL 3.x VBO 渲染动态世界?【英文标题】:How do I use OpenGL 3.x VBOs to render a dynamic world? 【发布时间】:2009-11-13 09:01:33 【问题描述】:虽然 OpenGL 3.x 本身的最新参考资料似乎很少,但 OpenGL 的实际低级操作相对简单。但是,我在尝试将如何操纵 VBO 以呈现动态世界的概念化时遇到了严重的麻烦。
显然,旧的立即模式方式不适用,但我该去哪里呢?我是否编写某种场景结构,然后将其转换为一组顶点并将其流式传输到 VBO,我将如何存储翻译数据?如果是这样,代码看起来如何?
基本上真的不确定如何继续。
【问题讨论】:
【参考方案1】:如果您的整个世界是真正动态的,您可以使用GL_STREAM_DRAW_ARB
使用标志并重置每一帧的数据。不要费心去操作它,只要尽可能高效地进行流式传输即可。
但是,我假设您的场景由多个相对于彼此移动的刚性对象组成。在这种情况下,为每个对象使用一个 VBO 并指定 GL_STATIC_DRAW_ARB
使用标志。然后,您可以为对象的每个实例设置模型视图变换,并使用每个实例的一次绘制调用来渲染它们。
经验法则(在 PC 上)是每 MHz CPU 发出不超过一个绘图调用。这是一个粗略的估计,但有一些道理。如果您保持低于此限制,则不必担心将多个独立对象放入单个 VBO 或其他性能技巧。
【讨论】:
+1 表示批处理是当今图形编码的最大问题。不过,在一些 OpenGL ES iPhone 编码之后,每 Mhz 1 个绘图调用对我来说似乎很乐观 - 我发现我被限制为 30-40 个批次以保持体面的帧速率! 好的,我应该补充一点,这条规则可能并不直接适用于移动设备。几年前,我在 GDC 的一些幻灯片上第一次看到它,它在 Windows PC 上为我提供了很好的服务。 顺便说一句,我刚刚在这里找到了幻灯片:ati.amd.com/developer/gdc/D3DTutorial3_Pipeline_Performance.pdf(主要是 D3D9,第 28 页上的 OpenGL)【参考方案2】:简答:
使用glMapBufferRange,只更新需要修改的子范围。
长答案:
诀窍是用 glMapBufferRange 映射已经存在的缓冲区,然后只映射您需要的范围。鉴于这些假设:
您的几何图形使用逐顶点动画变形 模型的顶点数在动画期间保持不变。然后您可以使用 glMapBufferRange 仅更新变化的部分,而保留其余数据。使用 glBufferData 的完整上传速度像乌龟一样慢,因为它们会删除旧的内存存储并分配新的存储。这是上传新数据的补充。 glMapBufferRange 只允许您读取/写入现有数据,它不会分配或释放。
但是,如果您使用骨架动画,不如将顶点变换作为每个顶点的 4x4 矩阵传递给顶点着色器,并在那里进行计算。每个顶点的数据当然是用 glVertexAttribPointer 指定的。
另外,请记住,您可以在顶点着色器中读取纹理数据,并且 OpenGL 3.1 引入了一些新的实例绘制调用; glDrawArraysInstanced 和 glDrawElementsInstanced。这些组合可用于特定于实例的查找。即您可以使用相同的几何数据绑定进行实例绘制调用,但发送位置或您需要的任何每个顶点数据作为纹理或纹理数组。这样可以避免混合和匹配不同的顶点数组数据集。
想象一下,如果您想渲染同一模型的 100 个实例,但具有不同的位置或配色方案。甚至纹理贴图。
【讨论】:
【参考方案3】:使用 VBO 并不意味着您必须只使用一次绘制调用来渲染整个场景。您仍然可以发出多个绘图调用,并在此过程中设置不同的转换矩阵。
例如,如果您使用的是场景图,则场景图中的每个模型都可以对应一个绘制调用。在这种情况下,使用 VBO 最简单的方法是为每个模型创建一个单独的 VBO。
作为一种优化,您可以将多个模型组合成一个 VBO,然后在进行绘图调用时传入非零偏移;这从 VBO 中提取了正确的模型。还希望将多个绘制调用组合成一个绘制调用,但如果它们需要具有独立的变换,则这是不可能的。 (实际上,如果您使用实例化或顶点混合,在某些情况下是可能的,但我建议先了解基础知识。)
【讨论】:
以上是关于如何使用 OpenGL 3.x VBO 渲染动态世界?的主要内容,如果未能解决你的问题,请参考以下文章