【问题标题】:OpenGL -- Render multiple meshes, with individual transformations, at onceOpenGL——一次渲染多个网格,带有单独的变换
【发布时间】:2014-06-13 05:51:47
【问题描述】:

我正在寻找一种同时渲染多个网格的方法,这样我就不必为每个网格发出绘制调用。我在这里处理的是 2D 渲染,一个典型的对象,比如一个正方形,里面可能只有两个三角形。但是,一个对象也可能非常复杂,有数千个三角形。

现在每个对象都可以自行移动。从概念上讲,为每个“对象”设置一个 VBO(或 VBO/IBO 对)是完全合理的:只要对象不发生变化,我每帧必须上传到 GPU 的只是变换信息:一个位置向量和一个方向值。或者,等效地,一个变换矩阵。

但这种方法的问题在于,对于 1000 个方形对象的场景,我需要初始化 1000 个 VBO 和 1000 个 IBO,并且为了渲染 2000 个三角形,需要进行 1000 个绘制调用,每帧设置 1000 套制服。

好的。如果所有这些对象都相同,我可以有一个 VBO/IBO 来描述它们,设置一个统一缓冲区对象(或者统一数组更合适——我仍然需要学习如何使用这些)和转换数据它们中的每一个,并发出一个实例化绘制调用,让顶点着色器通过使用它接收到的实例编号从 UBO 中提取转换数据。伟大的。

我只想更进一步。我想做相当于在不相同的网格上实例化的事情:我有 1000 个不同的对象,我很高兴在 1000 个单独的顶点/索引缓冲区对或一对巨大的顶点/索引缓冲区中描述它们。我想一次性将他们的转换数据发送到 GPU。只需让驱动程序/GPU 绑定或选择正确的顶点即可。

这可以吗?可以在不使用 SM4 几何着色器的情况下完成吗?

更新:我只是想到了一种可能的方法来实现这一点。我使用顶点属性作为我的“实例化”值,用它来索引包含转换的 UBO。这是这样做的方法吗?

【问题讨论】:

  • 这有点过早优化的味道。是什么让你认为你需要这样做,你不能只是以正常方式渲染这些“1000 个对象”?
  • 我可能不需要它。但我有兴趣学习如何以最好的方式使用这项技术来实现我的目的。我希望能够拥有非常多的对象,如果我可以通过一次调用将它们全部绘制出来,我将获得的不仅仅是避免函数调用开销的好处,我还可以获得视口剔除,而且我确信还有其他一些事情,免费。
  • 这如何给您视口剔除?实例化不会剔除任何东西;它不能。事实上,实例化的全部意义在于减少 CPU 开销,因此您尝试尽可能少地对每个实例进行处理。即使是快速的截锥体剔除也应该避免。
  • 你说得对。我想我的意思是天真的方法可以让我手动剔除,这实际上是一个优点而不是缺点。不知道为什么我在那里提到它。好的——每个对象都有一个顶点缓冲区(对)肯定有很大的好处。这样,如果我删除了一个对象,我就可以删除它的缓冲区,而无需更多的内务处理。所以,我现在正在寻找的是一种无需显式调用即可发出缓冲区绑定命令的方法。不可能?
  • 这并不能解释为什么您认为以正常方式绘制“1000 个对象”将是一个重大的性能问题。你试过了吗?是瓶颈吗?这些物体到底有多大?您尝试过哪些渲染方式?

标签: opengl geometry vertex-buffer geometry-instancing


【解决方案1】:

每个对象不需要一个 VBO。只需将所有对象连接到一个 VBO 或一小组 VBO 中。您可以在该 VBO 中寻址,或者向gl*Pointer 函数的数据参数添加偏移量,或者使用glDrawElementsBaseVertex 在绘制时添加偏移量。连接所有小对象的索引数组,而不是索引数组中只有 4 个索引。如果您正在使用一些条形或扇形原语,您可以使用glPrimitiveRestartIndex 设置一个特殊索引,遇到该索引时将启动一个新原语。

这样,您只需按使用的材质设置以及整体转换和着色器参数(即纹理、着色器、着色器制服)来拆分渲染调用。

【讨论】:

  • 如果你有很多对象,你最终会做很多glDrawArrays/glDrawElements调用——每个对象一个。根据我的经验,即使不更改统一变量或纹理和着色器程序,每帧超过几百个也会变得非常慢。我想知道 OpenGL 功能中是否有一些东西可以让您一次绘制许多具有完全不同网格的元素。
  • @axel22:如果一切都做对了,渲染时的实际瓶颈是第一个 Fillrate,即绘制操作对帧缓冲区和纹理从记忆。第二个是三角形计数。 glDraw… 调用具有一定的开销,因此单个 glDraw… 调用触发的渲染批处理越大越好。您将不得不对您的特定程序进行概要分析,但是对于批量大小来说,一个好的数字大约在 0.5k 到 2.5k 三角形之间。
  • 在我的例子中,每个网格的三角形数量大约为 1200,我渲染大约 1500-2500 个这样的网格,具体取决于场景。我发现如果我将所有这些网格复制到一个巨大的 VBO 中一次并在每一帧上调用 glDrawArrays,则动画在 FPS 高于 60 时非常流畅。当我通过调用 glDraw* 分别渲染每个网格时,帧速率会下降到 cca 5-15 FPS(在 GTX 650 Ti 上)。我的结论是我应该尽可能少地打电话给glDraw*。我的问题是,每当场景发生变化时,我都必须将所有内容复制到一个大 VBO 中,这需要 100 多毫秒,从而导致故障。
  • @axel22:更改 VBO 中的数据需要进行哪些更改?这可能是另一个严重的瓶颈。
  • 在一个大 VBO 的情况下,我只需复制所有网格实例的顶点数据(位置、法线和 tex 坐标,我在其中转换每个顶点的位置)。我为 cca 1500 个实例执行此操作,其中每个网格都有 cca​​ 1200 个三角形(占用 cca 100 毫秒)。在多个glDraw* 调用的情况下,改变数据的更改将为每个实例绑定不同的统一(例如转换矩阵)。实际上,我什至还没有这样做——出于测试原因,我只是在具有相同着色器程序#instances 次的单个网格上调用了glDraw*。
猜你喜欢
  • 2021-10-30
  • 2016-04-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多