WebGPU學習(十):介紹“GPU實現粒子效果”


大家好,本文介紹了“GPU實現粒子效果”的基本思想,並推薦了相應的學習資料。

本文學習webgpu-samplers->computeBoids示例,它展示了如何用compute shader實現粒子效果,模擬鳥群的行為。

上一篇博文:
WebGPU學習(九):學習“fractalCube”示例

下一篇博文:
WebGPU學習(十一):學習兩個優化:“reuse render command buffer”和“dynamic uniform buffer offset”

最終渲染結果:
截屏2019-12-26上午9.38.33.png-26.9kB

為什么不在CPU端實現粒子效果?

雖然在CPU端實現會更靈活和可控,但如果粒子數量很大(如上百萬),且與場景有交互,則最好在GPU端實現。

示例的實現思想

首先執行compute pass

代碼如下:

  const numParticles = 1500;

  ...

  let t = 0;
  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();
    }
    ...

    ++t;
  }

我們對這個pass進行分析:

particleBindGroups包含兩個storage buffer:ParticlesA和ParticlesB

ParticlesA存儲了上一幀所有粒子的數據。compute shader首先讀取它,然后計算出下一幀所有粒子的數據,最好寫到ParticlesB中。這樣就打了一個ping-pong操作;

注:storage buffer在shader中可被讀或寫,而uniform buffer、vertex buffer等在shader中只能被讀

dispatch到1500個instance,每個instance執行一次compute shader

compute shader計算每個粒子的數據時,需要遍歷其它的所有粒子,計算相互的交互作用。

一共有1500個粒子,共需要計算15001500次。
如果在CPU端執行,只能串行計算,一共需要計算1500
1500次;
如果在GPU端執行,GPU有1500個instance,每個instance並行地計算1500次,因此一共只需要計算1500次,大大提高了效率。

然后執行render pass

代碼如下:

  const numParticles = 1500;

  ...

  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);
      //ParticlesB使用“instance”的stepMode,被設置到第一個vertex buffer中
      passEncoder.setVertexBuffer(0, particleBuffers[(t + 1) % 2]);
      //vertices buffer(包含3個頂點數據,每個頂點數據包含x坐標和y坐標)使用“vertex”的stepMode,被設置到第二個vertex buffer中
      passEncoder.setVertexBuffer(1, verticesBuffer);
      //draw一次,繪制1500個實例(使用ParticlesB的數據),其中每個實例有3個頂點(使用vertices buffer的數據)
      //注:每個粒子作為一個實例,由包含3個頂點的三角形組成
      passEncoder.draw(3, numParticles, 0, 0);
      passEncoder.endPass();
    }
    ...
  }

推薦學習資料

大家可以參考WebGPU-8,來學習示例的具體的代碼。

雖然該文對應的示例代碼的版本比較老(如它的示例中是1000個粒子,而不是1500個粒子),但與本文對應的最新版本基本上相同,而且它對示例代碼分析得比較詳細,所以推薦大家學習。

另外,大家可以通過Get started with GPU Compute on the Web,學習如何使用compute shader計算矩陣運算。

參考資料

WebGPU-8
webgpu-samplers Github Repo


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM