暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

WebGPU入门--绘制三角形

Spatial Data 2021-07-12
4205

    本文是个WebGPU基础入门教程,阐述如何从0开始基于WebGPU+WGSL着色器语言渲染一个三角形。由于WebGPU仍然没有发布1.0,API和着色器语言WGSL变换非常快,本文是目前(2021-7-10)最新的API实现,最新API比笔者半年前接触的话语法上简化了不少。在正式开始尝鲜前,先看下WebGPU与WebGL2的对比:

            webgpu

webgl2

        从视觉效果上看,webgpu流光溢彩,动画流畅,webgl2显卡由于不咋的,带不动,卡顿严重,色彩暗淡,应该是不支持光线追踪导致的,结论是webgpu吊打webgl。

体验地址:https://toji.github.io/webgpu-clustered-shading/

需要安装chrome金丝雀版本并开启webgpu才能看,设置见下文。

一 WebGPU学习资料

官方地址:https://github.com/gpuweb/gpuweb

WebGPU API文档:https://gpuweb.github.io/gpuweb/#intro

WGSL着色器API文档:https://gpuweb.github.io/gpuweb/wgsl/

官方示例:https://github.com/austinEng/webgpu-samples


除官方资料外,还有比较丰富的社区资料。

知乎大神wonder-yyc专栏:https://zhuanlan.zhihu.com/p/95956384

还有一个github中文学习教程,这个练习内容简单,但是介绍为什么要学习WebGPU为什么要替换WebGL比较详细:

https://github.com/hjlld/LearningWebGPU

韩国的一个内容不错的学习仓库,示例内容比较丰富:

https://github.com/redcamel/webgpu/


除官方示例紧跟WebGPU API外,其他资料只能学习参考了,由于API变更和着色器从GLSL-->WGSL的转换,社区资料都运行不了了。


二 Hello WebGPU

       程序员学习任何语言,一开始都有个打印Hello World的代码,通常就一行代码,但无论学习WebGL还是WebGPU,hello world比你想象的要复杂多了,绘制三角形就是图形引擎的helloworld,来看看如何使用最新的API进行渲染学习吧。

2.1 环境准备

先下载谷歌浏览金丝雀版本,下载地址:

https://www.google.cn/intl/zh-CN/chrome/canary/

下载完毕,next安装,easy。

设置WebGPU支持:

打开chrome canary浏览器,在url地址中输入:

 chrome://flags/#enable-unsafe-webgpu

将webgpu启用。


2.2 示例说明

  • 获取webgpu的交互上下文context

       与开发canvas和webgl一样,首先要从浏览器获取交互上下文context:

    //获取显卡适配器
    const adapter = await navigator.gpu.requestAdapter({
    powerPreference: 'high-performance'
    });
    //获取设备
    const device = await adapter.requestDevice();


    //获取gpu交互上下文
    const context = canvas.getContext("gpupresent");
    // 交换链,用于显卡往显示器输出图像
      const swapChainFormat = "bgra8unorm";
      
    context.configure({
    device,
    format: swapChainFormat,
    });
    • 初始化顶点buffer

      webgl中常规操作,传入顶点数组给顶点着色器,webgpu里稍微麻烦点,参考示例代码:

      //测试三角形
      const vertexArray = new Float32Array([
      0.0, 0.5,
      -0.5, -0.5,
      0.5, -0.5
      ]);


      const verticesBuffer = device.createBuffer({
          size: vertexArray.byteLength,
          usage: GPUBufferUsage.VERTEX,//显示声明该buffer是给顶点着色器使用
      mappedAtCreation: true,
      });
      new Float32Array(verticesBuffer.getMappedRange()).set(vertexArray);
      verticesBuffer.unmap();
      • 定义渲染管线

              webgl中首先使用glsl着色器语言定义好顶点着色器,片元着色器,然后根据两个着色器创建一个渲染的program。同理,webgpu中是使用wgsl创建着色器,并创建渲染管线。

        //定义顶点和片元着色器
        const wgslShaders = {
        vertex: `
        [[stage(vertex)]]
        fn main([[location(0)]] a_position : vec2<f32>) -> [[builtin(position)]] vec4<f32> {
        return vec4<f32>(a_position, 0.0, 1.0);
        }`,
        fragment: `
        [[stage(fragment)]]
        fn main() -> [[location(0)]] vec4<f32> {
        return vec4<f32>(1.0, 0.0, 0.0, 1.0);
        }`
        };
        //定义渲染管线
         const pipeline = device.createRenderPipeline({
        vertex: {
        module: device.createShaderModule({
        code: wgslShaders.vertex,
        }),
        entryPoint: "main",
        buffers: [
        {
        /*
        步进值,也就是每个顶点需要占用几个储存空间,单位是 byte。
        我们是用 Float32Array 来储存顶点位置的,每个 32 位浮点数需要 4 个 byte;
        xyz三维顶点需要 3 个 32 位浮点数来分别表示,即 4 * 3 byte。
        xy二维顶点需要2个32 位浮点数来分别表示,即 4 * 2 个 byte。*/
        arrayStride: 4 * 2,
        attributes: [
        {
        // position
        //对应顶点着色器中 (location = 0)
        shaderLocation: 0,
        //0代表从头开始,不设置位移,有时候可以将多个顶点写一个buffer,根据offset位移选择用于不同地方
        offset: 0,
        //2个32位浮点数,如果3个32位浮点数,就可以写float32x3
        format: "float32x2",
        }
        ],
        },
        ]
        },
        fragment: {
        module: device.createShaderModule({
        code: wgslShaders.fragment,
        }),
        entryPoint: "main",
        targets: [
        {
        format: swapChainFormat,
        },
        ],
        },
        // 绘制模式
        /*
        enum GPUPrimitiveTopology {
        "point-list",
        "line-list",
        "line-strip",
        "triangle-list",
        "triangle-strip"
        };


        */
        primitive: {
        topology: 'triangle-list',
        }
        });
        • 创建渲染函数

               webgl中创建完program和各种buffer,buffer或vao绑定program后,需要写一个render函数,让webgl渲染,再把这个函数挂到动画函数里,webgpu也一样,需要声明一个渲染函数:

              function frame() {
          const commandEncoder = device.createCommandEncoder();
          // 获取当前纹理图像
          const textureView = context.getCurrentTexture().createView();
          // 渲染通道描述
          const renderPassDescriptor: GPURenderPassDescriptor = {
          // 存储图像信息
                   colorAttachments: [
          {
          // 指定存储图像的位置
          view: textureView,
          // 背景颜色
          loadValue: { r: 0.0, g: 0.0, b: 0.0, a: 1.0 },
          storeOp: 'store',
          // storeOp 存储选型 store 或 clear,必需显示声明
          // resolveTarget 多重采样
          },
          ],
          };


          //开启一个渲染通道
          const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
          //默认会开启一个视口viewport,宽高就是canvas的width height而不是clientwidth,clientheight
          //设置 渲染管线
                passEncoder.setPipeline(pipeline);
                // 设置顶点buffer,0就是渲染管线中shaderLocation:0定义的buffer位置
                // 意思在声明的位置绑定这个设置好的buffer
          passEncoder.setVertexBuffer(0, verticesBuffer);

          passEncoder.draw(3, 1, 0, 0);
          passEncoder.endPass();


          device.queue.submit([commandEncoder.finish()]);
            }

          好了,入门代码就这么多了,完整代码参考:

          https://github.com/FreeGIS/webgpu_learning/

          代码下载下来,三板斧:

            //安装依赖
            npm install
            // 编译
            npm run build
            // 启动
            npm run serve

            之后用chrome canary浏览器打开,效果如下:

            入门东西就这么多,如果要深入学习,首先需要图形学基础,其次根据官网和大神示例多理解消化吸收,功夫不负有心人的。。。

            文章转载自Spatial Data,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

            评论