Ambient Occlusion技术方案综述

所谓的Ambient Occlusion(AO),指的是当光源到某个物体表面上某点的射线被其他物体所遮挡时,用于计算此点反射到相机的光照数据的技术方案。AO其实是全局光照的一种副产物,不过因为在当前的硬件条件下,想要做到实时全局光照明还太过艰难,因此会通过多种表现效果的叠加来模拟全局光照表现,而AO则是其中使用非常广泛的一种。

AO技术能够给出物体表面弯曲信息等几何形状线索,是提高画面质感的一种非常常用的实现方案,为了实现这种效果,人们给出了各种不同的技术思路,这里专门开个帖子来将平时工作中遇到的有价值的实现思路做一下总结,一方面用于巩固积累,另一方面也希望在后面需要时能够快速甄选出合适的方案。

1. SSAO

SSAO是Screen Space Ambient Occlusion的缩写,是Crytek公司2007年在《Crysis》中首次提出的实时AO方案。

SSAO方案是通过后处理过程完成的,即只需要一个PS即可,整个过程所依赖的资源贴图为一张屏幕空间的深度贴图以及一张屏幕空间的法线贴图(也可以通过深度贴图推导出来)。

具体实现原理用文字描述的话大概可以概括为:

  1. 对每个需要计算AO数据的像素(即屏幕空间的每个像素)而言,以此像素所对应的法线为up方向,以给定的长度为半径,向着上半球发射若干条以半径为长度的射线(实际上,Crytek的SSAO的方案发射的射线方向并没有局限在上半球,而是分布在整个球体的,会导致墙面显得灰蒙蒙,具体可以参考文献[2])
射线与采样点示意图
  1. 根据深度贴图信息,统计各个射线的碰撞结果
  2. 假设发生碰撞的射线数目为N,总射线数目为M,那么此像素的AO结果就可以简单统计为1 - N/M。

这个方案的实施效果取决于每个像素发射的射线数目与长度,如果数目过多,长度过长,可能会导致性能的急剧下降,而如果数目不足,则可能会导致带状误差,如下图所示:

Samples Num Result

为了在保证AO质量的前提下尽可能的减少采样点数目(射线数目),在发出射线的时候,启用一个随机算法,使得每个像素的出射射线方向是打乱的,这样可以明显降低前面提到的带状误差,虽然实际上误差总量并没有变化,但是由于人眼对于规律性的误差容忍度较低,这种做法还是能提高显示的质量,之后如果在噪声的基础上加上一个模糊处理,还能进一步提升显示质量,如上图右边的小图所示。

此方案实施的对比结果如下图所示,可以看到,在开启了SSAO的场景中,其效果还是有非常明显的变化的。


SSAO Comparison

SSAO的优点在于:

  1. 计算消耗与场景复杂度无关,只取决于屏幕分辨率
  2. 支持动态场景
  3. 算法对其他数据的依赖度低,可以很方便的集成到图形渲染管线中

SSAO的问题在于:

  1. 无法在前向管线中使用:因为没有normal buffer
  2. 无法在VR等多视角渲染框架下使用,因为是屏幕空间的算法,最终为每个视角算出来的结果是无法匹配到一起的,因此看起来会十分的奇怪;此外,为了得到较好的显示效果,VR渲染的分辨率通常会比较高,因此常年受限于fill rate,而SSAO的两遍Post-process的处理更是使这个问题雪上加霜
  3. 计算消耗较高,因为对于每个像素,需要发出多条射线(数目过少质量下降),并对每条射线进行Ray Marching
  4. 质量较差,因为深度贴图只记录的是front face的信息,back face的信息是没有记录的,而物件的很多区域实际上是被back face所遮挡的,向着这些区域发射的射线在进行碰撞检测的时候返回的结果可能是未碰撞,因此导致整体会偏白,如下图所示
  1. 因为超出屏幕边缘的数据是拿不到的,因此随着相机的晃动,屏幕边缘位置的AO效果会出现闪烁
  2. SSAO引入的噪声要想消除会比较困难,即使强制消除,也会因此导致渲染质量的下降(模糊)

2. HBAO or HDAO

HBAO是horizon based ambient occlusion的缩写,而HDAO是High Definition Ambient Occlusion的缩写,其实这两者是等价的,只是后者是AMD给出的称呼而前者是NVidia给出的名字。

与SSAO一样,HBAO也是一个基于深度贴图+法线贴图实现的后处理过程,可以直接通过一个PS来完成。

在某个点P上的遮挡信息AO可以简单的用如下的公式来表示:

其中表示的是在整个球体空间内各个方向上的可见性函数,当从点出发,以为方向的射线遇到碰撞时返回为1,否则返回0,而表示的则是线性衰减函数(用于指示光源亮度的衰减,这个在SSAO中没有体现,而是将各个点的亮度数据做了简化处理,直接等同起来了)。

实现原理示意图

下面介绍下HBAO的实现原理。如上图(a)小图所示,我们用球面坐标系来进行表达,其中待计算点P到相机之间的连线用于表示球面坐标系的上方向,指向整个球面的天顶(zenith),而方位角(ezimuth angle)表示的是与垂直的球心横截面Plane上,指向北方的方向(是法线吗?不是,那怎么确定哪个方向是北方,是用真实的北方方向投影到这个平面上吗,还是用法线投影到平面上?无所谓,这里只需要选定一个方向作为积分的起始位置即可)到对应射线(这里的射线是各个采样方向的射线吗?是的)在此面上的投影方向的顺时针夹角,而仰角(elevation angle)表示的是对应射线与前面的横截面的夹角。

球面坐标系

之后,如前面(b)小图所示,用一条直线(这条直线我们称之为horizon line,水平线,虽然看起来好像并不是水平的?)来对球面进行拆分,这条水平线用一个有符号的水平角来表示,水平角表示的是实际上是这条水平线的仰角,这个水平角的定义为从与法线垂直且穿过P点的平面出发不断增加仰角直到不发生碰撞的最小仰角。

在这种情况下,如果点P周围的高度信息是连续的话,那么处于水平线下方的射线必定会与周边场景发生碰撞,因而也就无需再做进一步的射线相交检测了,因此前面的公式(2.1)可以改写成如下形式(即只考虑会发生碰撞的区域,将球面积分做了展开):

表示的是横截面上的积分方向,表示的是垂直于水平面的夹角方向(这里进行的是以视线方向作为up的上半球面采样),仰角的起始与终止积分点分别为与(这里这个余弦代表的是什么意思?这是球面积分公式中的积分项,任何需要进行球面积分的函数在转换为双重角度积分的时候,都需要乘上这一项),前面说过是水平线的仰角,而实际上指的是tangent angle,即当前点的切线(视线V垂直的横截面与法线N垂直的横截面之间在对应时的夹角)对应的仰角,因为要考虑符号,所以这个值在这个示意图中是一个负数。相对于公式(2.1)中的整而言,这里只考虑了朝向法线的半球区域,与之前SSAO的半球区域是一致的。

如果线性衰减函数

其中指的是P点到方向上的水平点(也就是碰撞点?)之间的距离,其中表示的是射线的作用范围,也就是积分球面的半径,在这种情况下就变得与无关,可以单独提取出来,先完成内部积分:

这个公式还是一个连续的积分,在PS执行的时候还是需要通过蒙特卡洛算法将之转换成累加形式,累加形式给出如下(对于某个像素而言,其法线与视线方向是确定的,那么对于任何的而言,其tangent angle都是不变的(为什么?正常来说,这个数值是会随着而改变的,但是如果处于简化计算的话,也可以将之设置为固定值),因此下面的累加直接使用来表示P点位置的tangent angle):

需要注意的是,这里的累加跟前面的积分公式比起来,少了前面的部分,这是因为蒙特卡罗累加需要在原积分函数的基础上除以各个采样点的概率函数,而这里的概率函数是均匀概率,其概率函数也就是,所以就与前面的正好抵消了。

这个公式中的N表示的是P点发出的射线的数目,而对于每条射线,要通过遍历射线上的多个深度sample(具体的sample数取决于AO的覆盖半径R)才能确定下对应的,如前面实现原理示意图的(c)小图所示,至于则是各个方向的输入光亮度(简化处理可以直接取)。

此外,如下图所示:

正弦可以转换成余弦,之后转换成常用的点乘指令:

在使用regular sample pattern的时候,很容易出现SSAO的banding artifacts,同样,这里可以对每个像素的射线角度分布进行随机化处理,对于每条射线上的sample distance也进行一遍随机化处理,这样可以大大提升显示质量。

下面来给出HBAO的具体实施步骤:

  1. 对于屏幕空间中的每个像素点,选取个方向,每个方向对应于屏幕空间上的一个角
  2. 对于每个角,我们可以计算出水平角(tangent angle)
  3. 之后沿着方向,按照一个固定的步长得到一个采样点(相机空间的坐标)
    3.1 对每个采样点,计算
    3.2 计算仰角
    3.3 ,其中会忽略那些的采样点
  4. 为了提升质量,每个像素的step size会进行一次jitter

上面介绍了的HBAO的算法的基本思想,但是在实际使用中,这个算法还会遇到一些问题,参考文献[5]为其中一些问题给出了对应的解决方案,感兴趣的同学可以移步前往观摩。

HBAO效果
Ray Marching结果

相对于SSAO而言,HBAO算法更加的物理正确,因此其效果也更加的接近真实,但是其性能消耗要远远超出SSAO(在NVIDIA显卡上有做加速),在实施过程中为了减少消耗,通常会在1/2分辨率情况下进行计算,但是这种做法一方面依然比SSAO算法要慢,而另一方面又会导致效果存在闪烁,Louis Bavoil为了解决这个问题,进一步对原算法进行了优化,提出了新一代的HBAO算法,我们通常称之为HBAO+。

3. HBAO+

在介绍具体的实现细节之前,先给出HBAO+与HBAO的效果对比,总的来说,HBAO+的执行速度是HBAO的三倍,而且能够呈现更为丰富的细节,听起来很神奇,那么到底是如何做到的呢,我们一起来深入了解一下。

总的来说,HBAO+算法实际上是HBAO算法加上Cache-Efficient Post-Processing算法的结果,而Cache-Efficient Post-Processing算法是通过将全分辨率的AO输出拆分成多张子图的AO输出,而每张子图的AO输出都是用固定的采样pattern,只是在各个子图之间做随机来降低cache miss率从而提升算法性能的,具体可以参考此前做过的关于Cache-Efficient Post-Processing的学习分享,从这个角度我们也知道,Cache-Efficient Post-Processing实际上不只是可以用在HBAO上,还可以用于其他的AO乃至更广泛的类似算法上。

下面给出上面几种算法在GTX 680上以1920x1080分辨率进行渲染时的执行效率对比(数据来源):

效率对比

HBAO+的问题在于在开启TAA的时候,由于前面帧数累加过来的数据的影响,在动态场景中或者相机突然移动的情况下,AO效果会有残影(要反应一会儿才能跟上?)。

4. SSDO

SSDO是Screen Space Directional Occlusion的缩写,是Ritschel在2009年提出的一项全局光模拟技术方案,相对于SSAO等AO技术,SSDO技术不但可以用于实现传统的AO模拟,同时还能输出平行光的阴影效果,同时如果增加一些额外的消耗,还可以实现物体颜色的反光与透光效果,而这么多的效果加成,其实现消耗并没有特别高,只是比SSAO稍微高一点,正是因为这个表现,SSDO也是各大游戏厂商常常考虑使用的方案。

Ritschel指出现存的各种AO技术方案是在原理上做了简化(将乘积的积分近似用积分的乘积来替代),其实施效果距离真实物理是有差别的,而SSDO方案通过directional occlusion pass与indirect bounce pass两个pass可以得到更为精确的全局光照模拟。

在directional occlusion pass中,SSDO跟SSAO一样,也会以法线为up方向发射若干射线,不同的是每个射线会给定一个随机的长度表示采样点的距离(SSAO方案会直接以半径为最大距离计算碰撞),得到若干采样点之后,判断各个采样点的遮挡信息,计算未被遮挡的采样点的相关信息(如方向,颜色等)以及遮挡点对应的碰撞点的颜色数据信息。

在indirect bounce pass中,将各个遮挡点看成一个点光源,其亮度为对应点的颜色,而非遮挡点则直接取用当前被计算位置的颜色数据,最终根据法线进行加权输出最终的颜色。

关于SSDO的其他细节,此前做过一篇学习报告,感兴趣的同学可以前往翻阅,这里就不做过多展开了。

5. AO Volume

AO Volume主要是为了解决传统AO算法只能实现小尺寸范围内的AO信息输出的问题而提出的,其特点在于不上不下:相对于精确的大尺寸阴影而言,这个算法给出的结果在质量上就比不了,但是性能上确实有较大提升;而相对于无法兼顾远距离阴影的传统AO算法而言,这个算法虽然速度上相对要慢很多,但是却能够给出一个远距阴影轮廓,在质量上取胜。

这个算法的核心要点在于:

  1. 某个多边形P对于某个法线为的点(假设此点为原点)的AO结果可以用BRW89中的公式进行计算:
    AO_P(\vec{n}) = \frac{1}{2\pi} \sum_{i = 0}^{k - 1} cos^{-1} (\frac{\vec{p_i} \cdot \vec{p_j}}{||\vec{p_i}|| \cdot ||\vec{p_j}||}) \vec{n} \cdot \frac{\vec{p_i} \times \vec{p_j}}{||\vec{p_i} \times \vec{p_j}||}

  2. 通过对场景中的所有多边形以AO半径为范围(即此多边形的影响范围是有限的)进行预计算(用此多边形对影响范围内的各个voxel的AO进行更新,经过所有多边形更新完成后,此点的AO受场景影响的AO信息就确定了),可以得到场景各个位置的AO结果,将这个数据以voxel形式(可以考虑稀疏八叉树,也可以直接写入到volume texture中)保存下来在运行时进行取用即可。

第二点中提到的实现过程也可以用在运行时,不过需要承担运行时对场景中可见的mesh数据进行一遍额外的绘制的消耗(不过因为shader执行简单,所以消耗也不会太高),但是对于静态物件而言,此部分数据是完全不变的,因此可以通过离线预计算出静态物件对场景的AO信息,在运行时的时候直接取用。

算法执行效率

上面的截图来自于AO Volume的原论文,整个算法的对比标准为RayTracing全局光方案,这里说的AO Volume方案指的是图中的AOV(new)方案,而Crytek方案指的是SSAO,从执行效率来看,在得到更优质量(更小误差)的情况下,AOV方案比SSAO方案的消耗更低(从这个角度来看,也可以考虑尝试使用实时方案)

6. Capsule AO/Shadow

Capsule AO也叫Capsule Shadow,是一种用胶囊体(Capsules)对动态物体(通常为带蒙皮的模型,比如角色等,可以用多个胶囊体来包裹四肢以及躯干等)进行模拟,以实现非直接光照环境下的动态软影模拟的技术。

Capsule AO实际上是脱胎于[Ren2006] & [Sloan 2007]中用球谐函数SH来对球形遮挡体的可见性进行模拟的一项技术,不过因为这原技术在表达长距离软影上的高消耗(至少需要2阶球谐函数进行表达),因此顽皮狗的Michal Iwanicki在其基础上推陈出行创造了Capsule AO。

与原技术一样,Capsule AO最开始也是用于表示球形遮挡体对场景的遮挡作用,对于场景中任意一个待计算点而言,其接收到的遮挡信息主要由两个分量组成:

  1. 环境光Ambient Occlusion,这个分量是通过计算球形遮挡体在待计算点上方向半球上的投影面积所占比例来得到。
  2. 方向光Directional Occlusion,这个分量则是通过从待计算点发射的以主光源反方向为方向的射线为轴线以一定角度(此角度由美术同学控制,用于调整阴影软化程度)为锥角的圆锥与球形遮挡体的相交区域所占比例求得。
Capsule AO计算示意图

对于环境光分量而言,其计算过程非常简单,有现成的公式可以使用;对于方向光分量而言,虽然也有现成的公式可供使用,但是由于计算过程稍显复杂,在运行时计算会不太划算。Michal Iwanicki给出了一个预计算方法来降低这一步计算的消耗:通过蒙特卡洛算法在离线时预计算出遮挡体与被渲染点几何关系与遮挡值的函数关系,并将结果存储到一张贴图中,在运行时直接采样使用。

示意图
预计算贴图

对于一个给定的锥角而言,遮挡值可以用遮挡体对于当前点的张角(subtended angle )以及到球心的射线与椎体轴线夹角的函数来表示,这个函数的结果被存储在一张2D贴图里(如上图所示),对于不同的锥角,可以分别输出一张贴图,多个2D贴图构成一个3D贴图即可。

锥角影响

如上图所示,不同的锥角影响着阴影的软化程度,锥角越大,软化程度越高。

上面只介绍了单个遮挡球对着色点的遮挡输出,对于由多个遮挡球表征的物体而言,某个点接收到的遮挡数据等于每个遮挡球的输出数据相乘的结果(这个做法虽然不是百分百的物理正确,理论上可能会导致双重遮挡,但是实际表现并没有出现需要注意的瑕疵)。

Capsule AO最开始是顽皮狗用在PS上的,整个算法的射线检测是放在SPU上计算的,总的时间消耗大概为6~7ms,但是由于均分到6个SPU上,因此总消耗不超过2ms,且在这个过程中GPU完全不受影响,可以并行处理其他事情,效率还算挺高的。

为了进一步加速计算的速度,Michal Iwanicki还给出了一个用胶囊替代球体来计算的优化处理(这也是为什么这个算法被称作Capsule AO的原因)。

胶囊体实际上可以看成是球体沿着某个方向进行拉伸处理得到的结果(即无数个连续的球体组合而成),在计算方向光AO分量的时候,可以将前面提到的椎体转换到胶囊体空间,之后沿着拉伸的坐标轴进行缩小(包括角度与轴线方向,这个收缩过程会导致输出的几何体不再是径向对称,从而使得相交检测变得更加的复杂,不过在给出的算法中,将这个不对称直接忽略,还是按照之前的相交检测算法进行处理,虽然结果不太精确,但是看起来并没有太大的瑕疵,且性能上可以得到更大的提升),从而使得输出的几何体正好对应于之前的球形遮挡体,之后就可以直接复用之前的算法进行计算处理。

Capsule AO通常是在1/4分辨率上进行计算输出的,由于输出的阴影本身就是低频的软影效果,因此这个降分辨率计算的做法并不会导致结果的异常,但是在性能上却能带来很大的提升。

总结一下,Capsule AO通常用于实现动态物体在场景中的软影效果,其输出的结果包含环境光下的AO与方向光下的AO,对于方向光下的AO,如果开启的是动态阴影的话,这一部分可以直接通过shadow map输出从而可以忽略,而对于整个场景使用lightmap来输出阴影效果的情况而言,这种做法就能够很好的兼顾性能与动态物体的阴影表现效果了。

7. DFAO

DFAO是Distance Field AO的缩写,这是一种通过使用SDF(Signed Distance Field)数据计算AO的技术方案。

由于场景是通过SDF进行表达的,因此对于每个像素而言,沿着法线方向发射多个cone,每个cone通过cone tracing可以计算得到这个cone的被遮挡程度,而在SDF表达方式下,这个计算过程是比较容易的(Sphere Tracing),最后将多个cone的遮挡程度叠加到一起就能得到一个较为准确的遮挡效果。

如果觉得多个Cone的计算消耗过于重度了,也可以考虑沿着bent normal方向只发射一个cone的方式来减少计算量。

DFAO的优点在于可以实现较大尺寸的AO效果,其缺点在于场景需要使用SDF进行表达,而这种表达方式目前在包体以及内存消耗上都是一个问题。

更进一步的技术细节可以参考【Siggraph 2015】Dynamic Occlusion with Signed Distance Fields

8. GTAO

GTAO是Ground Truth AO的缩写,这是一种对HBAO方案的改进。

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

具体可以参考【Siggraph 2016】Practical Realtime Strategies for Accurate Indirect Occlusion

9. VXAO

VXAO是Voxel Accelerated AO的缩写,这是在voxel表达的场景中的AO计算方案。

在Voxel表达的场景中,我们可以获取到更多的几何信息,因此就能够提供更为精确的AO效果,但是其计算消耗比其他的AO方案要高得多,实际上VXAO是VXGI方案的一部分。

VXAO方案总的来说可以分成三个步骤:

  1. voxelization,将场景从面片转换成voxel,这个过程的消耗取决于面片的数目(场景的复杂度)
  2. voxel post-process,这个过程会完成voxel的clearing、filtering以及downsampling逻辑,性能消耗取决于voxel的数目(场景分辨率)
  3. cone tracing,这个计算过程是在屏幕空间完成的,通过为每个像素分配多个cone(也可以只分配一个),在voxel空间完成cone的遮挡情况查询(cone tracing)即可得到最终的AO数值。

具体可以参考【Cyril Crassin 2011】Interactive Indirect Illumination Using Voxel Cone Tracing

VXAO方案的优点在于:

  1. 效果十分优秀
  2. 对于动态场景的支持也十分成熟
    不足之处在于:
  3. 计算消耗实在是有点高
  4. 可能只有高端GPU才能够支持voxel场景

AO算法相关的一些概念与技术手段

1. Bent Normal [10, 11]

我们在计算环境反射的时候通常直接使用当前像素从法线贴图中取出的法线进行,但是这个像素可能周边会存在一些遮挡,导致直接使用常规法线进行采样得到的结果不甚精准;

我们在进行光照计算的时候,直接使用未考虑周边遮挡的法线进行计算,得到的结果可能跟真实结果也会有一些差距;

如果考虑周边遮挡关系,将当前像素的法线做一个偏离,指向未被遮挡区域的中心点方向(即可以理解Bent Normal指的是未被遮挡区域的平均方向),那么上面提到的两个问题就可以得到很好的处理,而根据周边遮挡关系对常规法线进行处理计算后输出的新的法线,我们称之为Bent Normal。

Bent Normal示意图

使用Bent Normal有如下的一些好处:

  1. 可以增加环境光采样时的精度,得到更高质量的环境反射效果
  2. 可以在常规shading中得到带有AO效果的光照表现
  3. 可以用于增强离线AO效果,使之更加的接近Ray Tracing的Ground Truth

AO实现算法的优化手段

  1. 采样点与射线可以通过随机算法以噪点来减轻瑕疵
  2. 可以通过降低AO处理时分辨率来减少消耗(比如用1/2 x 1/2分辨率来进行计算处理)

参考文献

1. Screen space ambient occlusion
2. SSAO
3. Image-Space Horizon-Based Ambient Occlusion
4. NVidia - HBAO+
5. HBAO(屏幕空间的环境光遮蔽)
6. Approximating Dynamic Global Illumination in Image Space(SSDO)
7. 【GDC2013】Particle Shadows & Cache-Efficient Post-Processing
8. Ambient Occlusion Volumes
9. Lighting Technology of "The Last Of Us"
10. Bent Normals and Cones in Screen-space.
11. Bent Normals and Cones in Screen-space - Poster
12. AMBIENT OCCLUSION: AN EXTENSIVE GUIDE ON ITS ALGORITHMS AND USE IN VR
13. Horizon-Based Ambient Occlusion 算法说明
14. Practical Realtime Strategies for Accurate Indirect Occlusion

你可能感兴趣的:(Ambient Occlusion技术方案综述)