【Siggraph 2016】Practical Realtime Strategies for Accurate Indirect Occlusion

今天分享的是Siggraph 2016上的GTAO算法实现细节,原文链接在参考部分给出。

1. 简介

AO是对GI(Global Illumination)的简单模拟,在GI计算复杂度居高不下的情况下,AO效果对于提升场景光影自然度有着重要的作用,即使在有烘焙间接光(如lightmap)的情形下,AO的加入也可以提升光影效果,这是因为烘焙光照的分辨率通常较低,GI效果精度较差,而AO效果的添加则可以在一定程度上掩盖这个问题。

然而在AO计算上,目前并不存在针对任意场景的解析解,大多是通过经验的方式来产生感觉自然的效果,即使如此,现有的AO计算方案都面临着消耗过高的问题,而原文提出的Ground Truth AO(GTAO)方案是在HBAO方案的基础上经过改进得来,通过一系列的手段可以以较低的消耗完成计算(可以做到在当代主机上只需要0.5ms的消耗的实施效率)。除了效率的提升之外,这里还增加了near-field GI的模拟以及一种实现对probe-based illumination环境下的specular reflection效果方案GTSO的模拟。

2. 算法概览

完整的反射光强可以通过如下公式计算得到:

这是一个围绕x点的球面积分,其中表示入射光方向,表示出射光方向,而则是一个递归运算符(recursive operator),表示的是所有场景的反射radiance(reflected radiance in all the scene,没太看懂是什么意思,从后面的解释来看,这里就是只光源方向跟法线的点乘)。

这个公式要想在游戏中以实时帧率的方式计算出来在目前还不现实,这里只关心其中AO相关的部分。通过下面的一系列假设:

  1. 入射光来自于无穷远处的环境光,且这部分光照可以被x周边的几何物体所遮挡
  2. x周边的所有表面吸收率是100%,即不会出现散射或者反射
  3. x所在的表面是diffuse的
    可以将前面的公式转换成如下的形式:

其中是点x的albedo,是diffuse BRDF(从前面的式子中抽离出来,由于是环境光,因此也可以一并抽出来),则是方向上的遮挡情况,在某个给定的范围内,未被遮挡为1,被遮挡则为0。最后,AO可以用来表示,对应的是针对的一个球面积分。

HBAO使用了一个随着距离衰减的函数来模拟以应对AO算法中未考虑near-field的interreflections(即丢失了一部分光照输入)而导致的过暗效果,GTAO这里则采用了一种完全不同的做法,通过引入一个新的公式来填补了near-field的interreflections数据以得到一个更为真实的效果。

HBAO算法的计算公式可以表示为:

其中是球面积分转换为角度双重积分时自带的一项,前面的则是归一化因子,这里需要注意的是,HBAO公式计算得到的是AO的一个近似项,因此在头顶有个帽子。虽然HBAO是一个屏幕空间的算法,但是其消耗在当代GPU上依然无法达到预期,[Tim13a]给出了一系列的优化措施对这个算法进行了改进,可以一定程度上提升其计算效率。

GTAO则是在HBAO的基础上考虑了前人的优化措施,在性能上与效果上都做出了一定程度的改进提升。具体而言,这里解除了前面对表面必须是diffuse的假设,同时还考虑了near-field(近景处)occluder之间的相互反射,在这两个考虑的前提下,前面的公式(2)需要改写成如下形式:

其中是菲涅尔反射项,整个公式由两项累加得来,前一项表示的是GTAO部分,后一项则是GTSO部分。

GTAO部分是在原有的AO项()的基础上考虑了near-field occluder的interreflection之后结果,因此用表示。

GTSO部分中的表示的是specular occlusion项,这一项在使用的时候需要跟提前卷积过(preconvolved)BRDF项进行相乘。

3. 实施细节

3.1 GTAO

GTAO是脱胎于HBAO的算法,因此大体实施策略与HBAO类似,只是在其中一些地方做了改进修正:

  1. 水平角跟仰角都是相对于视角方向计算的(上图中标注的是应该是标注错了),我理解HBAO应该也是这样算的?
  2. HBAO中的可见性函数是一个随着距离而衰减的函数,但是在这里则是常量1(下面公式中的余弦不是来自于可见性函数,而是最前面积分中的光源方向与法线方向的点乘):

如上述公式所示,其中是x点的表面法线跟视线之间的夹角,而内积分的区间跟按照前面的示意图来看,应该有考虑符号的(比如是小于0的之类),这里余弦上的加号表示的是clamp:,转换为这个公式之后,内积分可以给出一个解析解,相当于只有外积分需要通过数值解来计算,大大简化了计算消耗。

要想计算内积分,首先就需要计算出积分范围跟,这里的做法是对于当前屏幕空间的像素,会选取周边n x n个像素作为采样范围,沿着当前水平角的正反方向各进行n/2个采样点的采样计算,并通过如下公式计算出对应的仰角:

其中,,这里的s是屏幕空间对应采样像素的世界坐标。此外,这里需要注意的是,为了使得计算得到的AO效果在近景处具有更好的质量,这里采样范围是会跟随x距离相机的远近而变化的,从而避免在距离相机较近的区域采用一个较大的采样范围而导致计算效率的下降。

前面公式中的内积分可以给出解析解,这里需要注意,这里解析解中增加了一个,这是为了保证当的时候积分结果为0而增加的;另外,解析解这里跟上下两边界在积分公式赋值结果的符号实现相同的,是因为考虑到两个角度符号的不一致,这里分解为两部分,一部分是从0到,另一部分则是从0到,将两者加起来,解析公式中的都取绝对值应该就好理解了。

不过这里有个问题是,上述解析公式成立是有条件的,即法线是位于由跟组成的平面P上的,但实际上这个假设绝大部分情况下都是不成立的,这里的做法是将法线投影到这个平面上,采用投影后的法线来完成上述计算,而前面的角度也就变成这个法线与的夹角。

考虑到法线本身投影是不需要归一化的,这里在最终的外积分中还需要将这个归一化的影响去掉:

由于这里只有0.5ms的预算,这里需要通过一系列策略来对算法进行加速,这里的做法有:

  1. 在半分辨率的情况下进行相关计算
  2. 将积分模拟的多次采样分散到多帧以及多个像素上来完成

这里来解释下第二点,具体做法有:

  1. 每个像素只沿着某一个水平角的方向进行采样,但是会通过对相邻的4x4的像素的数据进行重用,最后通过双线性采样进行重建
  2. 通过exponential accumulation buffer对6帧的数据进行累加,相邻6帧之间的旋转角度是不一样的
  3. 将上面的结果累加到一起,相当于每个像素进行了4x4x6=96个方向的采样。

由于GTAO中移除了HBAO中随着距离而衰减的可见性函数,因此会导致occlusion结果的跳变,为了缓解乃至消除瑕疵,这里采用了一种保守的衰减策略:

  1. 确保near-field的occlusion是具有ground-truth效果的
  2. 对于far-field的occlusion会通过一个线性blend函数将之逐渐下降到0,这个衰减发生在large-enough distance到maximum search distance之间。

最后,由于当前算法是没有办法侦测到对应几何体的厚度的,因此会导致一些本身很薄的物体产生了过于浓厚的遮挡阴影,这里的解决方案是建立在一个假设之上的,即物体的厚度跟此物体屏幕空间的尺寸存在一定的正比关系(不成立吧。。),在这个假设之下,就可以对前面的公式(6)进行修正:

这里的blend的采用的是exponential moving average(EMA)算法:

取。

image.png

前面说过,AO计算的一个假设是,所有输入光来自于无穷远处,且各个放箱单额光照强度是相等的,这个假设直接忽略了near-field表面的多次反射效果,因此在边角区域会偏暗,而实际上边角区域的效果则是AO发挥威力最为显著的地方,因此在这种计算下的AO方案表现并不是特别好。

实际上,边角区域也正是near-field interreflection占据主导地位的区域,前人通过一些衰减函数来实现这个区域的多次反射模拟,以给出一个看起来相对正确的结果,但是这种方式实际上是没有什么物理意义的。

这里为了以一种低消耗的方式来实现对near-field interreflection的计算,给出了一个假设,即每个点x周边一小块区域中的表面albedo数值是恒定不变的(这个假设通常是不准确的,不过由于这里考虑的是x相邻的遮挡面在一个紧凑范围内的interreflection,从而使得这个假设在一定的程度上是成立的),在这个假设下,可以得到near-field的GI跟AO数值的一个关联关系。

这个假设的作用除了是方便推导出GI跟AO之间的映射关系,同时也是为了避免对相邻表面每一个点进行albedo采样计算,降低运行消耗。

下一步就是根据x点的albedo计算出AO跟near-field GI的关联关系。这里的做法是在一系列代表各种类型的测试场景中(见下图)为一系列的albedo数值(ρ = [0.1, 0.2, 0.3, 0.4, 0.5, 0.7, 0.9])同时计算AO与多次反射的GI

下图给出了AO(横坐标)跟GI(纵坐标)之间的映射关系:

从图中可以看到,对于每个场景每个albedo中的对应采样点,都可以通过一个三次多项式来进行模拟,而经过对各个场景的分析与整合可以得到,通过一个随着albedo而变化的三次多项式可以实现对所有场景映射关下的近似模拟:

而这种模拟方式不但补全了前面说的near-field上的多次表面反射输入光强,同时还具有计算高效的优点。

3.2 GTSO

下面来介绍一下specular occlusion的相关内容,这个部分代表的是Lambertian-based AO中的光滑反射部分。

先来看下specular occlusion的作用,下面两图分别给出了将specular occlusion关闭与打开情况下的表现:

off
on

可以看到,在高光上添加真实遮挡信息可以实现更为自然真实的高光效果。

跟GTAO一样,这里先假设输入光源来自于无穷远处:

要想通过数值的方式来解出这个积分是很困难的,这里的做法是假设各个方向的输入光可见性是1( ),之后通过[Laz13,Kar13]中介绍的积分拆分方式将之拆分成两部分以实现近似模拟(不知道这个拆分是基于什么原理给出的?):

其中是归一化因子,目的是保证的积分结果永远处于[0, 1]范围之内,计算方式为:

按照AO的假设,入射光强在各个方向是恒定的,是x点处的法线分布函数,是half vector。

前一个积分表示的是实际的输入光源环境跟Cook-Torrance BRDF中的法线分布函数(可以通过一个圆周对称的lobe来表示)的一个卷积,如果我们将输入光源环境用一张irradiance cubemap来表示的话,那么这个卷积就可以在离线的时候针对不同的粗糙度计算完成,结果可以烘焙到一个cubemap mipmap中,从而使得这个部分的计算在运行时十分的高效。

后一个积分可以看成是在纯粹白色输入光()的输入下微表面反射结果,可以通过预计算的方式烘焙到一张贴图中,不过后面进一步的处理会抵消掉这个积分,因此不做进一步展开。

前面假设中输入光源的可见性在各个方向是不受遮挡的,但是这种假设终归是不正确的,为了将遮挡信息考虑进去,这里可以采用跟之前积分拆分同样的思路,将可见性函数从积分中拆出来,最终的公式给出如下:

公式中的就是specular occlusion部分,可以通过如下公式进行计算:

这里也是归一化因子,目的是保证处于[0, 1]范围之内。可以看到,occlusion是受BRDF的影响的,因此是一个随着方向而变化的数值。

这里由于跟前面的完全相等,因此二者可以抵消,最终得到的计算公式为:

上述公式如果取的话,就跟Ground Truth完全一致了,与前面不考虑specular occlusion的积分公式相比,这里只是将可见性函数塞入到BRDF积分中而非当成一个常量看待了。

下面来介绍一下specular occlusion部分要如何计算。

这里计算specular occlusion的想法是将可见性函数跟BRDF 都用一个近似模型来代替,以实现高效计算。

可见性函数可以用一个cone来代替,这个cone以bent normal作为方向,还有另一个关键参数ambient occlusion项(这个是干啥的?可以转换为cone的半径或者角度),而这两项都可以通过在运行时实时算得,当然也可以在离线烘焙出来,并将之放到一张贴图或者顶点数据中。选择cone的表示方法的原因是可以重用之前GTAO中的计算结果

cone的幅度(amplitude,也就是cone的张角)可以通过AO计算得到,假设bent normal周边的可见性是各向同性的话,那么可以表示为在所有水平角下的最大仰角(这里由于是各向同性的,因此有),在这个假设下,AO(这里不是occlusion,而是visibility?)可以表示为:

这个公式的推导可以参考下图(通过cos加权的球面积分求得AO的可见性):

根据AO,我们就可以得到这个张角的计算公式:

关于BRDF项,我们同样可以使用一个cone来进行表示,这个cone的中心就是反射点x,反射方向则为cone的方向。这里有一些假设,比如BRDF需要沿着圆周对称,这个假设实际上是不成立的,不过这个假设已经是十分通用了,前面对cubemap进行卷积也是在这个假设的基础上完成的。另一个假设则是BRDF数值是一个常量。在这些假设的基础上,specular occlusion可以表示为如下的公式:

上述公式中的两项可以通过下图左边小图来理解,分子是两个cone的交集,分母则是BRDF对应的specular cone。

这个公式是可以通过解析的方式计算出来的,虽然是在一系列的假设的情况下得出的结论,但是结果还可以。

虽然这个公式看起来已经十分精简了,但是在计算上还是太费了,难以做到实时运算,但是这个计算是可以提前完成的,将结果转换为可见性cone的角度、specular cone的角度以及两个cone之间的夹角(bent normal跟反射向量之间的夹角)三个变量的函数,从而将结果提前计算好存储在一张3D贴图中即可。因为是提前计算的,因此这里可以使用一些计算复杂度相对高一点,但是结果也更为精确的方式,即表示成可见性cone跟BRDF相乘的积分,其示意图如上图右侧小图所示。

其中是一个二值函数,只有当的时候为1(即两个cone之间会存在交集),其他时候为0。

假设反射微表面的BRDF是各向同性的,使用的NDF是可以通过一个粗糙度就能表达的GGX格式的,那么反射角度就能够用一个角度来表示:,其中是表面法线,在这个假设下(不考虑空间上的dependency),上面的公式就可以转换成一个由四个变量表达的积分:

也就是说,specular occlusion可以表示成四个变量的lookup table,此外,如果将表面法线跟bent normal等同起来,那么就有,就可以将这个lookup table的维度降低到三维(不过会引入一些误差)。

如果这个函数比较平滑的话,就可以用较低分辨率进行存储,比如分辨率的BC4压缩格式的每个像素消耗8-bits的lookup table,并在运行时采样使用。

4. 总结

原文中的技术总的来说可分成GTAO跟GTSO两块。

在AO上面,GTAO在HBAO的基础上移除了随着距离衰减的可见性函数,转而使用一个常量1作为可见性(不过恢复了正常积分中的光线与法线之间的余弦项),为了避免硬切导致的瑕疵,这里的做法是从一个较大的距离到最大的采样半径上使用一个从1到0的线性混合权重。为了模拟near-field的interreflection,原文通过对多个具有代表性的场景在不同的albedo作用下的GI跟AO之间的数值关系进行匹配映射,得到了两者之间的关系的解析模拟解。

在SO上面,则是通过积分拆分的方式将光滑表面的反射结果拆分成specular occlusion跟输入光卷积结果(输入光跟BRDF的发现分布函数的卷积,可以在离线的时候完成)的乘积,而specular occlusion又可以表示成由四个参数表示的lookup table,因此整个计算可以完全转换为离线计算,运行时采样的实现方式,从而以较低的成本获得光滑表面上带有specular occlusion的反射结果。

参考文献

[1] Practical Realtime Strategies for Accurate Indirect Occlusion - Thesis
[1] Practical Realtime Strategies for Accurate Indirect Occlusion - PPT

你可能感兴趣的:(【Siggraph 2016】Practical Realtime Strategies for Accurate Indirect Occlusion)