渲染管线
渲染管线用于渲染图形,常用的是将图形渲染至 <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(); // 完成对发出指令序列的记录,并将其封装到 GPUCommandBufferdevice.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
指定渲染目标,例如可以
- 渲染至 canvas
- 将渲染结果存储,做为纹理
- 将渲染结果叠加
- …
{ colorAttachments: [ { view: ctx.getCurrentTexture().createView(), // 将渲染结果输出至canvas storeOp: "store", // 写入颜色缓冲区 loadOp: "clear", clearValue: { r: 0, g: 0, b: 0, a: 1 }, // 将“清除”视图到一个指定的颜色 }, ],};