代码分析说明:
import { mat4, vec3 } from 'wgpu-matrix';
import { makeSample, SampleInit } from '../../components/SampleLayout';
import {
cubeVertexArray,
cubeVertexSize,
cubeUVOffset,
cubePositionOffset,
cubeVertexCount,
} from '../../meshes/cube';
import instancedVertWGSL from './instanced.vert.wgsl';
import vertexPositionColorWGSL from '../../shaders/vertexPositionColor.frag.wgsl';
const init: SampleInit = async ({ canvas, pageState }) => {
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
if (!pageState.active) return;
const context = canvas.getContext('webgpu') as GPUCanvasContext;
const devicePixelRatio = window.devicePixelRatio;
canvas.width = canvas.clientWidth * devicePixelRatio;
canvas.height = canvas.clientHeight * devicePixelRatio;
const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
context.configure({
device,
format: presentationFormat,
alphaMode: 'premultiplied',
});
// Create a vertex buffer from the cube data.
const verticesBuffer = device.createBuffer({
size: cubeVertexArray.byteLength,
usage: GPUBufferUsage.VERTEX,
mappedAtCreation: true,
});
new Float32Array(verticesBuffer.getMappedRange()).set(cubeVertexArray);
verticesBuffer.unmap();
const pipeline = device.createRenderPipeline({
layout: 'auto',
vertex: {
module: device.createShaderModule({
code: instancedVertWGSL,
}),
entryPoint: 'main',
buffers: [
{
arrayStride: cubeVertexSize,
attributes: [
{
// position
shaderLocation: 0,
offset: cubePositionOffset,
format: 'float32x4',
},
{
// uv
shaderLocation: 1,
offset: cubeUVOffset,
format: 'float32x2',
},
],
},
],
},
fragment: {
module: device.createShaderModule({
code: vertexPositionColorWGSL,
}),
entryPoint: 'main',
targets: [
{
format: presentationFormat,
},
],
},
primitive: {
topology: 'triangle-list',
// Backface culling since the cube is solid piece of geometry.
// Faces pointing away from the camera will be occluded by faces
// pointing toward the camera.
cullMode: 'back',
},
// Enable depth testing so that the fragment closest to the camera
// is rendered in front.
depthStencil: {
depthWriteEnabled: true,
depthCompare: 'less',
format: 'depth24plus',
},
});
const depthTexture = device.createTexture({
size: [canvas.width, canvas.height],
format: 'depth24plus',
usage: GPUTextureUsage.RENDER_ATTACHMENT,
});
const xCount = 4;
const yCount = 4;
// 实例化渲染的总数
const numInstances = xCount * yCount;
const matrixFloatCount = 16; // 4x4 matrix
// 一个矩阵的大小
const matrixSize = 4 * matrixFloatCount;
// 实例化需要的矩阵buffer的大小
const uniformBufferSize = numInstances * matrixSize;
// Allocate a buffer large enough to hold transforms for every
// instance. 实例化矩阵uniform buffer
const uniformBuffer = device.createBuffer({
size: uniformBufferSize,
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
});
const uniformBindGroup = device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [
{
binding: 0,
resource: {
buffer: uniformBuffer,
},
},
],
});
const aspect = canvas.width / canvas.height;
const projectionMatrix = mat4.perspective(
(2 * Math.PI) / 5,
aspect,
1,
100.0
);
type Mat4 = mat4.default;
// 模型矩阵的数组
const modelMatrices = new Array<Mat4>(numInstances);
// MVP 矩阵的数据
const mvpMatricesData = new Float32Array(matrixFloatCount * numInstances);
const step = 4.0;
// Initialize the matrix data for every instance.
let m = 0;
for (let x = 0; x < xCount; x++) {
for (let y = 0; y < yCount; y++) {
// 设置4行4列不同的位置,更改每一个模型矩阵
modelMatrices[m] = mat4.translation(
vec3.fromValues(
step * (x - xCount / 2 + 0.5),
step * (y - yCount / 2 + 0.5),
0
)
);
m++;
}
}
// 创建并设置视图矩阵
const viewMatrix = mat4.translation(vec3.fromValues(0, 0, -12));
const tmpMat4 = mat4.create();
// Update the transformation matrix data for each instance.
function updateTransformationMatrix() {
const 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(
modelMatrices[i],
vec3.fromValues(
Math.sin((x + 0.5) * now),
Math.cos((y + 0.5) * now),
0
),
1,
tmpMat4
);
mat4.multiply(viewMatrix, tmpMat4, tmpMat4);
mat4.multiply(projectionMatrix, tmpMat4, tmpMat4);
// 计算得到每一个mvp矩阵,存在一个大的float32Array中
mvpMatricesData.set(tmpMat4, m);
i++;
m += matrixFloatCount;
}
}
}
const renderPassDescriptor: GPURenderPassDescriptor = {
colorAttachments: [
{
view: undefined, // Assigned later
clearValue: { r: 0.5, g: 0.5, b: 0.5, a: 1.0 },
loadOp: 'clear',
storeOp: 'store',
},
],
depthStencilAttachment: {
view: depthTexture.createView(),
depthClearValue: 1.0,
depthLoadOp: 'clear',
depthStoreOp: 'store',
},
};
function frame() {
// Sample is no longer the active page.
if (!pageState.active) return;
// Update the matrix data.
updateTransformationMatrix();
// 提交所有的mvp矩阵
device.queue.writeBuffer(
uniformBuffer,
0,
mvpMatricesData.buffer,
mvpMatricesData.byteOffset,
mvpMatricesData.byteLength
);
renderPassDescriptor.colorAttachments[0].view = context
.getCurrentTexture()
.createView();
const commandEncoder = device.createCommandEncoder();
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
passEncoder.setPipeline(pipeline);
passEncoder.setBindGroup(0, uniformBindGroup);
passEncoder.setVertexBuffer(0, verticesBuffer);
// 这里的参数不同,设置了实例化的个数 numInstances
passEncoder.draw(cubeVertexCount, numInstances, 0, 0);
passEncoder.end();
device.queue.submit([commandEncoder.finish()]);
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
};
顶点着色器
struct Uniforms {
modelViewProjectionMatrix : array<mat4x4<f32>, 16>,
}
@binding(0) @group(0) var<uniform> uniforms : Uniforms;
struct VertexOutput {
@builtin(position) Position : vec4<f32>,
@location(0) fragUV : vec2<f32>,
@location(1) fragPosition: vec4<f32>,
}
@vertex
fn main(
// 实例化渲染对象的索引
@builtin(instance_index) instanceIdx : u32,
@location(0) position : vec4<f32>,
@location(1) uv : vec2<f32>
) -> VertexOutput {
var output : VertexOutput;
// 根据索引获取不同的mvp矩阵
output.Position = uniforms.modelViewProjectionMatrix[instanceIdx] * position;
output.fragUV = uv;
output.fragPosition = 0.5 * (position + vec4(1.0));
return output;
}
片元着色器
@fragment
fn main(
@location(0) fragUV: vec2<f32>,
@location(1) fragPosition: vec4<f32>
) -> @location(0) vec4<f32> {
return fragPosition;
}
总结步骤
实例化渲染的不同之处:
- 创建一个可以容纳所有实例化矩阵大小的buffer
- 为每个实例创建不同的模型矩阵,并将所有的模型矩阵与视图,投影矩阵相乘,放在一个大的Float32Array的数组中,保存所有的MVP矩阵
- 编码器设置draw函数时,传递实例化的数量
passEncoder.draw(cubeVertexCount, numInstances, 0, 0)
- 在顶点着色器中,根据内建的
instance_index
获取不同的mvp矩阵,具体如:
// 根据索引获取不同的mvp矩阵
output.Position = uniforms.modelViewProjectionMatrix[instanceIdx] * position;
网友评论