> 文章列表 > WebGPU学习(2)---使用VertexBuffer(顶点缓冲区)

WebGPU学习(2)---使用VertexBuffer(顶点缓冲区)

WebGPU学习(2)---使用VertexBuffer(顶点缓冲区)

在本文中,我们使用 VertexBuffer 绘制一个矩形。示例地址

1.准备顶点数据

首先,我们准备好顶点数据。定义顶点数据有多种方法,这次我们将在 TypeScript 代码中将其定义为 Float32Array 类型的数据。

const quadVertexSize = 4 * 8; // 一个顶点的字节大小.
const quadPositionOffset = 4 * 0;  // 矩形顶点位置属性的字节偏移量.
const quadColorOffset = 4 * 4; // 矩形顶点颜色属性的字节偏移量.
const quadVertexCount = 6; // 矩形中的顶点数.const quadVertexArray = new Float32Array([// float4 position, float4 color-1,  1, 0, 1,   0, 1, 0, 1,-1, -1, 0, 1,   0, 0, 0, 1,1, -1, 0, 1,   1, 0, 0, 1,-1,  1, 0, 1,   0, 1, 0, 1,1, -1, 0, 1,   1, 0, 0, 1,1,  1, 0, 1,   1, 1, 0, 1,
]);

2.创建VertexBuffer

接下来,使用 WebGPU API 创建一个 VertexBuffer。使用逻辑设备g_devicecreateBuffer()方法。

 // 利用矩形数据创建顶点缓冲区.const verticesBuffer = g_device.createBuffer({size: quadVertexArray.byteLength,usage: GPUBufferUsage.VERTEX,mappedAtCreation: true,});

3.将顶点数据填充到VertexBuffer

我们需要使用 GPUBuffer 的getMappedRange方法创建一个新的 Float32Array 类型变量, 并将quadVertexArray的顶点数据进行设置填充。设置完成后,使用unmap()方法取消内存映射。

  new Float32Array(verticesBuffer.getMappedRange()).set(quadVertexArray);verticesBuffer.unmap();

4.在RenderPipeline中设置顶点属性

接下来,在 RenderPipeline 中设置顶点属性。

  // 创建一个渲染管线const pipeline = g_device.createRenderPipeline({layout: 'auto',vertex: {module: g_device.createShaderModule({code: vertWGSL,}),entryPoint: 'main',buffers: [ {// 指定数组元素之间的字节距离。arrayStride: quadVertexSize,// 指定顶点缓冲区的属性。attributes: [{// positionshaderLocation: 0, // @location(0) in vertex shaderoffset: quadPositionOffset,format: 'float32x4',},{// colorshaderLocation: 1, // @location(1) in vertex shaderoffset: quadColorOffset,format: 'float32x4',},],},],},fragment: {module: g_device.createShaderModule({code: fragWGSL,}),entryPoint: 'main',targets: [{format: presentationFormat,},],},primitive: {topology: 'triangle-list',},});

在上面的 RenderPipeline 代码中,与上一篇文章不同的部分是vertex.buffers

arrayStride 指定顶点缓冲区中顶点之间的字节距离(以字节为单位)。
attributes.offset 指定从顶点缓冲区中的顶点开始到相应顶点属性的字节距离(以字节为单位)。
attributes.format 指定顶点属性的格式。
对于 attributes.shaderLocation,指定由顶点着色器的@location()属性指定的值。

5.用setVertexBuffer设置GPUBuffer

接下来,使用renderPassEncodersetVertexBuffer()方法设置 VertexBuffer。

  const commandEncoder = g_device.createCommandEncoder();const renderPassEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);renderPassEncoder.setPipeline(pipeline);renderPassEncoder.setVertexBuffer(0, verticesBuffer); // 设置顶点缓冲区renderPassEncoder.draw(quadVertexCount, 1, 0, 0);renderPassEncoder.end();g_device.queue.submit([commandEncoder.finish()]);

6. 在顶点着色器中使用顶点属性

使用 VertexBuffer 还需要更改顶点着色器的内容。

// 定义输出的结构体
struct VertexOutput {@builtin(position) Position : vec4<f32>,@location(0) fragColor : vec4<f32>,
}@vertex
fn main(// 顶点属性声明@location(0) position: vec4<f32>,@location(1) color: vec4<f32>
) -> VertexOutput {var output : VertexOutput;output.Position = position;output.fragColor = color;return output;
}

我们先来看一下每个输入顶点属性的声明。

在输入顶点属性变量前加上@location()属性,编号写在括号中,但此编号必须与 RenderPipeline 的 vertex.buffers.attributes.shaderLocation 中指定的编号匹配。

另外,对于顶点数据的输出,定义了一个结构体用于输出。

Position是该结构体的第一个成员变量,位置坐标对于顶点着色器而言比其他顶点属性更为重要,它被特殊对待并且有一个名为@builtin(position)的属性,它相当于GLSL的gl_Position

对于其他顶点属性(此处是fragColor),使用@location()属性指定顶点属性的编号,这个编号应与片段着色器中的属性编号相匹配。

现在让我们看一下片段着色器。

@fragment
fn main(@location(0) fragColor: vec4<f32>,
) -> @location(0) vec4<f32> {return fragColor;
}

作为从顶点着色器里传过来的变量fragColor,此处@location()属性中的编号必须与顶点着色器输出结构中fragColor@location()属性中的编号相匹配。

总结

通过上面的编码修改,现在我们可以使用 VertexBuffer 绘制矩形了。