圆弧裁剪算法c++_程序媛转TA之理论篇十二:反走样的几种算法原理

b5c86e243650250116e364af5208935a.png

本篇继续讲《全局光照》第4章。

本篇继续讲着色器管理。因为延迟着色需要管理好各种类型物体的着色计算。

对于BRDF模型,游戏场景中大量物体表面都可以用比较统一的材质参数来描述,剩下仅有一些特殊的物体如毛发,皮肤等使用其他特殊材质的模型。多种几何体混在一起使用相同着色器,避免不了分支的处理,对GPU性能造成严重影响,我们看一下神秘海域4是怎么管理的。

0445a0e7428d3fa65320b290518d868a.png

两个G-Buffer用来存储每种物体都需要的材质参数,例如法线,位置,粗糙度等,第三个可选的G-Buffer用来存储每种类型特殊的一些材质参数,包括织物,头发,皮肤以及丝绸等等。

GCN架构提供了解码方法允许对任何整数按位解码。

2f4a2f73ba76bc5bf5cba0c02abe4ab2.png

神秘海域的延迟着色阶段使用计算着色器以块为单位进行渲染,其中每个块包含16x16个像素点。在延迟着色阶段,计算着色器分两步处理块中像素:

1.对每个块执行材质类型定义。

2.对每个材质类型分别执行一次计算着色器。

伪代码如下:

0bff289719a0d97d8010c3cd310e521f.png

首先通过块里的所有像素的材质组合找到对应的shader索引,然后用这个shader去计算这个块的着色。

接下来我们讲一下延迟渲染的反走样。因为MSAA无法在延迟着色上使用,因为他会自动将片元着色器输出的结果拷贝多次,这使得本身就非常巨大的G-buffer更要使用多几倍的存储空间,这又大大增加了延迟着色计算中带宽的占用,因为每个像素要多读取几倍的数据。

而且即使存储和带宽都不是问题,但MSAA着重于解决几何走样,它仍然无法解决着色器走样。

ac0c6fd0511e4895afdcd6789e6a64f5.png

形态反走样MLAA核心算法是找出物体的轮廓信息,并利用这些轮廓信息计算出处于轮廓点上的像素的覆盖率,然后使用一个后处理阶段对轮廓上的像素和相邻的像素进行颜色混合。

它包括三个步骤:

1.根据一定的像素属性找出图像中不连续的像素,并标记这些像素中哪些边处于轮廓边缘。

2.利用这些边缘线段的几何特征计算每个像素与周围像素进行混合的像素权重值。

3.对周围邻近像素按照混合权重进行混合计算。

89589c8e4fe8b91b63aec2f67f43b3d5.png

SMAA是MLAA的增强算法。它包含三个渲染通道:

1.第一通道:首先边缘被存储到一个边缘纹理,纹理中的颜色表示每个边缘在像素中的位置,同时边缘像素的模板值被记录。

2.第二通道:记录每个轮廓上像素的混合权重,为了计算该权重,首先找出每条经过该像素左边和顶部边缘线段的模式,然后计算出该像素距离这些模式线段交叉边缘的距离,然后这两个距离值作为纹理坐标到一个预计算的纹理中进行采样,这个预计算纹理是根据线段模式计算出来的权重分布值。

3.第三通道:根据上一步的权重结果进行混合计算。

9bcd086415f62a6055d129d06e646563.png

进行边缘检测是根据亮度差是否超过阈值来确定的,对每个像素比较它左边和上边像素的亮度值,比较的结构是一个枚举值,这两个枚举值被存在一个纹理中,分别对应上边和左边是否处在边缘。

a379c676e231dc7959190479c6f94904.png

SMAA用一种适应性双阈值的策略来克服这种问题。其中灰色的点表示当前处理的像素点,黄色表示当前像素点可能的左边边缘,蓝色表示向量像素的边缘。首先计算出所有蓝色边缘中亮度插值病区最大值cmax,然后根据c2l>0.5 * cmax是否为真决定左边缘cl是否存在。

当边缘被标记出来后,混合权重计算包括三个步骤。首先计算出每个像素中心距离其所在形状两端的距离。然后,找出该像素所在形状的交叉边缘,并连接连个交叉边缘的中点构成一条轮廓线,最后根据这个轮廓线算权重。

cc71686154b8ea574dff4e68ab8809b1.png

17a9c154e3aa5f3c842a13be7e9159f6.png

我们有了每个像素到两个交叉边缘的距离,以及每个交叉边缘的类型,剩下就是利用这些值来计算边缘像素的覆盖面积。我们首先需要将交叉边缘的中点连接起来,然后计算每个像素内梯形的面积,由于每个像素涉及到大量的计算,所以SMAA将这些计算放到一个面积纹理中。

7ed3f4650f6bd7526180fc11415e394a.png

不过SMAA也有自己的问题,他是基于单个像素进行反走样,这样就无法处理着色走样了,例如高光函数等。于是就有了TAA,TAA的大概原理我们讲过。

0fcd31fef27d9c5807c399f9c8f7f4e7.png

对于静态场景,要使用TAA,在不同的帧当中相同的像素应该被使用一个抖动jittering操作,以使当前帧的像素被移动到一个超采样中子采样点的位置。抖动的位移通常使用某种随机分布函数产生。该随机分布应该具有低差异性,子采样点应该尽量布满整个像素区域。一般使用Halton sequence来产生子采样点的分布。

8b62ea1ba13b07e4d0eba28d6bd96c04.png

为了不影响渲染管线的结构和流程,这个像素抖动通常发生在顶点着色器,修改投影矩阵。

c54ed9109fafbf3a3c66ea0bd7d7cc0f.png

然后还需要一个空间过滤器,最简单使用盒子过滤器,求加权平均,但这样需要存储n个时间的绘制结果。所以一般优化成这样:

3ed3c98d32ae3ae1cfbf2aec773305ff.png

将当前渲染结果xt和之前所有帧混合的经过反走样处理的历史结果st-1混合,这是递归指数过滤器。混合系数α决定了图像由走样到平滑过渡的快慢。

908b305b39b41814514b1ace76599b3b.png

当alpha很小的时候,指数逼近结果和加权平均结果是一致的。

03278fdc4cd2f52b083d2b5fa427dd26.png

动态场景的话,情况会变得复杂。TAA理想状态需要除了采样点位置外完全一致的两个像素点,然而像素移动的时候,一切发生了变化。动态场景带来三个新的问题:

1.首先,需要要给额外的屏幕区域大小的缓存来存储每个历史颜色的位置,者通过延迟着色的G-buffer阶段通过将当前像素位置重投影到上一帧的摄像机的变换矩阵来计算。

2.由于重投影的操作,我们需要在上一帧图像的离散屏幕分辨率中找到一个位置来对历史颜色缓存进行采样,这涉及到重采样问题,因此可能使结果变得模糊。

3.由于场景发生变化,同一个位置像素除了采样点位置外可能发生了其他变化,例如光源遮挡关系,像素颜色值发生变化,这样的变化会导致重影。

0b5482bff4d7e70bbf417f59ee70b249.png

为了计算当前像素在历史颜色缓存中的位置,通常在延迟着色的几何通道中同时计算每个像素在上一帧相对于当前帧的偏移位置,这称为一个motion vector,后面使用一个额外的颜色缓存将偏移矢量直接输出到G-buffer中。为了保证精确度,通常使用RG16格式。这需要记录上一帧摄像机的信息以及上一帧每个物体本地到世界坐标变化矩阵mat02w。

6de63316733fa1ab0c335386c491bd78.png

e1b35cee583e073c2131d3dc44ea6a2f.png

需要注意的是,这里需要使用去除抖动的坐标,因为对于历史颜色缓存而言,它存储的是每个像素中心位置的颜色,如果我们使用抖动过的位置,那么计算的结果可能是两个完全不同的像素位置。

86682f33da9e1a84493e97352318554c.png

不过由于进行了过滤,最终颜色会变得模糊,尤其当历史颜色缓存累积的越多,它越被使用了更大范围的像素进行过滤。

TAA的模糊来源于每次迭代中,对历史颜色采样使用了双线性过滤器,而这些周围参与加权的像素颜色来源于更早历史周围的加权和,以此类推,随着时间的增加,参与加权的像素范围就越来越大。它的重投影的方差公式:

5abb817b5e8f748a7f10711abb7f217e.png

σG的平方是一个常数,用来表示抖动产生的随机数的方差,v表示像素中心距离每个子采样点的差值。所以,减少每个子采样点到像素中心的距离v,v越大,则总的过滤过程向外扩散的范围越大。可以对每个像素划分为四个子像素,但是不需要每帧渲染4次,而是将四个子像素独立存储为一个缓存对象,每一帧只更新一个子像素,然后将它们合并。

eedc6eed9b3e35752e1219fdc2aafa3c.png

另一个影响因素就是混合因子α,α越大,则历史颜色权重越低。

115efb23bf78f6df73c36bdf6c4d81e2.png

在UE4中,当像素附件的颜色对比度比较低时增加混合因子,而对比度高的时候减少混合因子。其次,可以使用一个单独的通道以后处理的方式对颜色缓存进行锐化处理,这通过对颜色缓存使用一个锐化过滤器来实现,例如神秘海域4的过滤器:

d75c9178dc17558697277909b0bb8372.png

不过这还不够,TAA还有重影的问题。

6e1bd2be63ddfd3ebdfd39bb0f99945a.png

TAA需要在混合时判断历史颜色是否失效,目前比较流行的时领域裁剪方案,这种方案基于一个假设,图像的颜色是连续的,即历史颜色应该位于当前时间帧内领域像素颜色的范围内。

56313e50f5bf0f2be377bbea8cf1a3e2.png

857c4b9386694709bee6bc01195c2e70.png

当我们计算出当前像素xt周围的3x3个像素的颜色范围之后,就可以对历史颜色的采样值和该范围进行比较。不过如果把无效的点直接抛弃,那么相当于没做任何反走样处理,所以我们我们可以找到一个离它最近的合法的点,例如Ch,并把它加入混合的颜色中。

f645c1cd688356f9aa901690211f08e4.png

不过因为多边形判断代价比较高,所以有了一种新方法,叫做方差裁剪。它首先求出3x3个邻域像素的颜色的方差,然后使用方差围绕期望值的变化范围建立一个AABB方差包围盒。

8449f398bbe61dde40b8d88cc6c5a9b4.png

102ba942f437f5be9856a9be00577686.png

r值大小决定了AABB方差包围盒的大小,r越大,重影效果越明显。

因为TAA面临重影模糊等问题,而且它无法有效的处理子像素的特征,例如草地,动物毛发等超薄超细的表面。聚集几何缓存反走样是2016年提出的新技术,全称是Aggregate G-buffer anti-aliasing, AGAA. 在多级纹理中,低分辨率的纹理通过从高一级分辨率纹理中提前过滤出来。它包括四步:

1.深度前向通道:在前向几何通道使用高密度的采样率对每个像素的可见性进行采样,这一步仅输出深度,法线和几何数据到G-buffer中。

2.聚集定义:按照子采样点的深度和法线特征,将该像素内的所有子采样点分成c个聚集,每个聚集包含多个子采样点。聚集定义仅表明每个子采样点的数据属于哪个聚集。

3.生成AG-buffer:使用第二个光栅化通道对几何场景进行渲染,但是此时开启早起的深度测试,并且设置深度比较为等于,只要那些处于第一步生成深度值的像素才被计算。每个子像素的几何数据被累积到根据定义聚集定义阶段定义的聚集当中。

4.在延迟着色阶段,使用AG-buffer进行着色计算。

95e266f2960e037a7c1cdcfbcdf1cf9f.png

在高密度可见性采样中,AGAA仅仅是找出所有可见的子像素,以及每个子像素的法线。它使用MSAA,保证足够多的几何细节被捕捉到。

聚集定义阶段目标是使用一个簇分配算法分配每个像素内的n可见的子采样点到c个聚集当中。聚集定义阶段的输出是一个子采样点到聚集的映射关系。

fe680c4115aac14b0fc8c132c2d77784.png

所有的前置过滤技术都基于一个假设,即所有被过滤的属性之间没有相关性,其中一个属性是完全独立宇另一个属性的,如果属性之间存在相关性,那么它的属性都不应该参与过滤。在几何数据中存在两种相关性,一个是和阴影有关,一个是法线方向。为了减少这种影响,AGAA使用基于距离的分簇算法,我们假设局部范围内,子像素之间的可见性和方向倾向于一致。

959dcd174583d086a7aafadece16416d.png

k是一个常数用来表示最大可以被标注为局部的距离。

4682326cd697b9dade5d36c30574c477.png

b818c3ccbca1c8e088778e3ff6a66769.png

20686e6411402436af15629433bff4e7.png

然后要生成聚集几何缓存数据。AGAA使用第二个光栅化几何通道,对整个几何场景的几何数据以nxmsaa的分辨率执行一次渲染,但是开启早期的深度测试,并设置深度测试为EQUALS,这样就有那些在前以通道可见的像素才会参与片元着色器处理。

d4c118d25e5bcfc2fa7a000313842f5c.png

15d40024f43171231d433dd7e4e479ad.png

这个技术目前还处在早起阶段,而且只能处理比较统一的光照模型。

嗯,就到这啦。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值