目前为止,MSAA仍是抗锯齿效果的黄金标准。然而MSAA需要硬件支持,并且要在RT中存放子像素信息,这大大增加了内存和带宽开销。在使用HDR管线或者G Buffer时此问题显得更加严重。
由于这些限制,基于后处理的抗锯齿方案逐渐成为主流。这类方案并不需要改变渲染管线,而是在图片中寻找被人眼识别为锯齿的像素,再对应模糊处理。morphological antialiasing(MLAA)即是其中之一。
MLAA的思路很简单,考虑锯齿图中的一个微元,它常常是下图中B的样子。而如果分辨率无限加大,可以用一条蓝线来标定实际的几何边界,如图A。如果我们能知道蓝线的实际形状,就可以根据直线斜率计算蓝线下面积与整个像素块面积之比a,然后用如下公式轻松实现抗锯齿效果(如图C):
Cnew = a*Cold + (1-a)Copp (其中Cold是处理前的颜色,Copp 是锯齿边(edgel)另一侧的对应像素。)
蓝线的斜率等于竖直跨度/水平跨度,对于水平锯齿边,竖直跨度永远为2个像素,要解决的问题只有3个:
1)待处理像素在锯齿边上的位置
2)锯齿边的水平跨度(或竖直跨度)
3)锯齿边界的形状,如下图。
这三个问题可以一起解决:
从待处理像素开始,沿锯齿边分别朝两侧搜索,寻找截边(crossing edge)
锯齿边的跨度以及像素所处的位置可以由左右搜索距离dleft和dright确定。
锯齿边界的形状则由两侧截边的情况而定。
左图黄圈内即是截边需要考虑的像素。中图列举了所有可能的9种情况,其中情况6并不需要做抗锯齿处理。
右图为AreaTex范例,每一块记录了一种情况下对应像素抗锯齿所需的混合权重。
然而,搜索过程和锯齿边界的判断需要大量采样,拿到采样结果后进行重新矢量化时还有大量的分支判断问题(如上图右侧的9种情况)。Jimenez通过巧妙利用硬件免费的bilinear filter来大量减少采样数;再引入一张预计算的AreaTex,使用像素位置、锯齿边跨度和边界情况作为索引,直接取出最终抗锯齿所需的混合权重。
至此MLAA的问题都得到解决,性能也足以流畅的运行。
让我们再重新梳理一下MLAA的算法框架。它基本分为如下三步进行:
1. 寻找位于锯齿边缘的像素,并标记出来。
2. 重新矢量化。
3. 混合对应像素。
一、边缘检测
边缘检测可以基于Depth,亮度,颜色或者其中几种混合判断。
算法需要检测水平和竖直两条边。 每个像素有4个相邻像素,理论上应该做4次边缘检测。但由于像素之间公用边,所以只要针对左侧和上侧的相邻像素进行检测。
这部的处理结果是用像素标记出的锯齿边。
二、重新矢量化
这一步的处理单元并不是像素,而是锯齿边。设定一个最长搜索距离,从当前位置起分别针对水平和竖直方向的锯齿边进行重新矢量化。重新矢量化的过程大量利用双线性采样,并最后采样AreaTex得出混合权重。
三、混合对应像素
这步根据混合权重来执行最终的抗锯齿过程,同样也可以使用手动设定采样位置配合免费的双线性采样来优化效率。
由于这一步的处理单元是像素,而不是锯齿边,所以并不方便将它与第二步合并。
图2中,红色表示像素左侧存在锯齿边,绿色表示像素上侧存在锯齿边,黄色表示二者都有。
图3中,RG表示水平锯齿边上下两侧像素的混合权重;BA通道存储了竖直锯齿边左右两侧像素的混合权重。
参考资料:
GPU Pro 2 Practical Morphological Antialiasing
Siggraph2011 Filtering Approaches for Real-Time Anti-Aliasing