前出塞
[唐代][杜甫]
挽弓当挽强,用箭当用长。
射人先射马,擒贼先擒王。
杀人亦有限,列国自有疆。
苟能制侵陵,岂在多杀伤。
导言
渲染中,我们输出的结果是一张最终显示到屏幕上的一张图片,其最小的基本单元是像素。像素实际上就是一个个细小的色块,当图片分辨率足够高,色块足够细小时,相邻色块之间的颜色差异肉眼很难辨别,展现在观众眼前的就是一幅细腻顺滑的画面,而当图片分辨率较低时,为了填充满同样大小的屏幕,色块的尺寸就会变大,此时色块之间颜色的区别就很难被忽视,尤其是当色块之间的颜色差别过大时,整个画面就会呈现马赛克式的锯齿状,如图1下面一列所示。锯齿用英文来表述的话,一般称作Aliasing或者Jaggies。 渲染输出画面的锯齿通常来源于多个方面:物体边缘、阴影以及高光等。
导致锯齿的原因有多种,总的来说,可以分为两大方面:
- 因为几何形状覆盖范围像素化而导致的锯齿,叫几何锯齿
- 因为光照计算随着像素而导致颜色跳变的锯齿,叫着色锯齿
这里重点关注着色锯齿。
为了使得画面变得更为精细,要么选择提升画面分辨率,降低色块尺寸,减少相邻色块之间的颜色差异;要么选择对色块的颜色进行处理,减少相邻色块之间的颜色差异。这种降低锯齿感的做法,我们称作抗锯齿Anti-Aliasing,简称AA。
采样理论
渲染从实质上来说,也可以算是一种采样(Sampling):对三维场景进行采样,输出2D的图像。按照香农采样定律的理论,要想通过对采样后的信号进行重建(Reconstruct)来获得完美的原始信号,就必须要保证采样频率不低于原信号最高频率的两倍。这里就隐含了一个完美重建的一个前提条件,那就是原信号的最高频率必须要要是有限的(band-limited),但实际上,如果用点阵来表示三维场景的话,那么问题边缘,阴影边缘等位置的采样通常就会有突变,导致其最高频率通常都是无限的。因此,如果用点阵来表示三维场景的话,就无可避免的会遇到锯齿问题。
主流的AA方法
上面说到,锯齿问题是渲染系统中不可绕过的问题,而为了解决这个问题,也涌现了一批优秀的解决方案,这些方案各有考虑,各有所长,下面对其中的一些主流的解决方案及其特点做一个简单的总结与归纳。
1. FSAA/SSAA
FSAA是Full-Screen AA的缩写,而SSAA则是 Super Sampling AA的缩写,虽然名字不同,但是两者指的是同一项AA技术。
前面说到,对付锯齿的方法主要有两类,其中一类是提高图像分辨率,降低色块的尺寸,从而减小色块之间的差异。这是最直观的理解,而从渲染原理的角度来考虑,则又有不同的解释。
如图2所示,对于屏幕中的每个像素而言,其最终显示的颜色是取决于覆盖于其上的三角面片对应位置的采样点的颜色。不可忽略的是,像素是有一定的宽度尺寸的,覆盖其上的三角面片可能不一定能将之完全覆盖,如果只覆盖住了一部分,就需要判定,是否应该用此面片上的颜色对像素颜色进行改写。那么,最简单粗暴的判定规则,就是根据像素中心点是否与三角面片有交集。所以上图左侧的图中,由于中心点未被覆盖,所以像素最终的输出颜色为白色。但实际上,此三角形也覆盖了此像素接近一半面积的区域,从主观的意识上来看,为了降低相邻色块之间的差异,取红白两色的中间值是比较恰当的。那么要怎样达成这个目标呢?
最直观的方法,就是将像素进一步细分成不同的小块,对每个小块进行上述的判定,像素最终的颜色,则由这些小块的颜色加权计算而来,如图2右图所示,将像素上的采样点一分为四,最终有两个采样点落在三角面片的覆盖范围上,如果权重相同的话,就实现了刚才所说的平均求值结果。将这个算法做一个通用处理,其计算公式大概如公式1所示:
其中,表示x,y处的像素颜色值,i表示采样点序号,表示第i个采样点的颜色值,表示第i个采样点的权重,一般情况下都是平均采样,所以各个采样顶点的权重值都是,n是单个像素中的采样点的数目,当n=1的时候,这种情况就退化为最原始的只在像素中心位置的唯一采样点的情况。
一般来说,某个pixel上的各个采样点的位置都是各不相同的,且这些采样点分布的模式也可能会随着像素的不同而变化。这种方法由于在每个像素上的采样点数目多于一个而被称为Supersampling/Oversampling Antialiasing(SSAA)或者也有人称之为Full-screen Antialiasing(FSAA)。
这个方法简单直观,行之有效,不过其缺点就是:由于单像素上的采样点数目多了,渲染pixel/fragment shader的时候的消耗就会成倍增加,通常SSAA在每个像素上的采样点数目为4,这就导致ps环节的渲染消耗是正常ps环节的4倍,即使有些项目中使用2x1或者1x2的方式进行SSAA,也有两倍的消耗。
除了这种需要一张更高分辨率的texture buffer的方法之外,还有另外一种使用与输出图像相同分辨率的Accumulation Buffer的AA方法,其基本原理很相似:都是将多个采样点的颜色值加起来之后除以n。不同之处在于,SSAA是使用一次pass,渲染出多个sample的颜色值;而Accumulation Buffer的版本则是使用多次pass,每次pass对view做一次x-或者y-轴上的一定程度的偏移(通常是半个像素单位),之后将结果输出到Accumulation Buffer中相加起来,为了避免Accumulation Buffer溢出,通常其bit数要比最终输出到屏幕上的buffer的bit数多一些。不过这种方法因为需要进行多次绘制,而且也需要进行Buffer之间的数据拷贝,所以消耗也非常的高。
2. MSAA
相对于SSAA的通俗易懂简单直观,MSAA(Multisampling Antialiasing)算法就要稍微复杂一点点,不过其消耗也更容易令人接受,这也是为什么MSAA可以算得上是一种标杆式的AA方法的原因。
SSAA的主要缺陷在于采样数目过多导致同一个像素的ps执行次数增多,进一步导致消耗过高,那么最直接的思路就是如何在降低采样数的前提下保证图像的质量。MSAA的方法就是在这种思想的指导下提出的。
SSAA有两个要素(假设我们需要一个2x2倍分辨率的采样buffer):
- 2x2倍于原输出buffer的ps输出buffer
- 每个sample一次的ps计算,即2x2倍于原ps计算量
MSAA保留了其第一个要素,而对其第二个要素进行了改造,使得每个像素只执行一次ps计算,在这种情况下,一次ps计算只有一个数值,而我们有4个sample,参考前面SSAA示例图(Figure 2)中右边的小图,我们可以看到,对于每个三角形面片而言,可以认为其覆盖在某个像素上的任意点上的颜色值是近似相同的(实际上可能会有轻微差别,通常情况下像素尺寸比较小,对于肉眼来说,这些差别可以忽略不计),那么对于这个红色三角面片覆盖的两个sample来说,其计算的结果可以认为是相同的,没有必要进行两次,只需要计算一次,之后将结果赋值到两个sample对应的buffer存储空间即可。为了使得这一次计算的结果更精确,一般会选择这些被三角面片覆盖的sample点的中心位置作为最终的采样点位置,对于图2右图中的情况来说,就是那两个采样点连线的中点的位置,这种做法叫做Centroid Sampling或者Centroid Interplotation,这个过程通常可以由GPU自动完成(如果GPU支持的话)。如果三角面片的完全覆盖了某个像素的所有采样点,那么就直接取像素的中心作为采样点进行计算。
这个过程大概可以用如Algorithm1的伪代码来描述:
如图3所示,对于中图中的像素而言,其四个采样点分别被两个三角面片覆盖,0,2,3号采样点被粉色三角面片覆盖,对此进行Centroid Sampling,其真实的采样点计算位置跟像素的中心正好重合,而1号采样点的真实采样点就是其自身所在的位置。
从刚才的伪代码中可以看到,对于处于物体边缘处的采样点群,执行的Centroid Sampling,而对于采样点被全部覆盖的情况则只对像素中心位置的采样点进行fs计算,而通常判断采样点群是否处于物体边缘的判定规则中,最常用的就是比较采样点群的中心位置是否与像素中心重合,如果重合则认为是处于边缘,而这种判定规则对于上图中的示例而言是不存在的,这是这种判定规则的一点瑕疵,在使用中需要注意。
MSAA渲染完成后,需要将subsample的数据转换成pixel color,这个过程称之为MSAA Resolve。早期的Resolve是在GPU的固定管线完成的,通过对所有subsample进行等权混合来输出最终的pixel color。而Nvidia在他们的GPU上提供了一个Quincunx subsample pattern的采样模式,可以支持不等权模式(详见本文后续部分的介绍)混合,这种pattern混合的结果会有较好的抗锯齿能力,但是同时也会因为导致一些高频细节的丢失而被人诟病。
刚才说到MSAA相对于SSAA,只改进了其FS计算频次过高的问题,对于SSAA所需的额外的存储空间过大的问题则未做处理,AMD在MSAA的基础上提出了一种叫做EQAA(Enhanced Quality AA)的改进方法,如图3所示,当一个像素中存在多个sample的数据一样的情况下,就会有冗余,而实际情况中,大多数像素中的sample都是存在这种情况的,因而导致了比较高的浪费。
为了减少浪费,EQAA决定将数据存储到一个Buffer Table中,而在原来MSAA存储数据的位置,只存储对应的数据在Buffer Table中的序号,从而用LUT的方式来降低内存的消耗。NVIDIA早于EQAA提出的CSAA(Coverage Sampling AA)也是采用的相同的思想实现的。
当所有三角面片都绘制完成后,MSAA就进入了与FSAA相同的环节,将所有的sample的数值整合起来求取平均值,并将平均值作为最终结果输出到屏幕上。这一步被称为resolve。不过需要注意的是,如果同时开启了MSAA与HDR,那么在MSAA Resolve 之后再进行HDR的Tone Mapping处理的话,可能会导致一些问题(因为Tone Mapping操作可能是非线性的,因此Tone Mapping与Linear Blend之间的顺序对结果会有较大影响),所以建议尽量在Resolve之前完成Tone Mapping。
默认情况下,MSAA的Resolve操作都是使用最常用的box filter来对像素中的所有fragment进行平均处理,而在07年,ATI通过将box filter替换成tent filter来获取相邻像素的采样信息,从而得到更为平滑的AA效果,这种方法被称为CFAA(Custom Filter AA),而这种方法也在那个时候起就被集成到了AMD的EQAA方法中。
实际上,在现代GPU的PS或者Compute Shader中,都是可以直接操控MSAA的整个处理过程的,在这种情况下,可以使用任意自己觉得可能不错的filter来实现图像的AA重建。通常来说,如果filter的覆盖范围过广,采样面积过大,就会导致高频信息过滤得更干净,锯齿感就越轻,但相应的,细节丢失也就更严重,且filtering的过程也更久。有人研究发现,使用Cubic SmoothStep或者横跨两到三个像素的B-Spline Filter 得到的效果是最好的。
MSAA的一个显著的问题是无法与延迟管线兼容,其基本原因在于想要按照MSAA方式生成GBuffer需要的带宽太高(不是GBuffer本身消耗,而是生成GBuffer的过程需要消耗),具体原因被击中的弹药架有做比较详细的解释:
延迟渲染到底能不能开MSAA?目前网上能查到的说法有以下几种:
- 延迟渲染在计算光照时已经无法获取像素的几何信息,因此无法使用MSAA
- 硬件不支持在延迟渲染管线中开启MSAA
- MSAA对显存和显存带宽的消耗过大,在性能上无法实现
理论上,在延迟渲染中开启MSAA,其作用阶段并不是光照计算阶段。如果希望通过对MultiSample GBuffer插值获取原始像素数据,那么插值法线和深度会导致异常的结果(因为相邻的像素可能是不同面上的)。这也是为什么会有第一种说法。
此外,MSAA本身是一种几何抗锯齿方法,并不能处理高光锯齿,因此没有必要跟前向渲染一样非要在计算光照时才进行。延迟渲染中启用MSAA,作用阶段是GBuffer的生成,也就是输出经过抗锯齿处理的BaseColor,然后正常计算光照。从这一点说,第三种说法是正确的,但原因并不是GBuffer扩大了,而是在渲染GBuffer的过程中消耗了多倍的资源。至于第二种说法,目前我查到的原因是DX9不支持MSAA MRT,而延迟渲染大多是通过MRT实现的,DX10.1以后就支持MSAA MRT了。因此这个可能是早期的某个限制条件之一,但是随着硬件与API的升级,这个限制条件已经被移除了。
现在的MSAA MRT要求所有RT的Sample数目完全一致,那么就是说,没有办法在Normal上使用一个sample而在BaseColor上使用多个Sample,而前面说过,我们不希望在Normal等贴图上应用MSAA,解决这个问题有两种思路:
- 单独增加一个pass绘制BaseColor,这个过程会有较高的时间消耗,通常不会考虑
- 为GBuffer中的不同贴图应用不同的RT Resolve算法,且不谈可行性,这种算法至少需要在生成GBuffer的过程中消耗较高的带宽,也就是前面提到的原因,因此依然处于不可接受的状况。
由于MSAA是基于几何形状覆盖来进行采样点的赋值的,如果当前fragment需要执行alpha blend,这时候需要分两种情况[3],参考如下PowerVR的描述:
when the fragment contains an edge. When performing blending, the edge flag set
by the ISP indicates if the standard blend path needs to be taken, or if the optimized path can be used. If the destination fragment contains an edge, then the blend needs to be performed individually for each visible subsample to give the correct resultant colour (standard blend). If the destination fragment does not contain an edge, then the blend operation is performed once and the colour is set for all visible subsamples (optimized blend). Once a tile has been rendered, the Pixel Back End (PBE) combines the subsample colours for each fragment into a single colour value that can be written to the frame buffer in system memory. As this combination is done on the hardware before the colour data is sent, the system memory bandwidth required for the tile flush is identical to the amount that would be required when MSAA is not enabled.
如果当前fragment并未处于三角形的边缘,那么说明整个fragment都是被覆盖在三角形内部的,此时可以采用optimized blend,只需要进行一次blend,之后将结果写入到多个sample中即可,如果处于边缘,那么blend操作就需要逐sample进行,消耗会比较高,也就是说,虽然MSAA是支持半透渲染,但是计算消耗会高过不透明渲染[4]:
Performing MSAA becomes costlier when there is an alpha blended edge, resulting in the graphics core marking the pixels on the edge to on edge blend. On edge blend is a costly operation, as the blending is performed for each sample by a shader in software. In contrast, on opaque edge is performed by dedicated hardware, and is a much cheaper operation as a result. On edge blend is also sticky, which means that once an on-screen pixel is marked, all subsequent blended pixels are blended by a shader, rather than by dedicated hardware.
此外,对于pixel shader计算导致的高光锯齿也是无能为力的。
总的来说,MSAA是业界知名度最高的抗锯齿方法,虽然MSAA有着诸多的不足,但是借着硬件支持的便利,在移动端又能够在on-chip上实现,因此是性价比相对较高的一种方案。
3. TAA
前面的AA技术都是在同一帧中,对像素上不同位置的采样点进行考虑,可以认为是在空间上做文章。而从另一个角度,实时渲染大多不会只渲染一帧,在一秒钟会进行多帧的连续渲染,前后两帧之间的信息是存在极大重复与关联的,是否可以考虑从之前多帧的渲染结果中提取相关信息来对当前帧的渲染结果做优化呢?这是一种基于时间维度的考虑,根据这种思想,诞生了Temporal AA,也就是常说的TAA,NVIDIA的TXAA以及之后的MultiFrame AA(MFAA)都可以归入这个范畴。
先不考虑时间维度,还是用空间维度来举例,之前的SSAA以及MSAA,都是通过将单个像素上不同采样点之间的渲染结果合并起来,作为最终的输出结果。那么从时间维度上来考虑,实施AA算法,应该也是通过将单个像素上的多个不同的采样点位置的颜色输出合并起来,只是不同的是,这些采样点分布在不同的帧,这就是TAA的基本思想。
一般来说,TAA的数据来源是来自不同帧的最终的输出图像,而这些图像可能是直接按照单像素单采样点渲染的最常规的渲染结果,也可能是来自于MSAA或者其他AA方法处理后的输出结果,通过对这些不同帧(通常是2~4帧)的输出结果做一个加权平均之后,就得到了输出到最终的屏幕上的图像画面。
刚才说到TAA是对不同帧的输出进行加权平均,这个地方就有两种实现方式:一种是不同帧的数据权重不同,比如在时间上比较靠前的帧的权重比靠后的帧的权重低(按照指数比例或者线性比例),另一种则是前后多帧的权重完全相同。
不同权重的TAA方法在相机与场景都固定不动的时候,图像会呈现一种不停闪烁抖动的噪音效果,所以通常用得比较多的是平均加权的TAA方法:比如对前后两帧做平均加权输出,每一帧的采样点位置一般都是不同的,且为了避免出现摩尔纹(moire pattern),对于不同的像素,其同一帧之内采样的pattern可能也是不同的。
使用TAA方法的好处是同一帧内,没有额外的采样消耗,只需要对多帧的结果做一个融合即可得到更好的输出结果。因此,相对于MSAA,得到相同的效果,其时间成本要更小一些。而使用TAA的缺点在于,如果多帧是非平均加权融合(即多帧结果的权重不同)的话,那么对于静态物体而言,就会呈现一种抖动闪烁的瑕疵,而对于移动速度过快的物体而言,就会在其运动轨迹上留下一片残影,而残影过多就会导致画面乱七八糟不清不楚。解决这个问题的方法有两种:一种是只对低速移动的物体使用TAA;而另一种,则是对当前帧的画面与之前帧的画面进行映射与投影校正(reprojection)。而实现reprojection的一个常用方法就是使用速度贴图(velocity buffer),所谓的速度贴图,就是屏幕上各点的移动方向与速度的映射,通过速度贴图,可以根据当前帧的数据快速定位到之前帧对应的像素在贴图上的位置,那些无法建立映射关系的采样点数据会被舍弃。由于速度贴图不需要增加额外的采样消耗,因此这种方法的成本比较低。
TAA+Velocity Buffer的抗锯齿方法因为MSAA与延迟渲染(Deferred Rendering)的不兼容而变得大行其道,而这种方法的效果也随着具体使用情景的不同而有所不同,因此涌现出了一批以此为蓝本创造出来的优化版的TAA方法,比如Wihlidal就提出了一种将TAA,EQAA以及众多的过滤算法结合起来的TAA算法,这种算法可以在保证显示质量的前提下进一步降低ps的执行时间。
3.1 TXAA
简单来说,TXAA实际上可以看成是MSAA与TAA的结合体,为了能够最大程度的提升显示质量,这里在计算的过程中既考虑了周边像素的贡献,也考虑了之前多帧的输入数据的贡献。
先是根据周边多个像素按照一定的权重构建出一个本帧像素的颜色值,之后根据一个速度向量求取到上一帧的累加像素点的位置,根据这个位置的颜色值与周边多像素在颜色空间中组成的凸多边形的关系,求取到最一致的颜色值,再利用此值与当前帧像素值按照一定的权重进行累加得到最终的累加颜色值。
3.2 MFAA
MFAA是Multi-Frame Anti-Aliasing的缩写。
4. MLAA
前面探讨的几种AA方法都是不需要任何的附加数据信息的辅助就可以直接实施的,但实际上我们知道,锯齿主要产生在几何形状的边缘或者颜色的交界处,如果能事先知道这些知识,就能够针对性的进行抗锯齿处理。09年Reshetov就根据这种思想提出了一种沿着边界线(edge)进行的AA方法,这种方法被称之为Morphological Antialiasing,缩写为MLAA。这种方法的提出再次激发了人们对于AA方法改进的热情,并且更加的侧重于边界线的定位与重构。
类似MLAA之类的抗锯齿方法,通常都是通过后处理来实现的:将渲染好的结果输入到MLAA中,通过MLAA后处理,输出更进一步的抗锯齿图形图像。这类方法包括了只能处理几何物体边缘锯齿的SRAA(subpixel reconstruction AA)方法,以及在渲染时同时输出用于定位边界线的相关数据的分析类方法GBAA(geometry buffer AA)以及DEAA(distance-to-edge AA)。
MLAA类方法的实施大致可以分成两步:
首先需要根据已有的数据信息(如最简单的就是一张color buffer)获取输出图像上的边界线信息。而单个像素对应的采样点数目越多,其输出的边界数据就越可靠,最终AA得到的效果也就越好,因此,Iourcha尝试用MSAA的采样点数据来获取边界线信息,证实这种方法确实有助于得到更好的AA效果。
之后对边界线上的像素进行blend处理。所谓的blend就是在图像上沿着某个方向将一定范围内的像素数据取出来做加权平均,而选取的blend方向通常都是与边界线的方向保持垂直(如directionally localized AA 即DLAA方法认为这种blend方式得到的效果要更好)
虽然MLAA方法能够增强输出图像的AA效果,但是这类方法也有着自身的几点不足:
- 边缘检测算法的精度不够,对于一些差异比较小的边界情况,可能无法检测出来
- 边缘检测算法对于一些变化频率较高的图像,比如一个黑白颜色逐像素交叉的图像,可能无法输出有效的边界信息。这种问题比较常见于文字渲染上。
- 边缘检测算法通常只能处理一些直线边缘情况,对于一些曲线边缘的检测效果就比较差
- 部分边缘检测算法的鲁棒性比较差,以至于有时候单个像素的误判就可能会导致极大的误差输出,使得这种算法的表现在帧与帧之间的差异显得极为明显。
- MLAA算法的时间复杂度与场景有关,对于草地的AA消耗比对于天空的AA消耗要高得多。
不过虽然MLAA有着上述的一些缺陷,但是总的来说,这种方法可以在可以接受的时间消耗以及内存消耗的基础上对AA效果进行增强,因此在很多应用场景都有一席用武之地。此外,只借助于color buffer就能实现的MLAA方法还可以从渲染管线中拆卸下来,做成一个独立的原件,在使用的时候可以非常方便对之进行启用与禁用,还可以集成到GPU的硬件功能中去。
MLAA方法中比较出名的两个方法分别是FXAA(fast approximate AA)以及SMAA(subpixel MLAA)。这两种方法的都是只需要color buffer 作为唯一的输入数据,每帧消耗大概在1~2ms之间,且这两种方法在经过MLAA之后,其输出的图像都还可以使用TAA再一次AA效果进行增强。此外需要注意的是,SMAA 还可以利用MSAA的多个采样点数据对AA效果进行增强。
4.1 FXAA
FXAA是NVidia发明的AA方法,在UE中有集成,这个方法是通过一次后处理来完成的,整个过程分为两步:
边缘检测:对SceneColor贴图进行边缘数据判定,这里的判定规则不是color,而是基于color换算得到的luma,因此可以检测出包含高光、阴影、几何边缘、半透物体在内的多种着色锯齿,从而使得其抗锯齿效果要优于MSAA(只支持几何锯齿)
边缘混合
针对边缘像素,对SceneColor进行相邻像素采样混合,采样数目越多,抗锯齿效果越好,但是消耗也越高。
4.2 SMAA
参考[1]
采样点纹
AA方法中的一个对AA质量与性能有着重大影响的因子就是Sampling Pattern(采样点纹),即采样点在像素中的分布方式。研究表明,对人眼感官影响最严重的锯齿现象是由水平或者垂直边缘(即与水平方向呈0度与90度夹角)导致的,其次是45度夹角的边缘。因此,RGSS(Rotated Grid SuperSampling)将四点方格grid做了45度旋转得到的pattern作为采样点纹来提升AA质量。如图4所示。
实际上,RGSS使用的Sampling Pattern是Latin hypercube或者说N-rooks采样的一个特例,N-rooks采样简单说就是在NxN的棋盘格里摆上N个采样点,需要保证每一行每一列有且只有一个采样点。这种采样方式对于水平或者垂直的锯齿问题有非比寻常的减轻效果,不过N-Rooks对于45度夹角的对角线形式的锯齿的处理能力就不咋地了。实际上没有一种固定pattern的采样方式是能解决所有问题的,只知道需要分布比较均匀的,相邻采样点之间的间隔不能太近或者太远,目前能够适应各种情况的比较好的采样pattern都是分层式的,即将Latin Hypercube纹样与其他的采样方式(如抖动jittering,Halton sequences以及泊松圆盘分布等)相结合的方法来实现AA。
Halton Sequence指的是采样点在像素范围内随机分布,却又保持一定的距离避免采样过密导致收益降低,通常来说,一个基础的Halton Sequence要比由GPU硬件实现的MSAA的表现要好。
此外,对于场景内一些呈规则分布的细小物件(如多条平行的细线)而言,如果使用固定pattern的采样方式来进行supersampling的话,很容易就会导致moire纹。解决这种问题的方法有两种:
一种是使用随机采样stochastic sampling,即将全屏像素分成多个group,每个group有自己的一套采样pattern,随机采样的原理是将重复的锯齿替换成随机的噪声点,而相对于锯齿纹理,噪声点更容易为人眼忽视,虽然这种方法可以一定程度上减轻锯齿的表现问题,但是如果随机纹理数不够多的话,就会存在很多像素使用的是相同的pattern,还是不能完全消除锯齿;
另一种方法是对于每个像素都使用不同的采样pattern,或者对于每个像素其每帧使用的采样pattern都是在变化的。基于这种思想提出的interleaved stochastic sampling方法经过证实在场景中,同样的消耗能得到比stochastic sampling更好的表现。部分硬件已经开始支持interleaved stochastic sampling方法,比如ATI的SMOOTHVISION就支持每个像素16个采样点,且支持对屏幕空间中的像素按照子区域进行stochastic sampling,比如说在每一个4x4的像素范围内,使用用户自定义的16个随机采样pattern进行采样,保证每个像素的采样pattern在4x4范围内都是不相同的,且各个像素的采样pattern也是随着时间而变化的。
另外采样pattern还可以通过将采样点位置放置到像素边缘的方式来扩张滤波半径从而得到更好的AA效果,且平均采样点数也要更少,比如NVidia 的Quincunx 方法(参考图4)就是使用这种原理实现的:对于处于中间的采样点,给与1/2的权重,而处于四个顶点上的采样点,则给予1/8的权重,由于四个顶点上的采样点会自动使用双边滤波的方式得到结果,所以当前像素的输出值实际上是对周边像素的值都进行了考虑得到的结果,且由于四个顶点上的采样点是被四个像素共用的,所以平均下来,一个像素的采样点是2 个。基于同样的思想,RGSS也可以进行相应的扩张而得到相应的效果,参考图5,由于RGSS的特点,这样的采样纹理可以在每个像素平均2个采样点的情况下得到接近于传统RGSS的AA效果,在水平方向与垂直方向有更好的AA效果,其最终的效果就如图5中右图所示,这个纹样有个自己的名字,叫做FLIPQUAD,这种方法最开始是在移动显卡上使用的。
不论是Quincunx还是FLIPQUAD方法,都可以用在TAA上。而有人得出结论,在平均两个采样点的TAA方法中,FLIPQUAD是其中效果最好的。
参考
[1]. Anti-Aliasing 技术盘点
[2]. A QUICK OVERVIEW OF MSAA
[3]. 深入剖析MSAA
[4]. MSAA Performance