渲染管线
渲染管线描述 GPU 如何把顶点变成像素:用哪个着色器、图元怎么组装、结果写进哪种纹理格式。它在绘制之前创建,绘制时只是被绑定,然后调用 draw()。
三角形实例已经跑通过一条管线。这一章把那几步拆开。
WGSL 源码先编译成 GPUShaderModule。编译错误不会让 createShaderModule() 抛异常,需要再读 getCompilationInfo():
const shaderModule = device.createShaderModule({ label: "三角形着色器", code: wgslSource,});
const info = await shaderModule.getCompilationInfo();const errors = info.messages.filter((message) => message.type === "error");if (errors.length > 0) { throw new Error(errors.map((message) => message.message).join("\n"));}顶点和片元可以放在同一个模块里,用不同的 entryPoint。也可以分成两个模块。
const pipeline = device.createRenderPipeline({ label: "三角形管线", layout: "auto", vertex: { module: shaderModule, entryPoint: "vs_main", }, fragment: { module: shaderModule, entryPoint: "fs_main", targets: [{ format: canvasFormat }], }, primitive: { topology: "triangle-list", },});各字段的含义:
layout: "auto"让实现根据着色器里的@group/@binding生成管线布局。没有外部资源时,这样最省事。显式布局在绑定组里再写。vertex.entryPoint必须是一个@vertex函数。fragment.targets[0].format必须和颜色附件的格式一致。画到画布上时,用navigator.gpu.getPreferredCanvasFormat()的返回值,一般是"bgra8unorm"或"rgba8unorm"。primitive.topology决定顶点如何组成图元。默认是"triangle-list":每 3 个顶点一个三角形。
常用拓扑:
| topology | 含义 |
|---|---|
"triangle-list" | 每 3 个顶点一个独立三角形 |
"triangle-strip" | 相邻三角形共用一条边 |
"line-list" | 每 2 个顶点一条独立线段 |
"line-strip" | 折线,相邻顶点连成一段 |
"point-list" | 每个顶点一个点 |
frontFace 默认 "ccw"(逆时针为正面),cullMode 默认 "none"(不剔除)。需要背面剔除时再改。
管线不会自己画。每帧要录制命令:
context.getCurrentTexture()取出画布当前要显示的纹理,再createView()。这个视图不能跨帧复用。beginRenderPass()声明颜色附件。loadOp: "clear"用clearValue清屏,storeOp: "store"把结果留下。setPipeline()、必要时setVertexBuffer()和setBindGroup(),然后draw(vertexCount)。pass.end(),encoder.finish(),最后device.queue.submit()。
const encoder = device.createCommandEncoder();const pass = encoder.beginRenderPass({ colorAttachments: [ { view: context.getCurrentTexture().createView(), clearValue: { r: 0.3, g: 0.3, b: 0.3, a: 1 }, loadOp: "clear", storeOp: "store", }, ],});pass.setPipeline(pipeline);pass.draw(3);pass.end();device.queue.submit([encoder.finish()]);draw(3) 的 3 是顶点个数,不是三角形个数。"triangle-list" 下 3 个顶点刚好一个三角形。顶点坐标仍然可以像三角形示例那样写在着色器的数组里;下一章改成从缓冲区读取。
同一个三角形,左边按 "triangle-list" 填充,右边用 "line-strip" 把三个顶点连回去。线框多画了一次起点,所以是 4 个顶点。
triangle-list
line-strip
坐标还写在着色器里。换成 缓冲区 之后,网格数据就可以从 JavaScript 上传。管线对象的字段说明见 GPURenderPipeline。