解释体积云与Weather Texture

解读

面试官问“体积云与Weather Texture”,并不是想听教科书定义,而是想确认三件事:

  1. 你是否亲手落地过大规模天空系统,知道性能红线在哪;
  2. 能否把美术需求(形状、动画、昼夜变化)翻译成技术方案(3D RT、Ray-marching、Shader、带宽、包体);
  3. 是否理解Weather Texture数据驱动天气中的角色,以及它和体积云采样如何联动。
    回答时务必用国内项目指标说话:Android 中低端机 GPU 占用 < 10 ms、包体增量 < 20 MB、内存 < 150 MB,否则会被直接判负。

知识点

  1. 体积云本质
    • 参与介质渲染:用密度场 σ(x) 和相位函数 g(Henyey-Greenstein)描述光与云滴的散射。
    • Unity 实现路径
      – 3D Noise RT(128³ 或 64³,R8 或 RGBA8)离线烘焙,运行时每帧只更新 1/8 切片做 4D 噪声流动。
      – Ray-marching 分两步:深度预采样(Depth Pre-pass)拿到云底高度,主步进 8~12 次,步长用深度区间动态二分
      – 光照用Beer-Lambert + 单次散射近似,预积分到 32×32×32 LUT,运行时三线性采样。
  2. Weather Texture
    • 2D 参数图(512×512 或 256×256,RGBA32F)覆盖整个地图,R=云量、G=云型(积云/层云)、B=降水强度、A=风场扰动。
    • 数据管线:策划在 Houdini/Substance 里画 Mask → 导出 EXR → 打图集 → Unity 里用**虚拟纹理(VT)**流送,内存常驻 < 5 MB。
    • 运行时:Shader 里用世界坐标 xz 采样 Weather Texture,把读出的四个参数插值到体积云噪声,实现千米级无缝天气过渡
  3. 性能与兼容性
    • OpenGL ES 3.0 以下机型 fallback 到公告牌云 + 序列帧,用同一套 Weather Texture 控制透明度,保证美术一致性。
    • GPU Instancing 做云粒子,DC < 50;高端机开体积,低端机关闭,用SystemInfo.graphicsShaderLevel 动态分支。
    • 发热控制:Ray-marching 在 Mali-G52 上 720p 耗时 8.2 ms,通过半分辨率 + TAA Upsample压到 4.1 ms,满足腾讯 WeTest 性能标杆。

答案

“体积云”是真 3D 密度场,核心是在像素着色器里做Ray-marching,每次步进采样 3D Noise 与 Weather Texture,计算散射与消光,最终输出颜色与透明度。
“Weather Texture”是一张覆盖全地图的 2D 参数图,RGBA 四个通道分别控制云量、云型、降水与风扰动,运行时以世界坐标采样,把天气数据注入到体积云 Shader,实现千米级平滑过渡,同时不增加额外 Draw Call
在 Unity 落地时,我会把 3D Noise 做成离线烘焙 + 运行时局部更新的 4D 噪声,Ray-marching 用深度预采样 + 动态步长,低端机 fallback 到公告牌,保证Android 中低端 GPU 耗时 < 10 ms、包体 < 20 MB

拓展思考

  1. 如果项目要求昼夜循环月光散射明显,可以把 Weather Texture 的 B 通道复用为“月光相位权重”,在 Shader 里用双 lobe 相位函数做月光银边,但需额外一次 3D 采样,GPU 增加 1.8 ms,是否值得?
  2. 当玩家乘飞机穿越云层时,会出现近场精度不足的问题,可用虚拟体积纹理(Sparse 3D Texture)做 64³→256³ 的 LOD 跳变,但 Unity 2022 才支持,国内主流 2020 LTS 如何自己用切片 2D Array 模拟?
  3. 为了做电竞直播的“上帝视角”,需要快速切天气做战术演示,Weather Texture 的 512² 图在显存里双缓冲,用Compute Shader 做 5 帧渐变即可无缝切换,但iOS A13 以下不支持浮点纹理,如何用 RGBA8 量化 + 自定义解码保证精度?