Skip to content

渲染管线

渲染管线用于渲染图形,常用的是将图形渲染至 <canvas></canvas>

API

创建(GPURenderPipeline)

const pipeline = device.createRenderPipeline({
layout: "auto", // 可以手动设置GPUPipelineLayout,或者为auto
vertex: {}, // 顶点着色器配置
primitive: {}, // 图元装配配置
fragment: {}, // 片元着色器配置
});
  • layout:定义渲染期间,GPU 资源的布局
    • GPUPipelineLayout:手动指定
    • auto

运行(GPUCommandEncoder)

为什么需要 GPUCommandEncoder?

渲染管线最终是在 GPU 上执行的,此时由 CPU 先给编码完毕,再交给 GPU 执行。所以 GPUCommandEncoder 其实就是一个翻译,将书写的代码翻译为 GPU 可执行的代码,以实现 CPU 和 GPU 的分离

const cmdEncoder = device.createCommandEncoder();
const renderPass = cmdEncoder.beginRenderPass({
colorAttachments: [], // 颜色缓冲区
depthStencilAttachment: [], // 深度缓冲区
});
renderPass.setPipeline(pipeline);
renderPass.draw(3); // 绘制多少个顶点
renderPass.end(); // 发出结束信号
const cmdBuffer = cmdEncoder.finish(); // 完成对发出指令序列的记录,并将其封装到 GPUCommandBuffer
device.queue.submit([cmdBuffer]); // 推入设备指令队列

Vertex(顶点着色器)

指定图元的顶点

在 Javascript 加载 shader 代码时,需要加载的是字符串,可以使用 Javascript 字符串加载或者单独写入其他文件并借助打包工具加载。在 Vscode 中,借用插件可以使得代码高亮

// 借助 WGSL 插件
const vertexShader = /* wgsl */ `
var v1 = vec3f(0.0);
`;
// vertexShaderCode 为WGSL的代码,为字符串导入
const vertexShader = device.createShaderModule({ code: vertexShaderCode });
{
vertex: {
module: vertexShader,
entryPoint: "main", // 绑定WGSL定义的shader的入口函数
buffers: [
{
arrayStride: 6 * 4, // 一个顶点的占用的字节长度
// 可以将这定义的6个数据分开传入,做为不同的参数
attributes: [
{
shaderLocation: 0, // 顶点缓冲区的位置
format: "float32x3", // 顶点缓冲区的数据格式
offset: 0, // 读取每组的[0,2]
},
{
shaderLocation: 1, // 顶点缓冲区的位置
format: "float32x3", // 顶点缓冲区的数据格式
offset: 3 * 4, // 每一组跳过前三个数据,读取[3,5]
},
],
},
// 可以指定多个缓冲区
{
arrayStride: 3 * 4, // 一个顶点的占用的字节长度
attributes: [
{
shaderLocation: 0, // 顶点缓冲区的位置
format: "float32x3", // 顶点缓冲区的数据格式
offset: 0, //
},
],
},
],
},
};

Vertex data

// 绑定Buffer,将 vertex.buffer 中的第0个,和vertexBuffer绑定
renderPass.setVertexBuffer(0, vertexBuffer);

Uniform data

const bindGroup = device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [
{
binding: 0,
resource: {
buffer: uniformBuffer,
},
},
],
});
renderPass.setBindGroup(0, bindGroup);

Fragment(片元着色器)

// fragmentShaderCode 为WGSL的代码,为字符串导入
const fragmentShader = device.createShaderModule({ code: fragmentShaderCode });
{
fragment: {
module: fragmentShader,
entryPoint: "main", // 绑定WGSL定义的shader的入口函数
targets: [
{
format: navigator.gpu.getPreferredCanvasFormat(),
},
],
},
};

Primitive(图元装配)

设置 GPU 以什么方式来绘制图形,分为点,线,面(三角形)

{
primitive: {
cullMode: "none",
frontFace: "ccw",
topology: "triangle-list",
},
};
  • cullMode(optional):指定剔除模式
    • none:默认
    • front
    • back
  • frontFace(optional):指定正面
    • cw(clockwise)
    • ccw(counterclockwise):默认
  • topology(optional):图元类型,分为点,线,三角形
    • point-list
    • line-list
    • line-strip
    • triangle-list:默认
    • triangle-strip

DepthStencilAttachment

ColorAttachments

指定渲染目标,例如可以

  1. 渲染至 canvas
  2. 将渲染结果存储,做为纹理
  3. 将渲染结果叠加
{
colorAttachments: [
{
view: ctx.getCurrentTexture().createView(), // 将渲染结果输出至canvas
storeOp: "store", // 写入颜色缓冲区
loadOp: "clear",
clearValue: { r: 0, g: 0, b: 0, a: 1 }, // 将“清除”视图到一个指定的颜色
},
],
};