3D场景中的大气散射效果分析-BLACKSMITH深度分享
早在Blacksmith的规划阶段,我们就想要设计出一个能比内置的雾气选项提供更多控制和细节的大气散射方案。而且,我们想要表现出如同电影般波澜壮阔的空中透视效果。
在我们着手该项目的散射方案之初,我们首先参考了来自Tomoyuki Nishita[1]的几篇论文,实现并测试了里面所提到的一些模拟方案。
在对各分镜进行了初步的实验和原型建模后,我们认为最好能够采用一个能在各个分镜中都有出色艺术表现的方案。我们所需要的,是一个能够从各方面逼真表现物理模型的解决方案,同时它应该允许我们能根据需求对规则作出修改。我们同时希望该方案不会对短片的运行效率带来太大的负担,所以我们当时定下的目标是能基于每个顶点,而不是各个像素来进行运算。
我们目标是对物理模型进行瑞利(Rayleigh)散射和米氏(Mie)散射的综合模拟。同时,我们还添加了用于表现各种低海拔散射的第三个元素;我们将之统称为高度散射。另一个物理基础模型分歧的关键就是我们决定继续使用HDR天空纹理,而不是通过程序的方法去生成天空和云彩。显而易见,这样的选择所带来的最大缺点就是:动态表现日夜变化这一点将变得更加困难(所幸我们在Blacksmith中并没有这方面的需求)。而它带来的最大优势就是:充分保留了我们在美术细节上的控制能力。
瑞利散射
在现实世界中,正是由于瑞利散射的存在,我们才能在白天的时候看见明朗的蓝天、在日出和日落的时候目睹一片绯红。
而在我们的模拟中,我们疏忽了太阳本身,仅仅关注构建由于日光的输入输出带来的散射所产生的色彩和黯淡。太阳的视觉呈现可以添加在天空的纹理中,或作为为米氏散射的一部分,或利用一个太阳耀斑的图片,又或它们的任意组合。在最基础的核心部分,瑞利散射的密度被归结成一个由瑞利相位函数调制的辉度指数。当然,我们在输入和提取的数据当中添加了一些额外的控制。由于我们并不模拟不同波长的光穿过大气层的情况,所以我们所计算的密度都是一些标量值。我们采用了一个HDR颜色斜坡,以允许入射散射光在水平到竖直向上范围内能够取到不同的色调,并采用了一个距离感知函数用于合成最终的色相。
不同散射配置下的瑞利散射分量
米氏散射
阳光在大气中的米氏散射造就了太阳的光晕、灰白色外观的云彩,以及我们看到的那些受污染城市上空的雾霾。与瑞利散射对所有的方向均匀地散射光线不同,米氏散射具有很强的方向性。
在我们的模拟中,我们利用米氏散射来呈现雾霾和环绕太阳的光晕。因此,我们对它非常频繁地进行着色,来弥补我们在瑞利散射中对太阳的忽略。从技术上说,我们的瑞利散射和米氏散射所用的函数非常相似,施加到输出的相位函数是它们之间最为显著的区别。和许多的其他实现一样,我们使用Henyey-Greenstein散射函数来控制米氏散射的各向异性——或其方向性。
不同散射配置下的米氏散射分量
那些研读过有关论文的行家可能会笑话我们所采用的一些名词,不过我们还是希望大家不要在意这些细节,在这方面给我们一些小小的“自由”。我们发现,在早些时候,人们在描述“天空散射”时采用“瑞利”这个名词,在描述“太阳光晕”时采用“米氏”这个名词。因此,即使从物理模型到执行模型的过程中这些名词被简化了,在这里,我们还是决定继续沿用它们。
高度散射
高度散射呈现了大量低海拔散射效果的混合形态,包括了辐射雾、地面霾和低洼云。
在我们的实现中,对高度散射的实现相当简单:直接通过一个定义的海平面和高度衰减得出高度散射的密度。然后,这将基于距离的指数密度缩放,整个着色的色调正是我们所期望的。
不同散射配置下的高度散射分量
散射遮挡
由于我们的散射主要由阳光向观察者的角度散射所引起——当它们离观察者较远,或者在往观察者的路上颗粒被吸收时——如果物体挡住了太阳的光芒,那么就应该引发一些合理的遮挡。
为了处理这种情况,我们在一个缩减的、屏幕外的缓冲区内,对定向光的级联阴影贴图进行射线匹配,并累计在射线方向上由遮挡所引起的缩减量。当把散射应用到输出像素时,我们利用一个边缘感知滤波器对此遮挡贴图进行上采样,并用它合成像素的最终颜色。在合成阶段我们遇到了一点小麻烦:因为我们的解决方案仅适用于是单散射,所以我们不能简单地屏蔽掉所有的入射散射光,因为这将让我们有一个非常黯淡的、不自然的图像。我们也不希望因此将整个方案扩展成更为复杂和代价高昂的多重散射。最后,我们发明了一个“间接因数”,指明一个特定参数来处理散射的比例,这样便不会显得过于直接。
不同散射配置下的正确遮挡
最终整合
现在剩下的就是结合不同的分量来合成最终图像了。将瑞利散射,米氏散射和高度散射的分量叠加起来,我们就得到一个还算不错的颜色组合。
将瑞利散射、米氏散射和高度散射结合起来
接下来,我们需要确保我们充分利用了前面提到的遮挡缓冲区。我们使用不同的强度参数来调整被施加到直接散射、间接散射、云和天空的散射量的遮挡强度。
组合完成后含有遮挡的散射
最后,唯一剩下的事情就是将混合好的散射与所绘制的图像结合起来。我们通过累计的总消光将所传输的图像变暗,再通过累积的入射散射来亮化它。这就产生了我们的示例场景的最终合成。
最终的合成
演示
我们将大气散射部分抽离到了一个独立的项目,你可以通过Asset Store下载它:
https://www.assetstore.unity3d.com/cn/#!/content/39939
除了所有构成这个方案的代码和着色器,该项目还包含用于生成文中图片所展示效果的所有图像预设置。其中包含的readme文档说明了这些不同配置选项的含义。