• 在 WebGPU 的片元着色器中访问帧缓冲坐标



    1. 技术说明

    • 使用最新 Edge/Chrome Canary 浏览器
    • 使用 VSCode 插件 LiveServer 的 HTTP 服务器对本机提供 5500 端口的页面服务,即 http://localhost:5500/index.html
    • 使用 es-module 风格的 JavaScript 实现

    2. 三角形例子

    先上效果,后面再解析片元着色器:

    image

    HTML

    html 部分就简单一些

    <canvas id="c" width="600" height="600" style="border: 1px solid darkseagreen;"></canvas>
    <script type="module" src="./main.js"></script>
    

    不出意外的话,你可以看到一个带暗绿色边框的 canvas,长宽均为 600 像素。

    JavaScript

    JavaScript 代码也比较简单,省略大部分动态代码和有无判断代码:

    const canvas = document.getElementById('c')
    
    const shaderText = `/* 着色器代码,后面会给 */`
    
    const init = async () => {
      const adapter = await navigation.gpu.requestAdapter()
      const device = await adapter.requestDevice()
      const context = canvas.getContext('webgpu')
      const presentationFormat = context.getPreferredFormat(adapter)
    
      context.configure({
        device,
        format: presentationFormat,
        size: [ 600, 600 ], // canvas 的画图尺寸
      })
      
      const pipeline = device.createRenderPipeline({
        vertex: {
          module: device.createShaderModule({
            code: shaderText
          }),
          entryPoint: 'vertexMain'
        },
        fragment: {
          module: device.createShaderModule({
            code: shaderText
          }),
          entryPoint: 'fragmentMain',
          targets: [{ format: presentationFormat }],
        },
        primitive: { topology: 'triangle-list' },
      })
      
      const render = () => {
        
        /* 
          每帧创建编码器并“录制”编码过程,最终提交给设备 
        */
        
        const commandEncoder = device.createCommandEncoder()
        const textureView = context.getCurrentTexture().createView()
        const renderPassDescriptor = {
          colorAttachments: [
            {
              view: textureView,
              clearValue: { r: 0.0, g: 0.0, b: 0.0, a: 1.0 },
              loadOp: 'clear',
              storeOp: 'store',
            },
          ],
        }
        const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor)
        passEncoder.setPipeline(pipeline)
        passEncoder.draw(3, 1, 0, 0)
        passEncoder.end()
    
        device.queue.submit([commandEncoder.finish()])
        
        requestAnimationFrame(render)
      }
      
      requestAnimationFrame(render)
    } // async function init
    
    init()
    

    我保留了完整的 rAF 帧动画结构。

    为了方便说明内置在片元着色器中的帧缓冲坐标变量,我将三角形顶点值写死在顶点着色器中,见下文。

    3. 着色器解析

    着色器代码:

    const shaderText = /* wgsl */`
    @stage(vertex)
    fn vertexMain(
      @builtin(vertex_index) VertexIndex: u32
    ) -> @builtin(position) vec4<f32> {
      var pos = array<vec2<f32>, 3>(
        vec2<f32>(0.0, 0.5),
        vec2<f32>(-0.5, -0.5),
        vec2<f32>(0.5, -0.5)
      );
      return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
    }
    
    @stage(fragment)
    fn fragmentMain(
      @builtin(position) FrameBufferCoord: vec4<f32>
    ) -> @location(0) vec4<f32> {
      var color = vec4<f32>(1.0, 0.5, 0.0, .5);
      let x: f32 = (FrameBufferCoord.x - 300.0) / 300.0;
      let y: f32 = (-FrameBufferCoord.y + 300.0) / 300.0;
      let r: f32 = sqrt(x * x + y * y);
      
      if (x > -0.1 && x < 0.1) {
        return vec4<f32>(1.0, 0.0, 0.5, 1.0);
      } else if (y > -0.1 && y < 0.1) {
        return vec4<f32>(0.0, 0.5, 1.0, 1.0);
      } else if (r < 0.4) {
        return vec4<f32>(FrameBufferCoord.rgb / 600.0, 0.5);
      } else {
        discard;
      }
    }
    `
    

    WGSL 褒贬不一,就不说它的语法如何了。

    主要是看片元着色器的输入,@builtin(position) FrameBufferCoord: vec4<f32>,它向每一个片元着色器传入了当前片元的帧缓冲坐标,类型是 vec4<f32>


    帧缓冲坐标图示

    帧缓冲在 WebGPU 规范中有说明,它的坐标轴、原点和坐标值域是这样的:

    image


    我对帧缓冲坐标进行了缩放、平移,也就是计算了 xy,将原点移动到 canvas 中央,然后把坐标区间从 [0, 600] 映射到 [-1, 1]

    然后就是最后的那个多步逻辑分支了,也很简单:

    • 第一个 if 对应图中的 粉色
    • 第二个 if 对应图中的 蓝色
    • 第三个 if 对应图中三角形区域内、蓝色、粉色像素外的 圆区域,半径是 0.4(映射后的半径),它使用帧缓冲坐标作为颜色值(除以帧缓冲的长宽 600 映射到了 [0, 1]),加了 0.5 的透明度
    • 最后其它的片元使用语句 discard 丢弃,即不渲染

    OK,今天就学到这里。

    源码

    微云

  • 相关阅读:
    对单片机存储分配新的认识
    超简单的word转swf 实现
    纯真IP数据库
    webservice 特殊字符处理
    QQ输入法导致win8 x64 装不上vs11,打不开记事本,等各种变态问题
    时间戳转换
    Remote Desktop Organizer 1.4.5
    Xcdoe 4.6 dbank下载
    链接复用
    Visual Studio 2012 序列号
  • 原文地址:https://www.cnblogs.com/onsummer/p/16198461.html
Copyright © 2020-2023  润新知