WebGpu旋转矩形

88 阅读8分钟
  • 引入gl-matrix矩阵库

npm install gl-matrix -S

  • index.html代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <canvas id="webgpu" width="500" height="500" style="background-color: black;"></canvas>
  <script type="module">
    import { vertex,fragment } from "./shader.js";
    import * as glMatrix from './node_modules/gl-matrix/esm/index.js';
    if(navigator.gpu) {
      console.log('浏览器支持WebGPU');
    }else{
      console.log('浏览器不支持webGPU');
    }
    //创建虚拟GPU设备对象
    const adapter = await navigator.gpu.requestAdapter()
    const device = await adapter.requestDevice()

    //创建canvas用于承载webgpu渲染的画布
    const canvas = document.getElementById('webgpu')
    //获取webgpu渲染的上下文对象
    const context = canvas.getContext('webgpu')
    //获取浏览器颜色格式
    const format = navigator.gpu.getPreferredCanvasFormat()
    //将webgpu对象与画布上下文关联起来   完成相关配置
    context.configure({
      device: device,
      format: format//获取浏览器颜色格式
    })
    
    //使用类型化数组定义图像坐标点     创建顶点缓冲区表示顶点数据
    const vertexArray = new Float32Array([
      //三角形三个顶点坐标的x,y,z值
      1.0, 0.0, 0.0,
      0.0, 1.0, 0.0,
      0.0, 0.0, 0.0,
      1.0, 0.0, 0.0,
      0.0, 1.0, 0.0,
      1.0, 1.0, 0.0,
    ])
    //当device.createBuffer执行时,会在电脑显卡GPU内存中开辟一片存储空间用来存储顶点数据
    //创建顶点缓冲区 
    const vertexBuffer = device.createBuffer({
      size: vertexArray.byteLength,//缓冲区长度
      usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST//缓冲区用途:用于存储顶点数据
    })
    //将顶点数据写入顶点缓冲区
    device.queue.writeBuffer(vertexBuffer,0,vertexArray)

    //uniform数据:缩放矩阵 浮点数
    const mat4Array = new Float32Array([0.5,0,0,0,0,0.5,0,0,0,0,1,0,0,0,0,1])
    const mat4Buffer = device.createBuffer({
      size: mat4Array.byteLength,//缓冲区长度
      usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST//缓冲区用途:用于存储顶点数据
    })
    device.queue.writeBuffer(mat4Buffer,0,mat4Array)
    const mat4T1 = glMatrix.mat4.create()
    glMatrix.mat4.translate(mat4T1,mat4T1,[-1,0,0])
    // const mat4TArray = new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,-1,-1,0,1])
    const mat4TArray = mat4T1
    const mat4TBuffer = device.createBuffer({
      size: mat4TArray.byteLength,//缓冲区长度
      usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST//缓冲区用途:用于存储顶点数据
    })
    device.queue.writeBuffer(mat4TBuffer,0,mat4TArray)

    const t = 0.5
    const tArray = new Float32Array([t])
    const tBuffer = device.createBuffer({
      size: tArray.byteLength,//缓冲区长度
      usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
    })
    device.queue.writeBuffer(tBuffer, 0, tArray)

    const colorArray = new Float32Array([1.0,1.0,0.0])
    const colorBuffer = device.createBuffer({
      size: colorArray.byteLength,//缓冲区长度
      usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST//缓冲区用途:用于存储顶点数据
    })
    device.queue.writeBuffer(colorBuffer,0,colorArray)
    //创建一个webgpu渲染管线对象pipeline
    const pipeline = device.createRenderPipeline({
      layout: 'auto',
      vertex: {//顶点相关属性配置
        module: device.createShaderModule({ code: vertex}),
        entryPoint: "main",
        buffers: [
          {
            arrayStride: 3 * 4,//一个顶点数据占用的字节长度
            attributes: [{
              shaderLocation: 0, //标记顶点缓冲区为0,也可以设置为其它值
              format: "float32x3", //一个顶点有3个float32
              offset: 0
            }]
          }
        ]
      },
      fragment: {//片元着色器引入
        module: device.createShaderModule({ code: fragment}),
        entryPoint: "main",
        targets: [{
          format: format
        }]
      },
      primitive: {
        //绘制三角形、线条、点
        topology: "triangle-list",//绘制三角形
      },
    });

    const bindGroup = device.createBindGroup({
      layout: pipeline.getBindGroupLayout(0),
      entries: [
        // {
        //   binding: 0,
        //   resource: {buffer: mat4Buffer}
        //   // resource: {buffer: tBuffer}
        // },
        {
          binding: 1,
          // resource: {buffer: mat4Buffer}
          resource: {buffer: colorBuffer}
        },
        {
          binding: 0,
          resource: {buffer: mat4TBuffer}
        }
      ]
    })
    // WGSL着色器语言


    //创建命令编码器对象
    const commandEncoder = device.createCommandEncoder();
    //创建一个渲染通道
    const renderPass = commandEncoder.beginRenderPass({
      colorAttachments: [{
        view: context.getCurrentTexture().createView(),//结果输出到canvas画布上,颜色缓冲区
        storeOp: 'store',
        loadOp: 'clear',
        clearValue: { r: 0.5, g: 0.5, b: 0.5, a: 1.0},//渲染的背景颜色

      }]
    })
    //给渲染通道设置渲染管线,可设置多条渲染管线
    renderPass.setPipeline(pipeline);
    //设置顶点数据缓冲区,即上述定义标记为0的缓冲区,也可以为其它
    renderPass.setVertexBuffer(0, vertexBuffer);
    renderPass.setBindGroup(0, bindGroup);

    //绘制命令
    renderPass.draw(3);
    //绘制完结束绘制
    renderPass.end()
    //将生成的GPU命令存入GPU命令缓冲区
    const commandBuffer = commandEncoder.finish()

    device.queue.submit([commandBuffer])
    //glMatrix.mat4.fromValues创建矩阵,一列列写
    // const mat4T = glMatrix.mat4.fromValues(1,0,0,0,  0,1,0,0,  0,0,1,0,  1,2,3,1)
    // const mat4S = glMatrix.mat4.fromValues(1,0,0,0,0,2,0,0,0,0,3,0,0,0,0,1)
    //创建一个单位矩阵
    const mat4 = glMatrix.mat4.create()
    //创建一个平移矩阵 沿着x轴平移2, 生成的mat4T即为平移矩阵
    const mat4T = glMatrix.mat4.create()
    glMatrix.mat4.translate(mat4T,mat4,[2,0,0])
    //生成缩放矩阵 x缩放10
    const mat4S = glMatrix.mat4.create()
    glMatrix.mat4.scale(mat4S,mat4,[10,1,1])
    //生成绕x轴旋转45度旋转矩阵
    const mat4X = glMatrix.mat4.create()
    glMatrix.mat4.rotateX(mat4X,mat4,Math.PI/4)
    //生成绕y轴旋转45度旋转矩阵
    const mat4Y = glMatrix.mat4.create()
    glMatrix.mat4.rotateY(mat4Y,mat4,Math.PI/4)
    //生成绕Z轴旋转45度旋转矩阵
    const mat4Z = glMatrix.mat4.create()
    glMatrix.mat4.rotateZ(mat4Z,mat4,Math.PI/4)
    //矩阵乘法运算 先平移2,再缩放10   生成模型矩阵 方法1
    // const modelMatrix = glMatrix.mat4.create()
    // glMatrix.mat4.multiply(modelMatrix,modelMatrix,mat4S) //后缩放
    // glMatrix.mat4.multiply(modelMatrix,modelMatrix,mat4T) //先平移
    
    //生成模型矩阵 方法2
    const modelMatrix = glMatrix.mat4.create()
    glMatrix.mat4.scale(modelMatrix,modelMatrix,[10,1,1])
    glMatrix.mat4.translate(modelMatrix,modelMatrix,[2,0,0])
    console.log(modelMatrix);

    //创建3维向量表示顶点坐标,对顶点p1进行变换,变换后的值存在p2中
    const p1 = glMatrix.vec3.fromValues(2,0,0)
    const p2 = glMatrix.vec3.create()
    glMatrix.vec3.transformMat4(p2,p1,modelMatrix)
  </script>
</body>
</html>

  • shader.js代码 WGSL着色器语言
const vertex = /*wgsl*/ `
// @group(0) @binding(0) var<uniform> t:f32;
@group(0) @binding(0) var<uniform> S:mat4x4<f32>;
// @group(0) @binding(2) var<uniform> T:mat4x4<f32>;
@vertex
fn main(@location(0) pos: vec3<f32>)-> @builtin(position) vec4<f32>{//@location(0)表示GPU显存中标记为0的顶点缓冲区顶点数据,如果在顶点缓冲区标记为1,则写1
  var pos2 = vec4<f32>(pos,1.0);//三维向量转成4维齐次向量
  // pos2.x -= 0.2;//所有顶点的x坐标偏移0.2
  //将所有顶点坐标缩放
  // 0.5  0   0   0
  // 0   0.5  0   0
  // 0    0   1   0
  // 0    0   0   1
  // var mat = mat4x4<f32>(0.5,0,0,0,0,0.5,0,0,0,0,1,0,0,0,0,1);
  //平移-1 -1
  // var mat = mat4x4<f32>(1,0,0,0,0,1,0,0,0,0,1,0,-1,-1,0,1);
  // pos2 = mat * pos2;
  // var s = mat4x4<f32>(t,0,0,0,0,t,0,0,0,0,1,0,0,0,0,1);
  // pos2 = s * pos2;
  //先缩放后平移
  // pos2 = T * S * pos2;
  pos2 = S * pos2;
  return pos2;
}
`

const fragment = /*wgsl*/ `
@group(0) @binding(1) var<uniform> color:vec3<f32>;
@fragment
fn main() -> @location(0) vec4<f32>{
  // return vec4<f32>(1.0,0.0,0.0,1.0); //红色片元
  return vec4<f32>(color,1.0); //红色片元
}
`

export {
  vertex,
  fragment
}

image.png 旋转矩形

  • index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <canvas id="webgpu" width="500" height="500" style="background-color: black;"></canvas>
  <script type="module">
    import { vertex,fragment } from "./shader.js";
    import * as glMatrix from './node_modules/gl-matrix/esm/index.js';
    if(navigator.gpu) {
      console.log('浏览器支持WebGPU');
    }else{
      console.log('浏览器不支持webGPU');
    }
    //创建虚拟GPU设备对象
    const adapter = await navigator.gpu.requestAdapter()
    const device = await adapter.requestDevice()

    //创建canvas用于承载webgpu渲染的画布
    const canvas = document.getElementById('webgpu')
    //获取webgpu渲染的上下文对象
    const context = canvas.getContext('webgpu')
    //获取浏览器颜色格式
    const format = navigator.gpu.getPreferredCanvasFormat()
    //将webgpu对象与画布上下文关联起来   完成相关配置
    context.configure({
      device: device,
      format: format//获取浏览器颜色格式
    })
    
    //使用类型化数组定义图像坐标点     创建顶点缓冲区表示顶点数据
    const vertexArray = new Float32Array([
      //三角形三个顶点坐标的x,y,z值
      1.0, 0.5, 0.0,
      -1.0, 0.5, 0.0,
      -1.0, -0.5, 0.0,
      1.0, 0.5, 0.0,
      -1.0, -0.5, 0.0,
      1.0, -0.5, 0.0,
    ])
    //当device.createBuffer执行时,会在电脑显卡GPU内存中开辟一片存储空间用来存储顶点数据
    //创建顶点缓冲区 
    const vertexBuffer = device.createBuffer({
      size: vertexArray.byteLength,//缓冲区长度
      usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST//缓冲区用途:用于存储顶点数据
    })
    //将顶点数据写入顶点缓冲区
    device.queue.writeBuffer(vertexBuffer,0,vertexArray)
    
    // const mat4TArray = new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,-1,-1,0,1])
    const mat4T1 = glMatrix.mat4.create()
    // glMatrix.mat4.translate(mat4T1,mat4T1,[-1,0,0])
    glMatrix.mat4.rotateZ(mat4T1,mat4T1,Math.PI/2)
    const mat4TArray = mat4T1
    const mat4TBuffer = device.createBuffer({
      size: mat4TArray.byteLength,//缓冲区长度
      usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST//缓冲区用途:用于存储顶点数据
    })
    device.queue.writeBuffer(mat4TBuffer,0,mat4TArray)

    const colorArray = new Float32Array([1.0,1.0,0.0])
    const colorBuffer = device.createBuffer({
      size: colorArray.byteLength,//缓冲区长度
      usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST//缓冲区用途:用于存储顶点数据
    })
    device.queue.writeBuffer(colorBuffer,0,colorArray)
    //创建一个webgpu渲染管线对象pipeline
    const pipeline = device.createRenderPipeline({
      layout: 'auto',
      vertex: {//顶点相关属性配置
        module: device.createShaderModule({ code: vertex}),
        entryPoint: "main",
        buffers: [
          {
            arrayStride: 3 * 4,//一个顶点数据占用的字节长度
            attributes: [{
              shaderLocation: 0, //标记顶点缓冲区为0,也可以设置为其它值
              format: "float32x3", //一个顶点有3个float32
              offset: 0
            }]
          }
        ]
      },
      fragment: {//片元着色器引入
        module: device.createShaderModule({ code: fragment}),
        entryPoint: "main",
        targets: [{
          format: format
        }]
      },
      primitive: {
        //绘制三角形、线条、点
        topology: "triangle-list",//绘制三角形
      },
    });

    const bindGroup = device.createBindGroup({
      layout: pipeline.getBindGroupLayout(0),
      entries: [
        {
          binding: 0,
          // resource: {buffer: mat4Buffer}
          resource: {buffer: mat4TBuffer}
          // resource: {buffer: tBuffer}
        },
        {
          binding: 1,
          // resource: {buffer: mat4Buffer}
          resource: {buffer: colorBuffer}
        },
        // {
        //   binding: 2,
        //   resource: {buffer: mat4TBuffer}
        // },
      ]
    })
    // WGSL着色器语言


    // //创建命令编码器对象
    // const commandEncoder = device.createCommandEncoder();
    // //创建一个渲染通道
    // const renderPass = commandEncoder.beginRenderPass({
    //   colorAttachments: [{
    //     view: context.getCurrentTexture().createView(),//结果输出到canvas画布上,颜色缓冲区
    //     storeOp: 'store',
    //     loadOp: 'clear',
    //     clearValue: { r: 0.5, g: 0.5, b: 0.5, a: 1.0},//渲染的背景颜色

    //   }]
    // })
    // //给渲染通道设置渲染管线,可设置多条渲染管线
    // renderPass.setPipeline(pipeline);
    // //设置顶点数据缓冲区,即上述定义标记为0的缓冲区,也可以为其它
    // renderPass.setVertexBuffer(0, vertexBuffer);
    // renderPass.setBindGroup(0, bindGroup);

    // //绘制命令
    // renderPass.draw(6);
    // //绘制完结束绘制
    // renderPass.end()
    // //将生成的GPU命令存入GPU命令缓冲区
    // const commandBuffer = commandEncoder.finish()

    // device.queue.submit([commandBuffer])
    //glMatrix.mat4.fromValues创建矩阵,一列列写
    // const mat4T = glMatrix.mat4.fromValues(1,0,0,0,  0,1,0,0,  0,0,1,0,  1,2,3,1)
    // const mat4S = glMatrix.mat4.fromValues(1,0,0,0,0,2,0,0,0,0,3,0,0,0,0,1)
    //创建一个单位矩阵
    // const mat4 = glMatrix.mat4.create()
    // //创建一个平移矩阵 沿着x轴平移2, 生成的mat4T即为平移矩阵
    // const mat4T = glMatrix.mat4.create()
    // glMatrix.mat4.translate(mat4T,mat4,[2,0,0])
    // //生成缩放矩阵 x缩放10
    // const mat4S = glMatrix.mat4.create()
    // glMatrix.mat4.scale(mat4S,mat4,[10,1,1])
    // //生成绕x轴旋转45度旋转矩阵
    // const mat4X = glMatrix.mat4.create()
    // glMatrix.mat4.rotateX(mat4X,mat4,Math.PI/4)
    // //生成绕y轴旋转45度旋转矩阵
    // const mat4Y = glMatrix.mat4.create()
    // glMatrix.mat4.rotateY(mat4Y,mat4,Math.PI/4)
    // //生成绕Z轴旋转45度旋转矩阵
    // const mat4Z = glMatrix.mat4.create()
    // glMatrix.mat4.rotateZ(mat4Z,mat4,Math.PI/4)
    // //矩阵乘法运算 先平移2,再缩放10   生成模型矩阵 方法1
    // // const modelMatrix = glMatrix.mat4.create()
    // // glMatrix.mat4.multiply(modelMatrix,modelMatrix,mat4S) //后缩放
    // // glMatrix.mat4.multiply(modelMatrix,modelMatrix,mat4T) //先平移
    
    // //生成模型矩阵 方法2
    // const modelMatrix = glMatrix.mat4.create()
    // glMatrix.mat4.scale(modelMatrix,modelMatrix,[10,1,1])
    // glMatrix.mat4.translate(modelMatrix,modelMatrix,[2,0,0])
    // console.log(modelMatrix);

    // //创建3维向量表示顶点坐标,对顶点p1进行变换,变换后的值存在p2中
    // const p1 = glMatrix.vec3.fromValues(2,0,0)
    // const p2 = glMatrix.vec3.create()
    // glMatrix.vec3.transformMat4(p2,p1,modelMatrix)
    // console.log(p1,p2);
    
    let angle = 0.0;
    function render(){
      angle+=0.05;
      const mat4T1 = glMatrix.mat4.create()
    // glMatrix.mat4.translate(mat4T1,mat4T1,[-1,0,0])
      glMatrix.mat4.rotateZ(mat4T1,mat4T1,angle)
      console.log(mat4T1);
      device.queue.writeBuffer(mat4TBuffer,0,mat4T1)
        //创建命令编码器对象
      const commandEncoder = device.createCommandEncoder();
      //创建一个渲染通道
      const renderPass = commandEncoder.beginRenderPass({
        colorAttachments: [{
          view: context.getCurrentTexture().createView(),//结果输出到canvas画布上,颜色缓冲区
          storeOp: 'store',
          loadOp: 'clear',
          clearValue: { r: 0.5, g: 0.5, b: 0.5, a: 1.0},//渲染的背景颜色

        }]
      })
      //给渲染通道设置渲染管线,可设置多条渲染管线
      renderPass.setPipeline(pipeline);
      //设置顶点数据缓冲区,即上述定义标记为0的缓冲区,也可以为其它
      renderPass.setVertexBuffer(0, vertexBuffer);
      renderPass.setBindGroup(0, bindGroup);

      //绘制命令
      renderPass.draw(6);
      //绘制完结束绘制
      renderPass.end()
      //将生成的GPU命令存入GPU命令缓冲区
      const commandBuffer = commandEncoder.finish()

      device.queue.submit([commandBuffer])
      window.requestAnimationFrame(render)
    }
    render();
    
  </script>
</body>
</html>

image.png