WebGPU 入门(三)
WebGPU 入门知识,包括其概念、优势及与 WebGL 的关系。重点讲解了如何用 WebGPU 绘制三角形,涵盖创建相关对象、配置 canvas、设置背景色、创建缓冲区、设置读取方式、编写着色器、创建渲染流水线等步骤。

推荐使用NSDT 3DConvert进行3D模型格式转换,支持glb、obj、stp、fbx、ifc等多种3D模型格式之间进行互相转换,在转换过程中,能够很好的保留模型原有的颜色、材质等信息。
着色器
声明 WebGPU 的着色器,创建着色器模块(GPUShaderModule)。
WebGPU 使用特有的 WGSL 着色器语言,顶点着色器和片元着色器可以写在一起的。
// 创建着色器模块
const vertexShaderModule = device.createShaderModule({
label: 'Vertex Shader',
code: `
@vertex
fn vertexMain(@location(0) pos: vec2f) -> @builtin(position) vec4f {
return vec4f(pos, 0, 1);
}
@fragment
fn fragmentMain() -> @location(0) vec4f {
return vec4f(1, 0, 0, 1);
}
`,
});
顶点着色器函数:
@vertex
fn vertexMain(@location(0) pos: vec2f) -> @builtin(position) vec4f {
return vec4f(pos, 0, 1);
}
@vertex
:装饰器,表示顶点着色器主函数。@location(0)
:缓冲区读取方式设置的 shaderLocation,这里拿到了两个浮点数。vec2f
:两个浮点数的向量,同理,vec4f 为 4 浮点数的向量。-> @builtin(position)
:表示函数的返回值会被设置为内置的顶点位置变量。WebGPU 是利用函数的返回值配合修饰符的方式进行内部变量赋值的。
片元着色器:
@fragment
fn fragmentMain() -> @location(0) vec4f {
return vec4f(1, 0, 0, 1); // 红色
}
@fragment
表示片元着色器主函数。-> @location(0)
表示将返回的颜色输出到位置为 0 的颜色附件上,简单来说,就是给对应点设置为对应颜色。
渲染流水线
创建渲染流水线,也就是把之前的设置组合起来,用哪个着色器的哪个函数作为入口、如何读取缓冲区等。
const pipeline = device.createRenderPipeline({
label: 'pipeline', // 标识,定位错误用
layout: 'auto', // 自动流水线布局
vertex: {
module: vertexShaderModule, // 着色器模块
entryPoint: 'vertexMain', // 入口函数为 vertexMain
buffers: [vertexBufferLayout], // 读取缓冲区的方式
},
fragment: {
module: vertexShaderModule,
entryPoint: 'fragmentMain',
targets: [
{
format: canvasFormat, // 输出到 canvas 画布上
},
],
},
});
将渲染流水线设置到 pass 上。
pass.setPipeline(pipeline);
将缓冲区绑定到管线的第一个顶点缓冲槽(slot)。
pass.setVertexBuffer(0, vertexBuffer);
绘制图元,这里要设置绘制几组,一组是两个点,所以要处以 2。
pass.draw(vertices.length / 2);
然后就是前面讲过的收尾代码。
pass.end(); // 完成指令队列的记录
const commandBuffer = encoder.finish(); // 结束编码
device.queue.submit([commandBuffer]); // 提交给 GPU 命令队列
至此,一个三角形就画好了。
绘制结果

完整代码
完整代码:
const render = async () => {
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('webgpu');
const canvasFormat = navigator.gpu.getPreferredCanvasFormat();
ctx.configure({
device,
format: canvasFormat,
});
const encoder = device.createCommandEncoder();
const pass = encoder.beginRenderPass({
colorAttachments: [
{
view: ctx.getCurrentTexture().createView(),
loadOp: 'clear',
clearValue: { r: 0.6, g: 0.8, b: 0.9, a: 1 },
storeOp: 'store',
},
],
});
// 创建顶点数据
// prettier-ignore
const vertices = new Float32Array([
-0.5, -0.5,
0.5, -0.5,
0.5, 0.5,
]);
// 缓冲区
const vertexBuffer = device.createBuffer({
// 标识,字符串随意写,报错时会通过它定位,
label: 'Triangle Vertices',
// 缓冲区大小,这里是 24 字节。6 个 4 字节(即 32 位)的浮点数
size: vertices.byteLength,
// 标识缓冲区用途(1)用于顶点着色器(2)可以从 CPU 复制数据到缓冲区
// eslint-disable-next-line no-undef
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
});
// 将顶点数据复制到缓冲区
device.queue.writeBuffer(vertexBuffer, /* bufferOffset */ 0, vertices);
// GPU 应该如何读取缓冲区中的数据
const vertexBufferLayout = {
arrayStride: 2 * 4, // 每一组的字节数,每组有两个数字(2 * 4字节)
attributes: [
{
format: 'float32x2', // 每个数字是32位浮点数
offset: 0, // 从每组的第一个数字开始
shaderLocation: 0, // 顶点着色器中的位置
},
],
};
// 着色器用的是 WGSL 着色器语言
const vertexShaderModule = device.createShaderModule({
label: 'Vertex Shader',
code: `
@vertex
fn vertexMain(@location(0) pos: vec2f) -> @builtin(position) vec4f {
return vec4f(pos, 0, 1);
}
@fragment
fn fragmentMain() -> @location(0) vec4f {
return vec4f(1, 0, 0, 1);
}
`,
});
// 渲染流水线
const pipeline = device.createRenderPipeline({
label: 'pipeline',
layout: 'auto',
vertex: {
module: vertexShaderModule,
entryPoint: 'vertexMain',
buffers: [vertexBufferLayout],
},
fragment: {
module: vertexShaderModule,
entryPoint: 'fragmentMain',
targets: [
{
format: canvasFormat,
},
],
},
});
pass.setPipeline(pipeline);
pass.setVertexBuffer(0, vertexBuffer);
pass.draw(vertices.length / 2);
pass.end();
const commandBuffer = encoder.finish();
device.queue.submit([commandBuffer]);
};
render();
WebGPU 入门 - 索引