- SignalDesk2 hr ago
chouheiwa, 快 10 年开发经验了 查看知乎原文 《阿凡达 2》一帧水算了 8,000 个线程小时 ,游戏一帧总共只有 16.6 毫秒 。这两个数摆在一起,游戏里的水火烟为什么难做,答案已经出来一半了。剩下那一半更有意思,因为现在你在 3A 大作里看到的最好看的海,压根没有做流体模拟。 《阿凡达:水之道》中的水体、浪花与飞沫 《阿凡达:水之道》中 Payakan 与 Lo’ak 在水中玩耍的镜头。水体、飞沫等效果由多个求解器协同完成。图片 © Wētā FX,来源:befores & afters 研发访谈 [1] 。 太长不看版 水、火、烟难做,根子上是三件事叠在一起。第一,它们没有表面,只是一团体积,游戏引擎整套管线却默认物体有表面。第二,描述它们的 Navier-Stokes 方程到今天都没有通解,数值逼近的开销随分辨率三次方涨,电影一帧可以烧几千个线程小时,游戏一帧只有十几毫秒。第三,人眼对水火烟的直觉是每天喂出来的,一点点不对劲都逃不过去。行业几十年的主流做法是绕开模拟,海面用 1999 年的 FFT 算法叠波浪,火用翻页贴图,烟云用光线步进加噪声,全靠近似。近似的代价就是海面重复、角色下水没浪、烟穿墙、火不蔓延这些你熟悉的假。真正的三维实时流体模拟到 2026 年仍然是奢侈品,神经网络那条路我也没查到任何一款商业游戏在正式用。 先把账算一遍 我一开始想弄清楚的其实是个很朴素的问题,水这东西到底比一个角色贵多少。 先看离线那头。《阿凡达:水之道》的数字是 Wētā FX 的制片 David Conley 在 2022 年 AWS 的年度大会上给的,AWS 官方博客有原文 [2] ,每一帧渲染 8,000 个线程小时,相当于云上 3,000 个 vCPU 跑一个小时。整个项目八个月里在 AWS 上烧掉 33 亿线程小时 。Unity 那边的技术回顾 [3] 补了另一组数,全片大约 2,225 个镜头 需要水效果,Wētā 为这部片子专门写了一套叫 Loki 的水工具,里面拆分了浪、水体、飞沫、雾、气泡、泡沫、薄膜、残留湿润好几个求解器。befores & afters 还采访 [1] 到一个细节,孩子们在海滩上打闹那场戏,身上的水滴模拟跑了 8 天。 Wētā 皮肤表面的薄膜与水滴模拟 手背上的薄膜与水滴模拟示例,蓝色显示附着、汇聚和滴落的水。这是制作过程中的可视化。图片 © Wētā FX,来源:befores & afters 研发访谈 [1] 。 再看实时这头。60 帧每秒,一帧 16.6 毫秒;主机上现在流行的 120 帧模式,一帧 8.3 毫秒。这十几毫秒要装下几何、光照、阴影、后处理、UI,特效只能分到一小块。有官方数字的例子是 Guerrilla 的云系统 Nubis,SIGGRAPH 2015 那篇分享 [4] 写得很清楚,整套体积云在 PS4 上跑进 2 毫秒 以内,这个数被反复当成能不能进游戏的门槛。 一帧的两种算法 8,000 线程小时换算成秒是 2,880 万秒,2 毫秒是 0.002 秒。中间差 10 个数量级 。这个差距不是靠硬件迭代几年能追上的,它决定了游戏这边从一开始就没打算真算,只打算算得像。 那问题就变成了,为什么这东西非得算这么多。 它没有表面 游戏引擎的整套管线,从建模到骨骼动画到光栅化,都是围绕一个假设建的,物体有表面,表面可以用三角形网格描述,而且网格拓扑基本不变。一个角色再复杂,也是一张皮加一副骨架。 水火烟正好全部踩在这个假设外面。它们是一团体积,不是一张皮。飞溅的时候一滴水分成两滴,落回去两滴合成一滴,网格拓扑随时在变。火焰的边界每一帧都不一样,烟更没有边界可言。网易雷火《逆水寒》程序组在讲他们 SPH 流体的 文章 里说得很直白,水无常形,流水的运动是游戏世界里最难表现的物理现象,传统游戏只能用面片加贴图动画去暗示它。 面和体 体积就得用体积的办法描述。要么把空间切成网格,每个格子存速度、压力、密度,这叫欧拉法;要么撒一堆粒子,每个粒子带着自己的属性跑,这叫拉格朗日法。两条路都要解同一组方程,也就是 Navier-Stokes 方程。 口语版就是,流体每一小块的加速度等于压力差推它、粘性拖它、外力拉它三者之和,同时体积不能凭空多也不能凭空少。左边第二项是速度乘自己的梯度,非线性就出在这,也是它没有通解的根源。Clay 数学研究所在 2000 年把三维 Navier-Stokes 解的存在性和光滑性列进七个千禧年难题 [5] ,每题悬赏 100 万美元,到现在没人领走。 数学上没通解,工程上就只能数值逼近。逼近的代价随分辨率三次方涨。一个 512³ 的三维网格就是 1.34 亿 个格子,每个格子至少存三个速度分量加压力密度,每帧全算一遍。而人眼偏偏对小尺度最敏感,火苗的抖动、烟的卷曲、浪尖的泡沫都是毫米级的湍流细节,分辨率一降这些先没。 这里要公平地说另一面,实时流体模拟本身在 1999 年就被证明可行了。Jos Stam 那篇《Stable Fluids》 [6] 开头写的就是这个: Until recently, it was believed that physical fluid models were too expensive to allow real-time interaction. 直到不久前,大家还觉得物理流体模型太贵,没法做实时交互。 他给出了一个对任意时间步都不会炸的求解格式,后面二十几年的实时流体基本都是从这篇长出来的。所以能不能实时解早就有了答案,悬着的问题是以游戏能接受的分辨率解出来的东西够不够好看。这条线的后续演进值得画一张时间线。 时间线上每一步都在放宽同一个约束,用更少的计算换更多的视觉细节。但游戏行业最后走的路,跟这条线关系没那么大。 你看到的海面是 1999 年的算法 这是我查到一半改了判断的地方。我最早的假设是,现在游戏里的海做得那么好,肯定是流体模拟终于跑起来了。翻到 Rare 在 SIGGRAPH 2018 讲《盗贼之海》的那篇 talk [7] 才发现,这款被公认海做得最好的游戏,海面用的是 FFT 高度场 ,跟流体模拟没关系。 《盗贼之海》技术分享中的环境、云与海面 Rare 在 SIGGRAPH 2018 技术分享中展示的游戏画面,右侧可以看到波浪、反光与白沫。来源:The Technical Art of Sea of Thieves,第 1 页 [7] 。 高度场的意思是,海面就是一张二维的布,布上每个点只记一个高度。这张布怎么动,靠的是 Jerry Tessendorf 在 1999 年提出的方法 [8] ,把海面当成无数个不同方向、不同振幅的正弦波叠加,用海洋学的 Phillips 谱决定哪些波该多哪些波该少,然后拿快速傅里叶变换一次性把整张布算出来。这套东西从《泰坦尼克号》那个年代一直用到今天,2025 年 arXiv 上一篇讲实时海洋框架的综述 [9] 里,它仍然是主干,后面的改进只是换更好的方向谱、叠多层位移。 高度场 一张布的好处是便宜到离谱,一次 FFT 就能出一整片海。坏处也全在布上。 第一个坏处是重复。FFT 出来的图天然是周期的,视角一拉高,海面就露出瓷砖一样的拼接感,做 FFT 海洋的开源项目 [10] 基本都要专门处理这个。第二个坏处是它没法卷。真正的浪会卷起来拍下去,布只能上下起伏,所以你在游戏里很少看到真正翻卷的碎浪,看到的多半是贴上去的泡沫贴图。第三个坏处是它不认识你。角色跳进去,布本身不知道有东西进来了,涟漪、飞溅、船体压出的尾流全要另外加一层交互系统去伪装。《神秘海域 4》的技术美术分享 [11] 里,水的部分有一大半篇幅都在讲这些叠上去的层。 腾讯光子在 GDC 2023 有一场叫《Open-World Water Rendering and Real-Time Simulation》 [12] 的分享,会议摘要里的说法是,过去大多数方案靠预生成的网格加离线流体模拟导出的流向图,实时流体模拟很少被用,而且需要顶级 GPU。这是 2023 年一线厂商的原话,到 2026 年我没查到有谁推翻它。 所以海面这块的结论是,游戏没有在算水,游戏在算一张会动的布,然后把水的其他特征一层层贴回去。光学那一层尤其麻烦,一个水面要同时应付这么多事。 折射、反射、菲涅尔、焦散、次表面散射、吸收,每一项单拎出来都是一个独立的渲染课题。《盗贼之海》那篇里连水的颜色都是在深水色和次表面散射色之间按视角插值出来的。反射这块最常见的偷懒法是屏幕空间反射,只反射屏幕里已经画出来的东西,摄像机一转,本来该映在水里的山没了,这就是很多游戏水面一晃就穿帮的原因。GTA 6 第二支预告出来之后 Digital Foundry 做过逐帧分析 [13] ,大水面还是屏幕空间反射,河流这类小水体才上了光线追踪反射,而且他们对水面的评价是有点噪、不算干净。这是 2026 年最烧钱的游戏,水面依然在妥协。 海面至少还有一张布可以当底。火连布都没有。 火为什么二十多年还是贴图 火焰在游戏里的主流实现,到现在仍然是一叠图片轮着播。行业里叫 flipbook 或者序列帧,把一段火焰动画切成几十张小图排在一张大贴图上,粒子系统每帧换一张。VFX Apprentice 有一篇讲 flipbook 的入门 [14] ,DEV 社区 2026 年还有人在教怎么做 sprite sheet [15] ,并且直接称它为今年的行业标准。 翻页火 为什么二十多年不变。因为火的视觉特征恰好是贴图最擅长掩盖的那种。火没有固定形状,所以你看不出它在循环;火自发光,所以它不需要跟场景光照对得很准;火的边缘本来就模糊,所以粒子叠加的接缝不明显。再加一层噪声图去扭曲 UV 坐标,让每个粒子播的那张图都稍微变形,肉眼就分辨不出重复了。 序列帧贴图与播放效果的对照动图 序列帧动图:左侧播放特效,右侧展示排在一张贴图里的各帧。这个例子能直观看到“把图片轮着播”的做法。来源:VFX Apprentice [14] 。 代价是这一叠图得有人先做出来。JangaFX 给他们的实时烟火工具 EmberGen 写的介绍 [16] 里,第一句就是在戳这个痛点,一张 flipbook 传统上要花一周以上才能调好,因为离线模拟慢,通道打包又复杂。他们的做法是把模拟搬到 GPU 上实时跑,美术调一下参数当场看到结果,最后再烘焙成序列帧交给引擎。这条路现在虚幻自己也走了,UE5 的 Niagara Fluids [17] 让美术不写代码就能做二维和三维的火水烟模拟,文档里明确写了可以烘焙成 flipbook。 看清楚这个流程就明白了,实时工具改变的是制作环节,交给玩家机器的还是那叠图。 公平起见得提一个反例。《Noita》这个游戏每一个像素都在被物理模拟,Nolla Games 在 GDC 2019 的分享 [18] 讲了他们的做法,跟 Navier-Stokes 方程无关,靠的是元胞自动机,每个像素按自己是沙、水、油还是火决定下一帧往哪走,世界切成 64×64 的块,用脏矩形标记哪些块需要更新,棋盘格排列做多线程。 http:// 80.lv 的采访 [19] 里讲得更细。它证明了火可以真的蔓延,油可以真的被点着。但它只有二维,屏幕就那么大。把同样的思路推到三维,格子数直接多出一个维度,又回到了三次方那个坑。 《Noita》中的液体、火焰与像素场景 《Noita》官方截图:同一场景中可以看到液体、火焰与下落的水滴,对应这里讨论的二维像素世界。来源:Noita 官网 [20] 。 火的问题说完了,烟的问题更麻烦,因为烟连自发光这个便宜都占不到。 烟和云走的是另一条路 烟、雾、云这一类,行业叫体积渲染。它们跟布和一叠图都不一样,是真的一团东西,光穿过去会被吸收、被散射,还会给自己投影。 主流做法是光线步进,ray marching。从摄像机往每个像素射一条线,沿线每隔一小段采样一次密度,把每一段的衰减乘起来。核心公式就是 Beer-Lambert 定律。 意思是光走过一段路之后还剩多少,取决于沿途密度的积分。密度越大、路越长,剩得越少。Alan Zucconi 的教程 [21] 和 Inigo Quilez 的文章 [22] 是这块最好的入门,2016 年 arXiv 上一篇专门讲实时体积云优化的论文 [23] 把各种加速手段都列了一遍。 光线步进 贵的地方在步数。每个像素几十上百步,每步一次三维纹理采样,1080p 就是两百万个像素乘上百步,再乘上给阴影的第二次步进。所以所有实用方案都在做同一件事,减少步数、降低分辨率、把结果在时间上摊开复用。 Frostbite 在 SIGGRAPH 2015 的方案 [24] 是把摄像机前方的空间切成一格格的视锥体素,他们叫 froxel,雾、粒子、体积光全塞进同一个三维纹理里统一计算,再配一张体积阴影图。Guerrilla 的 Nubis 走的是另一条线,云的形状不存数据,靠几层噪声函数现场生成,2017 年那版文档 [25] 加了大尺度天气建模,到《地平线:西之绝境》的 Nubis³ [26] 才换成体素云,玩家可以骑着飞行坐骑穿过去。虚幻这边现在的 Heterogeneous Volumes [27] 可以直接导入 Houdini 或者 EmberGen 吐出来的 VDB 稀疏体积文件,Unreal Fest 2023 有一场演示 [28] 把 Niagara Fluids、稀疏体积纹理和异构体积串起来讲。 Guerrilla Nubis³ 技术分享中的体积云 Guerrilla 在 Nubis³ 技术分享中展示的云层:受光边缘、内部阴影和云间空隙共同形成体积感。来源:Guerrilla 官方技术文章 [26] 。 这套流程从头到尾是这样的。 图里每一个环节都可以单独出问题。国内厂商的分享里最实在的是网易雷火讲体积云 [29] 那篇,云的反射不敢直接算,用一张 256×128 的低分辨率全景云混进天空盒;体积云和半透明粒子相交,他们明说这是很难处理的一个问题,试过的顺序无关透明方案在质量和性能上都不理想。雷火另一篇讲 天气魔法 的文章里,贴地云海为了让光线步进跑得动,先用网格位移把云的大形做出来再步进,接缝处出现竹节状的 artifact,最后拿 max blend 压掉。 这种一处处打补丁的记录,比任何原理讲解都更能说明这东西为什么难。原理是清楚的,
- 情报分类:硬件与数码
- 分类依据:内容涉及硬件、数码产品或通信卡
- 信息来源:资讯 / 知乎日报
- 发布时间:2026/9/22 07:07:52
- No replies yet