知乎地址:https://zhuanlan.zhihu.com/p/64993622
我的知乎专栏:https://zhuanlan.zhihu.com/c_1099268510815010816
最近我做好了简单的场景编辑和序列化,打算回来继续折腾渲染部分了,首先想要实现的就是TAA(temporal anti-aliasing),为什么阴影和AO之类的啥都没有却要先写TAA呢,因为TAA对整个管线的结构影响非常大,而且后面会有许多地方用得着temporal的方法来增加采样率,比如AO、阴影、SSR等等,所以就把抗锯齿先写了。git地址:
这一套做法一开始基本上是从虚幻里抄出来的,写的期间我受到了许多大佬的教育,所以最后还是决定(跟风)换掉虚幻里的许多做法,最后出来的效果还不完美但是已经勉强能看了,,,然后这将是一篇比较萌新向的文章(因为我就是萌新),会尽量写清楚点,要是有什么错误的话请评论区啪啪啪打我脸_(:з」∠)_
抗锯齿前后对比图如下:
截图的时候没注意把第二张图分辨率截大了点,懒得再回去截了(╯ ̄Д ̄)╯╘═╛
可以看到上面那张图中主要有两种锯齿,一种是几何的锯齿,比如右边那俩默认材质的柱子和方块的边上就有这种几何锯齿,还有一种是着色的锯齿,球上面的细细的闪光点就是这类。
之前的很多抗锯齿算法是把边缘找出来处理,这种做法只能解决geometry锯齿,对shading aliasing无能为力,要解决shading的aliasing,可以通过增加采样次数的方法,然而直接做多次采样的开销是非常大的,而TAA的做法是,把多次采样的过程分布到每一帧中去,也就是每一帧都利用前面几帧保存下来的数据,也就是所谓的“temporal”所指的意思了,如图。
当然了,这么做的前提是时间里的每一帧在不同的局部位置采样,也就是说每一帧我们采样的位置就不能像以前一样都在正中心采样了,而是要加一个小的偏移量,一般来讲,这个步骤在写GBuffer的时候完成,而且是通过改投影矩阵的方法:
要验证为什么要这么改就用xyz1去乘投影矩阵然后做一下齐次除法看下结果就知道了,然后这里修改的偏移量一般选的是低差异序列,具体用什么可以尝试,只要效果好就行,这里我抄的虚幻的,虚幻用的是Halton(2,3),如下
现在我们知道采样位置,比较trivial的想法就是存前7帧的结果和当前第八帧的渲染结果相加,然后除以8,问题是这样就要很大的空间来存(小几百兆),开销挺大的,一般的做法是存之前历史帧的累加结果,即 P n = α ⋅ c n + ( 1 − α ) ⋅ P n − 1 P_n=\alpha\cdot c_n+(1-\alpha)\cdot P_{n-1} Pn=α⋅cn+(1−α)⋅P