当前位置:首页 > Web开发 > 正文

WebGPU学习(二): 学习“绘制一个三角形”示例

2024-03-31 Web开发

克隆webgl-samplers Github Repo到本地。
(备注:当前的version为0.0.2)

实际的sample代码在src/examples/文件夹中,是typescript代码写的:

技术图片

学习helloTriangle.ts

打开helloTriangle.ts文件,我们来看下init函数的内容。

首先是shader代码 const vertexShaderGLSL = `#version 450 const vec2 pos[3] = vec2[3](vec2(0.0f, 0.5f), vec2(-0.5f, -0.5f), vec2(0.5f, -0.5f)); void main() { gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0); } `; const fragmentShaderGLSL = `#version 450 layout(location = 0) out vec4 outColor; void main() { outColor = vec4(1.0, 0.0, 0.0, 1.0); } `;

这里是vertex shader和fragment shader的glsl代码。

(webgpu支持vertex shader、fragment shader、compute shader,这里只使用了前面两个)

“#version 450”声明了glsl版本为4.5(它要放在glsl的第一行)

第2行定义了三角形的三个顶点坐标,使用2维数组保存(每个元素为vec2类型)。因为都在一个平面,所以顶点只定义了x、y坐标(顶点的z为0.0)

第5行的gl_VertexIndex为顶点序号,每次执行时值依次为0、1、2(vertex shader被执行了3次,因为只有3个顶点)(具体见本文末尾对draw的分析)

第9行是fragment shader,因为三角形为一个颜色,所以所有片段的颜色为同一个固定值

然后我们继续看下面的代码 const adapter = await navigator.gpu.requestAdapter(); const device = await adapter.requestDevice(); // 准备编译glsl的库 const glslang = await glslangModule(); // 获得webgpu上下文 const context = canvas.getContext('gpupresent');

第4行的glslangModule是import的第三方库:

import glslangModule from '../glslang'; 继续往下看 // 定义swapbuffer的格式为RGBA8位的无符号归一化格式 const swapChainFormat = "bgra8unorm"; // @ts-ignore: const swapChain: GPUSwapChain = context.configureSwapChain({ device, format: swapChainFormat, });

@ts-ignore是typescript用来忽略错误的。因为context的类型是RenderingContext,它没有定义configureSwapChain函数,如果编译该行typescript会报错,所以需要忽略错误。

第5行配置了swap chain。vulkan tutorial对此进行了说明:
swap chain是一个缓冲结构,webgpu会先将内容渲染到swap chain的buffer中,然后再将其显示到屏幕上;
swap chain本质上是等待呈现在屏幕上的一个图片队列。

接下来就是创建render pipeline const pipeline = device.createRenderPipeline({ layout: device.createPipelineLayout({ bindGroupLayouts: [] }), vertexStage: { module: device.createShaderModule({ code: glslang.compileGLSL(vertexShaderGLSL, "vertex"), // @ts-ignore source: vertexShaderGLSL, transform: source => glslang.compileGLSL(source, "vertex"), }), entryPoint: "main" }, fragmentStage: { module: device.createShaderModule({ code: glslang.compileGLSL(fragmentShaderGLSL, "fragment"), // @ts-ignore source: fragmentShaderGLSL, transform: source => glslang.compileGLSL(source, "fragment"), }), entryPoint: "main" }, primitiveTopology: "triangle-list", colorStates: [{ format: swapChainFormat, }], }); 了解pipeline

WebGPU有两种pipeline:render pipeline和compute pipeline,这里只用了render pipeline

这里使用render pipeline descriptor来创建render pipeline,它的定义如下:

dictionary GPUPipelineDescriptorBase : GPUObjectDescriptorBase { required GPUPipelineLayout layout; }; ... dictionary GPURenderPipelineDescriptor : GPUPipelineDescriptorBase { required GPUProgrammableStageDescriptor vertexStage; GPUProgrammableStageDescriptor fragmentStage; required GPUPrimitiveTopology primitiveTopology; GPURasterizationStateDescriptor rasterizationState = {}; required sequence<GPUColorStateDescriptor> colorStates; GPUDepthStencilStateDescriptor depthStencilState; GPUVertexStateDescriptor vertexState = {}; unsigned long sampleCount = 1; unsigned long sampleMask = 0xFFFFFFFF; boolean alphaToCoverageEnabled = false; // TODO: other properties };

render pipeline可以设置绑定的资源布局、编译的shader、fixed functions(如混合、深度、模版、cullMode等各种状态和顶点数据的格式vertexState),相对于WebGL(WebGL的一个API只能设置一个,如使用gl.cullFace设置cull mode),提升了性能(静态设置了各种状态,不需要在运行时设置),便于管理(把各个状态集中到了一起设置)。

分析render pipeline descriptor

vertexStage和fragmentStage分别设置vertex shader和fragment shader:
使用第三方库,将glsl编译为字节码(格式为SPIR-V);
source和transform字段是多余的,可以删除。

因为shader没有绑定资源(如uniform buffer, texture等),所以第2行的bindGroupLayouts为空数组,不需要bind group和bind group layout

温馨提示: 本文由Jm博客推荐,转载请保留链接: https://www.jmwww.net/file/web/41503.html