环境光遮蔽(Ambient Occlusion)“AO”为Amblent Occlusion的缩写,中文译为环境光遮蔽。在DirectX 10.1 API推出后,Amblent Occlusion升级为SSAO(Screen-Space Ambient Occlusion:屏幕空间环境光遮蔽);而在微软推出DirectX 11 API后,SSAO升级至HDAO(高解析度环境光遮蔽:High Definition Ambient Occlusion)。
我们有必要对AO(环境光遮蔽)的释义进行一个简单了解:AO是来描绘物体和物体相交或靠近的时候遮挡周围漫反射光线的效果,可以解决或改善漏光、飘和阴影不实等问题,解决或改善场景中缝隙、褶皱与墙角、角线以及细小物体等的表现不清晰问题,综合改善细节尤其是暗部阴影,增强空间的层次感、真实感,同时加强和改善画面明暗对比,增强画面的艺术性。可以说:
AO特效在直观上给我们玩家的感受主要体现在画面的明暗度上,未开启AO特效的画面光照稍亮一些;而开启AO特效之后,局部的细节画面尤其是暗部阴影会更加明显一些。
本章讲到了如何使用有效的实时环境光遮蔽技术,对物体遮蔽信息及环境进行预处理,综合这些因素给物体创建逼真的光照和阴影。本文大部分参考https://zhuanlan.zhihu.com/game-programming
1 概述
首先,本文中讲到,环境光遮蔽(Ambient Occlusion)一般而言有两种理解:
1)将环境光遮蔽视为“智能”的环境光项,其在模型表面的变化取决于在每点可见多少外部环境。
2)将环境光遮蔽视为一个漫反射项,其能有效地支持复杂分布的入射光线。
文中将考虑上述的第二种解释。
其基本思路是,假如预处理一个模型,计算它上面每个点可以看到多少外部环境,可以相反地计算有多少环境被模型的其他部分遮挡,然后在渲染时使用这个信息计算漫反射着色项的值。其结果是模型上的裂缝变暗,而模型的暴露部分会接收更多的光线,因此更明亮。这种效果实质上比使用标准的着色模型更逼真。
另外,这个方法可以扩展为使用环境光作为照明源,用代表各个方向入射光的环境贴图没来决定物体上每个点光的颜色。为了这个特性,除了记录在点上可以看到多少外部环境之外,也记录大部分可以光从哪个方向到达。这两个量有效地定义了从外面进入场景的未被遮挡的方向圆锥体,可以一起用来做为来自环境贴图的极端模糊的查询,模拟着色点上来自感兴趣的方向圆锥体的全部入射照度。
2 预处理步骤(The Preprocessing Step)
给定一个任意的着色模型,环境光遮蔽算法需要知道模型上每点的两个信息:
(1)该点的“可到达度(accessibility)”- 即该点上方半球的哪一部分未被模型的其他部分遮挡;
(2)未被遮挡的入射光的平均方向。
通过下图在平面上说明这两个概念。给定在表面上的点P,其法线为N,P点上半球的2/3被场景中其他几何体遮挡,半球另外的1/3不被遮挡。入射光的平均方向用B表示,其在法线N的右侧。大致来说,在P点的入射光的平均颜色,可以通过求围绕B矢量的未遮挡入射光的圆锥体的平均值得到。
下面贴出的伪代码显示了我们的基本方法。在每个三角形的中心,我们产生一组以表面法线为中心的半球形光线,跟踪每道光线进入场景,记录哪些光线与模型相交,标志不能从环境接收的光线,以及不被遮挡的光线。接着我们计算不被遮挡的光线的平均方向,这给出了入射光平均方向的近似值。(当然,我们计算的方向实际上可能会被遮挡,但我们选择忽略不计这个问题。)
Example 17-1 计算环境光遮蔽量的基本算法伪代码 (Basic Algorithm for Computing Ambient Occlusion Quantities)
For each triangle {Compute center of triangleGenerate set of rays over the hemisphere thereVector avgUnoccluded = Vector(0, 0, 0);int numUnoccluded = 0;For each ray {If (ray doesn't intersect anything) {avgUnoccluded += ray.direction;++numUnoccluded;}}avgUnoccluded = normalize(avgUnoccluded);accessibility = numUnoccluded / numRays;}
生成这些光线的简单方法是使用拒绝采样法(rejection sampling):检测在x,y和z为-1到1区间的3D立方体中随机生成的光线,并拒绝不在单位半球中与法线相关的光线。
能通过这次检测的光线方向可视分布理想的光线方向。列表17-2的伪代码表示出了此方法的实现思路。
当然,也可以用更复杂的蒙特卡洛(Monte Carlo)采样法来得到更好的样本方向的分布。
Example 17-2 使用拒绝采样法计算随机方向的算法伪代码(Algorithm for Computing
Random Directions with Rejection Sampling)
while (true) {x = RandomFloat(-1, 1); // random float between -1 and 1y = RandomFloat(-1, 1);z = RandomFloat(-1, 1);if (x * x + y * y + z * z > 1) continue; // ignore ones outside unit// sphereif (dot(Vector(x, y, z), N) < 0) continue; // ignore "down" dirsreturn normalize(Vector(x, y, z)); // success!}
另外,用图形硬件代替光线追踪软件,有可能加速遮挡信息的计算。
3 使用环境光遮蔽贴图进行渲染(Rendering with Ambient Occlusion Maps)
使用环境光遮蔽贴图进行着色的基本思想是:可以直接在着色点处使用之前已计算好的,有多少光线能到达表面的,优质的近似值信息。
影响这个数值的两个因素是:
(1)在此点上方半球的哪个部分不被点和环境贴图之间的几何体遮挡。
(2)沿着这些方向的入射光是什么。
下图显示了两种不同的情况。在左图中,只能看到着色点上面的一小部分,由方向矢量B和围绕它的方向圆锥体所表示,该点的可到达度非常低。而在右图中,沿着更大范围的方向有更多的光线到达给定点。
在预处理中计算的可访问性值告诉我们哪一部分半球可以看到环境贴图,而可见方向的平均值给出一个近似方向,围绕它计算入射光。虽然这个方向可能指向一个实际被遮挡的方向(例如,如果半球的两个独立区域未被遮挡,但其余的部分被遮挡,平均方向可能在这两者之间),但在实践中其通常运行良好。
【核心要点总结】
给定一个任意的着色模型,环境光遮蔽算法需要知道模型上每点的两个信息:
1)该点的可到达度(accessibility)。
2)未被遮挡的入射光的平均方向。
文中提出的环境光遮蔽方法,总结起来有三个要点:
- 采用了多种在实践中运行良好的近似方法。
- 主要为预处理操作,将相对昂贵的计算事先准备好,且仅计算在渲染时进行快速着色所需的正确信息。
- 预处理不依赖于光照环境贴图,因此可以轻松使用场景中的动态照明。
【本章配套源代码汇总表】
Example 17-1 计算环境光遮蔽量的基本算法伪代码(Basic Algorithm for Computing
Ambient Occlusion Quantities)
Example 17-2 使用拒绝采样法计算随机方向的算法伪代码(Algorithm for Computing
Random Directions with Rejection Sampling)
Example 17-3 使用可到达度和环境映射进行着色的片元Shader(Fragment Shader for
Shading with Accessibility Values and an Environment Map)
Example 17-4 latlong( )函数的定义(The latlong() Function Definition)
Example 17-5 computeBlur( )函数的定义(The computeBlur() Function Definition)
【关键词提炼】
环境光遮蔽(Ambient Occlusion)
拒绝采样(Rejection Sampling)
环境光遮蔽贴图(Ambient Occlusion Maps)