我们都知道,在渲染流水线中,顶点着色器对输入的顶点数据进行处理(如顶点的坐标变换和光照计算)以后,GPU会进行进行齐次除法并将顶点从三维空间转换到二维的屏幕坐标,接着将这些所需要的着色数据发送到光栅化阶段。
在光栅化的三角形遍历,我们知道,需要检查所有像素是否被一个三角形网格所覆盖,如果被覆盖,就会生成一个片元。
首先,这一步具体是什么意思呢?
图中每一个小矩形就是一个像素pixel,而每个小矩形中心的点就是“采样点”。整个大三角形就是“三角形网格”。检查每个像素是否被三角形网格覆盖的意思即为,每个像素的采样点是否被覆盖到了,如果被覆盖到了,就会生成一个片元。
那么,我们是如何通过三角形三个顶点的数据,得到片元的信息,用于Fragment Shader中进行光照计算的呢?
在《Unity Shader入门精要》一书中,在三角形遍历这里,书中写到:
三角形遍历的过程。根据几何阶段输出的顶点信息,最终得到该三角网格覆盖的像素位置。对应像素会生成一个片元,而片元的状态是对3个顶点的信息进行插值得到的。
到底这个“对3个顶点的信息进行插值”是什么意思?又是为什么通过对三个顶点进行插值就可以得到整个片元的状态(包括一些计算光照所需要的信息如屏幕坐标,深度信息等等)呢?
这边,我们首先要说线性插值的概念。
我们已经见过很多线性插值的例子了,比如在a和b点之间,构成的以t为参数的直线形式为 p = (1-t)a+tb.
这就是插值,因为当 t = 1的时候,p=b,当 t = 0 的时候, p = a。因为系数 t 和 1-t 是线性多项式,所以被称为线性插值。(参考虎书)
三角形是图形学程序中基本图形,像颜色这样的信息会标记在三角形顶点上,并且被插值到三角形当中。实现这种插值的坐标系被称为重心坐标系。
什么是重心坐标?(注意区别于重心)重心坐标系是如何实现插值的呢?
重心坐标是由三角形顶点定义的坐标。二维三角形中的一些关于重心坐标的定义如下:关于三角形重心坐标定义看这里
通过a,b和c三点构建非直角坐标系。那么p点的坐标就可以表示为
p = a + β(b-a)+ γ(c-a);
拆开就会得到:
p = (1-β-γ)a + βb + γc;
我们让 α ≡ 1 - β - γ,那么p = αa + βb + γc;
于是我们可以得到,α + β + γ = 1 另一种证明方式看这里
对于a,b和c构成的三角形,当
0<α<1
0<β<1
0<γ<1
p点在三角形内部 (也就是说不在内部的点p也可以通过重心坐标来表示),而(α,β,γ)就是p点的重心坐标。
那为什么我们可以用重心坐标来进行插值呢?
参考线性插值。我们前面说过,比如在a和b点之间,构成的以t为参数的直线形式为 p = (1-t)a+tb.这就是插值,因为当 t = 1的时候,p=b,当 t = 0 的时候, p = a。因为系数 t 和 1-t 是线性多项式,所以被称为线性插值。随着t的变化,p的位置也会发生变化,但是仍然在a与b之间的线段当中。
在三角形中,在上述p点的表达式当中,当其中一个坐标为0的时候,其他两个坐标位于0和1之间,该点在三角形边上;当其中有两个坐标为0,剩下一个为1的时候,该点位于顶点。
因此我们可以通过三个三角形的顶点,通过插值计算三角形内部某一个点p的颜色。如果三角形a,b,c的顶点上面的颜色分别表示为W1,W2,W3,那么p点的颜色可以表示为:
p(α,β,γ) = αW1 + βW2 + γW3
这就是利用p点的重心坐标,采用一种平滑的方式将三个顶点的颜色进行插值,从而得到的p点的颜色。
三角形重心坐标插值的作用?在图形学中,利用重心坐标在三角形内部进行任何属性(位置、纹理坐标、颜色、法线、深度、材质属性…)的插值。一般我们通过其他步骤都会得到三角形顶点上的属性,但继续计算时需要用到三角形内部的某点的属性值,利用重心坐标可以得到三角形内部该值的平滑过渡。
所以在三角形遍历中,我们就可以通过对三角形三个顶点的数据进行插值,从而得到该片元的信息(就是所谓的“片元的状态”)。
好,讲明白上述的东西以后,要来讲一讲我一直模糊的概念,锯齿和抗锯齿的问题。
首先,锯齿是怎么产生的呢?这就跟上面我们讲到的东西相关了。
还是这张图,我们可以看到,被三角形网格所覆盖的像素(覆盖了采样点)就会生成一个片元(红色的),但是,可以很明显的看到,有些三角形网格覆盖的区域其实覆盖到了像素的区域,**但是并没有覆盖采样点,**因此,并不会产生一个片元,而且这样的情况是很大程度会发生在三角形的边缘部分。三角形边缘变得锐利了,因此就产生了走样,也就是所谓的锯齿。
这种锯齿,是因为采样不足导致的。
那么我们应该如何解决呢?如何抗锯齿?
简单来说,像素中心的采样点没有被覆盖,所以不会生成一个片元,该像素点就不会被着色,所以才会导致边缘很锐利,不平滑。抗锯齿就是为了让该像素有颜色,使得边缘看起来平滑,这样就抗锯齿了。
两种技术,一个是超采样抗锯齿(Super Sample Anti-aliasing, SSAA)的技术,另一个是多重采样抗锯齿(Multisample Anti-aliasing, MSAA)。在这边讲MSAA,因为MSAA实在SSAA上发展来的。SSAA是理论上效果最好的抗锯齿方案,但在目前的硬件条件下这种性能开销是不可接受的,因此在SSAA的基础上发展出了MSAA。
首先,多重采样,顾名思义就是将一个采样点变成多个采样点。采样点在每个像素的中心,也就是多重采样使用了子采样点。
我们不再使用像素中心的单一采样点,取而代之的是以特定图案排列的4个子采样点(Subsample)。我们将用这些子采样点来决定像素的遮盖度。当然,这也意味着颜色缓冲的大小会随着子采样点的增加而增加。(采样点的数量可以是任意的,更多的采样点能带来更精确的遮盖率。)
左图,采样点未被覆盖,该像素的颜色为白色。右图特定位置的4个子采样点中有两个被覆盖了,那么此时应该如何决定像素的颜色呢?
这边提一下SSAA的原理。SSAA使用比正常分辨率更高的分辨率来渲染场景,当图像输出在帧缓冲中更新时,分辨率会被下采样(Downsample)至正常的分辨率。这些额外的分辨率会被用来防止锯齿边缘的产生。
如下图,SSAA原理是首先对每个像素取四个子采样点(几个可以自己定义),对四个子采样点都运行一下Fragment Shader,将三个顶点插值得到的颜色储存于每个采样点中,然后取平均值,得到最后的像素颜色。但是Fragment Shader运行的次数越多,对性能的消耗是很大的。虽然SSAA可以得到非常好的效果,但是这样的性能开销太大,相当于用4x来暴力渲染4倍分辨率图像。
那么能不能只计算每个像素的颜色,只运行一次FS,而对于那些子采样点只计算一个覆盖信息(coverage)和遮挡信息(occlusion),然后根据这两个信息来决定是否把像素的颜色信息写到每个子采样点里面呢?这就衍生了MSAA技术。
注意,每个子采样点都有颜色和深度,对于子采样点来说,他们共享着中间采样点的颜色值,但是每个子采样点都有独立的深度信息。覆盖,就是判断该子采样点是否被三角网格覆盖了。遮挡,就是模板测试和深度测试。
MSAA的区别就在于,它可以同样在一个像素中取四个子采样点,但只对像素中间的采样点运行了Fragment Shader来计算颜色。
然后可以对每个子采样点都进行深度测试和模板测试,然后通过判断覆盖和遮挡信息,将像素的颜色信息写入子采样点里面。没有通过测试的子采样点,就会不会写入颜色。
“MSAA是把像素扩大N倍,得到N个子采样点,再将扩大成N倍的渲染目标,再经过一个过程缩放成原始的渲染目标,这个过程称为resolve”
这句话是什么意思?
1.由于深度测试是在每个子采样点的级别而不是像素级别进行的,深度buffer必须相应的增大以来存储额外的深度值。在实现中,这意味着深度缓冲区是非MSAA情况下的n倍。
2.虽然我们只对每个像素进行着色,但是并不意味着我们只需要存储一个颜色值,而是需要为每一个子采样点都存储颜色值,所以我们需要额外的空间来存储每个子采样点的颜色值。所以,颜色缓冲区的大小也为非MSAA下的n倍。
还有一个最重要的问题,虽然我们可以通过覆盖和遮挡信息,来决定是否将像素中心采样点的颜色写入子采样点。那么最终这个像素的颜色到底是如何决定的?
当颜色缓冲的子样本被图元的所有颜色填满时,所有的这些颜色将会在每个像素内部平均化。
因为上图的4个采样点中只有2个被覆盖住了,这个像素的颜色将会是三角形颜色与其他两个采样点的颜色(在这里是无色)的平均值,最终形成一种淡蓝色。左边两个子采样点是三角形颜色,右边两个点无色,取平均值,淡蓝色。
1.光栅化阶段,对四个子采样点执行三角形覆盖判断,在一个四倍分辨率大小的coverage mask中记录每个子采样点被覆盖的情况。
2.像素着色阶段,在像素中心圆点处执行像素着色器。该点的位置、深度、法线、纹理坐标等信息由三角形三个顶点重心插值得到。图中计算得到像素颜色为紫色。
3.对四个子采样点执行模板测试与深度测试,并将测试通过的子采样点数据写入四倍分辨率的模板缓冲与深度缓冲。每个子采样点都拥有自己的深度值,依然是重心插值得到。
4.上图中左下两个子采样点通过了深度测试,并且coverage mask为1,因此将紫色复制到这两个子采样点对应的颜色缓冲中(依然是每个子采样点一个颜色,共四倍大小)。其他两个子采样点暂为背景色。
5.重复上述流程绘制第二个黄色三角形,将像素着色获得的黄色复制到右上角的子采样点中。
6.所有绘制结束之后,通过一个对高层透明的PASS,将四个子采样点的颜色插值获得最终输出的像素颜色。
为什么在延迟渲染中不能使用抗锯齿技术?
看这里看这里
如何解决?
看这里看这里
以上锯齿被称为几何走样,还有着色走样。
现代抗锯齿技术:
Post Processing AA技术。这一类东西包括FXAA,TXAA