本节摘要:无网格流体用 SPH:每个粒子用核函数向邻居插值密度,密度偏离静止值就产生压力,压力梯度把粒子推开,再加粘性和重力。邻居搜索用第 3 章同一套网格。SPH 好看、好耦合刚体,难保证不可压缩;要做大体积静水,网格流体或 PBF 更合适。先写能泼出去的一碗粒子。
阅读完本节,你应当能够:
一千个刚体球加接触也能“像液体”,但接触对数和求解迭代会打死预算,而且不可压缩性极差,会弹。SPH 把“流体微团”当成带核半径的采样点,力来自场而不是互补接触。粒子可以重叠,靠压力分开。这是另一套状态:位置、速度、密度、压力,没有朝向。
核半径 h 决定分辨率。h 太小,邻居不够,密度噪声大,粒子炸;h 太大,模糊成一团浆。经验:静止时每个粒子邻居 30 左右(三维),二维十几个。粒子间距 r0 ≈ h/2 量级,按核函数具体定。质量 m = 密度0 * 粒子体积。
for each particle i: rho_i = sum_j m_j * W(xi - xj, h) P_i = k * (rho_i / rho0 - 1) # 或 Tait 状态方程 for each particle i: a_pressure = -sum_j m_j * (P_i/rho_i^2 + P_j/rho_j^2) * gradW a_visc = 粘性项 v += (g + a_pressure + a_visc) * dt x += v * dt
压力公式多种。线性的好懂但压缩明显;Tait 指数硬一些。真正接近不可压缩要 PBF(位置基流体)或隐式压力投影。游戏水花、血液、魔法流体,显式 SPH 往往够。
| 方案 | 不可压缩 | 实现 | 与刚体耦合 | 适合 |
|---|---|---|---|---|
| 刚体球海 | 差 | 已有求解器 | 天然 | 演示错误 |
| 显式 SPH | 差到中 | 中 | 采样或接触 | 小规模特效 |
| PBF/XPBD 流体 | 较好 | 中 | 位置接触 | 游戏水体 |
| 网格欧拉流体 | 好 | 高 | 较烦 | 大体积、烟火 |
动手优先:先显式 SPH 泼一碗,再决定要不要 PBF。不要先写完整 Navier-Stokes 求解器。
邻居搜索就是 3.1 的均匀网格,格边长等于 h。只查邻格。这是 SPH 的性能心脏。粒子数上万时, squar 邻居是自杀。
自由表面处邻居少,密度被低估,压力变负,表面张力乱或粒子自爆。常用:密度钳制不低于 rho0 的某个比例;或用核修正除以核之和;或负压当 0。墙:若只用刚体碰撞推粒子,贴墙粒子半边没有邻居,密度又低,会往墙里吸。鬼粒子(墙外镜像)或把边界力单独加,专门补密度。
💡 关键直觉:SPH 的一切错误外观,多半是密度估错。先可视化密度颜色,再调压力系数。
粘性让流体成块而不是气体。过粘像糖浆,过低像喷雾。XSPH 把速度向邻居平均,是一种便宜粘性。表面张力用颜色场或凝聚力项,小尺度水珠才需要。
时间步受 CFL 限制:粒子不要一步跨过太大的 h 比例,压力波速高时 dt 要小。流体常常要比刚体更细的子步。刚体 60Hz,流体 120–240Hz 子步并不罕见。两边耦合时在细步上采样刚体位置。
刚体对流体:把刚体表面当边界,或对重叠粒子施加惩罚/投影。流体对刚体:把压力或碰撞冲量积到刚体上。浮力应自然出现——若箱子不下沉也不上浮,检查力是否成对。单向耦合(流体碰墙但墙不动)做瀑布够用,做船不够。
⚠️ 常见坑:压力系数 k 调到极大想消灭压缩,dt 没减,第一步粒子就飞出屏幕。先减 dt 或改 PBF,不要只加 k。
渲染:粒子可以画球,或用 marching cubes/各向异性核出表面。物理教程不必做出电影水面,但要能看密度。调试用假色:蓝低密红高密。
PBF 简介:预测位置后,用密度约束做 XPBD 式投影,迭代把密度推回 rho0,再更新速度。压缩明显改善,实现仍是粒子加网格。若你已经写了 5.1 的 XPBD,PBF 是同一套思想换约束形式。我更愿意在游戏水体上用 PBF 而不是把 SPH 的 k 拉上天。
烟和火常用网格加半拉格朗日平流,不是 SPH。SPH 做烟扩散贵且难散。别把一种粒子方法当所有流体。选方法看:要泼溅耦合刚体用粒子;要大片烟雾用网格。
毕业实验:盒子里静止粒子,密度应接近 rho0,不应持续沸腾。开一个缺口应流出来,流量随缺口增大。放入轻盒子应浮,重盒子应沉。沸腾不停先查边界密度和负压。
哈希网格与刚体宽相可以共用代码,不要复制一份。粒子数量变化大,每帧重建网格通常比增量更新简单且够快。
单位:粒子位置仍是米。有人用像素粒子,压力系数完全无法迁移。从第一滴水就用米。
从一组能静止的参数开始:粒子间距 0.05 米,h=0.1 米,rho0=1000,k 从小加到水面看起来硬但第一步不炸,dt 从 1/120 试起,粘性刚好消除抖动。记下这组数,换场景先复制再改一个量。没有“标准 SPH 参数”,只有“这碗水的参数”。
沸腾诊断:假色密度。表面发红内部发蓝是密度估错或负压没钳。贴墙粒子发黑往墙吸是缺邻居。满盒均匀红且仍动,是压力积分不稳,减 dt。粒子成晶体格子不流动,是粘性过大或 k 过大把它们焊死。
与刚体浮力:轻盒排水重量应大于重力。若不下沉不上浮,检查力是否只加在粒子上没还到刚体。可视化刚体受力箭头。单向耦合做瀑布时箭头可以为空,做船不行。
PBF 迭代 2–4 次通常够游戏水体。迭代 20 次会很硬但贵,且边界处理不好时会抖。先边界后迭代。SPH 转 PBF 时邻居网格可原样复用,不要重写宽相。
三维静止大约三十,二维十几个。太少密度噪,太多浆。h 和间距一起定,不要只改一个。
表面常被低估密度出现负压,粒子自爆或聚团。游戏里常把负压当 0。要表面张力再单独加凝聚力,不要靠负压装表面张力。
压力波速高,CFL 更严。刚体 60Hz、流体子步 2 到 4 常见。耦合在细步采样刚体。
压缩明显、加 k 就炸时。PBF 用密度约束投影,复用邻居网格。不要无限加 k。
烟要扩散和大体积,网格半拉格朗日更合适。SPH 做烟贵且难散。泼溅用粒子,烟火用网格。
没有标准参数。一次改一个量。
压力积分不稳,减 dt。
粘性或 k 过大焊死。
力没还到刚体。可视化受力箭头。
第一步飞走。改 PBF 或减 dt。
贵且难散。烟走网格。
比刚体暴力更早死。必须网格。
贴墙往里吸。
调「02-粒子流体SPH」时一次只改一个量,写下场景名、旧值、新值、醒岛数或能量变化。没有笔记的调参会在一周后变成传说。传说不能回归,不能交给同事,也不能在换库时当合同。 本条对应「02-粒子流体SPH」第 1 号备忘,和相邻条目不要合并成一句空话,分开验收。
「02-粒子流体SPH」的单元测试尽量不依赖图形窗口。能在无头模式跑的断言,才会每次提交都跑。只靠进游戏看一看,等于没有测试。把金字塔睡眠、速度交换、自由落体距离这类金标准写成断言。 本条对应「02-粒子流体SPH」第 2 号备忘,和相邻条目不要合并成一句空话,分开验收。
文档里的公式符号必须和代码注释同一张表。尤其是法向从谁指向谁、相对速度谁减谁、lambda 是力还是冲量。「02-粒子流体SPH」相关代码若混用两套符号,吸引接触和飞天会轮流出现,现场非常像随机 bug。 本条对应「02-粒子流体SPH」第 3 号备忘,和相邻条目不要合并成一句空话,分开验收。
性能上先问能不能少做。「02-粒子流体SPH」再快,也快不过把睡着的岛整岛跳过、把碎片到期删掉、把静态体移出动态表。减工作量优于把同样工作向量化。向量化平方算法只是错得更稳更快。 本条对应「02-粒子流体SPH」第 4 号备忘,和相邻条目不要合并成一句空话,分开验收。
可视化是「02-粒子流体SPH」的一部分而不是插件。接触生成时就把点、法向、深度留下来给绘制。事后从形状重新推断,会和求解器看到的数据不一致,你会修一个并不存在的偏差。 本条对应「02-粒子流体SPH」第 5 号备忘,和相邻条目不要合并成一句空话,分开验收。
固定时间步是「02-粒子流体SPH」所有数值实验的尺子。尺子在抖,你无法比较积分器,无法调 beta,无法谈确定性。谁把渲染帧时间直接喂进物理,谁就在用弹性的尺子量钢梁。 本条对应「02-粒子流体SPH」第 6 号备忘,和相邻条目不要合并成一句空话,分开验收。
「02-粒子流体SPH」里出现 NaN 要在源头断言:未归一化法向、零长度边当轴、除以过小的有效质量、四元数没归一。把 NaN 传到求解器再查,整场速度都会脏,回溯成本高一个数量级。 本条对应「02-粒子流体SPH」第 7 号备忘,和相邻条目不要合并成一句空话,分开验收。
和美术、策划交接「02-粒子流体SPH」参数时,用测试盒而不是形容词。说清楚二十厘米台阶能上、八十厘米不能,比说手感更跟手有用。测试盒还能在引擎升级后当合同跑一遍。 本条对应「02-粒子流体SPH」第 8 号备忘,和相邻条目不要合并成一句空话,分开验收。
「02-粒子流体SPH」若跨模块耦合,先单向再双向。布料先撞静地,流体先撞静箱,关节先吊静锚。双向一步到位时,你分不清是 A 推 B 写错还是 B 还力写错。控制变量不是学术,是省生命。 本条对应「02-粒子流体SPH」第 9 号备忘,和相邻条目不要合并成一句空话,分开验收。
睡眠和唤醒属于「02-粒子流体SPH」正确性,不只是性能。该醒不醒会穿模,不该醒全醒会卡顿。爆炸查询、关节边、运动学平台都是唤醒图的边。漏一条边,现场像鬼。 本条对应「02-粒子流体SPH」第 10 号备忘,和相邻条目不要合并成一句空话,分开验收。
下一节把 XPBD 用到布料和绳索上:弯曲、拉伸、对刚体的单向拉。