今天分享的是NVIDIA的优化版本TAA,也就是俗称的TXAA的实现分享,这里是原文地址
1. 基本思路
消除锯齿的两种思路,一种是预处理,另一种是增加采样点,但是这两种方案都有着各自的局限:

实际工作中,最常用的思路还是增加采样点,这种方法的弊端在于采样点数目过多会导致计算复杂度升高,帧率下降,因此一个非常常用的思路为将消耗分散到多帧,这就是所谓的TAA。

但是这种方法的一个问题在于,多帧的采样点并不一定是等价的(consistent),如果不做处理就会导致重影问题:


为了消除这个问题,大家提出了各种思路:深度判断,法线判断,材质ID判断等。但是这些方法都无法覆盖所有的case。传统的抗锯齿方案是对固定的几个采样点进行重用来消除锯齿,而TAA给出的解决思路对一个范围内的多个采样点按照一定的filter重建后的结果(通过多帧累加的方式完成)进行重用来实现更为稳定的锯齿方案:

如上图所示,通过多帧重用的方式,这里每帧只需要重用上一帧的一个像素即可,按照这种方式,就可以得到指数平均效果(exponential moving average, EMA)。通常本帧使用的权重为0.1,因为在
较小的情况下,EMA的表现就跟算数平均非常接近了,而且EMA作为一个平滑滤波器,能够有效滤除时间层面的高频噪声。
在TAA使用的过程中,我们需要根据当前像素的速度向量追踪到此像素在上一帧中的位置,但是由于各种情况的存在,按照这种方式追踪到的结果可能跟当前像素的结果并不一致,这时候需要给出一个判定准则来移除这种不准确的重用,常用的方案叫做neighborhood clipping,其基本思路给出如下:
举个例子,假如我们使用一个2x2像素宽的滤波算子对4x4个采样点进行混合,最终得到一个颜色值,最终得到的这个颜色值必定会处于由前面4x4个颜色值在颜色空间中组成的凸多边形中,处于简单考虑,如下图所示,用一个2D平面中的三角形表示这个凸多边形(实际上的凸多边形是由3D空间中的多个2D平面来定义的,而这些2D平面的数目则是随着采样点的数目与分布而变化的)。

将这些采样点滤波后输出的颜色值如下图所示:

而从之前多帧累加输出的颜色值与这个凸多边形的关系有如下两种:
- 如果此颜色值正好落于此凸多边形中(实际上可以忍受一定的误差),那么我们就可以使用之前TAA的计算公式将当前帧的滤波值与前面多帧的累加值进行累加输出
- 如果此颜色值落在此凸多边形之外,我们就不能直接重用,而是需要做进一步的处理


落在凸多边形之外的情况,最简单的处理方式就是将前面多帧的累加颜色与当前帧滤波颜色进行连线,之后求取此连线与凸多边形的交点作为此前多帧累加值的一个替代。

但是相交检测消耗过高,因此实际中通常是直接使用AABB Clipping的方案来近似:

但是这种做法的质量相对于相交检测而言会存在较大的偏差,从而导致重影问题,针对这个问题,NVIDIA团队提出了一种Variance Clipping的优化方案:

这个方案可以分成如下几步:
- 对这一帧的多个采样点(也就是前面举例中的4x4个采样点),求取其平均值与方差,m1就是平均值,而方差则需要通过
来求取。
- 以平均值为中心,以方差乘上一个系数
(这个值取1可以得到不错的效果,此数值越大,效果越稳定,但是重影问题也就越严重,此数值越小,就可能导致通过时间来抹平高频噪声的能力下降)为半径得到一个新的AABB,此外,为了避免新的AABB超出此前直接覆盖所有采样点得到的老的AABB,这里还会进行一次clamp操作:clamp(new AABB, older AABB)
- 使用新的AABB进行Clipping处理。
最终结果如下图所示:

这里是整个实现流程概览:

2. 相关问题
2.1 sub-pixel闪烁问题
这个方案的一个问题是,在实际情况中可能会出现variance在两帧之间发生剧烈变化的情况,比如从一个较大的方差(颜色分布比较分散)突然变化到一个较小的方差(颜色分布比较集中),虽然方差下降有利于降低重影瑕疵,但是如果这种情况在多帧之内反复发生(大->小->大->小),就可能导致闪烁,而这种情况在即使相机不动的情况下,由于像素抖动的原因,也会出现。

而由于Variance Clipping导致的闪烁是由于过度空间滤波导致,因此可以通过引入其他抗锯齿方案来消除,总的来说有如下三种解决思路:
- 增加采样点
- 通过法线滤波等方案来提升采样点质量
- 缩小抖动幅度(可能会有质量影响,需要做一个平衡)

2.2 多像素速度向量选择问题
TAA方案需要使用采样点的速度向量来追踪上一帧的累加数据,但是这里需要注意,由于我们这里是使用本帧一个小范围内的颜色数据重建得到的颜色值,在速度向量选择的时候,不能直接选择中心点的速度向量,否则,如下图所示的情况可能会得到0值,从而导致边缘等数据追踪出错:

更好的方案,应该是在所有像素中挑选最长的速度向量(移动最快的一个像素):

2.3 多采样点质效平衡问题
TAA在使用过程中有两种模式,一种质量优先,一种性能优先,具体实现逻辑给出如下:

在多层渲染的情况下,上述两种模式的处理方式也有所不同,分别是每层分别TAA与混合后TAA:

2.4 采样点随机逻辑
我们还可以使用TAA来消除噪声(通过给出一个随时间而变化的随机种子而完成)


2.5 大面积Variance Clipping
如果想在大尺寸kernel上使用VC,这里就不能按照之前的方式直接累加求取平均值跟方差了,而可以通过下采样的方式来对这个过程进行加速:

网友评论