粒子物理(Particles)

由 Rust + WebAssembly 驱动的 N 体引力模拟

🖱️ 单击 添加粒子团  |  刷新页面重新随机生成

📖 原理说明

N 体引力模拟:画布上的每个粒子都受到其他所有粒子的万有引力,粒子会自发聚集成团、互相环绕、合并。点击画布可以投放新的粒子团。

🧮算法原理

Δv = G · dt / (r² + ε²)每对粒子,ε = 20
两两计算引力
对每一对粒子计算引力 F ∝ G / r²,复杂度是 O(n²)。利用牛顿第三定律(作用力与反作用力大小相等、方向相反),每对粒子只算一次,同时更新双方,计算量减半。
软化因子
距离平方加上一个常数 r² + ε²(ε = 20)。否则两个粒子靠得太近时引力趋于无穷大,粒子会被瞬间弹飞。
数值积分
采用半隐式欧拉法,先用引力更新速度,再用新速度更新位置,时间步长 dt = 0.016s(约一帧)。引力大于 1500 时每帧拆成 3 个子步,提高稳定性。
阻尼与边界
每步速度乘以阻尼系数(例如 0.99),模拟能量损耗;粒子飞出边界后从对边回来。

🔄Rust 与 JavaScript 的分工

  1. Rustinit_particles(300, 900, 600):Rust 随机生成 300 个粒子,保存为全局状态。
  2. Rust每帧 JS 调用 step_particles(dt, 引力, 阻尼),由 Rust 完成全部物理计算。
  3. JSJS 调用 get_particles(),取回扁平数组 [x1, y1, x2, y2, …],用径向渐变画出发光粒子,并用半透明覆盖层产生拖尾效果。
  4. Rust点击画布时调用 add_cluster(x, y, 50, 30),在点击位置附近加入 50 个粒子。

⚡性能要点

  • 300 个粒子每帧约 4.5 万对引力计算。粒子数翻倍,计算量变成 4 倍。点击几次后可以在底部看到物理计算的耗时明显上升。
  • 这正是 CPU 方案的上限。可以对比「WebGPU N 体模拟」:同样的算法放到 GPU 上并行,可以跑上万个粒子。

源码crates/simulation/particles/src/lib.rswww/particles/index.js