堂前燕
物理 · 高中 · 流体力学 · Navier-Stokes · WebGL · 涡度 · 实时仿真

Stable Fluids · 实时烟雾仿真

拖鼠标在 GPU 上跑 Navier-Stokes —— 烟雾被你的速度场卷起,演化出涡和湍流。看流体力学最劝退的方程"活"在屏幕上。

不可压缩牛顿流体的运动方程:

ut+(u)u=1ρp+ν2u+F\frac{\partial \mathbf{u}}{\partial t} + (\mathbf{u} \cdot \nabla)\mathbf{u} = -\frac{1}{\rho}\nabla p + \nu \nabla^2 \mathbf{u} + \mathbf{F} u=0\nabla \cdot \mathbf{u} = 0

每一项都”讲故事”:

  • u/t\partial \mathbf{u}/\partial t:速度随时间变化
  • (u)u(\mathbf{u}\cdot\nabla)\mathbf{u}对流——流体被自己的速度带走(这一项是非线性的,所有混沌都来自它)
  • p/ρ-\nabla p / \rho压力梯度让流体从高压区流向低压区
  • ν2u\nu \nabla^2 \mathbf{u}粘性——动量在空间扩散
  • F\mathbf{F}外力(鼠标拖拽就在这里加入)
  • u=0\nabla \cdot \mathbf{u} = 0不可压缩约束(流出去多少必须流回来)

这个方程系是非线性的、有约束的、二阶的偏微分方程组。在 21 世纪初被列为千禧年七大数学难题之一(解的存在性与光滑性问题至今未解 → 100 万美元奖金等你拿)。

数值仿真它?1999 年 Jos Stam 发明了”Stable Fluids”算法,让你的 GPU 实时跑 Navier-Stokes。这个 demo 就是它的现代 WebGL 版本。

算法:把方程拆成 5 步

每帧时间步 dt,对速度场 u 做 5 件事:

1. 涡度增强(vorticity confinement)

数值耗散会让涡随时间衰减得过快。涡度增强人为强化已有的涡,让烟雾看起来”活生生”。这不在原始 Navier-Stokes 里,是图形学的工程技巧。

2. 对流(advection)

u\mathbf{u} \cdot \nabla 这一项。Stam 的巧招:半拉格朗日逆向追踪 —— 对每个网格点 x,问”上一时刻位于 x - dt·u(x) 的流体粒子,现在带着什么速度过来了?” 双线性插值即可。永远稳定,dt 想多大都行(精度差一些但不爆)。

3. 散度(divergence)计算

u\nabla \cdot \mathbf{u} —— 当前速度场的局部”外流量”。理想情况这应该是 0。

4. 压力(pressure)求解

解一个泊松方程 2p=u\nabla^2 p = \nabla \cdot \mathbf{u} 找到能”修正”散度的压力场。用 Jacobi 迭代 20-40 次足够(每次让每个像素 = 4 个邻居的平均减去散度的 1/4)。

5. 投影(projection)

uup\mathbf{u} \leftarrow \mathbf{u} - \nabla p。减去压力梯度,得到无散度的速度场。这一步保证质量守恒。

每个步骤都是一个 GPU 片元着色器 + 一对 ping-pong 纹理交换 read/write。整个 Navier-Stokes 仿真在现代 GPU 上 5-10 毫秒一帧,60 fps 完全 hold 得住

染料(dye)的角色

烟雾不是流体本身 —— 它是被流体携带的染料。每帧:

  1. 鼠标拖动 → 在那个位置注入染料(颜色)+ 一股速度
  2. 流体按 5 步演化
  3. 染料按 advection 公式 c/t+uc=0\partial c/\partial t + \mathbf{u}\cdot\nabla c = 0 被流场卷走

染料纯被动,不影响流场。所以你看到的”烟”其实是给透明流场上色的可视化。

真实物理 vs 你看到的

这个仿真做了几个工程上的妥协:

  1. 不可压缩约束 用单次 Jacobi 投影近似(高精度需要预条件共轭梯度,慢得多)
  2. 粘性扩散 没有显式扩散步 —— 数值耗散提供”假”粘性
  3. 对流耗散 半拉格朗日方法本身有 numerical diffusion,所以小涡涨大了就模糊
  4. 网格分辨率 192×192 / 768×768 —— 真实湍流应该有 Kolmogorov 尺度下的精细结构,这里被网格平均掉了

视觉上的现象——涡街、湍流混合、向上烟柱、缠绕的麻花——和真实流体几乎一模一样。视觉感性 ≠ 数值精度,但对教学和审美都够好。

试这些

  1. 快速直线拖动:注入一股很快的速度,看流速最快处的”流头”卷起来形成蘑菇云
  2. 慢速画 8 字:染料缠绕成精细花纹 —— 这就是混沌混合(Lagrangian chaos)
  3. 同一位置反复戳:每次注入新的颜色叠加,看染料如何被局部涡 mixing 成新色
  4. 拉高”涡度增强”:涡变得更剧烈,烟雾”更活”
  5. 拉低”染料衰减”:染料不消失,看长期混合后的画面
  6. 拉高”压力迭代”次数:得到更精确的不可压缩约束,烟雾不会”突然冒出来”

真实世界

  • 电影特效:CGI 烟、火、水、爆炸 —— 几乎全是这套算法的升级版(FLIP、APIC、PIC)
  • 天气预报:求解尺度更大的 Navier-Stokes,加上热力学和湿度
  • 风洞模拟:飞机设计、F1 赛车空气动力学、建筑风阻
  • 海洋模型:全球洋流、厄尔尼诺
  • 聚变反应堆:等离子体在磁场约束下的流动(磁流体力学 MHD)
  • 血管 / 心脏建模:医学手术规划
  • 航天器再入:高超音速气动加热

Navier-Stokes 的离散化方法 —— 有限差分、有限体积、有限元、谱方法、晶格玻尔兹曼 —— 是现代科学计算的核心,养活了 计算流体力学 (CFD) 这个完整学科。

致谢与协议

这个 demo 的算法和着色器结构基于 Pavel Dobryakov 的 WebGL-Fluid-Simulation(MIT 协议)做精简移植,重新适配了:

  • 站点的米色/朱砂/青绿/紫的调色板
  • 站点的 UI 控件风格
  • 移除了 bloom / sunrays 等装饰效果
  • 加上了教学正文 + 算法解释

Pavel 的原版有更多视觉特效和参数 —— 如果想体验”完整版”可以去原仓库的 在线 demo 玩一下。

一个深刻的观察

Navier-Stokes 方程写出来很短(两行),但解析求解几乎不可能 —— 即使是几何最简单的”球绕流”也没有闭式解。

数值上,5 个步骤 + 一些 GPU 纹理就够了。这就是计算性思维的力量:放弃”找公式”的执念,承认”可以快速迭代算出来”也是一种理解。

数学物理被这个思路接管的程度,比 20 世纪初的人能想象的要彻底得多。