我最近在寻找关于OpenGL光线追踪的教程。大多数教程都使用计算着色器。我想知道为什么他们不直接渲染到纹理,然后将纹理作为四边形渲染到屏幕上。
使用计算着色器的方法与屏幕四边形相比有什么优缺点?
我最近在寻找关于OpenGL光线追踪的教程。大多数教程都使用计算着色器。我想知道为什么他们不直接渲染到纹理,然后将纹理作为四边形渲染到屏幕上。
使用计算着色器的方法与屏幕四边形相比有什么优缺点?
简短回答:因为计算着色器提供了更有效的工具来执行复杂的计算。
长回答:
也许在跟踪场景时,它们所提供的最大优势(就跟踪而言)是能够精确地控制在GPU上执行任务的方式。当您正在跟踪一个复杂的场景时,这一点非常重要。如果您的场景很简单(例如,Cornell Box),那么差异可以忽略不计。如果您将某些球体在片段着色器中进行跟踪,那么您将可以整天做这个操作。访问http://shadertoy.com/可以看到现代GPU和片段着色器可以实现的各种疯狂效果。
但是,如果您的场景和着色非常复杂,则需要控制如何进行工作。在四边形中进行渲染并在片段着色器中进行跟踪,最多只会使您的应用程序挂起,同时驱动程序会哭泣,改变其法定名称并移居世界的另一面……在最坏的情况下,会导致驱动程序崩溃。如果单个操作花费的时间太长,许多驱动程序将终止(在标准用法下几乎从不发生,但当您开始尝试跟踪1M多边形场景时,会非常快地发生)。
那么您在片段着色器中需要做的工作太多了……下一个逻辑步骤是什么?好吧,限制工作量。绘制较小的四边形以控制您一次跟踪屏幕上的多少内容。或者使用glScissor。使工作负载变得越来越小,直到您的驱动程序可以处理它。
我们刚刚重新发明了什么?计算着色器的工作组!工作组是计算着色器控制工作大小的机制,对于进行这种复杂任务时,它们是比片段级别的hackery(译注:hackery意思是“技巧”、“窍门”)更好的抽象。现在,我们可以自然而然地控制要分派多少条光线,并且我们可以这样做而不必紧密耦合到屏幕空间。对于简单的跟踪器,这增加了不必要的复杂度。对于“真正的”跟踪器,这意味着我们可以轻松地在抖动的网格上进行子像素射线投射以实现AA、每个像素进行巨量光线投射以进行路径跟踪等。
计算着色器的其他功能对于高效、工业级跟踪器很有用:
总的来说,现代GPU的架构是设计为更自然地使用计算处理这种任务的。个人而言,我使用MLT、kd-tree加速和许多其他计算密集型技术编写了一个实时渐进路径追踪器(PT已经非常昂贵)。我尽可能地保持在片段着色器/全屏四边形中。一旦我的场景足够复杂需要加速结构,我的驱动程序无论我采用什么技巧都开始出现问题。我重新实现了CUDA(虽然不完全相同于计算,但利用了同样的基本GPU架构进步),一切都很好。
如果你真的想深入了解,可以看看这里3.1节:https://graphics.cg.uni-saarland.de/fileadmin/cguds/papers/2007/guenther_07_BVHonGPU/Guenter_et_al._-_Realtime_Ray_Tracing_on_GPU_with_BVH-based_Packet_Traversal.pdf。坦白地说,对于这个问题最好的回答将是关于GPU微架构的广泛讨论,而我根本没有资格给出那样的回答。查看像上面那篇现代GPU追踪论文将使您了解性能考虑有多深入。
最后需要注意的是:在光线追踪复杂场景时,计算着色相比于片元着色能够带来任何性能优势与栅格化/顶点着色器开销/混合操作开销等无关。对于复杂着色的复杂场景,瓶颈完全在追踪计算方面,而正如讨论过的那样,计算着色器可以更有效地实现这些工具。