← ALL ARTICLES
VFX与模拟4 MIN READ

体积云

全库目录221
本文目录6

云不是一张半透明表面,视线会穿过不同密度段。Weather Map决定大形,噪声雕刻体积,每段再估计到太阳的透射。有限步进需要空区跳过与历史重建,密度和光照变化时分别失效。

密度与光照

体积云先由 Weather Map 决定大尺度覆盖、云型和高度,再用 Perlin、Worley 等噪声雕刻细节。采样点的密度通常还会经过高度曲线调整,使云底、云体和云顶具有不同形态。

每个视线采样点需要估计朝向主光的透射率。完整二次积分过贵,因此会减少光照步数、使用锥形采样或缓存。云边银边来自强前向散射和较短的光路,不应只靠自发光硬提亮。

性能优化首先减少无效步进:

  • 用包围体求进入和离开距离;
  • 空密度区域使用更大步长;
  • 远处降低采样数和分辨率;
  • 对结果做时域重投影,并对相机切换和遮挡变化拒绝历史;
  • 对蓝噪声抖动后的结果做稳定过滤,避免固定条带。

半分辨率渲染能明显降低成本,但上采样必须参考深度,云与几何交界处还要处理历史失效。优化时同时观察采样数、屏幕覆盖率、历史稳定性和太阳方向变化。

密度字段与相函数

SampleCloudDensity 可以按明确的艺术模型实现。先把世界高度归一化为 h=(p.y-bottom)/(top-bottom),要求 top>bottom;区间外密度为零。高度包络可取 H=smoothstep(0,0.1,h)*(1-smoothstep(0.7,1,h)),天气图覆盖率 c、低频形状噪声 n 和高频侵蚀噪声 e 都在 [0,1]。定义:

d=H saturate⁡(n−(1−c)max⁡(c,ϵ)−kee)d=H\,\operatorname{saturate}\left(\frac{n-(1-c)}{\max(c,\epsilon)}-k_e e\right)

这里 epsilon>0,侵蚀强度 k_e>=0。覆盖率为零时密度为零;提高覆盖率会保留更多形状噪声,提高侵蚀强度会削薄云缘。天气图使用世界 XZ 坐标与固定平铺尺度,三维噪声使用世界位置乘频率,再加风速乘时间的平移。各项都是无量纲密度塑形,下面的每米消光系数负责把它变成光学厚度。这是便于复现的简化云型,不代表实际水滴质量分布。

相函数使用 Henyey–Greenstein 近似。按下文的相机射线与指向太阳的方向约定,令 mu=dot(rayDirWS,lightDirWS),则:

p(μ)=1−g24π(1+g2−2gμ)3/2,−1<g<1p(\mu)=\frac{1-g^2}{4\pi(1+g^2-2g\mu)^{3/2}},\qquad -1<g<1

PhaseHG 返回这个值。g=0 时各向同性,g>0 时面对太阳方向的前向散射更强。方向约定与 PBRT 中两个向量都指离散射点的写法不同,转换时余弦的符号也随之改变。

体积步进中的透射率

下面的 HLSL 沿相机射线积累单次散射。输入密度单位与消光系数必须匹配,stepLength 使用世界距离:

hlsl
float3 radiance = 0.0;
float transmittance = 1.0;
for (uint i = 0; i < stepCount; ++i) {
    float3 p = rayOriginWS + rayDirWS * (entryDistance + (i + 0.5) * stepLength);
    float density = SampleCloudDensity(p);
    float extinction = density * extinctionPerMeter;
    float stepT = exp(-extinction * stepLength);
    radiance += transmittance * (1.0 - stepT)
              * lightRadiance * PhaseHG(dot(rayDirWS, lightDirWS), g);
    transmittance *= stepT;
    if (transmittance < 0.01) break;
}

用 1-stepT 表示本步损失的能量,步长变化时结果比简单累加密度稳定。片段假设单次散射反照率为一,而且 lightRadiance 已按相函数使用的方向约定输入。一般介质还要乘 sigma_s / sigma_t;零消光段贡献为零。示例省略了到光源的阴影透射和多次散射。把步数减半并把步长加倍,整体亮度应接近;差异很大时说明积分、密度频率或抖动仍依赖步长。

步进区间从云包围体的 entryDistance 开始,到离开距离或最近不透明表面结束,rayDirWS 为单位向量。若从相机原点计步而没加进入距离,云层会跟随相机或在错误高度出现。相函数的正负也要明确:当 rayDirWS 从相机指向场景、lightDirWS 从采样点指向太阳时,二者点积可作为前向散射角余弦;换成光传播方向则需要反号。

取均匀云段每米消光0.2、长5米,段透射约0.368,反照率为一且光照与相函数乘积为2时,贡献是2*(1-0.368)=1.264。拆成两个2.5米段,以前段透射加权后仍得到相同结果。若第二段到太阳的路径更长,它的入射亮度应更低;正文最小代码的恒定lightRadiance不能表达这份自遮挡,完整实现需在每个采样点沿太阳方向累计消光。

空区跳过依赖保守的密度上界或粗级占用。如果只在一个点采到零就大步跳走,薄云可能整个被跨过。用已知厚度的薄云层检查最大步长,再逐级降低分辨率,才能把漏采和历史重建造成的变化分开。

验证方法

显示密度、视线透射与光源透射,步数加倍检查收敛。相机切换、风移动和太阳变化时拒绝过期历史,比较低分辨率重建边界;参与介质基本积分以大气散射和Froxel为前置。

实现状态:正文片段用于解释本页数据流与边界条件;完整宿主的编译、运行及性能测量尚未执行。可运行的独立模型以本页列出的具体测试为准。

相关主题

参考资料