WebGPU learning (VII): Learning "twoCubes" and "instancedCube" example

Hello everyone, Chrome-> webgpu-samplers-> twoCubes and instancedCube examples in this article to learn.

Both of these are the "rotatingCube" an example of the same. I suggest that you first learn this example, two examples in this article to learn

Last post:
WebGPU learning (six): Learning "rotatingCube" example

Learn twoCubes.ts

This example draws two cubes.

Compared with the "rotatingCube" example, which adds the following elements:

  • Save a ubo mvp matrix of two cubes
  • Mvp updated every frame two data matrices
  • draw twice, respectively provided corresponding uniformBindGroup

Below, we open twoCubes.ts file, in turn look at the New:

Save a ubo mvp matrix of two cubes

  • vertex shader定义uniform block

Because only a ubo, so only a uniform block, the same code rotatingCube Example:

  const vertexShaderGLSL = `#version 450
  layout(set = 0, binding = 0) uniform Uniforms {
    mat4 modelViewProjectionMatrix;
  } uniforms;
  ...
  void main() {
    gl_Position = uniforms.modelViewProjectionMatrix * position;
    ...
  }
  `;
  • Create a uniform buffer

code show as below:

  const matrixSize = 4 * 16; // BYTES_PER_ELEMENT(4) * matrix length(4 * 4 = 16)
  const offset = 256; // uniformBindGroup offset must be 256-byte aligned
  const uniformBufferSize = offset + matrixSize;

  const uniformBuffer = device.createBuffer({
    size: uniformBufferSize,
    usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
  });

uniform buffer to save the data of the two matrices mvp, but they can not be stored in a row, their starting position must be a multiple of 256, the actual uniform buffer memory layout:
0-63: a first matrix mvp
64-255: 0 (placeholders)
256-319: The second matrix mvp

The uniform buffer size is 256 + 64 = 320

  • Creating uniform bind group

Create two uniform bind group, by specifying the offset and size, correspond to the same uniform buffer:

  const uniformBindGroup1 = device.createBindGroup({
    layout: uniformsBindGroupLayout,
    bindings: [{
      binding: 0,
      resource: {
        buffer: uniformBuffer,
        offset: 0,
        size: matrixSize
      }
    }],
  });

  const uniformBindGroup2 = device.createBindGroup({
    layout: uniformsBindGroupLayout,
    bindings: [{
      binding: 0,
      resource: {
        buffer: uniformBuffer,
        offset: offset,
        size: matrixSize
      }
    }]
  });
  • Creating two mvp matrix

code show as below:

  //因为是固定相机,所以只需要计算一次projection矩阵
  const aspect = Math.abs(canvas.width / canvas.height);
  let projectionMatrix = mat4.create();
  mat4.perspective(projectionMatrix, (2 * Math.PI) / 5, aspect, 1, 100.0);
  ...
  
  let modelMatrix1 = mat4.create();
  mat4.translate(modelMatrix1, modelMatrix1, vec3.fromValues(-2, 0, 0));
  let modelMatrix2 = mat4.create();
  mat4.translate(modelMatrix2, modelMatrix2, vec3.fromValues(2, 0, 0));
  //创建两个mvp矩阵
  let modelViewProjectionMatrix1 = mat4.create();
  let modelViewProjectionMatrix2 = mat4.create();
  //因为是固定相机,所以只需要计算一次view矩阵
  let viewMatrix = mat4.create();
  mat4.translate(viewMatrix, viewMatrix, vec3.fromValues(0, 0, -7));

  let tmpMat41 = mat4.create();
  let tmpMat42 = mat4.create();

Mvp updated every frame two data matrices

Related code as follows:

  function updateTransformationMatrix() {
    let now = Date.now() / 1000;

    mat4.rotate(tmpMat41, modelMatrix1, 1, vec3.fromValues(Math.sin(now), Math.cos(now), 0));
    mat4.rotate(tmpMat42, modelMatrix2, 1, vec3.fromValues(Math.cos(now), Math.sin(now), 0));

    mat4.multiply(modelViewProjectionMatrix1, viewMatrix, tmpMat41);
    mat4.multiply(modelViewProjectionMatrix1, projectionMatrix, modelViewProjectionMatrix1);
    mat4.multiply(modelViewProjectionMatrix2, viewMatrix, tmpMat42);
    mat4.multiply(modelViewProjectionMatrix2, projectionMatrix, modelViewProjectionMatrix2);
  }

  return function frame() {
    updateTransformationMatrix();

    ...

    uniformBuffer.setSubData(0, modelViewProjectionMatrix1);
    uniformBuffer.setSubData(offset, modelViewProjectionMatrix2);
    ...
  }

updateTransformationMatrix two mvp update function matrix;
called twice setSubData, respectively mvp matrix updating the updated data to the same in a uniform buffer.

draw twice, respectively provided corresponding uniformBindGroup

code show as below:

  return function frame() {
    ...
    const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
    ...

    passEncoder.setBindGroup(0, uniformBindGroup1);
    passEncoder.draw(36, 1, 0, 0);

    passEncoder.setBindGroup(0, uniformBindGroup2);
    passEncoder.draw(36, 1, 0, 0);

    passEncoder.endPass();

    ...
  }

The first draw, second draw a cube, the corresponding set uniformBindGroup1;
second draw, second draw cube, the corresponding set uniformBindGroup2.

The final rendering results

Screenshot 4.18.16.png-55kB 2019-12-22 PM

Learn instancedCube.ts

This example uses the technique instance, by one Draw, a plurality of cubes drawn instances.

Compared with the "rotatingCube" example, which adds the following elements:

  • A ubo save all cube instance mvp matrix
  • Each cube frame updating all instances of the data matrix mvp
  • Specifies the number of instances, draw a

Below, we open instancedCube.ts file, in turn look at the New:

A ubo save all cube instance mvp matrix

  • vertex shader定义uniform block

code show as below:

  const vertexShaderGLSL = `#version 450
  //总共16个实例
  #define MAX_NUM_INSTANCES 16
  layout(set = 0, binding = 0) uniform Uniforms {
    //ubo包含mvp矩阵数组,数组长度为16
    mat4 modelViewProjectionMatrix[MAX_NUM_INSTANCES];
  } uniforms;
  layout(location = 0) in vec4 position;
  layout(location = 1) in vec4 color;
  ...
  void main() {
    //使用gl_InstanceIndex取到当前实例的序号(0-15),通过它获取对应的mvp矩阵
    gl_Position = uniforms.modelViewProjectionMatrix[gl_InstanceIndex] * position;
    ...
  }`;
  • Create a uniform buffer

code show as below:

  //16个立方体的排列顺序是x方向4个、y方向4个
  const xCount = 4;
  const yCount = 4;
  const numInstances = xCount * yCount;
  const matrixFloatCount = 16;
  // BYTES_PER_ELEMENT(4) * matrix length(4 * 4 = 16)
  const matrixSize = 4 * matrixFloatCount;
  const uniformBufferSize = numInstances * matrixSize;

  const uniformBuffer = device.createBuffer({
    size: uniformBufferSize,
    usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
  });

Here the difference is twoCubes data matrix mvp different instances are stored in a row, so that uniform size buffer is numInstances (16 th) * matrixSize.

  • Creating uniform bind group

Creates only one:

  const uniformBindGroup = device.createBindGroup({
    layout: uniformsBindGroupLayout,
    bindings: [{
      binding: 0,
      resource: {
        buffer: uniformBuffer,
      }
    }],
  });
  • Ready mvp matrix data

code show as below:

  //因为是固定相机,所以只需要计算一次projection矩阵
  const aspect = Math.abs(canvas.width / canvas.height);
  let projectionMatrix = mat4.create();
  mat4.perspective(projectionMatrix, (2 * Math.PI) / 5, aspect, 1, 100.0);
  ...
  
  
  
  let modelMatrices = new Array(numInstances);
  //mvpMatricesData用来依次存放所有立方体实例的mvp矩阵数据
  let mvpMatricesData = new Float32Array(matrixFloatCount * numInstances);

  let step = 4.0;

  let m = 0;
  //准备modelMatrices数据
  for (let x = 0; x < xCount; x++) {
    for (let y = 0; y < yCount; y++) {
      modelMatrices[m] = mat4.create();
      mat4.translate(modelMatrices[m], modelMatrices[m], vec3.fromValues(
        step * (x - xCount / 2 + 0.5),
        step * (y - yCount / 2 + 0.5),
        0
      ));
      m++;
    }
  }

  //因为是固定相机,所以只需要计算一次view矩阵
  let viewMatrix = mat4.create();
  mat4.translate(viewMatrix, viewMatrix, vec3.fromValues(0, 0, -12));

  let tmpMat4 = mat4.create();

Each cube frame updating all instances of the data matrix mvp

Related code as follows:

  function updateTransformationMatrix() {

    let now = Date.now() / 1000;

    let m = 0, i = 0;
    for (let x = 0; x < xCount; x++) {
      for (let y = 0; y < yCount; y++) {
        mat4.rotate(tmpMat4, modelMatrices[i], 1, vec3.fromValues(Math.sin((x + 0.5) * now), Math.cos((y + 0.5) * now), 0));

        mat4.multiply(tmpMat4, viewMatrix, tmpMat4);
        mat4.multiply(tmpMat4, projectionMatrix, tmpMat4);

        mvpMatricesData.set(tmpMat4, m);

        i++;
        m += matrixFloatCount;
      }
    }
  }

  return function frame() {
    updateTransformationMatrix();
    ...
    uniformBuffer.setSubData(0, mvpMatricesData);
    ...
  }

updateTransformationMatrix function updates mvpMatricesData;
called once setSubData, will be provided to the mvpMatricesData updated in uniform buffer.

Specifies the number of instances, draw a

code show as below:

  return function frame() {
    ...
    const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
    ...


    //设置对应的uniformBindGroup
    passEncoder.setBindGroup(0, uniformBindGroup);
    //指定实例个数为numInstances
    passEncoder.draw(36, numInstances, 0, 0);
    ...
  }

The final rendering results

Screenshot 4.18.23.png-143.2kB 2019-12-22 PM

Reference material

WebGPU规范
webgpu-samplers Github Repo

Guess you like

Origin www.cnblogs.com/chaogex/p/12081022.html