本文是对Approximating Dynamic Global Illumination in Image Space一文的翻译与学习,这篇文章介绍的是一种称之为SSDO的全局光照实现技术。
摘要
以目前的硬件水平来说,还难以实现物理真实的光照系统,一些复杂的光照效果通常是采用近似模拟的方法来实现的。一个常见的例子是环境光遮挡效果(Ambient Occlusion, AO),目前主流的近似方法是SSAO(Screen Space AO),这种方法通过在屏幕空间中搜集从当前像素输出的半球射线被阻挡的比例来实现对世界空间中对应位置的间接光强度的模拟,而实际上,这种方法不但可以用来计算遮挡情况,还可以进一步实现平行光阴影(Directional Shadow)以及间接光透光效果(indirect color bleeding)的模拟。本文针对这两种效果给出了一种屏幕空间的实现方案,且这种方案相对于经典的SSAO而言,只需要一个非常小的新增消耗就可完成,此外这种方案还可以跟其他用于模拟宏观结构光照效果的技术结合使用,其效果最差的情况下也不会低于SSAO,且不会引入额外的瑕疵。同样,由于此方案是屏幕空间的,因此也不会随着场景复杂度的上升而导致消耗的增加。
1 简介
对于大型动态场景而言,依然无法实现实时的全局光照,通常都是采用一些近似方法来进行模拟仿真,其中AO是最常用的一种方案,由于其实现简单计算高效,因而常用于电影与游戏行业中。但是,AO实现方案中可见性(visibility)与光照(illumination)是解耦的,因而其实现效果相对于物理真实的表现而言显得非常粗糙。
AO效果通常会使得洞穴类(cavitry)几何体变暗,但是却忽略了像素上所有输入光的方向信息,本文给出的方案对SSAO方法进行了扩展,以期望得到更为逼真的全局光效果,这种方法被称为SSDO,其基本特征给出如下:
会考虑输入光的方向信息
会包含间接光照的一次反射(one bounce of indirect illumination)
能够对标准的基于物体(object-based)的全局光照起到很好的补充作用
相对于SSAO而言,只需要增加少量的计算消耗
2 前人工作
Ambient occlusion (AO)(Cook and Torrance 1981,Zhukov et al. 1998)方法实现简单,计算速度快。不过物理真实的全局光照实际上是对各个方向上的illumination与visibility乘积的积分,而AO方法实际上却是采用illumination与visibility积分的乘积得到的,因此AO方案的实施效果相对于正确效果而言存在一定的差异。
对于静态场景而言,AO数据可以通过离线方法烘焙得到,Langer and B¨ulthoff 2000给出的实现方案通过将静态烘焙AO数据与动态光照结果相结合的方法可以得到一个高效的全局光效果。
对于动态场景而言,Kontkanen et al. 2005通过一种叫做AO Field的方法可以在刚体动态物件(允许刚体移动与旋转)存在的场景中得到实时全局光效果。而Kontkanen and Aila 2006给出的方案则支持特殊的蒙皮动画全局光照效果。Bunnell 2005给出的方案支持可形变物体与间接光反射效果,这种方案是通过使用一系列的disk对物体进行模拟而实现的。Hoberock and Jia 在2007年给出了一种更为鲁棒的实现方案,这种方案是在Christensen 2008方案的基础上,将其扩展到支持point-based ambient occlusion以及内部反射(interreflection)特性。Mendez et al. 2006方案通过对相邻面片数据的albedo color进行平均来实现color bleeding效果。
上面给出的方案不是依赖于surface数据离散化,就是采用ray-tracing方法,而这两种技术在具有大量动态物体的场景中都存在很大的局限性,而当前的可交互场景比如游戏中,都属于这种大量动态物体的情况。因此,屏幕空间AO方案开始涌现(Shanmugam and Arikan 2007; Mittring 2007; Bavoil et al. 2008; Filion and McNaughton 2008)。这种方案的特点是实现简单,计算高效,且可以看成是一个后处理方法,不需要依赖于几何体的surface信息,当然这种方法也是有缺点的,这个后面再说。
SSAO方法对于全局光的模拟效果还有点粗糙,缺少了PRT (Lehtinen and Kautz 2003)方案中对于directional occlusion(DO)以及内部反射(interreflections)的支持。光照烘焙需要存储大量的数据,考虑到空间消耗,通常会采用数据压缩的方法,这就导致了空间分辨率与方向分辨率(spatial & directional resolution)受限。PRT方法在场景比较简单或者说不复杂的时候,对于比较远距离的光照以及静态物体能够给出比较好的效果,对于实际游戏场景的支持可能会需要不低的消耗,而SSAO的优点在于计算简单。
本文给出的方案能够支持small surface details,所有角分辨率(all angular resolution),全频IBL(all frequency image based lighting)以及点光阴影。总的来说,就是在SSAO的基础上增加了对DO以及indirect bounce的支持,且能够保证real time级别的消耗。
3 Near-field Light Transport in Image Space
本文采用Segovia et al. 2006年提出的方法在贴图空间(image space)计算光照传播(light transport)输出。这种方法的输入是包含位置position与法线normal的framebuffer,输出的则是包含了光照信息像素(illuminated pixel)的framebuffer。整个算法包含两个pass,一个用于计算directional occlusion,另一个用于计算indirect bounce。
Direct Lighting using DO
标准SSAO的计算过程可以分成如下两步:
根据相邻像素的数据计算出当前像素的可见性,这个可见性称之为此像素的遮挡数据(occlusion)
将这个遮挡数据与像素未被遮挡时的光照信息(考虑所有输入光照方向)相乘,输出当前像素的illumination。
这个计算方法将可见性与illumination分成了独立的两部分,严格来说,这种做法是不正确的。本文尝试使用如下的方式来修复这种解耦:
对于每个待处理的像素而言,假设此像素的3D位置为,法线为, 其直接光照辐射亮度可以通过在半球面上均匀分布的N个采样方向计算得到,每个采样方向对应于一个固体角(solid angle)::
其中对应于方向上的输入辐射亮度,表示的是当前输入方向的光照是否可见,而表示的是漫反射BRDF,这个公式可以解释成illumination x visibility的积分(离散化之后就是求和)。
输入辐射亮度通常可以通过点光或者环境贴图等方式直接得到。
为了避免通过ray-tracing方式得到visibility数据,这里采用屏幕空间方法对此项进行近似:
- 对于这里的个采样点的每一个,生成一个范围在[0, ]的随机数,用于表示此点的延伸距离(延伸方向为), 是用户定义的长度。
- 这样就得到了个新的点:,这些点从方向上来看,均匀分布在半球上,且以当前点的法线为中心。
- 由于点所在的表面可能不是平面,因此上面得到的一系列点中可能部分处于之上,部分处于之下,如下图左图所示。处于之下的采样点可以看成是遮挡物occluder
上图给出了的一个示意图,其中A, B, D三点可以看成是处于表面之下的遮挡物,而C点可以看成是可见采样点,那么在实际计算中应该怎么判断采样点是否可见呢?简单来说,就是将采样点通过坐标变换从世界空间投影回屏幕空间,并根据对应点的深度来判断是否可见。
与上面的SSAO方法不同,SSDO方法不再计算所有采样点的illumination信息,而是只计算可见采样点(比如上图中的C点)的相关信息,其中会包含对于最终光照表现有重要影响的方向信息,此外,如果输入光照的颜色数据也会记录下来,用于实现带颜色的阴影效果,如下图所示。
Indirect Bounces
想要在渲染结果中包含一次间接光反射,就需要用到前面DO pass输出的framebuffer中的direct light信息。对于前面pass中的每个occluder(A, B, D),都会被用作当前点所接收到的间接光反射的输入点,而occluder在屏幕空间framebuffer中对应的像素的color会被当成此一点的反射辐射亮度(sender radiance),反射方向与此occluder在表面上对应位置的法线一致,之所以不直接将反射方向指向点,是为了避免一些背向点法线的采样点(比如上上张图中的A点)对点施加影响。
这些occluder对于点的辐射亮度的影响可以用如下公式表示:
其中表示的是点与第i个occluder之间的距离(为了避免除0错误,可以将这个数值的向下clamp到1),与表示的是sender(也就是occluder)以及receiver(也就是点?)点处的法线与光线传播方向之间(注意sender跟receiver的光线传播方向是不同的,前者是光源的光照方向,后者则是sender反光方向,也就是从sender指向receiver)的夹角(为什么要用这个余弦呢?是因为表面倾斜导致的实际反光面积的损失吗?)。表示的是occluder处的反射patch面积。如果假设的表面是平面的话,那么可以设定的初始值为,而考虑到表面的倾斜情况(曲面),实际上这个值会更大一点,通过调整这个数值,可以控制color bleeding效果的强度。在上上张示意图中,点A对于点P没有间接光贡献,同样点C由于对于点P没有遮挡关系同样也没有间接光贡献。
Implementation Details
本文提出的方法与标准的SSAO方法(Shanmugam and Arikan 2007)在实现步骤与消耗上差不多,本文提出的方法在visibility一项的计算上与SSAO方法是相同的,不同的是本文需要一些额外的计算用于着色处理。在这里给出的例子中,需要为已知的重要光源(比如说太阳光)增加一些额外的采样点,这些采样点会通过远距离物体产生的shadow map而非屏幕空间可见性数据来计算阴影效果。具体来说,这里会通过M x N套贴图用来存储M套采样数据,每套采样数据包含N个采样点,在运行时每个像素只需要从M套数据从任选一套出来使用即可。在最后还会通过一个geometry sensitive的模糊处理pass(Segovia et al. 2006)滤除由于采样点稀疏而导致的噪声。
4 Multiple Pixel Values
由于本文给出的方法也是在屏幕空间实施的,因此当前面说的indirect light sender也就是occluder在屏幕空间不可见时,可能会导致异常。
上图给出的效果图展示了,当occluder不可见时,本文给出的方法计算的color bleeding会逐渐消退以至于最终不可见,总的来说并没有出现什么奇怪的瑕疵,不过这个效果是在对结果做了偏移处理(biased,具体是啥?)之后得到的。如果偏移处理幅度比较浅的话,依然可能会存在问题(有什么问题?可能会采到错误的屏幕像素上的颜色,导致错误的color bleeding),这里提供了两种方法用于解决此问题,分别是Depth Peeling与Additional Cameras。
Single-depth Limitations
由于我们在屏幕空间只能拿到最前面一层的framebuffer & depthbuffer数据,因此前面介绍的occluder test的方法实际上是一种近似。在这种近似方法中,采样点可能会得到错误的归类,比如上面左图所示。在一些特殊的用例中,可能会出现本该是occluder的会被判定成可见,而本该是可见的却被判定成occluder。对于一个本可能是occluder但是被判定为可见的采样点而言(如上面左图中的采样点B),可以通过增加这个方向的采样点数目来规避(如右图所示),而对于被判定为occluder的可见的采样点(如左图中的A点),在单个深度buffer的情况下,就没有办法判定出来了。
Depth Peeling
Depth Peeling方法(Everitt 2001)说的是,通过对场景进行n遍绘制,可以得到每个像素的前n个depth数值,之后可以使用这个数据来改良前面的occluder test算法。当使用这种方法进行occluder test的时候,就不是只检测当前采样点与第一层depth(最前面的depth)数据的遮挡关系,同样还需要检测当前采样点与第二层depth的遮挡关系,对于一个封闭的几何体而言,通常第一层跟第二层depth对应的分别是这个几何体的frontface与backface,如果采样点深度大于第一层depth又小于第二层depth,表明此采样点处于这个几何体内部,此时这个采样点就是真的occluder了,当然对于一个depth复杂度更高的场景而言,depth可能不止两层,所以还需要比较第三层跟第四层的关系,以此类推(Lischinski and Rappoport 1998)。
上图展示了单个点光下的屏幕空间阴影效果,每个像素的屏幕空间阴影是通过N个采样点在光源空间的遮挡关系统计得到的,这N个采样点在从当前像素向着光源(点光)延伸方向上均匀分布,采样点范围为,因此最终实现的阴影效果跟这个数值有直接的关系。而正确的阴影效果只能通过depth peeling得到,这个方法的平均需要30%的消耗(相对于什么而言?),除了阴影计算所需要的可见性数据之外,这种方法还能够得到多层framebuffer下的color bleeding效果(因为按照depth peeling计算方式,同样可以得到多层framebuffer的直接光照数据)
Additional Cameras
Depth Peeling方法能够修正单个depth buffer限制下的很多瑕疵,而同样的,采用不同位置的相机绘制的结果也同样可以得到不同视角下的depth layer数据,从而解决单视角下隐藏区域数据导致的瑕疵。
采用不同相机位置绘制的view,不但可以得到一些单视角情况下超出屏幕范围的数据,还可以用于处理单视角下处于掠角(grazing angle)状态的面片情况,掠角情况下的面片对于color bleeding的贡献基本上是可以忽略的,从而导致数据缺失,而如果增加一个camera 视角的数据可以有效解决这个问题。新增的这个camera肯定是要与原始camera完全不同的,最好是以物体中心位置为端点,相对于原始camera成90度角。不过这种做法会导致新的occlusion问题:在某个视角下可见的物体,换一个视角可能会被其他物件所遮挡。当然,这个问题同样可以通过depth peeling方法解决。另一个快速解决方案就是采用一个比较大的近裁剪距离,将前面的遮挡物统统裁掉,不过这个数值可能不太好控制。作为一种折中方案,本文采取的是使用四个额外的不同位置的相机得到的标准depth buffer来修复瑕疵,这四个额外的相机所朝向的方向都跟原始相机所关注的区域一致,而每个额外相机的相对原始相机的位置则是按照一个归一化的displacement向量通过手动方式放置的,为了能够较好的适配场景内容,每个displacement向量都会通过场景的bounding sphere的半径进行scale。
如上图所示,使用一个额外的相机,可以从被遮挡的区域,屏幕外区域以及掠角表面区域取得color bleeding数据,为了降低内存消耗,额外相机对应的framebuffer将采用一个低分辨率绘制。这个方法的缺陷在于随着相机数目的增加,顶点shader执行时间会线性增加。对于一个如上图所示的简单场景而言,使用四个额外相机的计算消耗大概是58%,而对于面片数超出1M的复杂场景而言,这个数值就是160%。而如果使用另外一种实现方法(比如iso-surface raycasting或者point rendering),每个view用若干个点表示,每个点对应一条射线,将表示一个view的点数目恒定下来,那么此时渲染multiview的消耗将跟普通的single view的消耗一致。
从实施帧率对比来看,此方案想要在运行时使用,代价还是太高了。
5 Integration in Global Illumination
屏幕空间算法对于复杂场景的全局光照效果是有帮助的。为了避免直接使用原始几何物体数据进行计算带来的高消耗,这里给出的方案是先用一些粗糙的低模来计算全局光照,之后在运行时通过屏幕空间算法来对这个效果进行优化。这个思路在如下两种应用场景中得到了验证:
- 环境贴图光照(IBL)
- Instant Radiosity(Keller 1997),在这种算法中,间接光照是通过反射阴影贴图(reflective shadow map)计算得到的,因而产生了一系列的虚拟点光(virtual point lights,VPLs)。
跟前面介绍的算法的实现逻辑不同,这里不再计算小尺寸的阴影(small-scale shadows),而是计算全部的遮挡关系。因此间接光照数据需要考虑所有的VPLs,而间接光照可见性则需要从每个VPL对应的shadow map中来。当使用阴影贴图进行可见性检测的时候,这里需要创建一个简化的通过采样点表示的场景(比如使用depth map来表示)。另外,为了消除几何物体上的自阴影瑕疵,还需要为shadow map增加相应的bias,当然,这种做法的弊端就是导致一些阴影细节会丢失,尤其是当光照方向与表面方向垂直的情况。这些阴影细节的丢失可能会对效果造成重大影响,比如可能会导致使得物体看起来像是飘在空中的peterpan现象。当然,如果使用高分辨率的阴影贴图加上一套复杂算法,是可以消除这些问题的,但是其消耗会比较高,因此本文给出的是另外一套方案,通过屏幕空间算法重建这些阴影细节。
Shadow Mapping and Depth Bias
阴影贴图中每个像素的数值对应的实际上是这个像素中心位置处的depth数据,因此可以将一个像素看成几何表面上的一个small patch。
如上图所示,每个patch的朝向需要跟光照方向保持一致,所以每个patch的上半部分通常是处于光照之中的。而由于这部分区域相对于原始表面而言距离光源更近,因此会导致自阴影(self-occlusion)就不奇怪了(实际上是一半被遮蔽,一半被点亮)。而想要消除这个瑕疵,就需要将patch限制在原始surface之下,即将上图中的红色线条(表示原始patch)沿着光照方向推移至黑色线条位置,其实就相当于在阴影贴图上增加一个偏移数据,其中p指的是patch在世界坐标系下的尺寸,这个数值可以通过当前位置到光源的距离,阴影贴图分辨率以及当前点的张角(opening angle of spot)计算得到(这里说的是点光,对于方向光,只需要知道阴影贴图分辨率与当前阴影贴图覆盖的范围即可),而则是光照方向与表面法线方向的夹角。
Screen Space Shadow Correction
使用了前面的shadow depth bias方法之后,几何物体表面一些阴影细节(即几何微结构尺寸低于一个阴影贴图像素所覆盖的尺寸的情况下,这些几何信息所产生的阴影)将被丢失,这里尝试在屏幕空间中找回这些信息。在屏幕空间中,会先挑出那些没有被阴影覆盖的像素,由于前面一个处理中添加的阴影贴图bias是已知的,因此这里的基本策略是在这个未定义区域内(即上图中从红色线条到黑色线条的区域内)增加一些采样点。具体来说,就是在到这个线段上(其实覆盖的是原来patch中低于surface的上半部分,也就是P点左边的半部分区域,那么右边的半部分区域呢?根据光源的方向来看,右边的半部分不太可能会对P点产生投影。此外,这个地方只考虑一个方向,而实际上每个patch是二维的,需要考虑纵横两个方向的表现(当然,粗糙处理的话,可以将计算坍缩到一个方向上来进行计算))增加一些用户自定义的采样点,其中是指向光源的单位向量。对于这里的每个采样点,会按照前面第3章跟第4章所介绍的屏幕空间遮挡测试方法进行遮挡测试,如果其中一个采样点被测试为occluder,那么就将此点标记为P点的occluder,并在framebuffer中对应的像素上添加阴影效果。按照这种方法,阴影的精度就转换为occluder可见性的精度,只要每个occluder在屏幕空间中是可见的,那么就能够计算其在屏幕空间中产生的阴影。当然,这种方法只能处理那些尺寸高于屏幕空间像素在世界空间中尺寸的occluder,通过这种方法得到的实施效果如下图:
从实施消耗来看,这种方式对于帧率的影响实在是不小,都直接拦腰截断了。
Global Illumination
这张图给出了SSDO实施的效果对比。在这个场景中,环境贴图是通过一系列的点光以及每个点光所对应的shadow map来表示,左上角的图中,只使用了阴影贴图加上bias来渲染,可以看到一些阴影细节的丢失,而这个瑕疵通过直接乘上简单的AO数据并不能得到修正(Stewart and Langer 1997)。之后为屏幕空间中的每个阴影贴图像素,使用8个采样点来计算occluder阴影细节,效果实施如右上角图所示。
这张图是Instant Radiosity方法与本文方法相结合的实施效果对比。Instant Radiosity方法的一个相关问题在于,对于那些距离VPL比较近的阴影接收点而言,需要添加一个clamp操作来规避一些奇异点瑕疵(singularity artifacts),而对于相邻几何面片之间的反射数据可以通过在屏幕空间中计算得到,如下图所示:
虽然本文的实现算法也产生了奇异点瑕疵,但是采样点的局部密度相对VPL密度而言要高很多,而且这种屏幕空间修正方法可以扩展到任何使用VPL表示的光照类型,如面光源以及其他具有有限分辨率表示可见性的光源(Lehtinen and Kautz 2003; Ren et al. 2006; Ritschel et al. 2008)。
6 Results
Performance
由于方法是在屏幕空间中实施的,因此不论是directional occlusion还是indirect bounce都可以达到实时计算。下面的表给出了factory场景(1537K面片)中使用单个相机以及单层depth的消耗:相对于标准SSAO方法,DO计算只需要增加额外3.6%的消耗,而indirect bounce则需要额外的31.1%的消耗(猜测这个消耗主要在于带宽限制,而非计算消耗)。
Time-Quality Tradeoff
考虑额外的camera以及depth layer的话,那么计算消耗会增加30%~160%(2 depth layer & 4 additional cameras)。是否需要做这种处理,就看实际需要了,时间消耗是否值得这样程度的质量提升。
这张图给出了本文实施效果与PBRT(Pharr and Humphreys 2004)即Path Tracing的实施效果的对比。
Animated Scenes
由于是屏幕空间算法,因此对于场景动态与否基本上没有限制。
上图给出了可变的数值对于实施效果的影响对比,想要自动找到一个适合场景的数值是比较困难的,通常需要为 每个场景进行手动调整。对于每个密封的几何物体,初始的可以通过计算几何物体上的局部最大值(local maximum,什么东西的局部最大值?)的平均距离来得到。如果选择的数值较大,那么在single depth layer的情况中,在物体边缘可能会导致错误的阴影效果,其效果有点类似于depth darkening(Luft et al. 2006),这是因为如图6左图所示,处于最前方的物体可能会被错误的当成是occluder,虽然depth peeling可以解决这个问题,但是会有比较高的消耗。对于一些大尺寸occluder只有一部分处于半球hemisphere中的情况,可能会导致阴影裂缝(crack),为了避免这种瑕疵,这里会采用smooth-step函数对靠近sphere边缘的occluder的阴影进行平滑处理。