- wio
-
* 回复内容中包含的链接未经审核,可能存在风险,暂不予完整展示!
参考课程:
https://www.b******.com/video/BV1X7411F744?p=5
https://www.b******.com/video/BV1X7411F744?p=6
在上一节说到,所有的物体都会变为-1到1的立方体内,下一步就是光栅化了。
只需要定义上面这两个,其它的都可以算出来:
这个可以理解为数组,比如蓝色方块那个像素,就是(2,1),然后它的中心是(2.5,1.5),即(x+0.5,y+0.5)
缩放系数很简单,就是把宽高为2,拉伸到width,heigh。那么矩阵的缩放参数就是width/2,height/2
现在缩放完之后,还需要平移。因为之前是在0,0的位置,现在需要移动到屏幕的中央。屏幕的左下角是0,0,所以平移向量就是width/2,height/2。
上面这个变换,就称为视口变换。
经过了MVP和视口变换之后,三维空间的几何形体就被映射到了屏幕空间里,想要得到图像,需要用这些信息进行光栅化,将其变成像素。
三角形是最基础的多边形,用于算法建模及扫描模型。四边形建模方便对齐uv线,人工调整方便。
对屏幕中的像素中心进行采样,判断是否在三角形内部:
这里自然是之前讲过的叉乘,考虑三角形的顺序,都在左边或都在右边,都是在三角形的内部。
特殊情况:如果正好在三角形边上,不处理或者特殊处理,暂不讨论。
实际上只需要计算那个蓝色盒子内的像素是否在三角形内部。还有更快的优化方式,每一行都只找最左和最右:
观察行驶中的车轮,有时会发现车轮像是在倒转,这是因为眼睛采样不足,跟不上车轮的转动速度。
接下来的视频,建议先阅读一下这两个链接再继续看:
H264系列五 图像与滤波
H264系列六 傅里叶变换
这个概念有点复杂了,参考
可以简单地理解,是平均,做了一个平滑处理。比如,任何一个像素,都是它周围3乘3像素的平均,这样就模糊处理了。
时域的卷积等于频域的乘积???这句话没有去深入理解,从这句话开始,后边进度条一直到59分左右听的是一脸懵逼了……感谢弹幕提醒,需要补以下知识:
不过可以直接听进度59分以后的结论:
在进度1小时05分那里,继续往下看视频,建议先看一下MSAA的基本概念。下面这个文章让我完全看懂了:
MSAA,全名Multi-Sample Anti-Aliasing,是抗锯齿的一种方法。一般而言,一个像素只有一个采样点,这个点一般在像素的正中心。如果打开了MSAA,那么每个像素内的采样点将不止一个,例如2x,4x,8x,16x等每个像素的采样点分别为2个,4个,8个,16个。这些样本点在像素内的分布位置,是有一定讲究的(为了降低锯齿感),各家GPU所支持的pattern可能不太一样。例如下面的两种4x的pattern,读者可以自己想象一下右边的pattern的优点哦(一个实线矩形代表一个像素,一个黑色的点代表一个采样点)。
在了解MSAA之前,首先我们需要知道什么是锯齿?锯齿是怎么产生的?为什么要抗锯齿?
第一个问题,什么是锯齿?锯子相信绝大部分人都见过,图形学上的锯齿表现也是这种类似阶梯的形状,对于锯子而言锯齿是有利的,但是在图形学上,我们总希望尽量去降低这种锯齿感,如下图所示,你想在一块16x16的分辨率下画一个填充有红色的三角形,但最后的显示却如下图所示,填充红色的区域为最终显示的三角形。有的人可能会问,为啥我在显示器上没见过锯齿,那是因为现在的显示屏的分辨率一般都比较高,而在dpi较低的显示屏上会比较容易能看到锯齿。现在一般6英寸的移动手机的分辨率就能达到1080p,而很多27英寸的显示器的分辨率也才1080p,所以相比较之下,在桌面级显示器更容易能看到锯齿。
第二个问题,图形学上的锯齿是怎么产生的?锯齿是由于采样不足所造成的,我们知道线段是由无数个点组成的,所以如果你想准确的显示一条线段,你应该要采样无数个点,但是对于显示器而言,这不现实。首先显示器上的像素点是有大小的,然后像素的个数也是有限的,而且每个像素在物理上只能显示出一种颜色。这里顺便解释一下MSAA中的多个采样点是怎么回事,物理上决定每个像素只能显示一种颜色,这是肯定的,即使每个像素内有多个采样点最终也只能显示为一种颜色,那么增加采样点有什么意义呢?哈哈,又留下一个问题,别着急,请接着往下看。
第三个问题,为什么要抗锯齿?从上面的图我们可以看到,显示屏上显示的图形的边缘带有锯齿,这显然不是我们想要的,所以要想办法来降低边缘的锯齿感,给我们一个更好的视觉效果,这就是抗锯齿。
现在我们知道为什么要抗锯齿以及什么是抗锯齿了。那么接下来的问题就是怎么做抗锯齿了?
刚才我们有提到,正常情况下(no MSAA),每个像素只有一个采样点,一般在中间,如下图所示,右图是左图区域放大后的结果。
上图标出了两个像素的采样点,我们可以看到,只要采样点在三角形外,那么这个像素将不会被着色。判断采样点是否在三角形内是在光栅化的时候确定的。可想而知,这是不合理的,对于上面的像素,大概有1/4的面积其实是被三角形覆盖的,但是由于采样点在中心,中心没被覆盖的缘故导致这个像素被判断为不在三角形内而最终没被着色,所以该像素最后呈现的就是背景色(此处为白色);对于下面的像素,大概有3/4的面积在三角形内,由于中心的采样点在三角形内,所以最终呈现的就是红色。
那么MSAA是怎么做的呢?multi-sample顾名思义,就是增加采样点的个数,在一个像素内采样2个点,4个点,8个点,16个点等等。回到刚才留下的问题,增加采样点的意义在哪?对于no MSAA的情况,在光栅化的时候会输出一个mask来给出哪些像素被三角形覆盖了,这些被覆盖的像素再去做depth stencil test,幸存下来再去做fragment shading对像素着色;打开MSAA之后,这个mask会给出哪些采样点被三角形覆盖了,然后这些采样点会单独去做depth test 和stencil test,有幸存样本点的像素再去做fragment shading。
这里多说一句,默认情况fragment shading对于一个像素只做一次即使你打开了MSAA(为了性能),具体拿哪一个采样点去做,各家可能不太一样,一般可能是像素的中心。也就是说,最终你的像素是什么颜色(每个物理像素只能显示一个颜色),是由每个采样点决定的,fragment shading决定了被覆盖的采样点都是这种颜色,最后每个采样点的颜色求和平均之后就是像素的颜色。当然你也可以打开sample shading这个extension让每个采样点都做fragment shading,不过这样带来的性能消耗会很大。
好吧,再多说一句,一个像素的几个采样点可以是不同的颜色,为什么呢?因为一个像素可能会被多个三角形覆盖到,如下图所示,该像素被两个三角形覆盖,那么最后它的颜色就是2/4红色+1/4蓝色+1/4背景色。Fragment shading之后,结合采样点覆盖的mask将每个样本的颜色存储在buffer里面,所以对于4xMSAA,buffer的大小需要分配4倍。当然对于TBR(Tile based rendering)架构的设备,framebuffer的大小不用改变,只需要在tile buffer分配的时候增大相应的倍数就行了,写出去到framebuffer的时候,硬件会有一个resolve的操作,只写出最终的颜色。
那么MSAA我们现在大致了解了。回到刚才的话题,如果是4x的MSAA,那么上面的锯齿三角形会是什么样的呢?先看结果,如下图所示。
上图可以看到,对于上面的一个像素,4个采样点中有一个在三角形内,所以这个像素的颜色就是1/4的红色加上3/4的背景色(白色),最后呈现的是浅红色;对于下面的一个像素,有3个采样点位于三角形内,所以这个像素的颜色就是3/4的红色加上1/4的背景色(白色),最后呈现的是深红色,这看起来合理多了。
没有MSAA以及打开4xMSAA的对比图如下图所示,可以看到打开了MSAA之后,三角形的边缘呈现的是过渡色,这样的视觉效果更好,这就是抗锯齿的结果,当然如果继续增加采样点的个数,效果将会更好,但是天下没有免费的午餐,增加subsample的个数对应的性能消耗也会增加。
如果你看上面的图,觉得效果不明显,那是因为16x16的被我放大了很多倍,可以看下面的,是不是边缘稍微平滑了点呢。所以我们可以看粗来,MSAA其实就是增加像素内采样点的个数来平滑/模糊边缘,从而在视觉上欺骗我们的观众达到抗锯齿的效果。
此图是4MSAA,即一个像素点对应4个采样点,对于边界,我们根据其像素点包围的点数来计算其颜色值,比如边界像素点包含2个采样点,那么它最终的颜色就应该是本来的颜色2/4,同理,如果包含一个,就是颜色*1/4,最终 的效果如下:
这样看起来“圆滑”很多。其优点比较突出,效果也不错,但是注意,它不适合延迟渲染,这里说的不适合并不是不支持,延迟渲染也能支持MSAA,但是支持他就会大量浪费空间。
延迟渲染是什么,在这里不做过多介绍,总之他为了优化光照着色,需要一个GBuffer,这个就是临时渲染出的结果的缓存因为需要GBuffer,所以MSAA也需要进行临时保存,那么保存临时数据的后果则是浪费大量显存。
前面可知4x MSAA的显存消耗是4x的关系,假设我们按照2k的原生分辨率来做,一个RT就是64 MB,用来做MSAA的那个RT就会是不低于256MB。又因为GBuffer至少存depth,normal,basecolor3张RT,那三个MSAA RT就是768MB,这种消耗非常巨大,因此可以理解为“不支持”。
首先所有MSAA, SSAA, FXAA, TXAA等都是抗锯齿(Anti-Aliasing)技术。
锯齿的来源是因为场景的定义在三维空间中是连续的,而最终显示的像素则是一个离散的二维数组。所以判断一个点到底没有被某个像素覆盖的时候单纯是一个“有”或者“没有"问题,丢失了连续性的信息,导致锯齿。
最直接的抗锯齿方法就是SSAA(Super Sampling AA)。拿4xSSAA举例子,假设最终屏幕输出的分辨率是800x600, 4xSSAA就会先渲染到一个分辨率1600x1200的buffer上,然后再直接把这个放大4倍的buffer下采样致800x600。这种做法在数学上是最完美的抗锯齿。但是劣势也很明显,光栅化和着色的计算负荷都比原来多了4倍,render target的大小也涨了4倍。
MSAA(Multi-Sampling AA)则很聪明的 只是在光栅化阶段,判断一个三角形是否被像素覆盖的时候会计算多个覆盖样本(Coverage sample),但是在pixel shader着色阶段计算像素颜色的时候每个像素还是只计算一次。 例如下图是4xMSAA,三角形只覆盖了4个coverage sample中的2个。所以这个三角形需要生成一个fragment在pixel shader里着色,只不过生成的fragment还是在像素中央(位置,法线等信息插值到像素中央)然后只运行一次pixel shader,最后得到的结果在resolve阶段会乘以0.5,因为这个三角形只cover了一半的sample。现代所有GPU都在硬件上实现了这个算法,而且在shading的运算量远大于光栅化的今天,这个方法远比SSAA快很多。顺便提一下之前NV的CSAA,它就是更进一步的把coverage sample和depth,stencil test分开了。
需要继续扩展阅读下面三篇文章,我这里只做一些摘选
第一种思路自然就是在每个像素中进行多次采样,然后根据多次采样的结果综合来计算像素的颜色值。使用这种方式来实现的抗锯齿技术有MSAA,TAA。
第二种思路是通过后处理的方式,寻找屏幕中的像素块边界,然后根据边界的信息,将两侧的像素点颜色进行插值,这样就会得到平滑过渡的边缘,实现抗锯齿的效果。使用这种方式来实现的抗锯齿技术有FXAA,SMAA。
这四种抗锯齿技术是目前最主流、最常见的抗锯齿技术方案。本系列文章也将将逐一讲解这些抗锯齿技术。
MSAA是最早出现的抗锯齿技术,主要通过硬件实现的,是以前非常流行的抗锯齿方式。在以前游戏画面质量很低的时候,额外性能消耗不大。但是随着游戏画面的升级,游戏中每帧需要绘制的物体也越来越多,MSAA的额外消耗也越来越大。
FXAA、SMAA是通过后处理实现抗锯齿的方式,这类抗锯齿方式的开销固定,不会因为绘制物体的多少而影响开销。
TAA最早是用于实现一些如SSR、Volume Cloud中的降噪中。近些年来延迟渲染管线逐渐流行,而由于带宽限制,延迟渲染无法使用 MSAA,而后处理的抗锯齿又没有次像素的功能,因此TAA逐渐作为抗锯齿方式,跟随延迟管线流行起来,并被UE4发扬光大。
以Unity为例,URP支持MSAA、FXAA、SMAA三种抗锯齿方式,HDRP支持FXAA、SMAA、TAA三种抗锯齿方式。
优点:使用起来简单方便,抗锯齿效果非常好。
缺点:会额外消耗大量内存和带宽,特别是对于延迟渲染来说,GBuffer 本身就已经很大了,如果再使用 MSAA,额外的带宽消耗极大。因此延迟渲染一般不会使用 MSAA来作为实现抗锯齿手段。而目前大部分 PC 端游戏都是基于延迟渲染管线的,包括Unity 的 HDRP ,所以 PC 游戏一般不会使用 MSAA。
从对硬件的利用率上来说,MSAA 对硬件的利用率其实很低,因为很多时候我们想要抗锯齿的部分,都只是在物体边缘或者高光变化的高频部分。其他颜色不怎么变化,比较低频的地方,其实是不需要抗锯齿效果的。使用 MSAA 进行大量物体的渲染时,很多带宽是被浪费的。因此即使在手机上,目前使用 MSAA 的情况也比较少。可以说,MSAA 是一种比较经典,但是目前显得略有些过时的抗锯齿方式。
不过有一种情况,是后面我们将要讲的基于后处理的方式无法处理的,那就是渲染比较细小的物体时。比如渲染头发,细绳子等物体时,因为物体太细,常常得到的图像宽度甚至不足一个像素,这时就只能使用 MSAA 或者 TAA 进行处理了。
为了搞懂TAA,我又参考了别的文章,比如:
图形学基础 - 着色 - TAA抗锯齿
TAA 是一种抗空域走样的技术,也有人译为帧间抗锯齿。我在前文 《采样理论》 中把走样分为了时域走样(如旋转车轮)和空域走样(锯齿),但在此处 TAA 技术是为了解决空域走样的问题,注意不要混淆。
简单回顾一些空域抗锯齿 (Spatial Anti-Aliasing, SAA) 技术,最普及的莫过于 MSAA,被各大渲染引擎采用,但是 MSAA 并不适用于延迟渲染 (Deferred Rendering),随之出现了很多基于形态学的后处理抗锯齿技术,如 MLAA,FXAA,SMAA 等,虽然他们表现良好且支持延迟渲染,但是都会面临时间稳定性不强的问题,TAA 刚好解决了这一点。
如下图所示,SSAA在每帧都需要执行多个子像素采样,而TAA则是把这些采样点均摊到多帧,减轻了每帧的负担:
后面的内容我就不复制了,看到这张图应该大致明白TAA是怎么做的了。
--------------------------分隔线---------------------
TAA的原理和 SSAA 大致相同,都是每个像素点有多个采样点。但是不同与 SSAA 的方式, TAA (Temporal Anti-Aliasing) 综合历史帧的数据来实现抗锯齿,这样会将每个像素点的多次采样均摊到多个帧中,相对的开销要小得多。
TAA 本质上是和 SSAA 类似的原理,所以完全不会有后处理抗锯齿的次像素问题,AA效果可以和 MSAA 相当。由于 MSAA 在延迟渲染管线中无法使用,且使用 Motion Vector 也和延迟渲染比较契合,因此在 Unreal 引擎的带领下,TAA 目前也逐渐成为现代 3A游戏的标配。
当然其缺点也比较明显,虽然 TAA 实现的原理并不复杂,但是和渲染管线关系密切,需要改动的地方较多。可以说是牵一发而动全身,对于其他的每一项渲染功能,都需要考虑 TAA 的影响。
除此之外 TAA 中很容易出现一些闪烁,鬼影等问题,需要不同的应用场景进行处理。因为 TAA 和渲染管线是紧密相关的,在不同游戏引擎中,TAA的很多细节处理方式都不太一样。
除了增加每个像素点的采样数,我们实现抗锯齿的另一种方式就是后处理。考虑到大部分情况下,我们想要抗锯齿的部分,其实都只是在物体边缘或者高光变化的部分,我们通过后处理的方式,检测出图像块之间的边缘(通过卷积么???),然后根据边缘信息对边缘两侧的图像进行混合处理,达到抗锯齿的效果。这类基于后处理的抗锯齿方式也叫做形变抗锯齿/Morphological antialiasing。
比如下图中的图像,左边是待处理的图像,中间是找到的边界,右侧是将边界两侧像素混合后得到的抗锯齿效果。
形变抗锯齿的实现方式有非常多种,原理大致都是基于上面提到的过程。有两种形变抗锯齿经历了时间的考验,成为了目前最流行的抗锯齿方式,就是 FXAA 和 SMAA,我们这里就先来分析下 FXAA 实现的原理。
FXAA(fast approximate antialiasing) 是一种比较注重性能的形变抗锯齿方式,只需要一次 Pass 就能得到结果,FXAA 注重快速的视觉抗锯齿效果,而非追求完美的真实抗锯齿效果。
FXAA 的优点就是集成比较方便,只需要一个 Pass 来实现抗锯齿,同时提供了两个版本,可根据情况灵活选用,是目前手机上的最常用的抗锯齿方式。
FXAA的缺点是画面会略微有些模糊。而且由于FXAA是基于后处理判断边界来实现的,因此没有次像素特性,在光照高频(颜色变化很快)的地方会不稳定。单独看静态的场景没有问题,但是移动摄影机时,就会导致一些闪烁。
现在终于可以回到视频的1小时15分左右了,有了上面的知识点了解,再来听FXAA和TAA就很简单了。
看到有弹幕说原神用了TAA,嗯??
然后提到了DLSS,思路类似,不过是用深度学习,猜出来的