阶段 4 · 计算基础18 分钟
计算 + 渲染:GPU 粒子双缓冲
Ping-Pong Storage Buffer 与跨 Pass 数据流
计算着色器更新粒子,渲染管线读取更新后的缓冲区,一次提交完成模拟和显示。
LEARNING GOALS
先明确本课目标
1
组合 compute pass 与 render pass
2
实现 ping-pong bind groups
3
让 GPU 数据不经过 CPU 直接进入绘制
MENTAL MODEL · 再建立心智模型
两个缓冲区轮流扮演“上一帧输入”和“这一帧输出”,避免同一数据在计算中产生读写冲突。
MINIMAL EXAMPLE
为什么粒子更新需要两个状态缓冲区?
Ping-Pong 用 A 作为只读输入、B 作为写入输出,下一帧交换角色。这样每个 invocation 都读取同一代状态,避免在同一 Pass 中边读边覆盖。
javascriptlet source = 0;
function frame() {
const destination = 1 - source;
computePass.setBindGroup(0, groups[source]);
computePass.dispatchWorkgroups(groupCount);
renderPass.setBindGroup(0, renderGroups[destination]);
source = destination;
}- Compute 读 A 写 B 一次更新使用一致的旧状态计算新状态。
- Render 读 B 同一 CommandEncoder 中 Compute Pass 结束后,Render Pass 消费更新结果。
- 下一帧交换 不用复制全部粒子,只交换两个资源的逻辑角色。
粒子状态始终在 GPU 内循环,CPU 每帧只更新少量时间参数并提交命令。
OPTIONAL · 按需查阅完整术语表与概念边界遇到陌生术语时再展开,不打断主线实验。
展开⌄
命令Compute Pass记录计算管线与 dispatchWorkgroups 的命令阶段。着色器内建值instance_index当前实例序号,用于实例化绘制或从数组/存储缓冲区读取每实例数据。资源绑定Storage Buffer可承载大型结构化数组的着色器资源,可只读或读写,常用于计算和 GPU 驱动数据。模拟Ping-Pong Buffer两个资源轮流作为输入和输出,避免同一 pass 中对同一数据产生非法或不明确的读写。GPU 数据流Compute-to-RenderCompute Pass 写入 Buffer 或 Texture,后续 Render Pass 直接读取该结果而不经过 CPU 读回。模拟Simulation Step从上一时刻状态计算下一时刻状态的一次更新,通常使用 dt 控制时间尺度。渲染管线Point List每个顶点独立形成一个点图元的拓扑,适合简单粒子,也可改用实例化四边形获得更灵活外观。
LIVE EXPERIMENT · 先获得反馈
边改边运行
浏览器加载后会激活代码编辑器、独立运行环境与实时输出。
本课挑战
加入鼠标吸引力并让粒子按速度着色。
回到原理,解释刚才发生了什么
先用结果建立反馈,再按运行流程、核心原理和知识关系逐层深入。
CORE IDEA
核心讲解
GPU 内部闭环
粒子状态常驻 GPU;CPU 只更新时间和交互参数,避免每帧读回再上传。
隐式资源转换
WebGPU 根据 pass 边界和声明的 usage 管理必要同步。你仍需避免同一 pass 中不允许的读写别名。
OPTIONAL · 迁移时查阅WebGPU 与 WebGL2 对照主线实验完成后,再用完整映射处理跨平台或跨 API 迁移。
展开⌄
WebGL 用 Transform Feedback/纹理 Ping-Pong 模拟,WebGPU 用 Compute → Render 直接共享数据。
WebGL
顶点 shader 读取上一帧 Buffer,经 Transform Feedback 写入下一帧,再交换绑定。
WebGPU
Compute Shader 从 A 读向 B 写,RenderPass 直接把 B 当作实例/Storage 输入。
保留 Ping-Pong 思想,但移除 Transform Feedback 对顶点输出顺序和 draw 形状的限制。