代码分析说明:
// 数学库
import { mat4, vec3 } from 'wgpu-matrix';
import { makeSample, SampleInit } from '../../components/SampleLayout';
// cube 顶点相关数组
import {
cubeVertexArray,
cubeVertexSize,
cubeUVOffset,
cubePositionOffset,
cubeVertexCount,
} from '../../meshes/cube';
import basicVertWGSL from '../../shaders/basic.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.
// 创建顶点buffer
const verticesBuffer = device.createBuffer({
size: cubeVertexArray.byteLength,
usage: GPUBufferUsage.VERTEX, // 顶点数据buffer
mappedAtCreation: true, // 创建后立即映射内存
});
// 将顶点数据映射 顶点数据
new Float32Array(verticesBuffer.getMappedRange()).set(cubeVertexArray);
// 取消映射,数据映射完毕后必须需求映射,不然对应的显存与内存都无法操作
verticesBuffer.unmap();
const pipeline = device.createRenderPipeline({
layout: 'auto',
vertex: {
module: device.createShaderModule({
code: basicVertWGSL,
}),
entryPoint: 'main',
buffers: [ // buffer的数据格式设置
{
arrayStride: cubeVertexSize, // 每一个顶点数据的大小
attributes: [
{
// position 顶点数据
shaderLocation: 0, // 顶点数据的插槽位置
offset: cubePositionOffset, // 顶点数据的偏移量
format: 'float32x4', // 顶点数据的格式
},
{
// uv
shaderLocation: 1, // uv数据的插槽
offset: cubeUVOffset, // uv数据的偏移量
format: 'float32x2', // uv数据的格式
},
],
},
],
},
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', // 使用24位深度值
},
});
// 创建深度目标纹理
const depthTexture = device.createTexture({
size: [canvas.width, canvas.height],
format: 'depth24plus',
usage: GPUTextureUsage.RENDER_ATTACHMENT,
});
// uniform的大小 矩阵为 4 * 4 * 4
const uniformBufferSize = 4 * 16; // 4x4 matrix
// 创建一个uniform buffer
const uniformBuffer = device.createBuffer({
size: uniformBufferSize,
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
});
// 创建bindGroup对象
const uniformBindGroup = device.createBindGroup({
layout: pipeline.getBindGroupLayout(0), // 设置布局为渲染管线的bindGroupLayout 0
entries: [
{
binding: 0, // 设置bind
resource: {
buffer: uniformBuffer, // 设置buffer
},
},
],
});
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',
},
};
// 屏幕长宽比
const aspect = canvas.width / canvas.height;
// 设置投影矩阵
const projectionMatrix = mat4.perspective(
(2 * Math.PI) / 5,
aspect,
1,
100.0
);
// 定义mvp 矩阵
const modelViewProjectionMatrix = mat4.create();
function getTransformationMatrix() {
// 定义视图矩阵
const viewMatrix = mat4.identity();
// 平移视图矩阵
mat4.translate(viewMatrix, vec3.fromValues(0, 0, -4), viewMatrix);
const now = Date.now() / 1000;
// 旋转视图矩阵,相当于旋转相机
mat4.rotate(
viewMatrix,
vec3.fromValues(Math.sin(now), Math.cos(now), 0),
1,
viewMatrix
);
// 计算MVP矩阵
mat4.multiply(projectionMatrix, viewMatrix, modelViewProjectionMatrix);
return modelViewProjectionMatrix as Float32Array;
}
function frame() {
// Sample is no longer the active page.
if (!pageState.active) return;
const transformationMatrix = getTransformationMatrix();
// 将矩阵数据写入到uniform buffer中
device.queue.writeBuffer(
uniformBuffer,
0,
transformationMatrix.buffer,
transformationMatrix.byteOffset,
transformationMatrix.byteLength
);
// 设置颜色附件的view
renderPassDescriptor.colorAttachments[0].view = context
.getCurrentTexture()
.createView();
const commandEncoder = device.createCommandEncoder();
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
passEncoder.setPipeline(pipeline);
// 设置绑定组 0
passEncoder.setBindGroup(0, uniformBindGroup);
passEncoder.setVertexBuffer(0, verticesBuffer);
passEncoder.draw(cubeVertexCount);
passEncoder.end();
device.queue.submit([commandEncoder.finish()]);
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
};
顶点着色器
// 定义uniform 为结构体
struct Uniforms {
modelViewProjectionMatrix : mat4x4<f32>,
}
// 绑定组为 0 绑定插槽 0
@binding(0) @group(0) var<uniform> uniforms : Uniforms;
// 返回值结构体
struct VertexOutput {
// 内建 位置
@builtin(position) Position : vec4<f32>,
// 插槽 0 uv 相对于片元着色器的
@location(0) fragUV : vec2<f32>,
// 插槽 1 位置 相对于片元着色器的
@location(1) fragPosition: vec4<f32>,
}
@vertex
fn main(
// 入参,插槽0 传递顶点数据
@location(0) position : vec4<f32>,
// 入参, 插槽 1 传递uv数据
@location(1) uv : vec2<f32>
) -> VertexOutput {
// 定义返回值,在wgsl中,只有var 申明的变量是可修改的
var output : VertexOutput;
output.Position = uniforms.modelViewProjectionMatrix * position;
output.fragUV = uv;
output.fragPosition = 0.5 * (position + vec4(1.0, 1.0, 1.0, 1.0));
return output;
}
片元着色器
@fragment
fn main(
// 入参,对于与顶点着色器返回值的插槽 0
@location(0) fragUV: vec2<f32>,
// 入参,对于与顶点着色器返回值的插槽 1
@location(1) fragPosition: vec4<f32>
) -> @location(0) vec4<f32> {
return fragPosition;
}
顶点数据定义
// 每个顶点的数据长度,包括了位置、颜色、uv
export const cubeVertexSize = 4 * 10; // Byte size of one cube vertex.
// 位置的偏移量
export const cubePositionOffset = 0;
// 颜色的偏移量
export const cubeColorOffset = 4 * 4; // Byte offset of cube vertex color attribute.
// uv的偏移量
export const cubeUVOffset = 4 * 8;
// 总共的顶点数,用户传递给编码器的draw函数
export const cubeVertexCount = 36;
// prettier-ignore
export const cubeVertexArray = new Float32Array([
// float4 position, float4 color, float2 uv,
1, -1, 1, 1, 1, 0, 1, 1, 0, 1,
-1, -1, 1, 1, 0, 0, 1, 1, 1, 1,
-1, -1, -1, 1, 0, 0, 0, 1, 1, 0,
1, -1, -1, 1, 1, 0, 0, 1, 0, 0,
1, -1, 1, 1, 1, 0, 1, 1, 0, 1,
-1, -1, -1, 1, 0, 0, 0, 1, 1, 0,
1, 1, 1, 1, 1, 1, 1, 1, 0, 1,
1, -1, 1, 1, 1, 0, 1, 1, 1, 1,
1, -1, -1, 1, 1, 0, 0, 1, 1, 0,
1, 1, -1, 1, 1, 1, 0, 1, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 0, 1,
1, -1, -1, 1, 1, 0, 0, 1, 1, 0,
-1, 1, 1, 1, 0, 1, 1, 1, 0, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, -1, 1, 1, 1, 0, 1, 1, 0,
-1, 1, -1, 1, 0, 1, 0, 1, 0, 0,
-1, 1, 1, 1, 0, 1, 1, 1, 0, 1,
1, 1, -1, 1, 1, 1, 0, 1, 1, 0,
-1, -1, 1, 1, 0, 0, 1, 1, 0, 1,
-1, 1, 1, 1, 0, 1, 1, 1, 1, 1,
-1, 1, -1, 1, 0, 1, 0, 1, 1, 0,
-1, -1, -1, 1, 0, 0, 0, 1, 0, 0,
-1, -1, 1, 1, 0, 0, 1, 1, 0, 1,
-1, 1, -1, 1, 0, 1, 0, 1, 1, 0,
1, 1, 1, 1, 1, 1, 1, 1, 0, 1,
-1, 1, 1, 1, 0, 1, 1, 1, 1, 1,
-1, -1, 1, 1, 0, 0, 1, 1, 1, 0,
-1, -1, 1, 1, 0, 0, 1, 1, 1, 0,
1, -1, 1, 1, 1, 0, 1, 1, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 0, 1,
1, -1, -1, 1, 1, 0, 0, 1, 0, 1,
-1, -1, -1, 1, 0, 0, 0, 1, 1, 1,
-1, 1, -1, 1, 0, 1, 0, 1, 1, 0,
1, 1, -1, 1, 1, 1, 0, 1, 0, 0,
1, -1, -1, 1, 1, 0, 0, 1, 0, 1,
-1, 1, -1, 1, 0, 1, 0, 1, 1, 0,
]);
总结步骤
- 立方体为三维物体,所以需要开启深度测试,在
device.createRenderPipeline
的参数中,加入depthStencil
的配置,以及背面剔除cullMode: 'back'
- 数据不再Shader中,需要使用
device.createBuffer
创建buffer, 然后把顶点的数据通过new Float32Array(verticesBuffer.getMappedRange()).set(cubeVertexArray)
将数据添加到缓存区,,然后使用verticesBuffer.unmap
取消映射 - 加入了MVP矩阵,需要uniform buffer 传递,通过
device.createBuffer
创建buffer, ,并设置用例为usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
, - 设置 uniform的bindgroup
- 使用数据库,创建MVP矩阵,并通过
device.queue.writeBuffer
将矩阵数据写入到缓存区 - 在渲染管线中,设置buffer的数据布局格式
- 创建深度纹理
- 编码器中绑定uniform bindgroup
- 添加深度附件,用户深度的测试
- 顶点的buffer通过创建渲染管线的参数来设置,
device.queue.writeBuffer
写入,并在编码器中设置bufferpassEncoder.setVertexBuffer(0, verticesBuffer)
, 而 uniform buffer 是通过编码器的passEncoder.setBindGroup(0, uniformBindGroup)
来设置
网友评论