Skip to main content

3.5.3 聚集几何缓存反走样

时间反走样是当代主流基于延迟着色的渲染引擎使用的反走样技术,它具有和SSAA媲美的图像质量然而只需要更少的存储占用和计算量。尽管如此,TAA也面临着例如重影,模糊等比较严重的问题,此外,TAA也不能有效地处理子像素特征,例如草地,动物的毛发等超薄超细的表面,在这些表面中,每个像素可能包括多达七八个三角形图元,例如图(1)所示,子像素特征是一个不可或缺的指标,因此TAA往往要结合SSAA来处理子像素特征。

图(1):子像素特征是现代复杂游戏场景的重要特征,图中的树叶,桌布等红色的区域包含非常多的细节,每个像素可能包含多达七八个三角形图元

然而,我们已经知道,能够有效处理子像素特征的SSAA技术对延迟着色管线并不友好,由于每个子像素都需要存储对应的G-buffer数据,并且延迟着色计算阶段要读取所有G-buffer中的数据,因此,除了SSAA本身的着色计算外,内存占用以及读取G-buffer导致的带宽占用严重制约了SSAA在延迟着色中的使用。

为了减少延迟着色计算阶段读取多个子像素的几何数据带来的高带宽占用,Cyril Crassin[a:AggregateG-BufferAnti-Aliasing]等从纹理的预过滤(pre-filtering)得到启发:如果多个子像素的几何数据能够像多级纹理一样使用预过滤的方式提前将过滤的结果计算出来,那么延迟着色阶段就可以和非SSAA渲染一样仅需要读取一次几何数据便可以计算包含子像素的特征,这样的思路将对几何数据采样的采样率和着色计算的频率分离开来,不但减少了SSAA带来着色计算量,也大大减少了读取多个子像素几何数据带来的带宽占用。

Cyril Crassin等于第二年[a:AggregateG-BufferAnti-Aliasing-ExtendedVersion-]对该算法进行了扩展,本节以该扩展的版本为准,这种技术称为聚集几何缓存反走样(Aggregate G-buffer anti-aliasing,AGAA)。在多级纹理中,低分辨率的纹理通过从高一级分辨率的纹理中提前过滤出来,在AGAA中,高分辨率的子采样点几何数据被使用预过滤器提前过滤为一个称为几何聚集(geometry aggregate)的更小分辨率几何数据,每个几何聚集对应一个像素内一部分可见图元的覆盖率,深度,法线等相关表面属性,经过预过滤的G-buffer称为聚集几何缓存(geometry aggregate buffer,AG-buffer)。

图(2):AGAA的整个渲染流程以及每个阶段的功能,输入输出数据特征,AGAA使用预过滤的方式将高采样率的子采样点过滤为少量的聚集几何数据,从而降低延迟着色技术的计算量以及带宽占用

AGAA的处理过程包括4步,如图(2)所示:

  1. 深度前向通道:在前向几何通道使用高密度的采样率对每个像素的可见性进行采样,这一步仅输出深度,法线几何数据至G-buffer中。
  2. 聚集定义: 按照子采样点的深度和法线特征,将该像素内的所有子采样点分成cc个聚集,每个聚集包含多个子采样点。聚集定义仅表明每个子采样点数据属于哪个聚集,除此之外它不做任何其他处理。
  3. 生成AG-buffer: 使用第二个光栅化通道对几何场景进行渲染,但是此时开启早期深度测试,并且设置深度比较为等于,只要那些处于第一步生成深度值的像素才被计算,在此阶段,每个子像素的几何数据被累积到根据聚集定义阶段定义的聚集当中,此阶段输出AG-buffer。
  4. 延迟着色:在渲染管线的延迟着色阶段,使用AG-buffer(而不是G-buffer)进行着色计算。

以下分别讨论AGAA的每个阶段,以及相关的一些技术细节。

高密度可见性采样

为了避免输出大量的几何数据占据大量的内存,AGAA在第一个几何通道阶段并不输出所有数据到G-buffer,而仅仅是找出所有可见的子像素,以及每个子像素的法线,这些信息将被用于进行后面的聚集定义,如图(3)1-1所示。

此阶段使用GPU支持的多重采样技术(例如8×8\times MSAA),以保证足够的几何细节被捕捉到,此阶段可以使用最多每个像素32个子采样点。每个子采样点的法线使用相对于像素空间的(θ,ϕ\theta,\phi)球坐标系统表示,法线使用一个RG8的颜色缓存存储。

聚集定义

聚集定义(aggregate definition)阶段的目标是使用一个簇分配算法分配每个像素内的nn可见的子采样点到cc个聚集当中,如图(3)2-2所示,这通过一个计算着色器来实现,聚集定义阶段的输出是一个子采样点到聚集的映射关系,这个映射关系可以使用d=n×log2(c)d=n\times\log_2(c)位来存储,本节稍后会介绍。

图(3):AGAA的各个算法步骤,图中展示了一个像素的示意图,其中第2步使用一个计算着色器,它对每个像素使用一个实例,但是需要注意的是第3步处于分块着色中,它的计算单位是一个块(tile)而不是一个像素,同时在分块着色中AGAA的第3步和第4步是合并的,聚集几何数据生成后立即被分块着色器使用,这样节省不必要的数据输出和输入,造成带宽浪费

所有预过滤技术都基于一个假设,即所有被过滤的属性之间没有相关性(correlation),其中一个属性是完全独立于另一个属性的,如果属性之间存在相关性,例如一个子像素不可见了,则其他的属性都不应该参与过滤。在几何数据中存在两种相关性,一个是和阴影(即可见性)有关,另一个则是法线方向。为了尽可能减少这种相关性对聚集的影响,AGAA使用基于距离的分簇算法,因为我们可以假设在空间局部范围内,子像素之间的可见性和方向倾向于一致。

所以为了区分不同子采样点之间的聚集所属关系,我们需要计算每两个子像素之间的距离,子采样点aabb之间的距离dd可以通过下式计算:

d(xyza,xyzb,n^a,n^b)=(xyzaxyzb)/k2+(1n^zn^b)2 d(xyz_a,xyz_b,\hat{n}_a,\hat{n}_b)=|(xyz_a-xyz_b)/k|^{2}+\frac{(1-\hat{n}_z\cdot\hat{n}_b)}{2}

(式1

其中,kk是一个常数用来表示最大可以被标记为处于局部的距离,在原始论文中他们选择k=10cmk=10{\rm cm}。有了这个距离计算方法,聚集定义的算法如下:

1. 定义c个聚集
(a) 深度缓存中读取深度值,并将它转化为位置
(b) 计算所有子采样点的平均位置和平均法线
(c) 定义第一个聚集为子采样点$s_0$,它是距离平均距离最大的子采样点
(d) 定义第二个聚集为子采样点$s_1$,该子采样点距离$s_0$有最大的距离
(e) 通过找出距离已知距离的最大距离的子采样点来定义其他聚集
2. 分配剩下的子采样点到这些聚集
(a) 分配每个子采样点距离它最近的聚集
3. 为每个聚集存储一个子采样点位掩码

每个聚集存储一个与所有子采样点的映射关系,如图(4)所示,每个子采样点对于每个聚集拥有一个为掩码。

图(4):每像素中每个聚集元数据的内存布局,这里使用$8\times$ MSAA个子采样点以及c=4个聚集,$CS_0-CS_7$表示每个子采样点,以及每个子采样点在每个聚集分别拥有一位来存储是否与该聚集映射

生成聚集几何缓存

生成聚集几何缓存数据是AGAA中最重要的一步,也是该算法最核心的部分。AGAA基于多级纹理的预过滤技术,而所有预过滤技术都基于一个假设,即这些量和一个求和方程的其他项是线性无关的,使得它们可以被分离从来,以单独求其平均值。多级纹理的过滤技术就是提前将多个纹素按照一定的权重加权成一个纹素,然后直接供低分辨率的着色方程使用。而在着色方程中,多个子像素输入的几何数据也能够被分解成线性的组合,所以我们可以在着色计算之前,将这些几何数据进行过滤,按一定的权重求其加权平均值,然后直接供着色器使用。

为了求每个聚集中对应子采样点几何数据的平均值,AGAA使用第二个光栅化几何通道,它对整个场景的几何数据以n×n\timesMSAA的分辨率执行一次渲染,但是开启早期(在片元着色器之前)深度测试,并设置深度测试为EQUALS,这样就只有那些在前一通道可见的像素才会参与片元着色器的处理。

聚集几何缓存数据生成阶段的主要目的是将nn个子采样点的数据过滤到cc个(c<nc<n)聚集中,这需要用到目标无关光栅化(target independent rasterization)技术,这是NVIDIA的NV_framebuffer_mixed_samples扩展[a:NVIDIAOpenGLExtensionsSpecifications}提供的一个功能,它可以对深度测试使用更高的采样率,而对输出颜色目标使用更低的分辨率,在每个片元着色器仅输出到cc个颜色目标中的一个,这通过一个目标覆盖的位掩码设置:NV_sample_mask_override_coverage。

聚集几何数据生成阶段的伪代码如下:

1. 设置渲染相关状态:
(a) 关闭深度写入,并且设置深度测试为EQUALS
(b) 开启早期深度测试
(c) 开启模板测试,并仅使第一个通过深度测试的采样点通过
(d) 对渲染目标AG-buffer设置Additive blending
2. 渲染场景,对于每一个片元着色器,找出它对应的聚集以及该聚集对应的所有子采样点:
(a) 读取片元的覆盖率$M_f$
(b) 从聚集元数据中读取每个像素的元数据$D_a$
(c) 查找AggregateID:
i. $S_{id}$ = firstNonZeroBit($M_f$)
ii. AggregateID = ($D_a\gg$(Sid * MAX_BITS_AGGREGATE_ID)) & (MAX_NUM_AGGREGATES-1)
3. 计算与传统延迟着色一致的几何数据
4. 使用覆盖率$M_f$对几何数据进行加权计算
5. 将加权的几何数据输出到AggregateID对应的颜色目标

对于一般的光照模型,AGAA中的大部分几何参数的聚集都是直接计算其平均值来进行过滤,只有对法线会使用一个特殊的方式[a:Mipmappingnormalmaps}进行处理,这是因为直接加权的法线并不一定是归一化的,而强制归一化会损失一些信息,所以法线的加权需要考虑其变化的期望和方差来计算出一个更好的归一化加权结果。

延迟着色计算

AGAA中的聚集几何缓存数据生成阶段,以及延迟着色阶段通常是合并在一起的,这样聚集几何数据直接可以供着色计算使用,避免不同通道缓存数据的输出和输入。在Unreal Engine 4[a:AggregateG-BufferAnti-AliasinginUnrealEngine4]中这两个阶段被放在分块着色中按一个分块的单位进行处理。

尽管AGAA的核心思想非常优秀,但是目前该技术还处于比较早期使用阶段,仅针对少数比较简单的光照模型有验证,该技术还需要行业中大量的实践和改进。

AGAA中一些比较常见的问题可以通过提高聚集数量来改善,但是由于所有预过滤的几何参数依赖于一个相同的光照模型,所以AGAA仅适用于比较统一的光照模型,如果一个像素中的多个子采样点分别拥有不同的光照模型,则结果可能无法被正确呈现,这是该技术目标最大的弱点以及需要改进的地方。