你們好,本文學習Chrome->webgpu-samplers->twoCubes和instancedCube示例。html
這兩個示例都與「rotatingCube」示例差很少。建議你們先學習該示例,再學習本文的兩個示例git
上一篇博文:
WebGPU學習(六):學習「rotatingCube」示例github
該示例繪製了兩個立方體。web
與「rotatingCube」示例相比,該示例增長了如下的內容:canvas
下面,咱們打開twoCubes.ts文件,依次來看下新增內容:數組
由於只有一個ubo,因此只有一個uniform block,代碼與rotatingCube示例相同:函數
const vertexShaderGLSL = `#version 450 layout(set = 0, binding = 0) uniform Uniforms { mat4 modelViewProjectionMatrix; } uniforms; ... void main() { gl_Position = uniforms.modelViewProjectionMatrix * position; ... } `;
代碼以下:佈局
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要保存兩個mvp矩陣的數據,可是它們不能連續存放,它們的起始位置必須爲256的倍數,因此uniform buffer實際的內存佈局爲:
0-63:第一個mvp矩陣
64-255:0(佔位)
256-319:第二個mvp矩陣學習
uniform buffer的size爲256+64=320code
建立兩個uniform bind group,經過指定offset和size,對應到同一個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 } }] });
代碼以下:
//由於是固定相機,因此只須要計算一次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();
相關代碼以下所示:
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函數更新兩個mvp矩陣;
調用兩次setSubData,分別將更新後的mvp矩陣數據更新到同一個uniform buffer中。
代碼以下:
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,繪製第一個cube,設置對應的uniformBindGroup1;
第二次draw,繪製第二個cube,設置對應的uniformBindGroup2。
該示例使用instance技術,經過一次draw,繪製了多個立方體實例。
與「rotatingCube」示例相比,該示例增長了如下的內容:
下面,咱們打開instancedCube.ts文件,依次來看下新增內容:
代碼以下:
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; ... }`;
代碼以下:
//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, });
這裏與twoCubes不一樣的是,不一樣實例的mvp矩陣的數據是連續存放的,因此uniform buffer的size爲numInstances(16個)* matrixSize。
只建立一個:
const uniformBindGroup = device.createBindGroup({ layout: uniformsBindGroupLayout, bindings: [{ binding: 0, resource: { buffer: uniformBuffer, } }], });
代碼以下:
//由於是固定相機,因此只須要計算一次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();
相關代碼以下所示:
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函數更新mvpMatricesData;
調用一次setSubData,將更新後的mvpMatricesData設置到uniform buffer中。
代碼以下:
return function frame() { ... const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor); ... //設置對應的uniformBindGroup passEncoder.setBindGroup(0, uniformBindGroup); //指定實例個數爲numInstances passEncoder.draw(36, numInstances, 0, 0); ... }