webgpu用最简短的代码画一个三角形

发布时间 2023-10-23 19:44:40作者: zzatp

1.包含webgpu的初始化

2.三角形顶点缓冲的创建以及将cpu数据填充到gpu里

3.webgpu里着色器的编写,以及通过代码创建webgpu的着色器程序对象

4.通过顶点和像素阶段的描述创建一个渲染管线

话不多说直接贴代码:

<html>
<head>
  <meta charset="utf-8">
  <title>WebGPU Hello Triangle</title>
  <style>
body {
    font-family: system-ui;
    color: #f7f7ff;
    background-color: rgb(38, 38, 38);
    text-align: center;
}

canvas {
    display: block;
    margin: 0 auto;
    width: 100%;
    height: 100%;
}
  </style>
  <script type="module">
    // 获取canvas元素
    const canvas = document.getElementById("canvas");
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    // 获取WebGPU上下文
    const context = canvas.getContext("webgpu");
    // 定义顶点数据,三个顶点组成一个三角形
    const vertices = new Float32Array([
      -0.5, -0.5,   1.0, 0.0, 0.0, 1.0, // 左下角
      0.5, -0.5,   0.0, 1.0, 0.0, 1.0, // 右下角
      0.0, 0.5,   0.0, 0.0, 1.0, 1.0, // 顶点
    ]);
    // 定义顶点着色器代码,使用WGSL语言
    const wgslSource = `
        struct Vertex {
            @builtin(position) Position: vec4<f32>, // 顶点着色器输出的位置
            @location(0) color: vec4<f32>,          // 顶点着色器输出的颜色
        }
        // 顶点着色器
        @vertex fn vsmain(
            @builtin(vertex_index) VertexIndex: u32,    // 顶点着色器内建的顶点索引
            @location(0) position: vec2<f32>,   // 输入顶点的位置
            @location(1) color: vec4<f32>                   // 输入顶点的颜色
        ) -> Vertex
        {
            var vertex_out : Vertex;
            vertex_out.Position = vec4<f32>(position, 0.0, 1.0);
            vertex_out.color = color;
            return vertex_out;
        }
        // 像素着色器
        @fragment fn fsmain(in: Vertex) -> @location(0) vec4<f32>
        {
            return in.color;
        }
    `;
    // 初始化WebGPU相关对象
    async function initWebGPU() {
        // 获取适配器对象,表示GPU的物理设备
        const adapter = await navigator.gpu.requestAdapter();
        // 获取设备对象,表示GPU的会话
        const device = await adapter.requestDevice();
        // 配置上下文对象,指定设备和像素格式
        const format = "bgra8unorm";
        context.configure({
            device: device,
            format: format,
        });
        // 创建顶点缓冲区,用于存储顶点数据
        const vertexBuffer = device.createBuffer({
            size: vertices.byteLength,
            usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
            mappedAtCreation: true,
        });
        new Float32Array(vertexBuffer.getMappedRange()).set(vertices);
        vertexBuffer.unmap();
        // 创建着色器模块,用于编译着色器代码
        const shaderModule = device.createShaderModule({ code: wgslSource });
        // 定义顶点输入的数据布局,以及顶点着色阶段的其它信息
        const vertexSize = 4 * 6;
        const colorOffset = 4 * 2;
        const positionAttributeState = {
            format: "float32x2",
            offset: 0,
            shaderLocation: 0,
        };
        const colorAttributeState = {
            format: "float32x4",
            offset: colorOffset,
            shaderLocation: 1,
        }
        const vertexBufferState = {
            arrayStride: vertexSize,
            stepMode: "vertex",
            attributes: [positionAttributeState, colorAttributeState], // 顶点输入属性包含一个位置和颜色
        };

        const vertexStageDescriptor = {
            buffers: [vertexBufferState], // 顶点着色的顶点缓冲
            module: shaderModule,       // 顶点着色的shader
            entryPoint: "vsmain"        // 入口函数
        };

        const fragmentStageDescriptor = {
            module: shaderModule,
            entryPoint: "fsmain",
            targets: [ {format: "bgra8unorm" }, ],
            };
        const renderPipelineDescriptor = {
            layout: 'auto',
            vertex: vertexStageDescriptor,
            fragment: fragmentStageDescriptor,
            primitive: {topology: "triangle-list" },
        };
        // 创建渲染管线,用于指定渲染流程和状态
        const pipeline = device.createRenderPipeline(renderPipelineDescriptor);
        return { device, pipeline, vertexBuffer }; // 返回相关对象
    }
    // 渲染函数,用于绘制图形
    function render({ device, pipeline, vertexBuffer }) {
      // 获取颜色纹理,用于存储渲染结果
      const textureView = context.getCurrentTexture().createView();
      // 创建命令编码器,用于记录渲染命令
      const commandEncoder = device.createCommandEncoder();
      const colorAttachmentDescriptor = {
            view: textureView,
            loadOp: "clear",
            storeOp: "store",
            clearColor: [0.5, 0.5, 0.5, 1]
        };
      /* GPURenderPassDescriptor */
      const renderPassDescriptor = { colorAttachments: [colorAttachmentDescriptor] };
      // 开始一个渲染通道,指定颜色附件和清除颜色
      const renderPass = commandEncoder.beginRenderPass(renderPassDescriptor);
      // 设置渲染管线
      renderPass.setPipeline(pipeline);
      // 设置顶点缓冲区
      renderPass.setVertexBuffer(0, vertexBuffer);
      // 绘制三个顶点,组成一个三角形
      renderPass.draw(3, 1, 0, 0);
      // 结束渲染通道
      renderPass.end();
      // 结束命令编码器,获取命令缓冲区
      const commandBuffer = commandEncoder.finish();
      // 将命令缓冲区提交给GPU队列,执行渲染命令
      device.queue.submit([commandBuffer]);
    }
    // 调用初始化函数,然后调用渲染函数
    initWebGPU().then(render);
  </script>
</head>
<body>
  <canvas id="canvas" ></canvas>
</body>
</html>
效果如下: