2

我是图形编程新手,需要为我们正在创建的演示添加渲染后端。我希望你们能指出我正确的方向。

简短版本:有没有办法向 OpenGL 发送不同元素的数据数组,而不必为每个元素分别发出绘制命令?

长版:我们有一个 CUDA 程序(最终将是 OpenCL),它为我们计算一堆对象的一堆数据。然后我们需要使用例如 OpenGL 来渲染这些对象。

CUDA 内核可以生成我们的顶点,并且使用 OpenGL 互操作,它可以将这些顶点推送到 OpenGL VBO 中,而不必将数据传输回主机设备内存。但问题是我们有一堆(超过一百万是我们的目标)不同的对象。看起来我们最好的选择是分配一个 VBO 并将每个对象的顶点放入其中。然后我们可以调用 glDrawArrays 并使用该 VBO 中每个元素的偏移量和长度。

但是,每个对象可能有可变数量的顶点(尽管场景中的总顶点可以有界。)我想避免必须从 CUDA -> CPU 每帧传输开始索引和长度的列表,特别是给定这些绘图命令会直接返回到 GPU。

有没有办法用数据打包缓冲区,这样我们就可以只调用一次 OpenGL 来渲染缓冲区,并且它可以从该缓冲区渲染许多不同的元素?

(希望我也提供了足够的信息来避免这里出现 XY 问题。)

4

2 回答 2

3

一种方法是不要将它们理解为单独的对象,而是使它们成为使用单个绘图调用绘制的单个大对象。问题是,将对象彼此区分开来的数据是什么,这意味着您在各个调用之间更改了glDrawArrays/glDrawElements什么?

如果它是简单的东西,比如颜色,那么提供一个额外的每个顶点属性可能会更容易。通过这种方式,您可以使用单个绘图调用将所有对象渲染为一个大对象,并正确着色各个子对象(现在实际上仅在概念上存在)。附加属性的内存成本可能非常值得。

如果它有点复杂(比如纹理),您仍然可以使用额外的每个顶点属性来索引它,或者是纹理数组的索引(因为纹理数组应该支持 CUDA/OpenCL-硬件)或纹理坐标到单个大纹理的特定子区域(所谓的纹理图集)。

但是,如果这些对象之间的区别更复杂,例如不同的着色器或其他东西,您可能真的需要渲染单个对象并进行单独的绘制调用。但是您仍然不需要往返于 CPU。通过使用ARB_draw_indirect扩展(我认为这是自 GL 4.0 以来的核心,但可能在 GL 3 硬件(以及因此 CUDA/CL 硬件)上受支持,不知道),您可以将参数glDrawArrays/glDrawElements从一个额外的缓冲区(您可以像任何其他 GL 缓冲区一样使用 CUDA/CL 将其写入其中)。因此,您可以在 GPU 上组装每个单独对象的偏移长度信息,并将它们存储在单个缓冲区中。然后你做你的glDrawArraysIndirect循环偏移到这个单一的绘图间接缓冲区(各个对象之间的偏移现在是恒定的)。


但是,如果发出多个绘制调用的唯一原因是您想将对象渲染为单个GL_TRIANGLE_STRIPs 或GL_TRIANGLE_FANs(或者,上帝当心,GL_POLYGONs),您可能需要重新考虑只使用一堆,GL_TRIANGLES以便您可以渲染所有对象一个draw call。使用三角形带所节省的(可能)时间和内存可能会因多次绘制调用的开销而超过,尤其是在渲染许多小三角形带时。如果您真的想使用条带或扇形,您可能需要引入退化三角形(通过重复顶点)以将它们彼此分离,即使使用单个绘制调用绘制也是如此。或者您可以查看glPrimitiveRestartIndexGL 3.1 引入的功能。

于 2012-05-22T09:03:01.057 回答
0

可能不是最优的,但你可以在整个缓冲区上制作一个 glDrawArray ......

  • 如果你使用 GL_TRIANGLES,你可以用零填充你的缓冲区,并且只在你的内核中写入需要的顶点。这样,缓冲区的“空”区域将被绘制为 0 面积多边形(= 退化多边形 -> 根本不绘制)
  • 如果你使用 GL_TRIANGLE_STRIP,你可以这样做,但是你必须复制你的第一个顶点才能在 (0,0,0) 和你的网格之间制作一个假三角形。

这似乎有点矫枉过正,但是:-无论如何,您都必须能够处理尽可能多的顶点-退化三角形不使用填充率,因此它们几乎是免费的(尽管顶点着色器仍在计算中)

一个可能更好的解决方案是改用 glDrawElements :在内核中,您还为整个缓冲区生成一个索引列表,这将能够完全跳过缓冲区的区域。

于 2012-05-22T07:33:34.827 回答