先了解一些时域抗锯齿的方法:
TAA:
抖动
TAA 的主要原理是跨帧计算多个子像素样本,然后将它们组合成一个最终像素。最简单的方案是在像素内生成随机样本,但有更好的方法来生成固定序列的样本。选择一个好的序列以避免聚集非常重要,并且在序列中选择离散数量的样本:通常 4-8 个样本效果较好。在实践中,这对于静态图像比动态图像更重要。下面是一个具有 4 个样本的像素。
为了在像素内生成随机子样本,我们将投影矩阵沿视锥平面平移一个像素的一部分。抖动偏移的有效范围(相对于像素中心)是屏幕尺寸的像素数的倒数的一半,因此 [−1/2, 1/2]w 和 [−1/2, 1/2]h。
解析
下一阶段是解析过程。我们将收集样本并将它们合并在一起。解析过程可以采取两种形式,使用累积缓冲区或多个过去的缓冲区。累积缓冲区存储多个帧的结果,并通过混合当前抖动帧的一小部分(例如 10%)来每帧更新。这对于静态相机应该足够了。
鬼影
我们之所以会看到拖尾现象,是因为我们在当前帧的位置采样了前一帧。结果是图像的叠加,随着我们积累新帧而逐渐消失。由于问题是由相机移动引起的,让我们首先解决这个问题。对于不透明物体,相机运动相对简单,因为我们知道它们的世界空间位置可以使用深度缓冲区和相机投影的逆矩阵重建。这个过程称为重投影,涉及以下步骤:
- 从当前相机 C 生成的当前深度缓冲区读取深度
- 使用视图投影矩阵的逆矩阵进行反投影,将我们的屏幕空间位置转换为世界空间
- 使用先前的视图投影矩阵投影到先前相机 P 的屏幕空间
- 将屏幕空间位置转换为 UV 并采样累积纹理
深度拒绝
深度拒绝的想法是,我们可以假设深度值非常不同的像素属于不同的表面。为此,我们需要存储前一帧的深度缓冲区。这对于第一人称射击游戏效果很好,因为枪和环境相距很远。然而,在多种场景中可能会出错,例如植被或具有大量深度复杂性的噪声几何体。
模板拒绝
模板拒绝是一种定制的解决方案,适用于有限的内容集。其想法是用不同于背景的模板值标记“特殊”对象。这可以是主角、汽车等。为此,我们需要存储前一帧的模板缓冲区。在解析时,我们丢弃任何具有不同模板值的表面。需要特别注意避免硬边。
速度拒绝
基于速度拒绝表面在我看来更为稳健,因为根据定义,遮挡是由两个对象相对于相机的相对运动差异引起的。如果两个表面在两帧之间具有非常不同的速度,那么要么加速度很大,要么对象以不同的速度移动,并且一个突然变得可见。为此,我们需要存储前一帧的速度缓冲区。过程如下:
- 读取当前速度
- 使用速度确定先前的位置
- 将位置转换为 UV
- 读取先前的速度
混合因子衰减
这会在某些情况下修改混合因子。UE4 提到他们检测到即将发生的限制事件并减少混合因子。然而,这会重新引入抖动,必须谨慎处理。
强度/颜色加权
由于闪烁的原因是连续邻域中的高差异,强度加权试图衰减强度高的像素。这会稳定图像,但会以镜面高光为代价(它们变得更暗,因此对于像闪烁的沙子之类的东西,可以在 TAA 之后增强强度或在 TAA 之后添加它们)。对数加权将颜色转换为对数空间然后再进行任何线性操作,这偏向于低强度值。
纹理模糊
纹理模糊是 TAA 的另一个点。纹理在 mipmapping 过程中已经被模糊化,运行时被调整为选择适当的 mipmap,以最小化锯齿同时保持细节清晰。屏幕空间中的抖动在纹理空间中导致进一步的模糊。
透明度
透明度是一个棘手的问题,因为透明对象通常不会渲染到深度。低分辨率效果如烟雾和灰尘通常不受 TAA 影响,颜色限制也能很好地完成任务。然而,更一般的透明度如玻璃、全息图等,如果没有正确重投影,可能会受到影响并显得很差。有几种解决方案,这取决于内容:
- 将混合运动矢量写入速度缓冲区。这取决于内容,但它可以工作。实际上,即使将运动矢量写成实心物体也可以工作,如果对象的不透明度足够高的话。
- 引入每像素累积因子:这就是 UE4 所谓的“ResponsiveAA”。本质上,它会以像素抖动换取鬼影。对于非常详细的 VFX 很有用,如此处所示。
- 在 TAA 之后渲染透明度。这不推荐,除非你可能将它们渲染到一个离屏缓冲区,用边缘检测解决方案如 FXAA 或 SMAA 进行抗锯齿,然后合成回去。它可能会在边缘抖动,因为它是与抖动的深度缓冲区进行比较的。
相机切换
使用 TAA 时,相机切换会带来挑战。相机切换迫使我们使历史缓冲区无效,因为其内容不再代表当前渲染的帧。因此,我们不能依赖历史来生成一个漂亮的抗锯齿图像。这里有一些解决方法:
- 偏向收敛以加速过程。在相机切换后,我们需要尽快积累内容。
- 使用淡出和淡入。TAA 会在黑色部分积累,并在淡入时收敛。
- 在前几帧应用另一种形式的 AA 或模糊,以使收敛不那么突兀。
总而言之,这些都是一些权宜之计,但需要解决。另一个需要记住的是,帧率越高,这