用Microsoft DirectX光线跟踪改善渲染质量
Implementing Stochastic Levels of Detail with Microsoft DirectX Raytracing
细节层次(LOD)是指在细节可能不重要的情况下,用较低分辨率的网格替换远处的高分辨率网格。这种技术有助于减少内存占用和几何混叠。最重要的是,长期以来一直被用于提高游戏中的光栅化性能。但这是否同样适用于光线跟踪?
光栅化的渲染时间为O(N),其中N是三角形的数量,而光线跟踪的渲染时间明显优于O(logn)。因此,可能希望LOD对光线跟踪不那么重要,但仍然可以提供有价值的性能改进。
在这篇文章中,将探讨这个问题。此外,还演示了使用Microsoft DirectX光线跟踪(DXR)API实现随机详细等级的一种可能方法。假设对DXR有基本的了解。有关更多信息,请参阅以下参考资料:
- Introduction to NVIDIA RTX and DirectX Ray Tracing
- DX12 Ray Tracing Tutorial Part 1
- DirectX Raytracing (DXR) Functional Spec (Microsoft)
- D3D12 Raytracing Samples (Microsoft)
Figure 1. Screenshot from the DXR sample provided with this post. LODs are beneficial in open scenes like this with large depth ranges. Color indicates the LOD level. The most distant models are rendered with 128 times fewer triangles than the closest ones (eight LODs, 2x reduction in triangle count between each level).
Stochastic LODs
随机LOD是游戏用来在LOD之间创建更平滑过渡的主要技术。例如,在虚幻引擎4中,随机LOD称为抖动LOD转换。如果没有随机技术,lod之间的突然、离散转换可能会导致分散注意力的“弹出”伪影,即对象在外观上突然移动。随机LOD通过将像素随机分配给更近或更远的LOD,在LOD级别之间创建交叉融合(图2)。有关更多信息,请参见查找下一代:CryEngine 2。《三维图形与游戏课程中的SIGGRAPH高级实时渲染》,97-121页。
Figure 2. Stochastic LOD with eight steps of transition with color encoding (top row) and without (bottom row). The transition has been exaggerated (64x reduction in triangle count) for this figure to make it easier to see the transition. Click image to enlarge.
LOD in DXR
为了实现离散的LOD转换,根据每个对象到相机的距离,为其选择一个LOD。距离较远的对象使用较低分辨率的网格,距离较近的对象使用较高分辨率的网格。
在示例中,使用对象在相机空间中的深度来计算LOD参数,l在深度z_n处从0持续变化到深度z_f处的l,其中l是对象的LOD数。z_n和z_f是根据给定场景的特征选择的,很像光栅化中深度缓冲区的近距离和远距离。有关计算LOD参数的详细信息,请参见示例代码中的ChooseLod函数。然后直接使用给定对象的LOD参数选择其网格。
由于LOD参数随相机或对象的移动而变化,因此必须为每帧的每个对象重新计算LOD参数。然后,使用指向正确网格lod的更新指针重建*加速结构(TLAS)。游戏通常会在每帧重建TLAS来处理动态对象,因此这种重建不会产生任何额外的开销。
Secondary rays and LOD
大多数支持光线跟踪的游戏使用前面描述的离散LOD方法,因为既简单又有效。关键的是,还匹配许多混合渲染器中光栅化过程的LOD选择行为。混合渲染是游戏中常见的一种方法,其中带有主要命中数据的G缓冲区是使用传统的光栅方法生成的,而随后的光线跟踪过程则使用该数据作为计算反射或阴影等次要效果的起点。
无论主要命中是通过光栅化过程还是通过光线跟踪生成的,这都是需要记住的一件重要事情:任何从主要命中开始的次要光线都必须“看到”与主要光线(或光栅化器)相同的对象lod。如果不能保证这一点,则意味着由于主光线和次光线之间的场景表示不匹配,次光线可能会错过交点或找到虚假的命中(图3)。这反过来又会导致视觉伪影,如光线泄漏、反射中的对象消失或阴影区域不正确。
主光线和次光线在被主光线击中的对象的LOD级别上达成一致是不够的。其不相关的对象可能离主要攻击点很近,也会导致前面提到的健壮性问题,因此这些对象也必须是一致的。这个问题也不限于从主光线到次光线的过渡。也适用于更高级别的反弹,例如反射的反射、多反弹全局照明等。
Figure 3. Spurious shadows resulting from using a higher LOD for primary rays (LOD0) and lower LOD for secondary rays (LOD1).
也就是说,在某些情况下,如果应用程序故意违反一条路径上的所有光线都应看到相同的lod的原则,则可能是有益和可接受的。例如,可以想象根据场景的高细节表示来跟踪一条较短、因而相对便宜的次光线。如果短光线未命中,则可以从第一条光线的终点开始跟踪另一条较长的光线,但该光线的细节要低得多。
根据不同的因素,比如短射线和长射线之间的阈值距离以及场景细节的差异,这种方法可能会在性能上获得回报。但是,由于场景lod在短光线和长光线之间变化,应用程序必须接受无效或错过交点的可能性。因此,这种想法只有在某些误差可以接受的情况下才可行,例如非常漫反射效果或更高级别的光反弹。并没有在这篇文章中探索这条道路,而是想强调是进一步实验的起点。
Stochastic LOD transitions
对于随机LOD转换,对象可以处于两个LOD之间的中间状态转换。在TLAS中包含两个lod,但是需要一种方法来选择两个lod中的哪一个与每个像素相交。此外,该选择需要独立于每个对象进行,因为不同的对象可能位于LOD转换中的不同点上。
DXR支持在TLAS构建时指定一个8位实例掩码,然后将该掩码与每射线掩码组合,以确定是否应测试实例的交集或忽略实例。具体来说,这两个面具是安第斯在一起。如果结果为零,则忽略实例。使用此实例掩码功能在两个相邻的lod之间随机选择。
Transition interval
k_a=\min(0,lfloor l\rfloor)和k_b=\max(\lceil l\rceil,l-1)给出了过渡两边lod的索引,l的小数部分f=l-\lfloor l\rfloor给出了之间的过渡程度。
实际上,可能希望在较短的时间间隔内进行转换。如果在[0,1]中用t参数化转换间隔,其中0表示没有转换间隔,只是从一个级别到下一个级别的离散步骤,1表示转换发生在一个LOD的整个范围内,则可以将f修改为f'=\mathrm{clamp}((f-0.5)/t+0.5,0,1)。
图4显示了不同t值的f’。
Figure 4. Remapping the fractional part of the LOD parameter to shorten the duration of the transition, , from one LOD to the next.
Setting the instance masks
初始化了实例面罩,以便在每个光线面罩中设置一个随机位会产生所需的交叉溶解效果。对于每个对象,需要两个实例,一个用于LOD k_a,另一个用于k_b。将k_b的实例掩码中的位数(总共8位)设置为与转换度f'成比例。k_a的跃迁度是1-f’。通过补充k_b的掩码来设置k_a的实例掩码。
代码如下所示:
maskB = (1 << int((8 + 1)*f_prime) ) - 1;
maskA = (~maskB) & 0xFF;
在光线生成着色器中调用TraceRay时,将8位光线面罩设置为单个均匀随机位。这将根据各自的转换程度选择k_a或k_b。因为相邻光线可能使用不同的随机光线面罩,所以这会产生所需的随机过渡效果。
沿路径传播光线面罩
如前所述,当跟踪具有多个反弹的路径时,路径中的后续光线看到相同的lod是很重要的。为了使用随机方法实现这一点,对路径上的每条光线使用相同的光线面罩。DXR无法查询当前光线面罩,因此使用光线负载将其传播到最近的命中着色器。
Limitations
随机LOD实现在NVIDIA RTX GPUs上是完全硬件加速的,但是有一些限制。一个限制是实例和光线面罩仅由8位组成,这意味着对于随机LOD转换,只能得到8个级别(图2)。这一点越明显,移动越慢,lod之间的过渡间隔越宽。
另一个限制是,将实例和光线面罩用于随机详细等级会使无法用于其用途,例如启用或禁用某些几何体组。有时可以划分掩码位以适应不同的用例,但这进一步减少了转换级别的数量。
与离散LOD相比,随机LOD也会受到一些性能损失。通常,随机LOD会由于相邻像素对不同对象的跟踪而增加GPU的扭曲散度。实现的随机LOD还使TLAS中的实例数量增加了一倍,这稍微增加了TLAS的构建时间。
Stochastic
LOD example
图1显示了本文中包含的示例代码的屏幕截图。实现了光线跟踪反射、阴影和环境光遮挡。
场景中的对象根据其LOD着色,最高LOD为红色,最低LOD为蓝色。场景本身由几个实例armadillo模型组成。为模型的每个LOD建立一个底层加速结构(BLAS)。TLA是根据随机旋转的实例构建的,这些实例引用所选LOD的BLAS。
可以使用多个设置调整LOD参数。LOD范围(z_n和z_f)以标准化坐标表示,其中值1表示整个场景的半径。LOD偏移将偏移计算的LOD参数。
当相机静止时,帧累积到输出中以提供抗锯齿。每个子帧都有自己的随机种子。在多个帧上,随机LOD的累积产生两个LOD之间的混合。为了更好地看到随机LOD的效果,请暂停子帧。若要查看各个过渡级别,请将“过渡间隔”扩展到1并缓慢移动“细节层次偏移”滑块。这就是创建图2的方式。
示例中使用的LOD网格是使用QSlim生成的,QSlim是一个命令行实用程序,将OBJ格式的输入网格减少为指定数量的三角形。可能想用QSlim来试验自己的模型。 实施细节
大多数示例代码都是直接的DirectX 12。从DX12光线跟踪教程第1部分开始编写代码,但只保留了几个用于着色器绑定表布局和为光线跟踪管道创建状态对象的助手类。 LOD选择步骤在GPU上执行。为了简单起见,使用光线生成着色器而不是计算着色器来实现。这避免了设置另一个管道状态对象的需要。由于LOD选择和渲染之间共享了一些参数,因此将所有参数放在一个公共的常量缓冲区中。
LOD selection明暗器填充设备上的实例描述符数组,然后用于构建tla。相关的C++结构体D3D12MayRracTraceInStasySDEC使用了几个位字段。HLSL不支持位字段,因此必须手动将字段打包到uint中。
Performance
为了评估性能,提供了两种基准测试模式,可通过按以下数字键调用:
1: 浏览多个预定义视图。 Sweep through
several predefined views.
2: 扫描过渡间隔的范围。 Sweep through the range of transition
intervals.
图5显示了第一种模式的视图。第一个视图是从空中透视整个场景。随后的视图与平面的角度越来越小。
Figure 5. The four views used in the
benchmark: “top” (upper-left), “high” (upper-right), “low” (lower-left), and
“close” (lower-right).
图5显示了在NVIDIA GeForce RTX 2080Ti上获得的渲染时间,该RTX有64×60个实例和8个lod。渲染时间因视图而异。图6还显示了LOD可以实现显著的加速:对于几乎所有对象都以低LOD渲染的俯视图,可以达到2.2x。
Figure 6. Render times and speedups
for each view.
正如预期的那样,随机LOD比离散LOD稍慢,加速比更低。帧时间、LOD选择时间和TLAS构建时间的其组件独立于视图。看到平均LOD选择时间为0.018ms,TLAS构建时间为0.22ms。
图7显示了改变转换间隔的效果。此实验有助于分离相邻光线使用不同的光线面罩跟踪不同对象的影响。
Figure 7. The effect of varying the
transition interval for stochastic LOD on the “low” view.
随着跃迁间隔的增加,光线之间的发散量也会增加,这会对性能产生负面影响。但是,最高约为10%,这一成本是有限的。当过渡间隔为0时,渲染结果等效于离散LOD。唯一的区别是在TLAS中每个对象仍然有两个实例,而不是一个。这只占渲染时间的1.6%。
结论
源码地址:https://developer.download.nvidia.com/devblogs/DxrLod.zip
有了这篇文章和附带的示例代码,展示了一个直接的、离散的LOD机制,正在各种航运游戏中使用。表明,根据具体情况,这种机制所提供的加速效果是显著的。还演示了在DXR中实现完全硬件加速的随机LOD方法的一种可能方法,该方法在对性能影响很小的情况下显著减少了离散LOD的弹出伪影。
鼓励在这里下载示例的源代码和模型并使用。快乐的实验!