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
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);
...
}