共计 2247 个字符,预计需要花费 6 分钟才能阅读完成。
1 VAO 是 OpenGL 技术中提出来的
参考:
https://www.khronos.org/openg…,_VBOs,_Vertex_and_Fragment_Shaders_(C_/_SDL)
其中有一段文字记录了 VAO 是什么:
A Vertex Array Object (VAO) is an object which contains one or more Vertex Buffer Objects and is designed to store the information for a complete rendered object. In our example this is a diamond consisting of four vertices as well as a color for each vertex.
VAO 记录的是多个(一组)VBO 的 gl.bindBuffer 和 gl.vertexAttribPointer(WebGL API)的状态,省去切换另一组 VBO 时再次设置绑定关系和读取规定的老本。
外面也形容了 VBO 是什么:一个 VBO 能够是 position,也能够是 uv,甚至能够是 indices;当然,在 WebGL 中,你能够用 1 个 VBO 来存储 position + uv + normal,然而不能和 indices 混用(和 type 无关)。
2 WebGPU 天生就能先保留状态
WebGPU 不须要 VAO 了,源于 WebGPU 的机制,并不是过程式,所以不须要借助 VAO 保留绑定一组 VBO 并读取它的状态。
2.1 WebGL 与 WebGPU 相干 API 比照
当 device.createShaderModule 时,就有 buffers 属性形容着色器须要什么类型的数据,相似 gl.vertexAttribPointer 的作用;
而 gl.bindBuffer 的操作则由 renderPass.setVertexBuffer 实现;
对于数据的传递,gl.bufferData 的工作就由 device.createBuffer 时通过映射、解映射的机制将 TypedArray 传递进 GPUBuffer 来代替
2.2 谁代替了 VAO?
那么谁能在渲染时通知着色器,我有多组 VBO 要切换呢?
精确的说,VBO 这个概念曾经被 GPUBuffer + GPUShaderModule 代替了,由后者两个对象独特分担,GPUBuffer 专一于 cpu~gpu 的数据传递,GPUShaderModule 不仅仅是着色器代码自身,还承当着 GPUBuffer[type=vertex] 的数据如何读取的职能(代替了 gl.vertexAttribPointer 的职能)。
VAO 的职能则转至 GPURenderPipeline 实现,其 GPURenderPipelineDescriptor.GPUVertexState.buffers 属性是 GPUVertexBufferLayout[] 类型的,这每一个 GPUVertexBufferLayout 对象就相似于 VAO 的职能。
2.3 代码举例
下列只有一个 GPUVertexBufferLayout:
const renderPipeline = device.createRenderPipeline({
/* ... */
vertex: {
module: device.createShaderModule({code: ` /* wgsl vertex shader code */ `,}),
entryPoint: 'vertex_main',
buffers: [
{
arrayStride: 4 * 5, // 一个顶点数据占 20 bytes
attributes: [
{
// for Position VertexAttribute
shaderLocation: 0,
offset: 0,
format: "float32x3" // 其中顶点的坐标属性占 12 字节,三个 float32 数字
},
{
// for UV0 VertexAttribute
shaderLocation: 1,
offset: 3 * 4,
format: "float32x2" // 顶点的纹理坐标占 8 字节,两个 float32 数字
}
]
}
]
}
})
上面有两个 GPUVertexBufferLayout:
const renderPipeline = device.createRenderPipeline({
vertex: {
module: spriteShaderModule,
entryPoint: 'vert_main',
buffers: [
{
arrayStride: 4 * 4,
stepMode: 'instance',
attributes: [
{
// instance position
shaderLocation: 0,
offset: 0,
format: 'float32x2',
},
{
// instance velocity
shaderLocation: 1,
offset: 2 * 4,
format: 'float32x2',
},
],
},
{
arrayStride: 2 * 4,
stepMode: 'vertex',
attributes: [
{
// vertex positions
shaderLocation: 2,
offset: 0,
format: 'float32x2',
},
],
},
],
},
/* ... */
});
通过 renderPassEncoder.setVertexBuffer 就能切换 VBO 了:
renderPassEncoder.setVertexBuffer(0, bf0);
renderPassEncoder.setVertexBuffer(1, bf1);