WebGPU学习(十):介绍“GPU实现粒子效果”

你们好,本文介绍了“GPU实现粒子效果”的基本思想,并推荐了相应的学习资料。html

本文学习webgpu-samplers->computeBoids示例,它展现了如何用compute shader实现粒子效果,模拟鸟群的行为。git

上一篇博文:
WebGPU学习(九):学习“fractalCube”示例github

下一篇博文:
WebGPU学习(十一):学习两个优化:“reuse render command buffer”和“dynamic uniform buffer offset”web

最终渲染结果:
截屏2019-12-26上午9.38.33.png-26.9kBide

为何不在CPU端实现粒子效果?

虽然在CPU端实现会更灵活和可控,但若是粒子数量很大(如上百万),且与场景有交互,则最好在GPU端实现。学习

示例的实现思想

首先执行compute pass

代码以下:优化

return function frame() {
    ...

    const commandEncoder = device.createCommandEncoder({});
    {
      const passEncoder = commandEncoder.beginComputePass();
      passEncoder.setPipeline(computePipeline);
      passEncoder.setBindGroup(0, particleBindGroups[t % 2]);
      passEncoder.dispatch(numParticles);
      passEncoder.endPass();
    }
    ...
  }

咱们对这个pass进行分析:google

bind group包含两个storage buffer:ParticlesA和ParticlesB

ParticlesA存储了上一帧全部粒子的数据。compute shader读取它,并计算出下一帧全部粒子的数据,写到ParticlesB中,打了一个ping-pong操做;spa

注:storage buffer在shader中可被读和写,而uniform buffer、vertex buffer等在shader中只能被读.net

dispatch到1500个instance,每一个instance执行一次compute shader

compute shader计算每一个粒子的数据时,须要遍历其它的全部粒子,从而实现相互的交互做用,模拟鸟群行为。
一共有1500个粒子,共须要计算1500*1500次。若是在CPU端执行,太花时间;而在GPU端执行,则每一个instance只须要计算1500次,大大提升了效率。

而后执行render pass

代码以下:

const renderPipeline = device.createRenderPipeline({
    ...
    vertexState: {
      vertexBuffers: [{
        // instanced particles buffer
        arrayStride: 4 * 4,
        stepMode: "instance",
        attributes: [{
          // instance position
          shaderLocation: 0,
          offset: 0,
          format: "float2"
        }, {
          // instance velocity
          shaderLocation: 1,
          offset: 2 * 4,
          format: "float2"
        }],
      }, {
        // vertex buffer
        arrayStride: 2 * 4,
        stepMode: "vertex",
        attributes: [{
          // vertex positions
          shaderLocation: 2,
          offset: 0,
          format: "float2"
        }],
      }],
    },
    ...
  });
  
  ...

  const vertexBufferData = new Float32Array([-0.01, -0.02, 0.01, -0.02, 0.00, 0.02]);
  const verticesBuffer = device.createBuffer({
    size: vertexBufferData.byteLength,
    usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
  });
  verticesBuffer.setSubData(0, vertexBufferData);
  
  ...

  return function frame() {
    ...

    const commandEncoder = device.createCommandEncoder({});
    ...
    {
      const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
      passEncoder.setPipeline(renderPipeline);
      passEncoder.setVertexBuffer(0, particleBuffers[(t + 1) % 2]);
      passEncoder.setVertexBuffer(1, verticesBuffer);
      passEncoder.draw(3, numParticles, 0, 0);
      passEncoder.endPass();
    }
    ...
  }

有两个vertex buffer:
ParticlesB使用“instance”的stepMode,被设置到第一个vertex buffer中;
vertices buffer(包含3个顶点数据,每一个顶点数据包含x坐标和y坐标)使用“vertex”的stepMode,被设置到第二个vertex buffer中。

draw一次,绘制1500个实例(使用ParticlesB的数据),3个顶点(使用vertices buffer的数据)。

注:每一个粒子做为一个实例,由包含3个顶点的三角形组成。

推荐学习资料

你们能够参考WebGPU-8,来学习示例的具体的代码。

虽然该文对应的示例代码的版本比较老(如它的示例中是1000个粒子,而不是1500个粒子),但与本文对应的最新版本基本上相同,并且它对示例代码分析得比较详细,因此推荐你们学习。

另外,你们能够经过Get started with GPU Compute on the Web,学习如何使用compute shader计算矩阵运算。

参考资料

WebGPU-8
webgpu-samplers Github Repo

相关文章
相关标签/搜索