使用WebGPU绘制三角形

创建日期:2024-07-14
更新日期:2025-01-12

render-pipeline.html

<!doctype html>

<html lang="zh-cn">
  <head>
    <meta charset="utf-8" />
    <title>渲染管线</title>
    <link rel="shortcut icon" href="/favicon.ico" />
    <meta
      name="viewport"
      content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"
    />
    <script id="shader" type="text/wgsl">
      struct VertexOut {
          @builtin(position) position : vec4f,
          @location(0) color : vec4f
      }

      @vertex
      fn vertex_main(@location(0) position: vec4f, @location(1) color: vec4f) -> VertexOut {
        var output : VertexOut;
        output.position = position;
        output.color = color;
        return output;
      }

      @fragment
      fn fragment_main(fragData : VertexOut) -> @location(0) vec4f {
        return fragData.color;
      }
    </script>
  </head>

  <body>
    <canvas id="canvas"></canvas>
    <script>
      async function init() {
        if (!navigator.gpu) {
          throw Error('WebGPU not supported')
        }
        const adapter = await navigator.gpu.requestAdapter()
        if (!adapter) {
          throw Error("Couldn't request WebGPU adapter.")
        }
        const device = await adapter.requestDevice()

        const canvas = document.querySelector('#canvas')
        const context = canvas.getContext('webgpu')
        const shaders = document.querySelector('#shader').innerText
        const shaderModule = device.createShaderModule({
          code: shaders
        })

        context.configure({
          device,
          format: navigator.gpu.getPreferredCanvasFormat(),
          alphaMode: 'premultiplied'
        })

        const vertices = new Float32Array([
          0.0, 0.6, 0, 1, 1, 0, 0, 1, -0.5, -0.6, 0, 1, 0, 1, 0, 1, 0.5, -0.6, 0, 1, 0, 0, 1, 1
        ])

        const vertexBuffer = device.createBuffer({
          size: vertices.byteLength,
          usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
        })
        device.queue.writeBuffer(vertexBuffer, 0, vertices, 0, vertices.length)

        const vertexBuffers = [
          {
            attributes: [
              {
                shaderLocation: 0,
                offset: 0,
                format: 'float32x4'
              },
              {
                shaderLocation: 1,
                offset: 16,
                format: 'float32x4'
              }
            ],
            arrayStride: 32,
            stepMode: 'vertex'
          }
        ]

        const pipelineDescriptor = {
          vertex: {
            module: shaderModule,
            entryPoint: 'vertex_main',
            buffers: vertexBuffers
          },
          fragment: {
            module: shaderModule,
            entryPoint: 'fragment_main',
            targets: [
              {
                format: navigator.gpu.getPreferredCanvasFormat()
              }
            ]
          },
          primitive: {
            topology: 'triangle-list'
          },
          layout: 'auto'
        }
        const renderPipeline = device.createRenderPipeline(pipelineDescriptor)
        const commandEncoder = device.createCommandEncoder()

        const clearColor = { r: 0.0, g: 0.5, b: 1.0, a: 1.0 }
        const renderPassDescriptor = {
          colorAttachments: [
            {
              clearValue: clearColor,
              loadOp: 'clear',
              storeOp: 'store',
              view: context.getCurrentTexture().createView()
            }
          ]
        }
        const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor)
        passEncoder.setPipeline(renderPipeline)
        passEncoder.setVertexBuffer(0, vertexBuffer)
        passEncoder.draw(3)
        passEncoder.end()
        device.queue.submit([commandEncoder.finish()])
      }

      function start() {
        init()
      }
      start()
    </script>
  </body>
</html>