GraphicsLab 之 Atmospheric Scattering (一)
作者:i_dovelemon
日期:2020-10-11
主题:Atmospheric Scattering, Volume Scattering, Rayleigh Scattering, Mie Scattering, Single Scattering, Multiple Scattering
引言
Atmospheric Scattering(大气散射),是很多 3A 大作的标配。好的大气系统,能够给游戏带来很好的体验,诸多的游戏都花费了大量的精力来构建一整套复杂的大气系统。
一般来说,一个大气系统至少要包含:天空渲染(Sky Rendering),云层渲染(Cloud Rendering)和各种光线效果(Light Shaft, Volumetric Lighting)等。
今天这篇文章,主要是想和大家聊聊天空渲染(Sky Rendering)的部分,希望通过几篇文章,讲解清楚天空渲染(Sky Rendering)背后的知识以及相关实现的方法。
体积散射
在具体讲解 Sky Rendering 之前,我们先来了解一下 Volume Scattering 相关的知识。
学过渲染的同学可能都知道,物体渲染的方式主要分为两个大的类别:一种是基于物体表面(Surface)进行建模的渲染,比如石头,建筑,角色等等大量的物体都是采用这种方式进行渲染。除此之外,还有一种基于体积(Volume)的渲染方式,比如烟,雾,云等等通过大量微小颗粒聚集而成的物体就需要采用这种方式进行渲染。
基于体积的渲染和基于表面的渲染之间的区别在于,基于表面的渲染是假设物体处在真空当中,表面与表面之间的光线是以一种不会发散,衰减的方式进行传播的。当然现实世界中的物体都不是如此的,空气中存在大量的微小颗粒,这些颗粒会导致光线在传播过程中发生散射(Scattering)。
对于 Sky Rendering 来说,由于大气中存在了大量的粒子,太阳光线经过大气,传播进入眼睛之后就经过了这样的一系列散射过程。所以,Sky Rendering 就是一种基于体积的渲染方式。
体积散射过程
这里介绍一些基本的 Volume Scattering 的概念。首先整体上描述下 Volume Scattering 包含了哪些部分:
Absorption | 光线在传播过程中,碰撞到物体的时候,一部分能量会转换成为热能的形式丢失 |
Emission | 光源物体,会增加新的光线到环境中去 |
Scattering | 光线在传播过程中,碰撞到物体的时候,从一个方向散射到另外一个方向上去 |
其中,Absorption 和 Emission 比较简单,容易理解,这里不再赘述。但是对于 Scattering 部分,就比较复杂。
对于观察者来说,直接向观察者照射的光线在传播过程中,会由于散射到其他方向,导致一部分的能量丢失。这部分称之为 Out-Scattering,如下所示:
同样的,由于 Scattering,一些原本不会直接照射到观察者眼中的光线也会被散射进来,导致一部分的能量增加。这部分称之为 In-Scattering,如下所示:
由于 Absorption 和 Out-Scattering,都会导致光线在传播过程中能量丢失,所以将这两部分丢失能量合在一起,称之为 Attenuation 或者 Extinction。
Extinction 表示的是单个点的能量衰减程度,那么对两个点之间线段上能量的衰减,我们使用一个 Transmitance 来表示,即经过该段线段之后,由于 Absorption 和 Out-Scattering 只有一部分光线传输出去,如下图所示:
对于表面渲染来说,我们使用 BRDF 表示入射光线有多少被反射到指定的方向上去。同样的,对于体积渲染来说,也有类似的表示,我们称之为 Phase Function,它定义了向某个特定方向散射的概率分布情况。
这里只是简单的,概念性的介绍了体积渲染相关的知识,更多的细节可以查看参考文献 [1] 进行了解。
天空渲染
前面介绍了一点 Volume Scattering 相关的背景知识。由于天空主要是有各种颗粒组成的,所以对天空的渲染,实际上就是体积渲染。
知道了渲染的方式,那么接下来,我们需要对大气进行建模,以便于了解它的组成,从而配合体积散射相关的理论,进行最终的渲染。
大气模型
大气实际上是一层包裹着地球的一团气体,天空的颜色就主要是太阳光照射到这层气体,进行散射之后所呈现出来的。
这层气体具有一定的厚度,所以我们将整个大气建模成一个包裹着地球的具有一定厚度的空心球壳。
同时大气中颗粒密度并不是均匀分布的,大体上来说,随着海拔的升高,颗粒密度越来越低。我们通过一个名为 Density Ratio 的函数来描述这种随海拔升高,密度下降的特性。
大气中存在各种各样的粒子,它们的光学性质并不相同。为了简化问题,主要将大气粒子分为两种大类:Air Molecules 和 Aerosols。
Air Molecules 表示大气中较小的粒子,通过 Rayleigh Scattering 模拟散射情况。
Aerosols 表示大气中较大的粒子,通过 Mie Scattering 模拟散射情况。
如下图是这种大气模型的概念示意图:
其中,Re = 6360 km,表示的是地球的半径。Ra = 6420 km 表示的是大气的半径。数据来源于参考文献 [2]。
天空颜色
对大气进行建模之后,我们需要知道怎么去渲染大气。通常我们使用一个后处理 Pass 来渲染天空作为场景的背景。那么对于每一个像素来说,它所呈现的颜色是来源于什么了?
对于每一个像素来说,我们都可以从观察者位置投射出一条观察射线,观察射线会与大气层相交,而在整个相交的线段上面,所有的大气颗粒都可以通过对太阳光散射的形式贡献颜色,如下图所示:
观察射线与大气层相交得到 AB 线段,对于 AB 线段上的任意一点 P 来说,都可能通过散射在 PA 方向贡献一部分光照。
所以对于像素的颜色来说,我们就需要计算出这条 AB 线段上每一个点 P 向 PA 方向上散射出来的光照的总和,该总和值,就是当前像素的颜色。
大气散射方程
正如前面一节所说的,像素的颜色是 AB 线段上所有点 P 在 PA 方向散射出来的光照的总和。假设我们已经知道了点 P 接受的光照强度为 $I_p$,那么有多少的光照强度被散射向 A 点了?定义如下公式来表达:
$$I_{out} = I_p * S(\lambda ,\theta ,h)$$(Eq 1)
其中 $S(\lambda ,\theta ,h)$ 表示了有多少光照强度被散射向 PA 方向,即大气散射函数(Atmospheric Scattering Equation)。
值得注意的是,这里只是讨论了有多少被散射向 PA 方向,并不是说 A 点接受了多少来自 P 点的光照强度。注意区分这里的细微差别。原因在于,P 点散射向 PA 方向的光照强度,经过 PA 线段的大气层之后,也会有衰减发生。
而 $S(\lambda ,\theta ,h)$ 的定义如下所示:
$$S(\lambda ,\theta ,h)=\beta (\lambda ,h) * \gamma (\theta)$$(Eq 2)
其中:
$\beta (\lambda ,h)$ 表示的是,由于 Out-Scattering 导致了有多少能量被散射向各个方向上去,我们称之为散射系数(Scattering Coefficient);
$\gamma (\theta)$ 表示的 Phase Function,即在 $\theta$ 角度方向上散射出去的概率;
$\lambda$ 表示的是光线的波长,不同的波长散射系数可能并不一致,对普通渲染来说,即对光照的 RGB 分量分别有不同的散射系数;
$h$ 表示的是海拔高度,前面 Atmospheric Model 一节已经说过,随着海拔高度的不同,大气密度不同,所以散射系数也并不相同;
$\theta$ 表示的是点 P 处接受的光线入射方向与散射方向 PA 的夹角,如下图所示:
这两个函数组合在一起,就描述了点 P 处有多少光照强度反射向了 A 点。
散射系数
从上面一节中我们得知:$\beta (\lambda ,h)$ 表示的是在海拔高度 $h$ ,光线波长 $\lambda$ 处的散射系数。前面提到过大气模型中,大气密度是随着高度慢慢变的稀薄,所以为了模拟这种关系,提出了一个 Denstiy Ratio 的函数来描述这种关系。
而这种关系主要就体现在散射系数中,所以我们可以将散射系数的函数拆分成如下两个函数的组合:
$$\beta (\lambda ,h)=\beta (\lambda,0) * \rho (h)$$(Eq 3)
其中:
$\beta (\lambda,0)$ 表示的是海拔高度 0 处的散射系数;
$\rho (h)$ 表示的是随海拔高度变化的 Denstiy Ratio 函数,完整定义如下所示:
$$\rho (h)=e^{-\frac{h}{H}}$$(Eq 4)
其中:
$H$ 表示的大气的平均密度所在的高度,一般称之为 Scale Height。
我们之前说过,大气中粒子成分过于复杂,所以分为了两个大类,分别使用 Rayleigh Scattering 和 Mie Scattering 进行模拟。为此上面提到的几个数据都分别给出 Rayleigh 和 Mie 的参数:
Type | $\beta (\lambda,0)$ | $H$ | |
Rayleigh | R | 3.8e-6f | 7994m |
G | 13.5e-6f | ||
B | 33.1e-6f | ||
Mie | R | 21e-6f | 1200m |
G | 21e-6f | ||
B | 21e-6f |
数据来源与参考文献 [2]
Phase Function
回忆之前说到的 Atmospheric Scattering Equation:
$$S(\lambda ,\theta ,h)=\beta (\lambda ,h) * \gamma (\theta)$$(Eq 2)
目前只剩下了最后一个 $\gamma (\theta)$ 没有说明了。之前介绍 Volume Scattering 的时候说过,这个函数是 Phase Function,用于表示在指定方向上,光线散射出去的概率。
同样的,这里也给出 Rayleigh Scattering 和 Mie Scattering 对应的 Phase Function:
Type | $\gamma (\theta)$ |
Rayleigh |
$\gamma (\theta) = \frac{3}{16 \pi}(1 + \mu^{2})$ $\mu = \cos \theta$ |
Mie |
$\gamma (\theta) = \frac{3}{8 \pi}\frac{(1-g^{2})(1+\mu^2)}{(2+g^2)(1+g^2-2g\mu)^{\frac{3}{2}}}$ $g=0.76$ $\mu = \cos \theta$ |
数据来源与参考文献 [2]
Transmittance Function
前面一节,讲述了线段 AB 上点 P,在接受到入射光 $I_p$ 的情况下,有多少关照强度被散射到 PA 方向上去。之前说过,这里只是计算了有多少被散射到 PA 方向,而达到 A 点处的并不是这么多。由于大气的原因,在 PA 路径上依然会发生散射的现象,这就导致了达到 A 点的将小于 P 点散射向 PA 方向上的光线。而根据前面基础概念一节中的接受,这个比例关系我们通过一个名为 Transmittance Function 的函数来描述,如下所示:
$$I_a = I_{pa}*T_{pa}$$(Eq 5)
而,
$$T_{pa}= e^{-{\int_{0}^{d}\beta(\lambda,h)}}$$(Eq 6)
公式来自参考文献 [1]
其中 $d$ 表示 PA 之间的距离,$e$ 是自然数。
实际上,Transmittance Function 描述了任意两个点之间,由于大气散射导致的能量衰减所剩余的光照比例,不仅可以描述上述 P 点到 A 点路径上能量的衰减情况,也可以描述空间中任意两点之间的衰减。
Single Scattering vs Multiple Scattering
根据前面两节的描述,我们知道了 AB 线段上任意一点 P 在经过大气散射之后,达到 A 点的光照能量为:
$$I_a = I_p * S(\lambda ,\theta ,h) * T_{pa}$$(Eq 7)
前面两节已经讲述了 $S$ 和 $T$ 是什么,现在唯一剩下的就是 $I_p$ 本身了。
这里就要涉及到 Single Scattering vs Multiple Scattering 了。对于 Single Scattering 来说,$I_p$ 可以认为只有太阳光 $I_{sun}$ 到 P 点这条散射路径会做出光照贡献。
而对于 Multiple Scattering,情况就很复杂了。由于太阳光进入大气之后,会被粒子散射,导致光照方向进行多次改变,所以不仅仅有太阳光直接入射到 P 点的光照,也会有空气中粒子对其他方向的太阳光经过多次散射之后,反射而来的光照。
这里,为了简化问题,降低实现难度,我们只考虑 Single Scattering。也就是说,
$$I_p = I_{sun} * T_{cp}$$(Eq 8)
其中,$T_{cp}$ 表示的 CP 线段上的衰减 Transmittance。C 点为太阳光入射向量与大气层最外围相交的点。
将 Eq 8 带入 Eq 7 即可得到太阳光 $I_{sun}$ 入射向点 P,讲过 PA 方向散射之后,到达 A 点所剩余的光照:
$$I_a = I_{sun} * T_{cp} * S(\lambda ,\theta ,h) * T_{pa}$$(Eq 9)
天空渲染方程
前面几节,完整的讲述了 Single Scattering 下,AB 线段上任意一点 P 经过太阳的入射和大气的衰减,最终达到 A 点的光照强度。但是最开始的时候,我们也讲述了,天空的颜色实际上是 AB 线段上所有点贡献的最终结果。单单一个 P 点的结果,还是无法得到最终的颜色,我们需要累积 AB 线段上所有点 P反射到 A 上的光照能量的总和,才是最终需要显示的天空的颜色,即:
$$I_{final}=\int_{A}^{B}{I_{sun} * T_{cp} * S(\lambda ,\theta ,h) * T_{pa}}$$(Eq 10)
这个公式,即为最终需要计算视线沿着 AB 方向所看到的天空的颜色。
总结
本篇文章主要讲述了天空渲染背后的理论知识以及相关的数学原理。当然很多公式背后的原理并没有给出来,一方面是篇幅有限,另外一方面是我自己也没有搞明白是怎么推导出来的。作为程序员,我这里只是帮助大家梳理清楚和最终实现最接近的数学原理,更深层次的原理,感兴趣的可以根据参考文献继续了解。强烈建议阅读参考文献[2]和[3] 来了解更多相关的背景知识。
接下来的一篇文章,将向大家展示如何实现这里的数学公式,从而渲染出题图所示的天空出来。
参考
[1] Physically Based Rendering From Theory to Implementation Third Edition