问题描述
我目前正在使用带有几何着色器的 openGL 在渲染过程中获取点并将它们转换为三角形。
我有 n 个点列表,每个点将呈现为 n 个三角形(第一个点列表每个变成一个三角形,第二个变成两个三角形,等等)。我已经尝试为这些列表中的每一个交换几何着色器,其中 max_vertices 是每个列表的最小值。使用 OpenGL,我似乎无法控制最终如何通过几何着色器在 GPU 上实现这一点,有些驱动程序似乎处理得非常慢,而其他驱动程序处理得非常快。
有什么方法可以最佳地执行此特定任务,理想情况下是利用我知道每个元素所需输出三角形的确切数量和总数这一事实?如果可能的话,我很乐意为此使用几何着色器的一些替代方法。如果 Vulkan 能成功,我也很乐意尝试它。
解决方法
您想要的是几何的任意放大:采用一个点基元并从中产生任意多个完全独立的基元。 GPU 拥有的工具是几何着色器(或者只是使用计算着色器手动生成顶点数据,但这可能不会更快,而且肯定会消耗更多内存)。
虽然 GS 的性能并不出名,但有一种方法可以让您加快工作速度。由于特定调用中的所有基元将生成特定数量的基元,因此您可以通过使用顶点 instanced rendering 来避免让每个 GS 输出多个基元。
在这里,您使用 glDrawArraysInstanced
。您的 VS 需要将 gl_InstanceID
传递给 GS,GS 可以使用它来确定从顶点生成哪个三角形。也就是说,GS 只生成一个三角形,而不是在 n
上循环生成 n
三角形。但是它被调用了 instanceCount
次,并且每次调用都应该生成第 gl_InstanceID
个三角形。
现在,这样做的一个缺点是生成的三角形的顺序会有所不同。在您的原始 GS 代码中,每个 GS 从一个点生成所有三角形,来自一个点的所有三角形将被渲染,然后从另一个点渲染任何三角形。通过顶点实例化,您可以从所有点获得一个三角形,然后从所有点生成另一个三角形,依此类推。如果渲染顺序对您很重要,那么这将不起作用。
如果这很重要,那么您可以改用 geometry shader instancing。这与顶点实例化类似,只是实例计数是 GS 的一部分。每个 GS 调用只负责一个三角形,您可以使用 gl_InvocationID
来决定在哪个三角形上使用它。这将确保来自一组 GS 实例的所有基元将在来自另一组不同 GS 实例的任何基元之前被渲染。
缺点就是我所说的:实例计数是GS的一部分。与实例化渲染不同,实例的数量被烘焙到 GS 代码本身中。因此,您将需要一个单独的程序来处理每个 三角形数。 SPIR-V specialization constants 使您可以更轻松地构建这些程序,但您仍然需要维护(和交换)多个程序。
此外,虽然实例化渲染对实例数量没有限制,但 GS 确实有限制。而且这个限制可以小到 32(这是一个 very popular number)。