八、多物体资源绑定

53 阅读2分钟

下面通过,Buffers、B with Offsets、DynamicOffsets、Instance等几个方面介绍webGPU如何绑定多个物体资源。

Buffers

基于旋转正方体和旋转立方体的例子,我们来介绍如何绑定多个物体资源。

// initPipeline 函数 修改 ////////////////////////////////////////
//创建多个mvp Buffer 与 uniformGroup
const mvpBuffer1 = device.createBuffer({
    label: 'GPUBuffer store 4x4 matrix',
    size: 4 * 4 * 4,
    usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
})
const uniformGroup1 = device.createBindGroup({
    label: 'Uniform Group with Matrix',
    layout: pipeline.getBindGroupLayout(0),
    entries: [
        {
            binding: 0,
            resource: {
                buffer: mvpBuffer1
            }
        }
    ]
})
const mvpBuffer2 = device.createBuffer({
    label: 'GPUBuffer store 4x4 matrix',
    size: 4 * 4 * 4,
    usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
})
const uniformGroup2 = device.createBindGroup({
    label: 'Uniform Group with Matrix',
    layout: pipeline.getBindGroupLayout(0),
    entries: [
        {
            binding: 0,
            resource: {
                buffer: mvpBuffer2
            }
        }
    ]
})
const mvpBuffer = {mvpBuffer1, mvpBuffer2};
const uniformGroup = {uniformGroup1, uniformGroup2};


// draw 函数 修改//////////////////////////////////////////////
passEncoder.setVertexBuffer(0, pipelineObj.vertexBuffer)
{
  // 分别绑定两个组,并绘制
    passEncoder.setBindGroup(0, pipelineObj.uniformGroup.uniformGroup1)
    passEncoder.draw(cube.vertexCount)
    passEncoder.setBindGroup(0, pipelineObj.uniformGroup.uniformGroup2)
    passEncoder.draw(cube.vertexCount)
}
passEncoder.end()


// run 函数 修改 ///////////////////////////////////////////
// 分别设置两个矩阵信息
let aspect = size.width / size.height
const position1 = {x:2, y:0, z: -8}
const rotation1 = {x: 0, y: 0, z:0}
const scale1 = {x:1, y:1, z: 1}
const position2 = {x:-2, y:0, z: -8}
const rotation2 = {x: 0, y: 0, z:0}
const scale2 = {x:1, y:1, z: 1}
// start loop
function frame() {
    // rotate by time, and update transform matrix
    const now = Date.now() / 1000
    {
        // first cube
        rotation1.x = Math.sin(now)
        rotation1.y = Math.cos(now)
        const mvpMatrix1 = getMvpMatrix(aspect, position1, rotation1, scale1)
        device.queue.writeBuffer(
            pipelineObj.mvpBuffer.mvpBuffer1,
            0,
            mvpMatrix1
        )
    }
    {
        // second cube
        rotation2.x = Math.cos(now)
        rotation2.y = Math.sin(now)
        const mvpMatrix2 = getMvpMatrix(aspect, position2, rotation2, scale2)
        device.queue.writeBuffer(
            pipelineObj.mvpBuffer.mvpBuffer2,
            0,
            mvpMatrix2
        )
    }
    // then draw
    draw(device, context, pipelineObj)
    requestAnimationFrame(frame)
}
frame()

Buffers with Offsets

基于 Buffers 修改

// initPipeline 函数 修改 //////////////////////////////////
//创建-个mvp Buffer 与 多个uniformGroup,通过offset绑定数据
const mvpBuffer = device.createBuffer({
    label: 'GPUBuffer store 4x4 matrix',
    size: 256*2,
    usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
})
const uniformGroup1 = device.createBindGroup({
    label: 'Uniform Group with Matrix',
    layout: pipeline.getBindGroupLayout(0),
    entries: [
        {
            binding: 0,
            resource: {
                buffer: mvpBuffer,
                offset: 0,
                size: 4 * 16,
            }
        }
    ]
})
const uniformGroup2 = device.createBindGroup({
    label: 'Uniform Group with Matrix',
    layout: pipeline.getBindGroupLayout(0),
    entries: [
        {
            binding: 0,
            resource: {
                buffer: mvpBuffer,
                offset: 256,
                size: 4 * 16,
            }
        }
    ]
})
const uniformGroup = {uniformGroup1, uniformGroup2};


// frame 函数 修改 ///////////////////////////////////////
...
device.queue.writeBuffer(
    pipelineObj.mvpBuffer,
    0,
    mvpMatrix1
)
...
...
device.queue.writeBuffer(
    pipelineObj.mvpBuffer,
    256,
    mvpMatrix2
)
...

DynamicOffsets

基于 Offsets 修改

// initPipeline 函数 修改 //////////////////////////////////
// 为dynamicOffset创建组布局
const dynamicBindGroupLayout = device.createBindGroupLayout({
    entries: [
        {
            binding: 0,
            visibility: GPUShaderStage.VERTEX,
            buffer: {
                type: 'uniform',
                hasDynamicOffset: true,
                minBindingSize: 0
            }
        }
    ]
})
// 为dynamicOffset创建管道布局
const dynamicPipelineLayout = device.createPipelineLayout({
    bindGroupLayouts: [dynamicBindGroupLayout]
});
const pipeline = await device.createRenderPipelineAsync({
    label: 'Basic Pipline',
    layout: dynamicPipelineLayout,//设置管线layout
    vertex: {
...    
const uniformGroup = device.createBindGroup({
    label: 'Uniform Group with Matrix',
    layout: pipeline.getBindGroupLayout(0),
    entries: [
        {
            binding: 0,
            resource: {
                buffer: mvpBuffer,
                size: 4 * 16,
            }
        }
    ]
})  
...

// draw 函数 修改 //////////////////////////////////
const offset = new Uint32Array([0, 256])
{
    passEncoder.setBindGroup(0, pipelineObj.uniformGroup,offset,0,1)
    passEncoder.draw(cube.vertexCount)
    passEncoder.setBindGroup(0, pipelineObj.uniformGroup,offset,1,1)
    passEncoder.draw(cube.vertexCount)
}

Instance

基于旋转正方体和旋转立方体的例子修改

// initPipeline 函数 修改 /////////////////////////////////
// 创建一个4x4xNUM STORAGE缓冲区来存储矩阵
const mvpBuffer = device.createBuffer({
    label: 'GPUBuffer store n*4x4 matrix',
    size: 4 * 4 * 4 * NUM, // 4 x 4 x float32 x NUM
    usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST
})

// draw 函数 修改 ///////////////////////////////////
passEncoder.draw(cube.vertexCount, NUM)//NUM渲染模型数量


// run 函数 修改 /////////////////////////////
const NUM = 1000; //全局变量
...
// run()
// 初始化MVP矩阵数据
  let aspect = size.width / size.height
  const scene = []
  const mvpBuffer = new Float32Array(NUM * 4 * 4)
  for(let i = 0; i < NUM; i++){
      // craete simple object
      const position = {x: Math.random() * 40 - 20, y: Math.random() * 40 - 20, z:  - 50 - Math.random() * 50}
      const rotation = {x: 0, y: 0, z: 0}
      const scale = {x:1, y:1, z:1}
      scene.push({position, rotation, scale})
  }

// frame 函数 修改 ////////////////////////////////
// 动态生成MVP矩阵,先保存到mvpBuffer数组中
  for(let i = 0; i < scene.length - 1; i++){
      const obj = scene[i]
      const now = Date.now() / 1000
      obj.rotation.x = Math.sin(now + i)
      obj.rotation.y = Math.cos(now + i)
      const mvpMatrix = getMvpMatrix(aspect, obj.position, obj.rotation, obj.scale)
      mvpBuffer.set(mvpMatrix, i * 4 * 4)
  }
  // 写入mvpBuffer
  device.queue.writeBuffer(pipelineObj.mvpBuffer, 0, mvpBuffer)  



// 顶点着色器修改 ////////////////////////////////////
@binding(0) @group(0) var<storage, read> mvpMatrix : array<mat4x4<f32>>;

main(
    @builtin(instance_index) index : u32,
    ...
output.Position = mvpMatrix[index] * position;