Chapter 68: Fluid Simulation
本章建立的能力是:看到一帧烟雾、水花、爆炸云或魔法流体效果时,能够追踪它背后的 simulation state,判断它使用网格、粒子还是混合方法,解释稳定性问题来自时间步、压力求解、边界条件还是渲染映射,并给出可复用的排查顺序。
贯穿本章的材料是一帧实时特效:角色落地时,地面产生一圈水花,冲击波把烟雾卷成环形涡旋,水花边缘生成泡沫粒子,近处用透明水面和泡沫 sprite 表现,远处用体积雾表现。这个 frame 看起来是一张图像,工程上由速度场、密度场、压力场、粒子 buffer、边界 SDF、surface mesh、volume texture 和 shader 参数共同决定。
图形学流体模拟的目标偏向视觉可信、交互稳定和预算可控。科学计算流体力学更关心物理量预测精度,两者的验收标准不同。本章讨论的 fluid simulation 以渲染系统为目标:simulation 每帧输出可渲染状态,renderer 把这些状态转成颜色、透明度、法线、泡沫、阴影和运动连续性。
本章的结论可以先给出:流体效果质量由四个契约决定。表示契约决定状态存在哪里;求解契约决定速度、压力和体积如何更新;稳定契约决定误差如何被约束;渲染契约决定 simulation state 如何进入 volume rendering、surface reconstruction、foam shader 和后处理管线。
68.1 流体模拟基础与数学模型
流体模拟的最小状态可以从速度场开始理解。速度场(velocity field)是在空间中每个采样位置记录一个速度向量的对象;烟雾密度、水面标记、温度、燃料和泡沫会随着这个速度场移动。贯穿案例中,冲击波把烟雾推成环形结构,本质路径是外力先改变局部速度场,速度场再搬运密度和粒子,最后 renderer 才看到旋转的烟雾边缘和水花方向。
压力(pressure)在实时流体里常被当作约束求解的结果。若某个区域速度向外流出大于流入,这个区域会表现出局部膨胀;若速度向内流入大于流出,就会局部压缩。散度(divergence)用于度量这种局部体积变化,常写作 。对不可压缩流体(incompressible fluid),求解器希望最终速度场满足 ,也就是每个小区域的体积倾向保持稳定。
Advection 是“用速度场搬运状态”的步骤。网格方法通常从当前格点沿速度方向反向追踪,找到上一时刻的采样位置,再读取上一帧的密度或速度。粒子方法则让粒子携带位置、速度、质量和额外属性,通过积分更新位置。贯穿案例中,烟雾体积的 density texture 由 advection 搬运,水花粒子的位置由速度积分更新,二者都依赖同一类时间推进逻辑。
粘性(viscosity)描述动量在邻域之间扩散的程度。高粘性液体会让局部速度差被快速抹平,涡旋衰减更快;低粘性烟雾和水更容易保留细节。图形工程中,粘性参数通常服务视觉结果:烟雾需要柔和扩散时提高扩散项,清水水花需要保留尖锐运动时降低数值耗散。
一次典型网格流体 step 可以拆成以下路径。图中每个节点都对应可调试的状态,任何一个状态出现异常,最终画面都会产生膨胀、漏液、抖动、细节消失或边界穿透。
Projection 是把带散度的临时速度场修正成更接近不可压缩状态的步骤。它先通过压力方程求出 pressure field,再从速度场中减去压力梯度。Mark Harris 在 GPU Gems 的 Fast Fluid Dynamics Simulation on the GPU 中把这个思路整理成适合 GPU 纹理计算的 pass 序列,核心经验是把速度、压力、散度和密度都视为可采样的图像资源。
这个数学模型的工程意义在于:视觉问题需要回到状态变量定位。烟雾整体变胖,优先看散度和 pressure solve;水花方向错误,优先看外力注入和速度边界;涡旋过早消失,优先看 advection 的数值耗散、粘性参数和 vorticity 补偿;泡沫漂浮位置错误,优先看粒子状态与水面或速度场的同步关系。
68.2 网格与粒子方法的实现对比(Eulerian / Particle)
Eulerian 方法把流体状态放在固定空间网格上。格点或 cell face 记录速度,cell center 记录压力、密度、温度等标量。它适合烟雾、雾、火焰、低分辨率水体和体积特效,因为体积采样、压力投影、边界体素化和 shader raymarch 都能自然对接 texture。贯穿案例中的环形烟雾适合用 Eulerian grid:velocity texture 负责运动,density volume 负责可见形状,temperature 或 fuel texture 负责热上升和发光。
Particle 方法把流体状态放在移动点上。每个粒子携带位置、速度、质量、密度、寿命、泡沫强度等属性,邻域搜索决定粒子之间的相互作用。它适合飞溅、泡沫、喷泉、小水滴和自由表面细节,因为粒子能随边界分裂、飞散和聚集。贯穿案例中的水花边缘和泡沫更适合粒子表达,renderer 可以把它们转成小 sprite、mesh splat 或 foam mask。
| 方法 | 状态位置 | 压力或密度约束 | 视觉优势 | 主要成本 | 适用效果 |
|---|---|---|---|---|---|
| Eulerian grid | 固定网格或 3D texture | 通过 pressure solve 修正散度 | 烟雾体积、平滑涡旋、体积光照 | 分辨率随体积立方增长 | 烟、雾、火、低分辨率水体 |
| SPH | 移动粒子 | 通过核函数估计密度并迭代修正 | 飞溅自然、自由表面直观 | 邻域搜索和密度迭代 | 小水滴、喷泉、局部液体 |
| PIC | 粒子携带物质,网格求解速度 | 网格上做压力投影 | 稳定、噪声低 | 速度传回粒子时耗散较强 | 稳定液体、教学实现 |
| FLIP | 粒子携带速度增量,网格求解压力 | 网格上做压力投影 | 细节保留强、涡旋更活跃 | 噪声和粒子分布管理成本高 | 影视液体、复杂水花 |
SPH(Smoothed Particle Hydrodynamics)用核函数从邻近粒子估计密度、压力和力。它的直觉很直接:某个粒子附近粒子过多时,密度偏高,压力项会把粒子推开;邻域粒子过少时,自由表面形成。Macklin 和 Müller 的 Position Based Fluids 把不可压缩约束放进 Position Based Dynamics 框架,用位置约束迭代换取实时应用中的稳定交互。
PIC 和 FLIP 属于粒子与网格混合方法。它们让粒子携带物质位置和细节,再把速度转移到网格上完成压力求解。PIC 从网格直接读取新速度,结果更稳定但细节衰减明显;FLIP 把网格速度变化量加回粒子,涡旋和飞溅保留更好,但需要通过 PIC blending、粒子重采样和速度滤波控制噪声。影视液体常使用 FLIP 思路,因为自由表面细节和大尺度体积保持都需要兼顾。
选择方法时先看效果的可见形态,再看交互预算。烟雾、火焰和魔法雾的可见部分是体积密度,网格方法更直接;喷溅水滴和泡沫的可见部分是离散点,粒子方法更直接;大体积水面既要保持体积又要产生飞溅,混合方法更合适。贯穿案例中,烟雾体积用 grid solver,水花主体用 FLIP 或简化粒子,泡沫用粒子衍生状态,这个组合比单一表示更容易控制预算。
68.3 可视化流体模拟在渲染中的实装
流体 renderer 的输入并非“流体”这个抽象对象,而是一组 GPU 资源。烟雾通常输入 density volume、temperature volume、velocity volume 和 lighting cache;水体通常输入 surface level set、粒子 buffer、normal field、foam buffer 和 depth;爆炸云还会输入 emission、fuel、temperature 和 noise mask。贯穿案例的 frame graph 中,simulation pass 写入这些资源,render pass 再把它们绑定到 volume shader、water shader 和 particle shader。
Surface reconstruction 是把离散状态转成可着色表面的过程。粒子液体可以先转成隐式场,例如每个粒子向空间贡献一个平滑核,再从场中提取等值面;也可以做 screen-space fluid rendering,把粒子深度和厚度渲染到屏幕空间,再经过滤波和法线重建得到水面外观。前者适合离线或高质量缓存,后者适合实时局部水花和交互演示。
Volume rendering 用 raymarch 沿视线累积密度和光照。每一步从 density texture 读取烟雾浓度,从 lighting 或 shadow volume 读取受光程度,再用 transmittance 控制透明度衰减。GPU Gems 3 的 Real-Time Simulation and Rendering of 3D Fluids 展示了 simulation 与 3D volume rendering 的典型连接:模拟网格输出体积状态,渲染阶段用体积采样把状态转成可见烟雾。
泡沫和喷溅常作为液体的二级可见状态生成。一个常见规则是:当粒子速度高、局部曲率大、碰撞冲量大或液面附近扰动强时生成 foam particle。foam particle 本身寿命较短,属性包括位置、速度、大小、透明度、湿度和贴图随机种子。它不负责主液体体积守恒,负责向观众传达高速碰撞、破碎和白水细节。
渲染接入需要明确资源生命周期。Compute shader 更新 velocity texture 后,后续 pressure pass 会读取它并写入 pressure texture;projection pass 又会读取 pressure 和 velocity,写出 corrected velocity;volume render pass 读取 corrected velocity 生成 motion vector 或调试可视化。若 API 使用显式同步,barrier 需要表达写后读、layout transition 和 queue ownership。若引擎使用 frame graph,这些依赖应由 resource usage 自动推导,但调试时仍要能定位每个 pass 的读写关系。
贯穿案例可以分成三条渲染路径。烟雾路径是 velocity volume → density advection → volume raymarch → composite;水面路径是 particle/grid state → surface reconstruction → water material → transparent pass;泡沫路径是 impact event → foam particle buffer → billboard or mesh splat → transparent pass。这三条路径共享时间步和边界信息,但 shading 参数各自独立。画面中一圈水花和一团烟雾同步扩散,来自 simulation time、collision event 和 render interpolation 的一致性。
调试可视化应覆盖 simulation state 到 final color 的每个转换点。速度场可以用箭头或颜色编码,散度可以显示为正负热力图,压力残差可以显示为迭代收敛曲线,粒子密度可以显示为邻域计数,泡沫生成可以显示触发 mask。只看 final color 会把 solver、资源同步、透明排序和材质参数混在一起;分层可视化能把问题定位到具体 pass。
68.4 稳定性与求解精度优化策略
流体稳定性首先由时间步控制。CFL condition 用来约束一个时间步内信息跨越的空间距离,常用直觉是 应处在可控范围内,其中 是网格尺度。半拉格朗日 advection 在较大时间步下仍能保持数值稳定,但会带来额外耗散和模糊。粒子方法也受时间步影响:一步移动距离过大时,邻域搜索会跳变,碰撞和边界条件会出现穿透。
Pressure solve 的精度决定不可压缩效果。Jacobi 迭代实现简单,适合纹理 ping-pong;Gauss-Seidel、conjugate gradient 或 multigrid 收敛效率更高,但实现和并行调度更复杂。实时项目常用固定迭代次数换取稳定 frame time,离线项目常用 residual threshold 控制误差。画面中的体积膨胀、液体缩水、边界渗漏和烟雾呼吸感,都可能来自压力残差过高。
Vorticity confinement 是给数值耗散补回涡旋视觉的一种工程项。半拉格朗日 advection 会让小尺度旋转逐渐变软,vorticity confinement 根据旋度强度和方向向速度场加入局部旋转力。它能让烟雾边缘更卷曲,也会提高能量感。参数过强会让涡旋像人为噪声,参数过弱会让烟雾很快变成均匀团块。判断依据应回到速度场和最终密度形状,而非单独盯着颜色贴图。
Boundary condition 决定流体与场景几何的关系。固体边界通常来自 SDF、碰撞体网格或体素化 mask;速度边界决定流体在墙面处停止、滑动、反弹还是流出。自由滑移适合无粘墙面效果,粘性边界适合贴附感强的液体,open boundary 适合烟雾从模拟域边缘离开。贯穿案例中的地面水花若穿过地面,优先检查 collision SDF、粒子投影、速度法线分量和 substep,而非直接修改水面 shader。
Substep 把一个渲染帧的时间间隔拆成多个模拟小步。它提高高速运动和碰撞事件的稳定性,也会增加 solver pass 数量。实时引擎常把 substep 作为质量档位:低端平台使用较少 substep 和更低网格分辨率,高端平台提高 substep、pressure iterations 和粒子数量。离线制作可以把 simulation cache 与 render frame 解耦,用更密的 simulation sample 支撑 motion blur 和高质量表面重建。
稳定性排查应按状态路径推进。第一步固定输入事件和时间步,确认外力、碰撞体和边界在每帧一致;第二步显示 velocity、divergence、pressure residual 和 particle neighbor count;第三步关闭泡沫、噪声和复杂材质,只观察 density、surface 或粒子位置;第四步逐步恢复 vorticity、foam、lighting 和透明混合。这个顺序能把数值问题和渲染问题分离。
精度优化要和视觉预算绑定。提高网格分辨率会增加 texture memory、pressure solve pass 和 raymarch 采样成本;提高粒子数量会增加邻域搜索、排序、buffer 带宽和透明绘制成本;提高 pressure iterations 会增加 compute time;提高 raymarch steps 会增加 fragment 或 compute 负载。性能 profiling 中应把 solver time、render time、bandwidth、barrier、overdraw 和透明排序分开记录。
68.5 流体效果在游戏与影视中的表现
游戏流体常把效果拆成可控的局部系统。烟雾用低分辨率 3D grid 或 2D flipbook,加上 vector field、noise 和 lightmap;水面用高度场、Gerstner wave、屏幕空间扰动和局部粒子;爆炸用预烘焙 volume、flipbook、粒子和后处理。实时项目关注 frame time、内存、平台差异、交互响应和美术可控性,完整 Navier-Stokes 求解只覆盖局部高价值区域。
影视流体更重视镜头级可信度和可缓存性。大规模海浪、破碎水花、浓烟、云团和爆炸通常使用离线 solver,把高分辨率 simulation cache 输出给 surface reconstruction、volume rendering、foam pass 和 lighting pass。Robert Bridson 的 Fluid Simulation for Computer Animation 代表了图形动画语境下的流体资料路线:先建立数值求解基础,再把自由表面、FLIP、边界和渲染接入统一到制作管线。
烟雾效果的关键可见对象是密度轮廓、涡旋细节和光照层次。游戏中常用低分辨率 simulation 加噪声补细节,或者直接使用 flipbook volume;影视中会使用高分辨率体积缓存、温度驱动浮力、燃料燃烧和多重散射近似。若目标是角色周围一团互动雾,实时 grid solver 足够;若目标是主镜头中翻滚的爆炸云,缓存和离线 volume rendering 更稳。
水体效果的关键可见对象是自由表面、反射折射、白水、飞溅和接触边界。开放世界海面常用频谱波或程序波生成主表面,再用局部粒子表现船尾浪和泡沫;近景液体飞溅更依赖 FLIP、SPH 或 PBF;小规模 UI 或魔法水球可以用粒子和屏幕空间重建。判断方法是先确认观众看到的是大面积连续表面、局部喷溅还是泡沫细节,再选择主表示。
爆炸和魔法特效在物理准确性上通常让位于镜头语言。冲击波需要强方向性和快速能量衰减,魔法流体需要形状可控、颜色稳定和节奏明确。工程上会把 simulation field 与 artist mask 混合:速度场提供自然卷吸,curve 和 event trigger 控制爆发时机,noise field 提供细节扰动,shader 控制颜色、发光和透明度。这样能把流体感和导演性结合起来。
| 效果类型 | 常用 simulation state | 常用渲染路径 | 预算取舍 |
|---|---|---|---|
| 互动烟雾 | velocity、density、temperature volume | volume raymarch、低分辨率上采样、体积阴影近似 | 降低网格分辨率,保留速度与密度调试 |
| 地面水花 | particle buffer、collision SDF、foam state | sprite、mesh splat、screen-space fluid | 控制粒子数量和透明 overdraw |
| 大面积海面 | wave spectrum、高度场、foam mask | water material、reflection、SSR、normal blend | 主表面程序化,局部 splash 粒子化 |
| 影视液体 | FLIP particles、level set、foam cache | mesh surface、volume/foam pass、motion blur | 提高缓存分辨率和重建质量 |
| 魔法流体 | velocity field、artist mask、event curve | emissive volume、particle trail、post-process | 牺牲物理精度换取形状和节奏控制 |
贯穿案例的最终方案可以这样拆分:地面冲击事件生成 radial impulse,烟雾 grid 接收外力并更新 density,水花粒子从接触环生成并受重力和碰撞约束,foam particle 从高速度和撞击区域派生,renderer 分别执行 volume raymarch、transparent water pass 和 foam pass。若画面预算紧张,优先保留事件同步、边界正确和密度轮廓,再降低粒子数量、raymarch steps 和泡沫分辨率。
最小自检任务
给定一个实时特效目标:角色落地后产生环形烟雾和地面水花,目标平台要求每帧 simulation 与 rendering 总成本控制在 4 ms 内。当前症状是:烟雾环在 0.5 秒后变成均匀雾团,水花粒子偶尔穿过地面,泡沫和水面边界不同步。请写出你会选择的主要流体表示、渲染资源路径和排查顺序。
答案要点
主要表示应拆成两条路径:烟雾使用 Eulerian grid,状态包括 velocity、density、temperature 或其他控制场;水花和泡沫使用粒子 buffer,状态包括 position、velocity、lifetime、foam intensity 和随机种子。这个组合匹配可见形态:烟雾是连续体积,水花和泡沫是离散飞溅细节。
渲染路径应从 simulation resource 推到 shader resource。烟雾路径是外力注入速度场,advection 更新 density volume,volume shader 做 raymarch 和光照近似;水花路径是碰撞事件生成粒子,粒子更新后进入 sprite 或 screen-space fluid pass;泡沫路径从高速碰撞区域派生 foam particle 或 foam mask,再进入透明 pass。
烟雾变成均匀雾团时,应先检查 advection 耗散、粘性参数、grid 分辨率和 vorticity confinement,再看 volume shader 的噪声、密度 remap 和光照。若 velocity field 中涡旋已经消失,问题在 solver 或参数;若 velocity field 保留涡旋但 final color 看起来糊,问题在渲染采样、上采样或透明合成。
水花穿过地面时,应先检查 collision SDF 或地面碰撞体与粒子 simulation 使用的坐标空间,再检查 substep、粒子投影、速度法线分量和边界反弹。若粒子位置已经在 debug view 中位于地面下方,shader 无法修正主问题;若粒子位置正确但渲染看起来穿透,需要检查 depth test、soft particle depth 和透明排序。
泡沫和水面边界不同步时,应检查 foam 生成使用的水面状态版本、simulation 与 rendering 的 frame latency、buffer barrier 和 interpolation。foam 应来自同一帧或同一插值时间点的水体状态;若 foam 使用上一帧粒子,水面使用当前帧 surface reconstruction,边界就会产生可见漂移。
预算控制应先保护状态正确性,再降低视觉采样。可降低 smoke grid 分辨率、raymarch steps、foam 粒子数量和透明绘制半径;pressure iterations、substep 和边界修正需要保留到画面稳定所需的最低值。最终判断以 debug view 和 pass timing 共同确认。
本章知识点总结
- 速度场:速度场记录空间中流体状态的运动方向和强度,是烟雾密度、水花粒子和泡沫事件共同依赖的基础状态。
- 压力场:压力场用于修正局部体积变化,pressure solve 的精度会直接影响膨胀、缩水、漏液和呼吸感。
- 散度:散度度量一个区域的流入流出差异,不可压缩流体求解会把最终速度场推向零散度状态。
- Advection:Advection 负责用速度场搬运速度、密度、温度、表面标记或粒子属性,数值耗散会削弱涡旋和尖锐细节。
- 网格法:Eulerian grid 适合烟雾、火焰、体积雾和低分辨率局部水体,因为它能直接对接 texture、pressure pass 和 volume shader。
- 粒子法:SPH、PBF、PIC 和 FLIP 适合自由表面、水花、喷溅和泡沫细节,因为粒子能自然表达分裂、飞散和聚集。
- 混合方法:PIC 和 FLIP 把粒子细节与网格压力求解结合起来,适合需要体积保持和自由表面细节的液体效果。
- 表面重建:Surface reconstruction 把粒子或隐式场转换成可着色水面,screen-space 方法适合实时局部水花。
- 体积渲染:Volume rendering 通过 raymarch 累积密度、光照和透明度,适合烟雾、雾、爆炸云和魔法体积效果。
- 泡沫状态:泡沫通常来自高速、碰撞、曲率或扰动区域,它表达破碎细节,不承担主液体体积守恒。
- CFL 条件:CFL condition 用时间步、速度和空间尺度约束状态推进,过大的时间步会放大邻域跳变和边界穿透。
- 涡旋补偿:Vorticity confinement 能补回 advection 丢失的小尺度旋转,但参数需要通过速度场和密度轮廓共同判断。
- 边界条件:Boundary condition 决定流体与地面、墙体、开放域和碰撞体的关系,是漏液、穿透和贴附问题的首要检查对象。
- 资源路径:Simulation pass 输出 texture 和 buffer,render pass 读取这些资源生成体积、水面和泡沫,显式同步或 frame graph 需要表达读写依赖。
- 预算取舍:实时项目先保证事件同步、边界正确和状态稳定,再调低网格分辨率、粒子数量、raymarch steps 和透明绘制成本。
- 排查顺序:流体问题应先看 simulation state,再看资源同步,最后看 shader 和合成结果,这个顺序能把数值问题与渲染问题分离。