赵锴源
(四川大学计算机学院,成都610065)
环境光遮蔽(AO)能够近似计算出物体表面的遮挡程度,并根据遮挡程度来调节环境光照的强度,进而增强渲染结果的层次感和明暗对比。目前实时渲染领域常用的AO 算法主要分两类:
一类是在屏幕空间利用事先生成好的G-Buffer 来计算AO。由于该类算法只需要处理2D 数据,计算量较小,易于在光栅管线上实现,所以早在2007 年就已经被Crytek 应用在游戏中,即SSAO 算法[1]。而在随后的几年中,业界又不断对SSAO 的积分公式和采样方式进行了改进,从而又衍生出了采样效率更高,效果更好的基于屏幕空间几何信息的AO 算法,如:HBAO[2]、VAO[3]和T-SSAO[4]等算法。但是由于屏幕空间的几何信息只局限于2D 空间,所以这类AO 算法无论怎么改进都无法反映出屏幕空间以外的遮挡情况。
另一类是通过对储存了场景的几何信息的体素数据进行Ray March 来计算AO,如:DFAO[5]和VXAO[6]。这类算法由于使用了3D 纹理来储存场景的体素化数据,因此它们在计算AO 时不再缺失屏幕空间以外的遮挡信息。但是由于受到显存大小和显卡带宽的限制,这类算法所使用的3D 纹理的分辨率一般都不高,这就使得计算出的AO 结果会比Ground Truth AO 缺少很多细节。
而要得到最接近Ground Truth AO 的结果,最直接的方法便是利用光线追踪技术来计算3D 场景的AO。通过使用光线追踪技术,可以在计算AO 时准确地获取到3D 场景中任意位置的几何信息,进而使计算出的AO 结果也非常准确。但是在RTX 显卡面世以前,由于受显卡性能的限制,实时的光线追踪几乎是不可能实现的。而在2018 年NVIDIA 推出了基于图灵构架的RTX 显卡以后,实时光线追踪便成为了可能。因此,基于RTX 光线追踪技术,可以实现出效果更接近Ground Truth AO 的实时AO 算法。
虽然NVIDIA 的RTX 显卡让实时光线追踪成为可能,但是现阶段RTX 显卡的光线追踪能力仍比较有限。如表1 展示的便是在RTX2070 的平台上,在不使用反走样的情况下,以1080p 的分辨率,用光线追踪为65 万面片的场景计算AO 时,在不同的每像素投线数下对应的光线追踪耗时。从表1 可以看出,当每像素投线数达到16 条时,光线追踪耗时就已经有10 毫秒左右,如果再加上阴影和反射等其他光追效果,整个场景的渲染效率将很难满足实时的要求。
表1 不同每像素投线数对应的光追耗时
因此为了保证渲染效率,每帧所能投射的AO 探测线的数量非常有限,如此一来便给AO 计算带来了比较严重的噪声问题,如图1 所示。而为了解决噪声问题,本文主要从:①提高AO 探测线的采样效率;②利用时相关性对AO 计算的历史样本进行复用;③对AO 结果进行滤波处理这三个方面入手进行处理。
图1 在低AO投线数下(每像素16条)的噪声问题
由此引出本文算法的整体思路,如图2 所示:①获取场景在当前帧的G-Buffer;②根据当前帧和上一帧的G-Buffer 对屏幕上的每个像素点进行时间相关性的检测,并标记出时间相关性失效的像素点;③在屏幕上的每个像素点,根据时间相关性失效与否,投射不同数目的探测线来计算AO;④在满足时间相关性的像素点处,累积复用历史样本;⑤根据累积复用程度对结果进行滤波处理。
图2 算法整体思路
要为屏幕上的每个像素点生成探测向量并计算AO,需要将每个像素点对应在场景中的坐标和法线作为输入。此外,时间相关性检测和滤波处理也都需要将场景的深度作为输入。因此,本文算法的第一阶段便是要获取场景的G-Buffer。
考虑到目前RTX 显卡的光线追踪性能仍比较有限,整个场景的渲染还不能全由光线追踪来完成,而是需要光栅管线与光线追踪管线协作来完成渲染。因此,本文算法直接使用光栅管线的G-Buffer 作为输入。如此一来,当光栅管线与光线追踪管线协作渲染时,便可以省去额外用光线追踪计算G-Buffer 的开销。同时,这样做也能够让本文算法可以直接兼容光栅管线的TAA 反走样操作,进而省去了对AO 结果额外做反走样处理的开销。
在使用光线追踪技术计算AO 之前,需要检测每个像素点的时间相关性,以便在光线追踪时根据时间相关性失效与否,动态调整每个像素点需要投射的AO探测线的数量。在本小节,本文只讨论针对静态场景的时间相关性的检测。
本文首先使用重投影技术[7]计算出将当前帧屏幕像素点重投影到上一帧屏幕的像素坐标,如式(1)所示。其中,tcur为像素点在当前帧的坐标,Pcur为当前帧的投影矩阵,Vcur为当前帧的视点矩阵,Vpre为上一帧的视点矩阵,Ppre为上一帧的投影矩阵,tpre为重投影到上一帧屏幕的像素坐标。
在得到当前帧像素点重投影到上一帧的像素坐标后,还需要检测重投影的像素坐标是否有效,以便排除因为视点移动造成的遮挡而无法复用的像素点。检测的方法是:比较当前像素点的视点深度dcur与其重投影到上一帧的视点深度dpre,当两个深度的差距很小时,说明重投影有效,否则均无效。检测的表达式如式(2)所示,其中ϵ 是用来调节检测严格程度的阈值。
在完成时间相关性检测后,本文算法会将通过检测的像素点的重投影坐标存入纹理中,而未通过检测的像素点的重投影坐标则会被标记为(-1,-1),这样在随后复用历史样本的阶段就无需再重新计算每个像素点的重投影坐标。
本文计算每个像素点的AO 值的基本思路是:首先,从G-Buffer 中读取像素点对应在世界坐标下的位置p 和法线n。然后,用光线追踪的方式,向以p 为起点以n 为中心的半球空间随机投射N 条探测向量。最后根据式(3)计算出该像素点对应的AO 值。其中,单位向量ωi表示第i 条探测向量的朝向。而函数V 则表示从坐标点p 沿方向ωi发射的探测向量的可见性,当探测向量被遮挡时函数V 返回0,否则返回1。
由于表达式(3)中含有法线n 与探测向量ωi夹角的余弦(即,式中的n∙ωi),因此越偏离法线n 的探测向量ωi对最终AO 结果的贡献越小。所以,为了提高探测向量对最终结果的贡献率,本文算法使用了概率按n与ωi夹角的余弦分布的随机向量。生成以z 轴为中心的且概率按n 与ωi夹角的余弦分布的随机向量的表达式如式(4),其中ξ1和ξ2是一对相互独立的且取值在[0,1)间的均匀随机数,而本文算法是使用Halton 序列来生成这对随机数的。
在使用了概率按n 与ωi夹角的余弦分布的随机探测向量之后,原式(3)中的余弦项(即,式中的n∙ωi)就在计算中被消掉了,因此最终计算AO 值的表达式如式(5)所示。
此外,为了减少AO 探测的性能开销,每个像素点投射的探测向量数N 是根据该像素点的累积复用程度来确定的,当累积复用的历史样本数较低时N 的取值较大(一般为8-16 条),当累积复用的历史样本数较高时N 的取值较小(一般为1-2 条)。需要说明的是,无论累积复用的程度有多高,N 的取值都不能为0。因为通过重投影复用的历史样本是存在误差的,而且随着不断的累积复用越旧的历史样本积累的误差越大,所以只有在每帧都加入一些新的探测样本,才能保证该帧的累积复用结果中的误差不会过大。最后,在完成AO 计算之后,本文算法会将每个像素点使用的探测向量数N 保存到一张纹理中,以便在随后的累积复用阶段可以方便的读取。
在计算完新一帧的AO 结果之后,需要利用重投影技术将符合时间相关性的历史样本累积复用到新的结果中,以便增加新的结果中的样本数,提高AO 结果的质量。由于重投影操作已经在时间相关性检测阶段完成,所以本阶段仅是对通过时间相关性检测的像素点执行累积复用操作。对于任意像素点p 累积复用的表达式如式(6)所示。
其中,Caccum( p )为像素点p 总的累积样本数,AOpre(p)为上一帧的累积复用结果,N(p)为当前帧新增加的探测样本数,AOcur(p)为当前帧计算出的AO 值。在完成累积复用计算之后,本文算法会将N(p)的值累加到Caccum中,并将新的Caccum保存到纹理中,以便在下一帧的计算中使用。此外,同样是由于通过重投影复用的历史样本存在误差,所以为了控制带有误差的陈旧历史样本在累积复用结果中的占比,本文算法将Caccum的最大值限制在了1500。
为了进一步改善AO 结果的质量,本文算法对经过累积复用处理后的结果进行了滤波。本文使用的滤波算法是Louis Bavoil 和Johan Andersson 在2012 GDC上介绍过的一种改进高斯滤波算法。该滤波算法把中心像素与临近像素的深度关系纳入考虑,在不需要额外边界检测的情况下,就能做到在滤波的同时保留物体的边界细节。此外,为了提升滤波效率,该算法采用了变步长滤波的策略,在靠近中心像素的范围,以1 倍步长采样;而在远离中心像素的范围,以2 倍步长采样。因此,在相同采样数下,该滤波算法能够实现更大的滤波半径[8]。
最后,为了避免因过度滤波而造成的细节丢失,本文算法会根据累积复用程度对滤波结果和未滤波结果进行线性混合。对于累积复用程度较低的区域,噪声问题非常明显,则优先考虑噪声问题,所以需要滤波结果的占比高一些;而对于累积复用程度较高的区域,噪声问题已经很小,则优先考虑保留细节,所以需要未滤波结果的占比高一些。
硬件配置:Intel Core i3-4160 CPU 3.60GHz 处理器,8G 内存,NVIDIA GeForce RTX-2070 显卡;
系统配置:Win10(1809),DirectX 12;
场景配置:65 万三角面片的室内静态场景;
渲染配置:屏幕分辨率1920×1080,开启TAA 反走样。
(1)在相机静止不动的情况下,如图3 所示。整个屏幕的所有像素点都处于高累积复用的状态。加上生成G-Buffer 的耗时,整个算法的一帧耗时仅为2.73 毫秒,fps 达到366 帧/秒,同时画面质量也和Ground Truth AO 非常接近。
(2)当相机漫游场景时,如图4 所示。在物体的边界附近,以及新移入视野的物体附近,像素点会因为时间相关性失效而处于低累积复用的状态。此时,为了保证这些区域的AO 质量,需要提高投射的探测向量数,因此渲染耗时会略有增加。加上G-Buffer 的耗时,整个算法的一帧耗时为3.10 毫秒,此时fps 为322 帧/秒。又由于单帧所能增加的探测向量数是比较有限的,所以时间相关性失效的区域的AO 质量会略有下降。虽然在相机漫游状态下本文算法的效率和质量都有所下降,但下降幅度都不大,其中耗时仅增加了0.37毫秒左右,而AO 结果也仅是在局部出现了轻度模糊。由此说明,即使在因相机移动画面部分区域的时间相关性失效的情况下,本文算法仍比较稳定。
本文提出了一种基于RTX 光线追踪技术的AO 算法,该算法充分考虑了当前帧与历史帧之间的时间相关性,并根据时间相关性动态调整AO 的探测、复用和滤波,进而在保证高效率的同时又能得到高质量的AO结果。实验表明,本文算法在应用于静态场景时,无论相机处于静止还是移动状态,都能以较高效率得到较高质量的AO 结果。
图3 相机静止时的截图
图4 相机漫游时的截图
图5 Ground Truth AO