GPU Gems 2 · Chapter 16. Accurate Atmospheric Scattering
在 GPU shader 中数值积分 Nishita 大气模型,区分 Rayleigh/Mie scattering、optical depth 与 phase function,并通过专用 camera paths 和 HDR 保持实时与稳定。
学习目标
- 能从相机 ray 与 atmosphere sphere 的交点确定有效积分区间 A→B
- 能解释 Rayleigh 与 Mie scattering 的粒子、波长和方向性差异
- 能把嵌套积分离散成 shader loop,并理解 optical depth、in-scattering 和 surface attenuation 的关系
- 能按 camera 在大气内/外选择专用 shader path,避免端点和条件分支处理混乱
- 能用 floating-point buffer 与 exponential exposure 将散射结果映射到可显示范围
逼真的天空可以从一条射线开始。↡从相机经过场景顶点、并在大气球内产生散射贡献的视线与行星表面和 outer atmosphere 的交点定义了需要计算的线段;只对这段做数值积分,才能同时支持地面、飞行和太空视角。
1. 确定 A→B 与 sample points
若 camera 在 atmosphere 内,A 就是 camera position;若 camera 在 space,A 是 ray 与 outer sphere 的交点。目标 vertex 同理:在 atmosphere 内时 B 是 vertex,否则用 sphere intersection 得到 B。将 A→B 分成 n 个 sample segments,在中心或规则位置取 P₁…Pₙ,每个 sample 代表太阳光进入、在此散射、再朝相机传播的一小段贡献。
积分可以用 trapezoid-like weighted sum 实现:sample count 提高会提高精度,也会线性增加 shader loop 工作。由于射线长度和 density 变化不均匀,可以把相邻采样间距纳入权重,并在 near horizon 或高密度区域分配更多样本。
2. Rayleigh、Mie 与 phase function
↡由空气中小分子造成、对短波长散射更强并形成蓝天/红霞的散射类型按波长大致呈 1 / λ⁴ 的差异:蓝光到处散开,长路径的太阳光则逐渐失去蓝绿,形成黄橙红的 sunset。
↡由尘埃、污染或水滴等较大气溶胶造成、波长较均匀且常强烈前向的散射类型不应和 Rayleigh 共用同一颜色常量;它带来灰白 haze、太阳周围的 halo,也能作为彩虹等粒子效果的基础。两类散射都要按 RGB 和各自 scale height 计算。
↡根据视线与光线夹角以及各向异性参数 g,决定散射向相机方向分配多少光的角度函数控制方向性。Rayleigh 可以用 g = 0 的对称情形;Mie 常用负值表示前向散射。phase function 对角度敏感,放在 fragment 阶段比 per-vertex 计算更不容易出现 tessellation artifacts。
3. optical depth 与 in-scattering
↡沿一条光路把随高度指数衰减的 density 乘以路程积分,表示光被大气散射掉的平均程度或 optical thickness 是内层积分。sample 点高度 h 的 density 可用 exp(-h / H₀) 表示;H₀ 是 scale height,决定密度下降有多快。沿 sample 到太阳的路径计算一份,到相机的路径再计算一份,两个路径的衰减共同决定 sample 能贡献多少光。
↡从太阳进入大气、在每个 sample 点散射并朝相机增加的光量积分把 solar intensity、Rayleigh/Mie scattering constants、phase function 和两条 optical depth 的 exponential attenuation 组合起来。若渲染的是有反射面的 terrain,还要把 surface-emitted/reflected radiance 乘上从表面到相机的 out-scattering attenuation,并加上沿视线的新 in-scattering;天空本身没有表面反射项。
4. 把嵌套积分变成实时 shader
直接实现会产生外层 camera samples × 两个内层 optical-depth samples × 两种散射 × 三个颜色通道的巨大计算量。历史做法使用 2D lookup table;本章的 GPU 路径则把可行的积分与采样放进 shader,通过较小 sample count 和多种 shader variant 维持实时。
相机在 atmosphere 内时,针对“到相机中途结束”的 ray,可以用从 sample 到 atmosphere top 的 optical depth 减去从 camera 到 top 的那一段。若 vertex 在 camera 上方,反转相关 ray 方向,避免 lookup/积分高度落到预期范围之外。↡根据 camera 位于大气内或太空、以及目标在地面/天空/大气外选择的独立 shader 组合把这些边界条件挪到 material/shader 选择阶段,减少一个通用 shader 里难以验证的动态分支。
5. HDR:把 radiance 安全地映射到屏幕
散射方程的结果很容易超出 0..1,直接写入普通 8-bit framebuffer 会剪裁亮部或让整体过暗。使用 floating-point render target 保存 radiance,最后再用 exponential exposure 映射:color_display = 1 - exp(-exposure × color_radiance)。↡先在浮点 framebuffer 保留高范围 radiance,再用可调 exposure 映射到 0..1 的高动态范围处理像相机光圈一样控制整体亮度,曝光应有合理上下限,否则白天可能被压成月光,月光又被拉爆。
6. 动手实验:样本、视角与曝光
下面的实验把 outer sample count、camera altitude、Mie 的 g 和 HDR exposure 暴露出来。调低 samples 可以看到成本下降但精度风险上升;把 g 推向更负值会增强太阳方向的 haze;改变 exposure 只应改变显示映射,不应改变相对的 RGB 散射比例。
Atmospheric scattering 实验
观察数值采样、相机高度、Mie 相位方向和曝光如何影响天空蓝、太阳 haze 与 shader 预算。
增加采样能改善积分精度,但 shader loop 预算上升。 g 越接近 -1,Mie 越偏向光源方向;不要设为 ±1。
小结
- A→B 由 camera ray 与 atmosphere sphere 的交点决定,支持地面到太空的统一视角。
- Rayleigh 负责短波长偏色,Mie 负责气溶胶与前向 halo,两者需要分开建模。
- optical depth 描述路径衰减,in-scattering 把各 sample 的太阳光贡献累积到视线。
- shader variant、合理 sample budget 和 fragment 阶段 phase function 共同把嵌套积分变成实时算法。
- floating-point buffer 与 HDR exposure 保留散射的动态范围,再稳定映射到显示颜色。
练习
问题 1|积分区间 相机在大气内、顶点在太空时,如何确定散射 ray 的 A 和 B?
问题 2|散射模型 为什么 Rayleigh 和 Mie 要分别计算?phase function 的 g 应该如何使用?
问题 3|实时与 HDR 如何降低嵌套积分成本,同时避免结果在普通 framebuffer 中失真?
名词解释
本章出现的专业名词,用大白话再讲一遍。
- camera ray
- 从相机穿过场景顶点、在大气中产生散射的视线。
- Rayleigh scattering
- 小分子造成、对短波长更强的散射。
- Mie scattering
- 气溶胶造成、波长较均匀且可强前向的散射。
- phase function
- 按夹角与各向异性参数 g 分配散射方向的角度函数。
- optical depth
- 沿光路积分密度与距离,表示大气衰减程度的量。
- in-scattering
- 太阳光在 sample 点散射并沿视线增加的光量。
- specialized camera path
- 按 camera 内外和目标类型选择的独立 shader 处理路径。
- HDR exposure
- 在浮点 radiance 上用可调指数映射到显示范围的处理。