WebGPU N 体模拟(N-body)
GPU 计算 5,000 ~ 20,000 个粒子 —— 初始数据由 Rust WASM 生成
🖱️ 单击 添加粒子团 |
O(n²) WGSL 计算着色器 |
对比 CPU 粒子物理
📖 原理说明
与「粒子物理」相同的 N 体引力模拟,但物理计算和渲染都放到 GPU 上完成,可以实时模拟成千上万个粒子。Rust/WASM 只负责生成粒子的初始位置和速度。
🧮算法原理
工作组数 = ⌈粒子数 / 256⌉每个线程负责 1 个粒子
- 计算着色器(Compute Shader)
- 用 WGSL 编写。每个 GPU 线程负责一个粒子,遍历所有其他粒子累加引力,然后更新自己的速度和位置。每个工作组 256 个线程,每帧派发
⌈粒子数 / 256⌉ 个工作组。
- 同样是 O(n²)
- 算法复杂度和 CPU 版一样,但 GPU 有成百上千个核心同时计算,所以能处理的粒子数多出一两个数量级。
- 数据不回传
- 粒子状态
[x, y, vx, vy] 始终保存在 GPU 的存储缓冲区里。顶点着色器直接按 vertex_index 从同一个缓冲区读取位置,以点的形式绘制,全程不回到 CPU。
- 加色混合
- 渲染管线使用
src-alpha + one 的混合方式,粒子重叠的地方会叠加变亮,形成发光效果。
- 简化处理
- 为了代码简洁,计算着色器直接原地读写同一个缓冲区,没有使用双缓冲。同一帧里有些粒子可能读到已经更新过的位置,这对视觉效果的影响可以忽略。
🔄Rust 与 JavaScript 的分工
- JS页面加载时逐项检测 WebGPU 环境(API、浏览器版本、适配器、设备、Canvas 上下文),全部通过才开始运行。
- RustJS 调用 Rust 的
gen_gpu_particles(数量, 900, 600),得到扁平的 Float32 初始数据,一次性上传到 GPU 缓冲区。
- GPU每帧 JS 只把引力等参数写入一个 28 字节的 uniform 缓冲区,然后提交一个计算通道和一个渲染通道,其余工作全部由 GPU 完成。
⚡性能要点
- CPU 与 GPU 之间每帧几乎没有数据传输,这是 GPU 计算能获得高性能的关键。
- 可以把粒子数量调到 20,000,再与 CPU 粒子物理(300 个粒子)对比两者的差距。
- 需要 Chrome / Edge 113+ 和可用的 GPU。虚拟机或没有硬件加速的环境会显示检测失败的原因。
源码crates/simulation/webgpu/src/lib.rswww/webgpu/index.js内含 WGSL 着色器