WebGPU入门

检测浏览器是否支持webgpu

chrome 113版本以上已经完全支持

//包含该属性就证明支持
console.log(navigator.gpu);

渲染流程

1.png

常见api

    device.createRenderPipeline();//创建渲染管线
    device.createComputePipeline();//创建计算管线
    device.createShaderModule();//创建着色器模块
    device.createCommandEncoder();//创建命令对象(绘制和计算命令)
    device.createBuffer();//创建缓冲区

通过顶点缓冲区创建顶点数据

  • usage的属性值可以是下面两种
  • GPUBufferUsage.VERTEXT : 表示用于该缓冲区是顶点缓冲区,就算存储顶点数据的缓冲区
  • GPUBufferUsage.COPY_DST : 表示该缓冲区可以写入顶点数据,作为复制顶点数据的目的地
//渲染一个物体,需要先通过顶点坐标来定义该物体的几何形状
    //通过顶点缓冲区创建顶点数据
    const vertexBuffer = device.createBuffer({
        size: vertextArray.byteLength,//根据字节长度,此时是4*9=36字节
        //缓冲区用途:作为顶点缓冲区|可以写入顶点数据
        usage: GPUBufferUsage.VERTEXT | GPUBufferUsage.COPY_DST
    });

webgpu的着色器语言是wgsl

wgsl基础类型
       bool  布尔
      u32   无符号整数
      i32   有符号整数
      f32   32位浮点数
      f16   16位浮点数
      
      
      wgsl可以用var声明变量
      var a:u32;
      a=2;
      var b:f16=1.0;
      
      wgsl类型推断
      var c=20;
      
      两个变量进行运算,需要保持一样的数据类型
      
      
      函数
      fn add(x:f32,y:32)->f32{
       return x+y;
      }
      
      if语句
      var a:f32=2.0;
      if(a>10.0){
      }
      
      for语句
      var n:u32=10;
      for(var i:u32=0;i<n;i++){
       s+=0.05;
      }
      
      
      向量表示颜色
      四维向量有四个分量,可以用来表示颜色的RGBA
      var color:vec4<f32>=vec4<f32>(1.0,0.0,0.0,1.0);//红色不透明
      
      向量表示位置
      三维向量vec3<f32>表示具有三个分量,可以用来表示xyz坐标
      var pos:vec3<f32>;
      pos=vec3<f32>(1.0,2.0,3.0);
      等价于vec4<f32>(1.0,2.0,3.0,1.0);
      三维向量转四维向量
      var pos2=vec4<f32>(pos,1.0)
      二维向量转四维向量
      var pos2:vec2<f32>(1.0,2.0);
      var pos4:vec4<f32>(pos2,3.0,4.0);
      
      
      
      结构体
      struct pointLight{
       color:vec3<f32>,
       intensity:f32
      }
      
      var light1:pointLight;
      light1.color=vec3<f32>(1.0,0.0,0.0);
     light1.intensity=0.6;

颜色缓冲区

通过WebGPU渲染管线各个功能处理后,会得到图形的片元数据,或者说像素数据,这些像素数据,会存储到显卡内存颜色缓冲区中。
你可以类比顶点缓冲区和理解颜色缓冲区,顶点缓冲区的功能是存储顶点数据,颜色缓冲区 的功能是存储渲染管线输出的像素数据。
颜色缓冲区和顶点缓冲区类似,可以创建,不过有一个比较特殊,就是canvas画布对应一个默认的颜色缓冲区,可以直接使用。
如果你希望webgpu绘制的图形,呈现在canvas画布上,就要把绘制的结果输出到canvas画布对应的颜色缓冲区中。
重点:其实存储就是片元着色器处理生成的一个个片元,只不过这个缓冲区不需要创建,有默认的
如果不想让颜色显示出来,则可以手动创建一个颜色缓冲区,把片元着色器生成的片元数据存储在自己创建的颜色缓冲区中

2.png

webgpu坐标系

坐标原点在canvas画布的中间位置,x轴向右,y轴向上,z轴垂直画布向屏幕内;x和y的范围是[-1,1],z是[0,1]

对于webgpu坐标系,图形学中称为标准化设备坐标系(简称NDC),因为坐标范围是-1~10~1的相对值,所以把NDC称为归一化设备坐标系也行。

webgpu默认(注意是默认)的渲染规律是,渲染的xyz如果超出了范围(x和y的范围是[-1,1],z是[0,1]),会被裁剪

那么默认情况下,WebGPU会如何渲染上面顶点坐标定义的三角形?

为了更好理解,假设在WebGPU的3D空间中,存在一束平行光线,沿着z轴照射到XOY平面(实际上就是x0y)上,这时候3D空间中的三角形会在XOY平面上产生投影,就像生活中,人在太阳光 下,会地面上产生投影。
这时候,z轴上的任何顶点,投影后,其实都在坐标原点,这样上面一个等边三角形,三个点投影后,就是两个点在x和y轴, z轴上的点投影到坐标原点,这样三个点连接起来,渲染的投影结果就是一个直角三角形。

矩阵补充

3.png
4.png

上图中坐标平移的实现;内部的一些过程可以说明,为什么有齐次坐标而这些变换(平移旋转缩放)又为什么用矩阵计算

旋转矩阵

绕x轴旋转.png
绕y轴旋转.png
绕z轴旋转.png

模型矩阵

在图形学中经常会提到模型矩阵的概念,其实模型矩阵就是平移/缩放/旋转矩阵的统称;或者说是平移/缩放/旋转矩阵相乘得到的复合矩阵

几何变换顺序对结果的影响?

假设一个顶点原始坐标(2,0,0)。
先平移2、后缩放10:如果先沿着x轴平移2,变为(4,0,0),再x轴方向缩放10倍,最终坐标是(40,0,0)。
先缩放10、后平移2:如果先x轴方向缩放10倍,变为(20,0,0),再沿着x轴平移2,最终坐标 是(22,0,0)。
可以发现上面同样的平移和缩放,顺序不同,变换后的顶点坐标也不相同。

以矩阵乘法不符合一般乘法的(交换律)

5.png
6.png
7.png

模型矩阵:先计算所有几何变化对应的矩阵的乘积,得到一个模型矩阵,再对顶点坐标(转换为齐次坐标)进行变换。


8.png

一般不推荐每次变换都和顶点坐标进行计算;但是这种计算的话,矩阵和齐次坐标的计算靠近顺序参考图9

9.png

单位矩阵

10.png

11.png

计算开源库gl-matrix

webgpu计算理论上不需要自己编写算法,可以借助于开源库 gl-matrix,提供了矩阵,向量,四元数等等与图形学相关的数学计算函数

12.png

[补充视频]https://www.bilibili.com/video/BV11M41137UH?p=12&vd_source=1fe868f5f3b4b37f6561498b82c13364

基础案例

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <canvas id="webgpu" width="500" height="500"></canvas>
</body>

</html>
<script type="module">
    import { vertex, fragment } from "./shader.js";
    //浏览器请求gpu适配器
    const adapter = await navigator.gpu.requestAdapter();
    //获取gpu设备对象,通过gpu设备对象device的webgpu api可以控制gpu渲染过程
    const device = await adapter.requestDevice();
    const canvas = document.getElementById('webgpu');
    const ctx = canvas.getContext('webgpu');
    const format = navigator.gpu.getPreferredCanvasFormat();//获取浏览器默认的颜色格式
    ctx.configure({
        device,  //webgpu渲染器使用的gpu设备对象
        format
    });

    //类数组
    const vertextArray = new Float32Array([
        0.0, 0.0, 0.0,//顶点1坐标
        1.0, 0.0, 0.0,
        0.0, 1.0, 0.0
    ])

    //渲染一个物体,需要先通过顶点坐标来定义该物体的几何形状
    //通过顶点缓冲区创建顶点数据
    const vertexBuffer = device.createBuffer({
        size: vertextArray.byteLength,//根据字节长度,此时是4*9=36字节
        //缓冲区用途:作为顶点缓冲区|可以写入顶点数据
        usage: GPUBufferUsage.VERTEXT | GPUBufferUsage.COPY_DST
    });
    /**
     * usage的属性值可以是下面两种
     * GPUBufferUsage.VERTEXT   :  表示用于该缓冲区是顶点缓冲区,就算存储顶点数据的缓冲区
     * GPUBufferUsage.COPY_DST  :  表示该缓冲区可以写入顶点数据,作为复制顶点数据的目的地
     **/


    //顶点数据写入顶点缓冲区:0 表示从vertextArray的数据开头开始读取数据
    device.queue.writeBuffer(vertexBuffer, 0, vertextArray)

    //创建渲染管线
    //渲染管线类似于工厂流水线,流水线上不同的功能单元,完成不同的零部件生产,渲染管线上面提供用于3D渲染的不同功能单元
    //根据需要可以创建多个渲染管线
    const pipeline = device.createRenderPipeline({
        layout: 'auto',//默认这么写即可
        vertex: {
            //顶点着色器
            buffer: [//顶点所有的缓冲区模块设置
                {//其中一个顶点缓冲区的设置
                    arrayStride: 3 * 4,//一个顶点的字节长度
                    attributes: [{//顶点缓冲区属性
                        shaderLocation: 0,//GPU显存上顶点缓冲区标记存储位置,例如,当前buffer内部可以是多个缓冲区,此处是标记
                        format: "float32x3",//表示一个顶点包含3个浮点数
                        offset: 0,//表示arrayStride每组顶点数据间隔字节数
                    }]

                }
            ],
            module: device.createShaderModule({ code: vertex }),//顶点着色器代码
            entryPoint: "main",//指定着色器代码的入口函数
        },
        fragment: {
            //片元着色器
            module: device.createShaderModule({ code: fragment }),
            entryPoint: "main",
            targets: [
                {
                    format,//和webgpu上下文配置的颜色格式保持一致
                }
            ]
        },
        //这个和图元装配相关,指定绘制的形式
        primitive: {
            //绘制三角形,线条,点
            topology: "triangle-list",//三角形绘制顶点数据,如果顶点数超过三个,则三个为一组画一个三角形

            //line-strip :多个定点依次连线(不闭合)
            //point-list : 每个顶点坐标对应位置渲染一个小点
        }
    });

    //创建命令编码器对象:commandEncoder可以控制渲染管线pipeline渲染输出像素数据
    const commandEncoder = device.createCommandEncoder();
    //创建渲染通道对象
    //之前的webgpu  api默认不会执行,还需要配置GPU命令编码器对象commandEncoder实现
    //beginRenderPass中参数很多,例如colorAttachments(颜色附件),depthStencilAttachment(深度/模板附件)
    //colorAttachments属性就和颜色缓冲区有关,属性值是数组,数组中元素是对象,每个对象和一个颜色缓冲区相关,每个对象具有view,loadOp,storeOp,clearValue等属性
    //需要把渲染管线的像素数据存储到多个颜色缓冲区时候才需要colorAttachments设置多个元素对象,一般设置一个即可
    const renderPass = commandEncoder.beginRenderPass({
        colorAttachments:[
            {
                //指向用于canvas画布的纹理视图对象(canvas对应的颜色缓冲区)
                //该渲染通道renderPass输出的像素数据会存储到canvas画布对应的颜色缓冲区中
                view:ctx.getCurrentTexture().createView(),
                storeOp:'store',//像素数据写入颜色缓冲区
                loadOp:'clear',
                clearValue:{r:0.5,g:0.5,b:0.5,a:1.0},//背景色

            }
        ]
    });
    //设置该渲染通道对应的渲染管线
    //可以根据需要创建多个渲染通道,每个渲染通道都可以控制自己对应的渲染管线输出对象
    renderPass.setPipeline(pipeline);
    //顶点缓冲区数据和渲染管线shaderLocation:0 bioassay存储位置关联起来
    renderPass.setVertexBuffer(0,vertexBuffer);
    //绘制顶点数据
    renderPass.draw(3);//3是因为上面只有三个点
    renderPass.end();//结束命令
    //darw,createRenderPipeline等webgpu的api不能直接在GPU上面运行,需要转换为GPU指令(命令),才能控制GPU运转
    //命令编码器.finish()创建命令缓冲区(生成GPU指令存入GPU命令缓冲区)
    const commandBuffer=commandEncoder.finish();//返回一个命令缓冲区对象,同时编码api为GPU指令存入缓冲区
    //命令编码器缓冲区中命令传入GPU设置对象的命令队列queue
    device.queue.submit([commandBuffer]);//之所以是数组,因为命令缓冲区可能是多组
</script>

引入的wgsl代码文件,此处使用js导出字符串形式

//@vertex表示字符串里面的代码是顶点着色器代码,在GPU渲染管线的顶点着色器单元上执行
//fn声明一个函数,命令为main,作为顶点着色器代码的入口函数

//location是wgsl语言的一个关键字,通用用来指定顶点缓冲区相关的顶点数据,使用location时候需要加上@前缀,@location()
//小括号里面设置参数
//@location(0) 表示GPU显存中标记为0(shaderLocation)的顶点缓冲区中的顶点数据

//wgsl顶点着色器代码中,很多时候用四维向量vec4表示顶点的位置坐标,vec4第四个分量一般默认1.0
const vertex =/*wgsl*/`
@vertex
fn main(@location(0) pos:vec3<f32>)->@builtin(position) vec4<f32>{
    var pos2=vec4<f32>(pos,1.0);//pos转齐次坐标
    pos2.x-=0.2;//偏移所有顶点的x坐标,而不只是一个点,区别去js的语法
    //return返回之后,渲染管线的下一个环节就可以使用了
    return pos2;
}
`

//内置关键字
//position是wgsl语言的一个内置变量,表示顶点数据
//builtin,表示和其配合使用的是内置变量(例如position)
//使用时候前面要加@ 例如:@builtin(position);一般用于返回值类型,表示通知下一个环节,返回值是顶点位置坐标数据


//通常片元着色器输出的片元像素数据,会存储在显卡内存上,location(0)含义简单理解为输出的片元数据存储到显卡内存上
//并把存储位置标记为0(存储到0,然后供后续使用),用于渲染管线的后续操作和处理;也就是说此处的location(0)和顶点着色器中的location(0)不相干
const fragment=/*wgsl*/`
@fragment
fn main()->location(0) vec4<f32>{
    return vec4<f32>(1.0,0.0,0.0,1.0);//片元设置为红色
}
`;
export {vertex,fragment}

为什么webgpu使用矩阵?

首先,3D的旋转平移缩放操作可以通过矩阵的乘法实现计算,不需要特殊的处理;齐次,gpu可以进行矩阵的并行计算,虽然单个计算速度小于cpu,但是总体速度更快。

比较 GPU 和 CPU ,就是比较它们两者如何处理任务。CPU 使用几个核心处理单元去优化串行顺序任务,而 GPU 的大规模并行架构拥有数以千计的更小、更高效的处理单元,用于处理多个并行小任务。

CPU 拥有复杂的系统指令,能够进行复杂的任务操作和调度,两者是互补关系,而不能相互代替。

GPU 是大规模并行架构,处理并行任务毫无疑问是非常快的,深度学习需要高效的矩阵操作和大量的卷积操作, GPU 的并行架构再适合不过。简单来说,确实如此,但是为什么 GPU 进行矩阵操作和卷积操作会比 CPU 要快呢?

真正原因是 GPU具有如下特性 :
(1) 高带宽
(2) 高速的缓存性能
(3) 并行单元多

在执行多任务时, CPU 需要等待带宽,而 GPU 能够优化带宽。

举个简单的例子,我们可以把 CPU 看作跑车, GPU 是大卡车,任务就是要把一堆货物从北京搬运到广州。 CPU(跑车〉可以快速地把数据(货物〉从内存读入 RAM 中,然而 GPU (大卡车〉装货的速度就好慢了。不过后面才是重点, CPU (跑车)把这堆数据(货物)从北京搬运到广州|需要来回操作很多次,也就是往返京广线很多次,而 GPU (大卡车)只需要一 次就可以完成搬运(一次可以装载大量数据进入内存)。换言之, CPU 擅长操作小的内存块,而 GPU 则擅长操作大的内存块 。 CPU 集群大概可以达到 50GB/s 的带宽总量,而等量的 GPU 集群可以达到 750GB/s 的带宽量。

如果让一辆大卡车去装载很多堆货物,就要等待很长的时间了,因为要等待大卡车从北京运到广州,然后再回来装货物。设想一下,我们现在拥有了跑车车队和卡车车队(线程并行〉,运载一堆货物(非常大块的内存数据需要读入缓存,如大型矩阵)。我们会等待第一辆卡车,但是后面就不需要等待的时间了,因为在广州会有一队伍的大卡车正在排队输送货物(数据),这时处理器就可以直接从缓存中读取数据了。在线性并行的情况下, GPU 可以提供高带宽,从而隐藏延迟时间。这也就是GPU 比 CPU 更适合处理深度学习的原因。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容