Skip to content

现代游戏中的体积云与天气效果

作者 Oleg Sidorkin,Cinevva 联合创始人兼 CTO

Stylized AAA scene with towering volumetric storm clouds, lightning, rain shafts, and a wet stone road reflecting the sky

几周前我写过一篇关于现代 AAA 游戏实际使用的渲染技术的文章。其中天空和天气这一块写得比较单薄,因为它值得单独成篇。云、雾、雨、雪是把一个地形 demo 变成一个"地方"的关键系统。它们之间共享的代码也比看起来要多。体积云、地面雾和上帝光柱都是同一套 raymarch。湿润道路、积雪堆积和脚印痕迹都是同一种位移加 PBR 技巧。风是一个方向向量,场景里所有东西都从它读取。

下面是 2026 年大厂们如何搭建这套系统的一份带主观倾向的简短导览,配上每个技术背后的论文和引擎演讲。

1. 基于物理的天空与大气

大气散射是基础。天空的颜色,地平线的雾霾,远山为什么会发蓝,日落时为什么是橙色,全都来自光线穿过空气时的散射。现代引擎从物理出发计算这些:瑞利散射负责蓝色,米氏散射负责太阳周围的光晕,臭氧吸收带来天顶处那种深紫色。

最初 2008 年的 Bruneton 方法把所有东西都烘焙进 4D 查找表,结果限制了动态时间变化,并且在低太阳角度时会有 LUT 伪影。Sébastien Hillaire 在 2020 年的更新(也就是 UE5 的 Sky Atmosphere 组件用的那一套)把高维 LUT 换成了几张 2D 纹理加上一个每帧更新的多重散射近似。它能跑在手机上,也能跑在高端 PC 上。

Physically based sky at golden hour with smooth orange-to-blue gradient over a distant mountain silhouette

深入阅读:

2. 用 Perlin-Worley 噪声做体积云

现代游戏体积云的奠基技术,源自 Andrew Schneider 2015 年关于 Horizon Zero Dawn 的演讲。云不是 mesh。它们是由分层噪声定义的 3D 密度函数:低频的 Perlin-Worley 混合给出云的整体形状,更高频的 Worley 噪声把轮廓侵蚀成飘渺的边缘。一张天气图(用世界 XZ 坐标采样的 2D 纹理)按区域控制覆盖率、云类型和降水。一条基于高度的渐变曲线在不同海拔之间混合积云、层云和卷云的剖面。

渲染器从相机出发把一条射线打进云体,沿途累积密度和散射。2017 年的 "Nubis" 版本加入了区域级编辑和动画,最初的 PS4 实现整个天空大约只花 2 毫秒。今天大部分发布体积云的工作室还在沿用这篇论文的血脉。

A 3D cloud shape decomposed into stacked Perlin and Worley noise patterns showing how detail erodes the silhouette

深入阅读:

3. 体素云与 Nubis³

2023 年 Nubis 的演进彻底抛弃了 2.5D 形状表示,转而用真正的 3D 体素。每个体素直接存云的密度,这样美术可以像雕刻地形一样去雕刻和动画化云的形状。换成更密集的表示带来的代价,由压缩有向距离场加速的 raymarch 和稀疏体素数据的聪明升采样补回来。

最终效果是那种你可以飞穿过而不会看出底层把戏的云海。对大多数工作室来说有点过火,但这是高端正在走的方向。

A cumulus cloud shown decomposed into a 3D voxel grid, with smoothed wispy edges blending the chunky interior

深入阅读:

4. 分层云渲染与 2D 背板

不是每家工作室都用得起完整的体积云,也不是每个相机角度都需要。很多游戏会混合多种技术:高空卷云做成滚动的 2D 层,中空积云用体积射线,低空层云用一片薄薄的参与介质平板。地平线那一带通常用一张预烘焙的天空 cubemap,体积通道在某个淡出距离之外混入它。

这种分层是让云的预算保持理智的关键。一种单一云型按全质量跑可以吃掉 4 到 6 毫秒;不同高度用不同质量分层,同样的观感能用一半成本撑下来。

Sunset sky split into three cloud layers: high cirrus wisps, mid cumulus puffs, and low stratus haze near the horizon

深入阅读:

5. 用 froxel 网格做体积雾

雾是一个 3D 场,不是一个 2D 屏幕特效。现代标准做法是 froxel 网格:一张和相机视锥体对齐的 3D 纹理,每个单元("froxel" = frustum + voxel)存密度和已照亮的颜色。一个 compute shader 把每个光源的散射注入到网格里,沿视线方向累积消光,再用一个全屏 pass 应用结果。

这就是为什么你能看到从窗户透进来的光柱、点光源周围的彩色雾、爆炸周围可见的体积。它也是把远处物体溶进空气里的"大气透视"的底层机制。这个技术由 Bart Wronski 为刺客信条 4 引入,由 Sébastien Hillaire 在 Frostbite 里标准化。

Camera frustum visualized as a 3D froxel grid with smaller cells near the camera and bigger cells far away, fog particles inside

深入阅读:

6. 上帝光与晨昏光柱

雾气里可见的阳光不是另一个独立特效。只要雾密度和阴影图都对同一个 compute shader 可见,它们就直接从雾系统里掉出来。当 shader 把光注入到 froxel 时,它会在该 froxel 的世界位置采样阴影图。处于阴影的单元保持黑暗,被照亮的单元拾取太阳颜色。让相机射线穿过结果,被照亮的单元就连成了连续的光柱。

也有更便宜的屏幕空间变体(从太阳位置往深度缓冲做径向模糊),在移动端或低端硬件上仍是正确的选择。它们漏掉屏幕外的太阳位置,但几乎不花钱。

Dawn forest with sun rays slicing through tree trunks and ground fog, forming clear god ray shafts

深入阅读:

7. 闪电与随机天气事件

闪电是一个单帧效果,由两部分组成:闪电网格本身,以及场景级别的色调映射和光照响应。闪电本体通常是用递归线段细分算法生成的程序化 billboard mesh,加入抖动制造混乱,朝地面方向逐渐变细。有些引擎把它当成屏幕空间的加性闪光来渲染,另一些则当成完全光照的自发光几何,用单帧点光源注入向世界投光。

有意思的部分是其他一切:云底从下方被点亮,地面亮上两帧,自动曝光要花几帧才能恢复,延迟的雷声根据距离播放。做得好的话,这能把一个 16 毫秒的闪光变成一段 5 秒的序列,让天气变成一件正在世界里发生的事,而不只是发生在世界之上。

Lightning fork striking from a thundercloud at dusk, lighting the cloud bottoms and a small village silhouette

深入阅读:

8. 雨粒子、雨网格与屏幕空间水滴

现代游戏里的下雨很少只用粒子。便宜又有说服力的方案是一小组滚动纹理,铺在垂直或屏幕对齐的四边形上,用和场景里其他东西一样的太阳和天空 probe 来打光。靠近相机的位置,单独的条状粒子补充细节。在相机镜头本身上,水滴纹理、滑动的水痕、撞击波纹一起卖出"你正在风暴里面"的感觉。

风影响雨的方向。同一个风向量推动云的天气图、压弯草、倾斜雨的四边形。一个场景级向量,几十个消费者。

Heavy night rainstorm with sheets of rain illuminated by distant headlights and ripples on a wet road

深入阅读:

9. 湿表面、水洼与波纹

下雨却不改变地面,看起来立刻就假。湿表面通过压暗 albedo(水吸收入射光)、压平法线(水膜让微表面变平滑)、降低粗糙度(水在掠角下接近完美镜面)来响应。shader 改动很小,视觉变化很大。

水洼是基于 mask 的:一张基于高度或顶点绘制的 mask 定义低洼处,随着"湿润度"参数上升而充满水。波纹是雨滴撞击触发的 flipbook 法线贴图。真正讲究的实现会让湿润状态随时间累积起来,所以一场长时间的暴雨会慢慢把世界泡透,一阵短暂的小雨只会让高处变暗。

Wet cobblestone street at night with shallow puddles reflecting neon signs and ripple rings from raindrop impacts

深入阅读:

10. 积雪堆积与形变

雪是雨的对称问题:世界必须记住它,而不只是承受它。标准做法是用一张自顶向下的"积雪堆积"纹理,随时间在所有能看到天空的地方累积起来(用一张自顶向下的深度或阴影图计算)。地形 shader 采样这张 mask,在受光区域混入雪的材质,在暴露区域混入积雪深度的位移。

脚印和车辙渲染到一张以玩家为中心的滑动形变图里。相机移动时,旧脚印会滚出去,纹理环绕回来。地形或积雪 shader 采样这张形变图,在被写入过的地方把顶点往下推。战地 5 的雪用硬件 tessellation 实现这点;更便宜的方案用高密度地形 mesh 加顶点位移就够了。

Stylized snowy landscape with fresh footprints, drifts piled against rocks, falling snowflakes, and hazy distant mountains

深入阅读:

11. 风作为全局系统

风不是粒子效果。在量产引擎里,它是一个全局向量(有时是一个低分辨率 3D 场),场景里每一个动态系统在自己的顶点 shader 里读取它。草叶弯曲、树枝摇摆、布料飘动、叶子飘落、雨倾斜、烟雾对流、云的天气图滚动。一个 uniform 每帧更新,几十个消费者。

更高级的版本是一张"风格网",按世界位置采样方向和强度,从而支持带局部阵风的风暴、避风的山谷、建筑物背后的尾流。植被通常还会在编辑期烘焙一个逐顶点的偏移,这样同款树就不会齐步摇摆。结果是一个以相同节奏呼吸的世界。

Strong wind blowing grass and trees sideways with leaves swirling in the foreground under stormy clouds

深入阅读:

12. 沙尘暴、暴风雪与极端天气

恶劣天气自成一类渲染。沙尘暴是一团厚重、不透明、贴地的雾,带强方向偏向和激进的远距雾。暴风雪在近相机加上一阵粒子风暴,并降低能见度。火山灰和烟用的是同一套架构,换颜色而已。

让这些天气真正成立的不是粒子,是耦合:太阳变暗,天空变色,后处理调色变化,环境音切换,脚步声变了,玩家说话声变闷(如果他还能说话)。渲染器只是信使,沉浸感来自游戏里每个系统在同一时间一起响应。

A wall of orange dust and sand rolling across a desert plain with bruised sky above and clear blue behind

深入阅读:

13. 时间变化与动态天空

实时时间变化是让上面所有系统都值得发布的乘数。太阳方向和颜色在 24 分钟或 24 小时周期内更新。大气 LUT 随太阳角度更新。云的光照逐帧重算。阴影级联重新指向。反射 probe 刷新。环境色变化。后处理曝光自适应。

做这件事不出现可见瑕疵,大体上是一个关于纹理缓存和时域稳定性的故事。快的方案在固定太阳角度预计算天空再插值;慢的每帧重算。Hillaire 2020 的大气模型快到可以重算,这就是 UE5 直接用它的原因。云的天气图跟着风滚动,覆盖率自然变化,没人需要编辑关键帧。

Three vertical bands across one landscape: dawn pink, noon blue, sunset red, all sharing the same hill silhouette

深入阅读:

14. 天气状态机

这一切之下是一台小小的状态机。大多数游戏发布的天气状态在 4 到 12 个之间(晴、多云、阴、小雨、大雨、雷暴、雾、雪、暴风雪、沙尘暴),每个状态由一组参数定义:云的覆盖率和类型、风速和风向、降水类型和强度、环境色调、音频画像、后处理调色。

状态之间的过渡就是参数集在 30 到 120 秒内做线性插值。过渡不是特殊情况,只是两个状态被 lerp,每个渲染子系统在那一帧读取当前参数值。天气可以是脚本化的(剧情需要风暴),可以是种子化的(每个区域每个游戏日确定,这样同一个世界里两个玩家看到的天气一致),也可以在一张区域格网上完整编辑。最干净的管线把这三种都当成不同的调度器写进同一个参数缓冲。

Side-by-side of the same scene under clear sun on the left and stormy rain on the right with wet glistening surfaces

深入阅读:

15. 那些电影感时刻

整套栈的存在是为了几个标志性时刻。站在山脊上看着风暴前锋滚滚而来。看一束阳光穿透林冠的空隙烧出来。从洞穴里走出来撞进雪里。飞穿过一朵积云,看到它内部的光。

这些是玩家会截图的瞬间。也是上述每个系统都必须同时工作的瞬间:体积云、大气散射、带阴影集成的雾、湿 PBR、植被上的风、时间变化调色、天气状态之间的过渡,全部合成进同一帧里。其中任何一个出错,魔法就断了。

Looking down across a sea of clouds onto a single mountain peak rising through, with sunlit cloud tops and shadowed valleys below

这些对浏览器意味着什么

大部分技术都能干净地映射到 WebGPU。我们已经在开放世界浏览器引擎里发布了基础大气雾、equirectangular skybox 混合和屏幕空间距离雾。更难的那些(完整体积云、froxel 网格雾、带动态水洼的湿 PBR、积雪形变图)是现在地形管线稳定下来之后明显的下一步。Spike 24 从 skybox 做每片元雾色采样是这块拼图的一部分。一个 compute shader 云 raymarcher 喂回同一张大气 LUT,是下一步。

好消息是浏览器硬件下限现在足够高了。WebGPU compute、3D 纹理、indirect dispatch、timestamp 查询都有。Hillaire 2020 的大气已经被多次移植到 WebGL。Schneider 的 Nubis 有 GLSL 的开源参考实现,机械式编辑就能翻成 WGSL。再没有什么渲染层面的原因让浏览器游戏不能拥有和主机同款的天空。剩下的只是工程上的原因,工程上的原因正是我们喜欢的那种。

整套栈的扩展阅读

如果你想要一个把这一切串起来的来源,SIGGRAPH "Advances in Real-Time Rendering in Games" 档案(advances.realtimerendering.com)有 2014 年以来天气和大气的权威演讲。要看具体游戏怎么渲染天空和天气的量产拆解,Adrian Courrèges 的 GPU 性能分析文章里有 GTA V、Horizon Zero Dawn 和 Doom Eternal 的逐帧拆解。专门讲天空和大气数学的,scratchapixel.com 的体积渲染章节是最温和的入门,Hillaire 的开源实现仓库是量产级参考。