写在前面

昨天从美术的角度出发,对AO贴图参与到次世代建模流程中的过程进行了学习。今天从图形学角度学习环境光遮蔽。

封面图截取自实时渲染GI|Ambient Occlusion:AO、SSAO、HBAO

1 AO

AO,即Ambient Occlusion,环境光遮蔽(感觉叫间接光遮蔽更好kkk),被设计出来模拟全局光照,模拟物体之间产生的阴影,在不打光的时候就能增加体积感。它是实现全局光照中部分物体局部光照和阴影真实化的一种技术方式,函数实现方式并非严格遵循现实的物理模型,但其效率高、效果好的优点还是被当前游戏广泛采用(叙述参考自环境光遮蔽)。

1.1 AO技术的发展*

这里可能题外话比较多,不仅仅是游戏层面的。

  • 3D软件:实际上,AO这项技术是在2002年被提出的,早期应用在了各种3D软件上,需要预渲染、速度还慢,并没有一开始就用在游戏上
  • 动画:2005年获得奥斯卡提名的《鲨鱼故事》(Shark Tale)运用了类似原理的Ambient Occlusion技术
  • 绘画领域:AO画法——通过绘制AO图层来获得3D绘画效果
  • 游戏:AO技术(SSAO)真正用于游戏其实是在2007年

接下来涉及的内容都会是游戏领域的应用了。

1.2 AO解决的问题

这部分可以结合2.2小节一起理解。

补足光线弹射的不足

对于游戏画面来说,如果不开AO,系统默认唯一光源是太阳or月亮,其他在现实生活中本应该是真实光源的火把、路灯等,本身都是可以小范围、固定的阴影,但在游戏制作中为了节省性能肯定不会每个光源都给个真实的点光源,一般都是一个个模型(总不能每个小光源都渲染出来吧),这样这些小范围光源产生的阴影一定是没办法渲染出来的。需要AO去帮助解决。

物体凹凸感

对于一些有细节雕刻的物体,例如大的建筑物,如果不开SSAO,整个物体表面看上去会很“平”、没有凹凸感,就像是一张平平的贴图(但其实低模实际上就是平平的kkk),不真实。开SSAO后,那种凹凸感就出来了。

拿老头环画面举例(用了《艾尔登法环》PC版优化指南提供的素材,侵删):

上(AO关闭) 下(AO开到最高)

2 AO理论

AO Theory虽古早但不能不学!AO理论衍生自反射方程——AO影响光照计算的过程与漫反射和高光反射都有联系。

2.1 讨论反射方程

(我不确定这里能不能这样取标题,但我觉得本身AO就是在间接光层面给直接光照一个补充的,所以这样取问题应该不大吧。)

拿漫反射来说,以一个Lambert表面为例,计算表面接受来自一整个半球\Omega各个方向的入射光线,即辐亮度。在不考虑AO的情况下,出射光的辐亮度L_{0}与入射到表面的辐照度(Irradiance)E成正比,辐照度只与着色点p位置和着色点法线方向n有关。再假设各个方向入射的辐亮度(Radiance)是一个常量L_{i},则计算整个半球面上的辐照度E

E(p,n)=L_{i}\int_{I \in \Omega }^{}(n\cdot l)^{+}dl=\pi L_{i}

和出射光的辐亮度L_{0}的计算公式为 

L_{0}(p,v)=\int_{\Omega }^{}f(l,v)L_{i}(p,l)(n\cdot l)dw=f_{Lambert}\pi L_{i}

其中,BRDF中的漫反射项f_{Lambert}=\frac{\rho }{\pi },与传统模型简单的diffuse不同,\rho是储存物体albedo材质的颜色,RGB三个通道,为一个Vector3f量。

计算完辐亮度后,物体表面某点的辐射(光照)强度为:

I=L_{0}cos\theta  

由于L_{0}是个恒值,所以会呈现出一个光滑的物体表面。于是,当一个场景(仅漫反射)不加入AO渲染,由于漫反射Radiance是常量,这意味着场景中只要有光源,所有物体表面着色效果都一样,下图(图截取自参考文章)是漫反射+阴影后的效果:

计算时认为光照不到的物体直接全部处在阴影下,跟阴影融为一体了,并没有考虑到可见性的影响,先放上加入AO后的效果(图源仍然是参考文章):

2.2 讨论可见性

这里的可见性我没有找到明确的解释,我的理解分为3个层面:

  • 物体处于阴影下的软阴影
  • 由于光弹射(间接光)带来的可见性
  • 丰富物体凹凸感效果

处于阴影下的软阴影

在真实世界中,特别是一个场景中有多光源的影响,即使是处在其他物体阴影下的物体,还是会向周围投射一定的软阴影,如上图的这个小细节:

本应该处于太阳光照射下阴影中的桌面,还是会向地面投射淡淡的软阴影。这样做是可以丰富人物、物体的立体感的,不至于带来悬浮、失真的非真实感。

光弹射带来的可见性

写到这里才发现,这两条实际上都是属于间接光范畴,我分得有失偏颇,但确实更好的帮助我理解,就先这么着吧:)

上图中除了阴影部分,会发现处于太阳光阴影下的场景仍旧可见,上图应该是没有加漫反射颜色,如果上色了就感觉像是“在自身颜色基础上叠上了一层阴影的灰”的效果,这就是AO模拟全局光照的另一个体现层面。

物体凹凸感

这里在1.2小节已经提过了,就是下面这种感觉:

同样,也是增加立体感。

2.3 公式中加入AO

那我们如何考虑这个可见性?我们假设遮挡处的光线为0(并不考虑光线弹射),直接手动修正可见性:

E(p,n)=L_{i}\int_{I \in \Omega }^{}v(p,l)(n\cdot l)^{+}dl=\pi L_{i}

L_{0}(p,v)=\int_{\Omega }^{}f(l,v)L_{i}(p,l)(n\cdot l)dl=f_{Lambert}\pi L_{i}

其中, v(p,l)——是一个随射线相交距离变化的量,如果沿入射方向很快收到了遮挡(有交点),则v(p,l)的值就越小,理解它可以结合下图(图源参考文章),

它表示了当前着色点p在入射方向l上的可见性,[0,1]上由不可见(完全遮蔽)到可见(无遮蔽),以此为画面增加更多的细节。单独拉出来一个积分项,那这一项就是正经的计算环境光遮蔽项了:

k_{A(p)}=\frac{1}{\pi }\int_{\Omega }^{}v(p,l)(n\cdot l)dl

表示了半球面非遮蔽率,于是辐照度表示为

E(p,n)=k_{A}\pi L_{i}

3 AO的优化

3.1 Bent Normal

还记得上一篇烘焙AO贴图时的过程吗?AO贴图一般会跟法线贴图一起烘焙出来,在渲染时会使用物体自身的法线去采样AO贴图。那如果想体现不同环境光照下的阴影渐变,就换个法线贴图采样——Bent Normal去采。

关于Bent Normal可以参考UE5官方的介绍:Bent Normal Maps

问题1 单一环境光照

上述AO都是假设单一环境光照,即Lambert模型中的Radiance相同。这就导致:模拟自遮挡的阴影效果不会随着物体凹凸的程度改变阴影的深浅

用Bent Normal替代Normal做shading,可以优化上述的问题,获得更好的阴影效果。对比如下:

问题2 漏光问题

漏光问题大概是下面这种情况:

UE文档截图

解决方案就是用Bent Normal去采样环境贴图,从更优化的方向计算采样的光线。

3.2 更好的AO系数

问题 多次弹射 MutiBounce

其次,由于推导AO时不考虑光线弹射,假设遮挡处光线为0,这将导致加上AO的效果与光追AO的效果相比会暗一点。那么如何弥补丢失的光线?

MultiBounceAO

用更好的AO系数,目前用的较多的是MultiBounceAO函数,如下:

截图自 游戏中的全局光照(三) 环境光遮蔽/AO

4 AO的实现方式

实现AO,要么离线预计算要么实时Render。

4.1 AO预计算

AO贴图

  • AO贴图如何烘焙

AO贴图就是预计算AO,【技术美术美术部分】AO贴图的烘焙及应用介绍了AO贴图如何烘焙及应用(3D软件中应用)。

  • AO贴图如何参与到渲染中

这里结合Unity的代码来谈谈AO贴图如何参与到渲染中的,Unity Standard shader提供的放置AO贴图的参数项:

        _OcclusionStrength("Strength", Range(0.0, 1.0)) = 1.0
        _OcclusionMap("Occlusion", 2D) = "white" {}

Unity的Standard shader将GI计算分为两部分:

  • 直接光照:BRDF
  • 间接光照:UnityGI

也就是整个计算为:color =  Direct.diffuse + Direct.specular + Indirect.diffuse + Indirect.specular

UnityGlobalIllumination.cginc中的函数UnityGI_Base函数,计算间接光diffuse部分:

inline UnityGI UnityGI_Base(UnityGIInput data, half occlusion, half3 normalWorld)
{
    ...

    o_gi.indirect.diffuse *= occlusion;
    return o_gi;
}

以及UnityGI_IndirectSpecular函数,计算间接光specular部分:

inline half3 UnityGI_IndirectSpecular(UnityGIInput data, half occlusion, Unity_GlossyEnvironmentData glossIn)
{
    ...
    return specular * occlusion;
}

最后都有一个*occlusion, 这个参数就是通过采样AO贴图得到的结果。

结合上述代码,更能看出,AO贴图就是直接简单地将采样出的值直接与GI间接光计算结果相乘,这样AO贴图中的黑色部分就可以抑制环境光强度了,以达到环境光遮蔽的效果。

AO Volume

严格来讲这也是AO预计算技术的一种,因为是离线对需要产生AO Volume的物体做local space下的遮挡信息计算,效果对比的部分会在后面某个小节涉及。

4.2 实时Render*

离线烘焙AO,在光照计算时直接采样,这是预计算的方法。随着硬件的提升,足以支撑实时计算AO,这就是下一篇文章会讲的,基于AO衍生出的SSAO等算法。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐