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

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

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

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

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

代码如下:

  1. return function frame() {
  2. ...
  3. const commandEncoder = device.createCommandEncoder({});
  4. {
  5. const passEncoder = commandEncoder.beginComputePass();
  6. passEncoder.setPipeline(computePipeline);
  7. passEncoder.setBindGroup(0, particleBindGroups[t % 2]);
  8. passEncoder.dispatch(numParticles);
  9. passEncoder.endPass();
  10. }
  11. ...
  12. }

我们对这个pass进行分析:

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

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

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

代码如下:

  1. const renderPipeline = device.createRenderPipeline({
  2. ...
  3. vertexState: {
  4. vertexBuffers: [{
  5. // instanced particles buffer
  6. arrayStride: 4 * 4,
  7. stepMode: "instance",
  8. attributes: [{
  9. // instance position
  10. shaderLocation: 0,
  11. offset: 0,
  12. format: "float2"
  13. }, {
  14. // instance velocity
  15. shaderLocation: 1,
  16. offset: 2 * 4,
  17. format: "float2"
  18. }],
  19. }, {
  20. // vertex buffer
  21. arrayStride: 2 * 4,
  22. stepMode: "vertex",
  23. attributes: [{
  24. // vertex positions
  25. shaderLocation: 2,
  26. offset: 0,
  27. format: "float2"
  28. }],
  29. }],
  30. },
  31. ...
  32. });
  33. ...
  34. const vertexBufferData = new Float32Array([-0.01, -0.02, 0.01, -0.02, 0.00, 0.02]);
  35. const verticesBuffer = device.createBuffer({
  36. size: vertexBufferData.byteLength,
  37. usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
  38. });
  39. verticesBuffer.setSubData(0, vertexBufferData);
  40. ...
  41. return function frame() {
  42. ...
  43. const commandEncoder = device.createCommandEncoder({});
  44. ...
  45. {
  46. const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
  47. passEncoder.setPipeline(renderPipeline);
  48. passEncoder.setVertexBuffer(0, particleBuffers[(t + 1) % 2]);
  49. passEncoder.setVertexBuffer(1, verticesBuffer);
  50. passEncoder.draw(3, numParticles, 0, 0);
  51. passEncoder.endPass();
  52. }
  53. ...
  54. }

有两个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个粒子),但与本文对应的最新版本基本上相同,而且它对示例代码分析得比较详细,所以推荐大家学习。

WebGPU-8
webgpu-samplers Github Repo

版权声明:本文为chaogex原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/chaogex/p/12101154.html