← ALL ARTICLES
VFX与模拟8 MIN READ

大气散射

全库目录221
本文目录14

太阳接近地平线时,光在空气中走得更远,颜色与亮度一起改变。大气渲染需要同时计算太阳到散射点、散射点到相机的损失,再累积沿视线进入相机的光。查找表(LUT)缓存这份计算中的稳定维度;表的坐标、积分边界和更新依赖共同决定结果。

大气介质与射线区间

以行星中心为原点,地表半径为 RgR_g,大气外半径为 RtR_t。位置 x\mathbf x 的高度是 h=∥x∥−Rgh=\|\mathbf x\|-R_g,距离统一用米。一个可实现的球对称模型使用密度 ρR(h)=e−h/HR\rho_R(h)=e^{-h/H_R}、ρM(h)=e−h/HM\rho_M(h)=e^{-h/H_M},其中 HR,HMH_R,H_M 是尺度高度。海平面的瑞利散射系数 βR\boldsymbol\beta_R、气溶胶散射与吸收系数 βMs,βMa\boldsymbol\beta_{Ms},\boldsymbol\beta_{Ma} 都按 RGB 通道分别给出,单位为每米。

局部散射系数为 σs=βRρR+βMsρM\boldsymbol\sigma_s=\boldsymbol\beta_R\rho_R+\boldsymbol\beta_{Ms}\rho_M,消光系数为 σt=σs+βMaρM\boldsymbol\sigma_t=\boldsymbol\sigma_s+\boldsymbol\beta_{Ma}\rho_M。臭氧等吸收层可再添加独立密度与吸收系数。密度、系数和高度单位必须一起传给预计算与运行着色器;例如真实路径1千米、消光系数0.001/米,正确光学深度为1000×0.001=1;若路径数值仍用千米的1直接相乘,就得到0.001,光学深度被缩小一千倍,透射率从约0.368错误地变成约0.999。

相机射线为 o+td\mathbf o+t\mathbf d,d\mathbf d 是单位向量。对任一球半径 RR,令 b=o⋅db=\mathbf o\cdot\mathbf d、q=∥o∥2−R2q=\|\mathbf o\|^2-R^2,交点为 t=−b±b2−qt=-b\pm\sqrt{b^2-q}。先求外球的进入与离开位置,将进入位置截到0,再用地表球最近的有效正交点截短区间。相机在大气外时先移动到进入点;视线未穿过外球时没有大气贡献。地表遮挡后的背面空气不参与积分。

单次散射积分

两条光路的透射

从 aa 到 bb 的透射率为 T(a,b)=exp⁡[−∫abσt(x) ds]T(a,b)=\exp[-\int_a^b\boldsymbol\sigma_t(\mathbf x)\,ds],RGB 分量各自计算。均匀介质每米消光0.1,十米路径得到 e−1≈0.368e^{-1}\approx0.368;两个五米段各为0.607,相乘得到同一结果。

令 s\mathbf s 指向太阳,d\mathbf d 指向观察射线的远方,取 ν=d⋅s\nu=\mathbf d\cdot\mathbf s。太阳光沿 −s-\mathbf s 传播,散射光沿 −d-\mathbf d 回到相机,因此 ν=1\nu=1 表示看向太阳的前向散射。瑞利相函数为 pR(ν)=3(1+ν2)/(16π)p_R(\nu)=3(1+\nu^2)/(16\pi)。气溶胶可用 Henyey–Greenstein 近似 pM(ν)=(1−g2)/[4π(1+g2−2gν)3/2]p_M(\nu)=(1-g^2)/[4\pi(1+g^2-2g\nu)^{3/2}],∣g∣<1|g|<1;g>0g>0 使太阳附近更亮。两者在完整立体角上的积分均为1,gg 改变方向分配。

太阳垂直入射辐照度为 E⊙\mathbf E_\odot。散射点 x\mathbf x 向太阳的路径若穿过地表,取 T⊙(x)=0T_\odot(\mathbf x)=0,否则积分到外球。局部每单位距离的源项为:

j(x)=E⊙T⊙(x)[βRρR(x)pR(ν)+βMsρM(x)pM(ν)].\mathbf j(\mathbf x)=\mathbf E_\odot T_\odot(\mathbf x) [\boldsymbol\beta_R\rho_R(\mathbf x)p_R(\nu) +\boldsymbol\beta_{Ms}\rho_M(\mathbf x)p_M(\nu)].

沿视线的天空辐亮度是 L=∫0dendT(o,x(s))j(x(s)) ds\mathbf L=\int_0^{d_{end}}T(\mathbf o,\mathbf x(s))\mathbf j(\mathbf x(s))\,ds。观察路径和太阳路径的透射各出现一次。只积分观察路径会让被地球遮住太阳的空气仍然发亮,也会丢失日落时较长太阳路径对短波光的过滤。

分段累积

把区间分成小段,在每段中点计算密度、太阳透射与源项,近似它们在本段恒定。累计透射初值为1、辐亮度初值为0。长度 Δs\Delta s 的一段更新为:

L+=Tprefix j 1−e−σtΔsσt,Tprefix∗=e−σtΔs.\mathbf L\mathrel{+}=\mathbf T_{prefix}\,\mathbf j\, \frac{1-e^{-\boldsymbol\sigma_t\Delta s}}{\boldsymbol\sigma_t},\qquad \mathbf T_{prefix}\mathrel{*}=e^{-\boldsymbol\sigma_t\Delta s}.

每个通道在消光接近零时用极限 Δs\Delta s,实现可用 -expm1(-sigmaT*ds) 避免小量相减。取标量模型 σt=0.1/米\sigma_t=0.1/米、σs=0.04/米\sigma_s=0.04/米、入射光乘相函数为1,十米散射为 0.4(1−e−1)≈0.252850.4(1-e^{-1})\approx0.25285。分成两段时第一段贡献0.15739,第二段经前缀透射后贡献0.09546,总和不变。真实指数密度只在小段内恒定,增加段数应使结果收敛;十米均匀算例不能证明地平线采样已充足。

透射查找表

坐标与预计算

球对称介质中,到外球的透射只依赖半径 r=∥x∥r=\|\mathbf x\| 和射线天顶余弦 μ=x^⋅d\mu=\hat{\mathbf x}\cdot\mathbf d,因此可存为二维 RGB 表。穿地射线单独判断:μ<0\mu<0 且 r2(μ2−1)+Rg2≥0r^2(\mu^2-1)+R_g^2\ge0。把穿地和不穿地的值直接混合插值会在地平线泄光。

一个向地平线分配更多采样的映射使用:

H=Rt2−Rg2,ρ=r2−Rg2,d=−rμ+r2(μ2−1)+Rt2,H=\sqrt{R_t^2-R_g^2},\quad \rho=\sqrt{r^2-R_g^2},\quad d=-r\mu+\sqrt{r^2(\mu^2-1)+R_t^2}, xr=ρ/H,xμ=d−(Rt−r)ρ+H−(Rt−r).x_r=\rho/H,\qquad x_\mu=\frac{d-(R_t-r)}{\rho+H-(R_t-r)}.

该坐标只覆盖到外球且不穿地的半射线。宽 WW、高 BB 的表使用 u=[0.5+xμ(W−1)]/Wu=[0.5+x_\mu(W-1)]/W、v=[0.5+xr(B−1)]/Bv=[0.5+x_r(B-1)]/B,使参数端点落在首末纹素中心。预计算每个纹素时先还原 ρ=Hxr\rho=Hx_r、r=ρ2+Rg2r=\sqrt{\rho^2+R_g^2}、d=Rt−r+xμ[ρ+H−(Rt−r)]d=R_t-r+x_\mu[\rho+H-(R_t-r)],再求 μ=(H2−ρ2−d2)/(2rd)\mu=(H^2-\rho^2-d^2)/(2rd);dd 接近零的外球出射点取 μ=1\mu=1,透射为1。然后沿这条射线按前节积分消光,写入 RGB 透射。

例如取无量纲教学球 Rg=1,Rt=2R_g=1,R_t=2、地面向上射线 r=1,μ=1r=1,\mu=1,有 ρ=0,d=1,xr=xμ=0\rho=0,d=1,x_r=x_\mu=0,结果存入左下首纹素中心。增加半径或向地平线转动会改变实际路径长度,坐标不能只用“高度、角度归一化”几个字代替。

有限线段与太阳查询

同一条不穿地射线上,点 a,ba,b 到外球的距离满足路径拼接,因此 T(a,b)=Ttop(a,d)/Ttop(b,d)T(a,b)=T_{top}(a,\mathbf d)/T_{top}(b,\mathbf d)。若向前延伸会穿地,则沿反方向查到外球,取 Ttop(b,−d)/Ttop(a,−d)T_{top}(b,-\mathbf d)/T_{top}(a,-\mathbf d)。两者都要求实际线段位于大气内且没有地表遮挡。极小透射的比值会下溢,浓介质可保存光学深度再做差,或直接对短段积分。

太阳查询在每个散射点计算 rr 和 μs=x^⋅s\mu_s=\hat{\mathbf x}\cdot\mathbf s,通过地表遮挡后查这张表,省去内层太阳光路积分。太阳圆盘可按有限角半径采样多条太阳方向再平均;点太阳模型在日落边界给出硬切换,有限圆盘会逐渐被地平线遮住。

散射缓存与运行合成

天空方向缓存

固定相机高度、太阳方向及介质参数后,可把单次散射积分存为天空视图表。一个易实现的坐标约定是以局部竖直轴建立正交基,表横轴存方位角 ϕ=2πu\phi=2\pi u,纵轴存 μ=2v−1\mu=2v-1,还原方向 d=1−μ2(cos⁡ϕ e1+sin⁡ϕ e2)+μeup\mathbf d=\sqrt{1-\mu^2}(\cos\phi\,\mathbf e_1+\sin\phi\,\mathbf e_2)+\mu\mathbf e_{up}。每个纹素按完整视线积分,段内向太阳查透射表,得到 RGB 天空辐亮度。运行时把观察方向投到同一基底,取 atan2 得到方位角,再查表。

横轴环绕、纵轴钳制;地表相交与天空射线分支应分开,或者在地平线两侧保留独立采样,避免线性插值混合不同边界。均匀角度表在地平线和太阳光晕处容易欠采样,可增加局部分辨率或使用非线性方向映射。相机升高、太阳转动会改变此表;球对称密度下相同高度的水平平移可在匹配局部基底后复用。

高阶散射

单次散射只接收直射太阳。高阶散射还接收其他方向已经散射过的光。其核心计算是对方向积分。定义 Ln(x,ω)L_n(\mathbf x,\boldsymbol\omega) 为从x朝单位方向omega查询到的第n阶入射辐亮度,因而入射光传播方向为−omega,去往相机的方向为−d。下一阶局部源项为 jn+1(x,d)=σs(x)∫4πp((−ω)→(−d))Ln(x,ω) dωj_{n+1}(\mathbf x,\mathbf d)=\sigma_s(\mathbf x)\int_{4\pi}p((-\boldsymbol\omega)\rightarrow(-\mathbf d))L_n(\mathbf x,\boldsymbol\omega)\,d\omega,再沿视线按相同透射积分得到 Ln+1L_{n+1}。本页HG的余弦为dot(omega,d);omega=d时入射与出射传播方向相同,g>0应得到前向峰,不能改成负点积。含两种散射介质时各用自身系数与相函数后相加;地面反射需作为入射边界加入。

离散参考方法把天顶余弦分为 MM 档、方位角分为 KK 档,每个方向权重为 4π/(MK)4\pi/(MK),查询上一阶辐亮度并求加权和。每一阶完成后才能供下一阶读取,累积 L1+L2+⋯L_1+L_2+\cdots。完整球对称方向场需要半径、视线天顶余弦、太阳天顶余弦和视线与太阳夹角四个参数;它不能由固定相机的一张二维天空表任意查询。

实时的低维多次散射表通常进一步近似角分布,再缓存高度和太阳天顶角等变量。采用这类压缩时须明确近似的角分布与迭代方式;单纯给二维纹理命名“多次散射”并没有定义高阶光的来源。本页可复现模型覆盖单次散射与透射坐标,高阶递推给出计算定义,完整高阶预计算未在本机执行。

空气透视与资源依赖

远处物体只需要积分到物体深度 dd。保存沿视线累计的散射 L(0,d)L(0,d) 和 RGB 透射 T(0,d)T(0,d),按 C=L(0,d)+T(0,d)CsurfaceC=L(0,d)+T(0,d)C_{surface} 合成。可按屏幕方向和距离切片建立三维缓存:每条方向从近到远执行分段更新,在各深度端点写出累计量。距离切片较稀时近处轮廓会染上错误深度的雾,需用真实物体深度插值并检查遮挡边界;RGB 透射压成单通道会丢失波长相关消光。

资源依赖是介质参数与半径生成透射表,透射表与太阳状态生成单次散射及其后续阶,当前相机状态再生成天空视图和空气透视。只改变相机方向时可复用覆盖全部方向的天空表;改变曝光只影响最后显示。修改密度、吸收或行星尺度则要重建依赖它们的全部缓存,否则画面会同时使用两套不同的大气。

验证方法

运行 python 知识库/examples/mechanism_depth/water_atmosphere/test_reference.py。独立 CPU 模型检查均匀消光、分段散射合成、零消光极限、非穿地射线的透射坐标往返、指数密度球壳积分、9×5透射表生成与纹素节点查询及前向相函数,并包含水面频域检查。此模型没有构建完整天空纹理,也没有执行高阶散射、GPU 着色器或性能捕获。

实际大气实现还需选择垂直、掠射和近地表三条射线,加倍积分段数记录 RGB 收敛;以直接积分对照 LUT 查询,分别统计插值和积分误差。改变相机高度与太阳天顶角,检查地平线分支和缓存更新;把不透明平面放在多个已知距离,验证空气透视仅积到各自深度。地平线漏光、日落跳变和远景染色分别指向几何分支、太阳可见比例与有限深度合成。

相关主题

参考资料