WebGPU學(xué)習(xí)(七):學(xué)習(xí)“twoCubes”和“instancedCube”示例
大家好,本文學(xué)習(xí)Chrome->webgpu-samplers->twoCubes和instancedCube示例。
這兩個(gè)示例都與“rotatingCube”示例差不多。建議大家先學(xué)習(xí)該示例,再學(xué)習(xí)本文的兩個(gè)示例
上一篇博文:
WebGPU學(xué)習(xí)(六):學(xué)習(xí)“rotatingCube”示例
下一篇博文:
WebGPU學(xué)習(xí)(八):學(xué)習(xí)“texturedCube”示例
學(xué)習(xí)twoCubes.ts
該示例繪制了兩個(gè)立方體。
與“rotatingCube”示例相比,該示例增加了以下的內(nèi)容:
- 一個(gè)ubo保存兩個(gè)立方體的mvp矩陣
- 每幀更新兩個(gè)mvp矩陣數(shù)據(jù)
- draw兩次,分別設(shè)置對(duì)應(yīng)的uniformBindGroup
下面,我們打開twoCubes.ts文件,依次來看下新增內(nèi)容:
一個(gè)ubo保存兩個(gè)立方體的mvp矩陣
- vertex shader定義uniform block
因?yàn)橹挥幸粋€(gè)ubo,所以只有一個(gè)uniform block,代碼與rotatingCube示例相同:
const vertexShaderGLSL = `#version 450
layout(set = 0, binding = 0) uniform Uniforms {
mat4 modelViewProjectionMatrix;
} uniforms;
...
void main() {
gl_Position = uniforms.modelViewProjectionMatrix * position;
...
}
`;
- 創(chuàng)建uniform buffer
代碼如下:
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要保存兩個(gè)mvp矩陣的數(shù)據(jù),但是它們不能連續(xù)存放,它們的起始位置必須為256的倍數(shù),所以u(píng)niform buffer實(shí)際的內(nèi)存布局為:
0-63:第一個(gè)mvp矩陣
64-255:0(占位)
256-319:第二個(gè)mvp矩陣
uniform buffer的size為256+64=320
- 創(chuàng)建uniform bind group
創(chuàng)建兩個(gè)uniform bind group,通過指定offset和size,對(duì)應(yīng)到同一個(gè)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
}
}]
});
- 創(chuàng)建2個(gè)mvp矩陣
代碼如下:
//因?yàn)槭枪潭ㄏ鄼C(jī),所以只需要計(jì)算一次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));
//創(chuàng)建兩個(gè)mvp矩陣
let modelViewProjectionMatrix1 = mat4.create();
let modelViewProjectionMatrix2 = mat4.create();
//因?yàn)槭枪潭ㄏ鄼C(jī),所以只需要計(jì)算一次view矩陣
let viewMatrix = mat4.create();
mat4.translate(viewMatrix, viewMatrix, vec3.fromValues(0, 0, -7));
let tmpMat41 = mat4.create();
let tmpMat42 = mat4.create();
每幀更新兩個(gè)mvp矩陣數(shù)據(jù)
相關(guān)代碼如下所示:
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函數(shù)更新兩個(gè)mvp矩陣;
調(diào)用兩次setSubData,分別將更新后的mvp矩陣數(shù)據(jù)更新到同一個(gè)uniform buffer中。
draw兩次,分別設(shè)置對(duì)應(yīng)的uniformBindGroup
代碼如下:
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();
...
}
第一次draw,繪制第一個(gè)cube,設(shè)置對(duì)應(yīng)的uniformBindGroup1;
第二次draw,繪制第二個(gè)cube,設(shè)置對(duì)應(yīng)的uniformBindGroup2。
最終渲染結(jié)果

學(xué)習(xí)instancedCube.ts
該示例使用instance技術(shù),通過一次draw,繪制了多個(gè)立方體實(shí)例。
與“rotatingCube”示例相比,該示例增加了以下的內(nèi)容:
- 一個(gè)ubo保存所有立方體實(shí)例的mvp矩陣
- 每幀更新所有立方體實(shí)例的mvp矩陣數(shù)據(jù)
- 指定實(shí)例個(gè)數(shù),draw一次
下面,我們打開instancedCube.ts文件,依次來看下新增內(nèi)容:
一個(gè)ubo保存所有立方體實(shí)例的mvp矩陣
- vertex shader定義uniform block
代碼如下:
const vertexShaderGLSL = `#version 450
//總共16個(gè)實(shí)例
#define MAX_NUM_INSTANCES 16
layout(set = 0, binding = 0) uniform Uniforms {
//ubo包含mvp矩陣數(shù)組,數(shù)組長度為16
mat4 modelViewProjectionMatrix[MAX_NUM_INSTANCES];
} uniforms;
layout(location = 0) in vec4 position;
layout(location = 1) in vec4 color;
...
void main() {
//使用gl_InstanceIndex取到當(dāng)前實(shí)例的序號(hào)(0-15),通過它獲取對(duì)應(yīng)的mvp矩陣
gl_Position = uniforms.modelViewProjectionMatrix[gl_InstanceIndex] * position;
...
}`;
- 創(chuàng)建uniform buffer
代碼如下:
//16個(gè)立方體的排列順序是x方向4個(gè)、y方向4個(gè)
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,
});
這里與twoCubes不同的是,不同實(shí)例的mvp矩陣的數(shù)據(jù)是連續(xù)存放的,所以u(píng)niform buffer的size為numInstances(16個(gè))* matrixSize。
- 創(chuàng)建uniform bind group
只創(chuàng)建一個(gè):
const uniformBindGroup = device.createBindGroup({
layout: uniformsBindGroupLayout,
bindings: [{
binding: 0,
resource: {
buffer: uniformBuffer,
}
}],
});
- 準(zhǔn)備mvp矩陣數(shù)據(jù)
代碼如下:
//因?yàn)槭枪潭ㄏ鄼C(jī),所以只需要計(jì)算一次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用來依次存放所有立方體實(shí)例的mvp矩陣數(shù)據(jù)
let mvpMatricesData = new Float32Array(matrixFloatCount * numInstances);
let step = 4.0;
let m = 0;
//準(zhǔn)備modelMatrices數(shù)據(jù)
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++;
}
}
//因?yàn)槭枪潭ㄏ鄼C(jī),所以只需要計(jì)算一次view矩陣
let viewMatrix = mat4.create();
mat4.translate(viewMatrix, viewMatrix, vec3.fromValues(0, 0, -12));
let tmpMat4 = mat4.create();
每幀更新所有立方體實(shí)例的mvp矩陣數(shù)據(jù)
相關(guān)代碼如下所示:
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函數(shù)更新mvpMatricesData;
調(diào)用一次setSubData,將更新后的mvpMatricesData設(shè)置到uniform buffer中。
指定實(shí)例個(gè)數(shù),draw一次
代碼如下:
return function frame() {
...
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
...
//設(shè)置對(duì)應(yīng)的uniformBindGroup
passEncoder.setBindGroup(0, uniformBindGroup);
//指定實(shí)例個(gè)數(shù)為numInstances
passEncoder.draw(36, numInstances, 0, 0);
...
}
最終渲染結(jié)果

浙公網(wǎng)安備 33010602011771號(hào)