目前 WebGPU 的标准还没有完全确定下来,需要下载开发者版本的 Chrome Canary 才能开启 WebGPU。(目前正式版中 Chrome 96 / 97 其实已经支持 WebGPU了,但并不是完全支持,98 (据说)会正式支持 WebGPU)。
WebGL 与 WebGPU
WebGL 的基础是 OpenGL。OpenGL 的初始版本可以追溯到 1992 年,整个 OpenGL 的设计是基于状态机模型。状态驱动的 OpenGL 难以利用今天 GPU 并行的特点。对于今天的多核计算的设备,使用 OpenGL 非常难以发挥机器的全部计算能力。WebGL 基于 OpenGL,这些 OpenGL 的问题也继承到了 WebGL了。
除此之外,OpenGL 也被各大厂商抛弃。微软的 Windows 有 Direct X,Apple 则提出了 Metal,并且直接不支持新版本的 OpenGL 4,科纳斯则提出了“次世代 OpenGL“ 的 Vulkan。这些新的图形编程语言的特点都是为了适应多核并行的高性能计算。
当然,复杂度也要比 OpenGL 更高。如果学习过 DirectX 12(支持多线程)和 DirectX 11,应该很清楚,DirectX 12 比 11 复杂多了。
需要注意的是,WebGPU 和 WebGL 并没有关系,它不需要显式地依赖 OpenGL ES。从某种意义上,wgsl 也是一门新的语言,然后经由浏览器翻译成更加底层的实现,至于底层是 Vulkan、Metal 或者是 Direct X 等等,则已经不是重点了。
由于 WebGPU 的入口是浏览器,它的标准会尽可能的大众化,也就是不能对设备要求过高,否则这个标准的通用程度也会是一个很大的问题。不可能要求每个需要使用 WebGPU 的用户的 GPU 都是 RTX 3090。可以理解,这也导致标准的复杂性会相应增加。
WebGPU 基本概念
GPUAdapter
:WebGPU 中将物理的 GPU 硬件视为GPUAdapter
GPUDevice
:管理资源(它可能有自己处理单元的显存)GPUQueue
:执行 GPU 命令的队列GPUBuffer
(缓冲区)和GPUTexture
(纹理):在 GPU 内存(显存)中烘焙的物理资源GPUCommandBuffer
和GPURenderBundle
是用户记录命令的容器GPUShaderModule
包含着色器代码。GPUSampler
或GPUBindGroup
,配置 GPU 使用其它物理资源的方式
安全性
和 Web 一样,会保证是当前页面数据。由于通过浏览器翻译成底层的 GPU 语言,在翻译阶段会严格限定指令集并进行验证,避免出现未定义行为。
还有其他的针对安全问题的考虑,暂时不讨论
图形学基础概念
如果没有清楚一些基础概念,无论是学 WebGL 还是 WebGPU 都是无法深入下去的。(由于篇幅有限,这里只是简单概述一下,后续需要具体用到的概念再展开)
图形系统
计算机图形系统也是一个计算机系统,因此它肯定也包含了一个通用计算机的所有部件。图形系统包括以下 6 个部分:
- 输入设备(鼠标、键盘、手绘板…)
- 中央处理单元(CPU)
- 图形处理单元(GPU)
- 存储器(包括常说的内存、显存)
- 帧缓冲区(比如前后缓冲区)
- 输出设备(显示器、投影等等)
这是一个通用的模型,从手机到计算机都可以用这个模型。
像素和帧缓存
现代所有图形系统都是基于光栅的。输出设备看到的图像是由图形系统产生的图形元素组成的序列:
- 图形元素也叫做像素(pixel)
- 像素阵列称为光栅(raster)
- 帧缓冲区(framebuffer)中的像素称为分辨率(resolution),决定了图像中可以分辨出多少细节
- 帧缓冲区的深度(depth)或者精度(precision)表示的是像素所用的比特数,深度为 1 的帧缓冲区只能有 2 1 2^1 21 = 2 种颜色,而深度为 8 的帧缓冲区可以表示 256( 2 8 2^8 28) 种颜色。
- 全彩(full-color)显示中,每个像素 24 比特(现在基本上都有 32 比特)。这样系统可以逼真地表示大多数图像,也被称为真彩色(true-color)系统,或者 RGB 系统。
- 高动态范围(High Dynamic Range,HDR)给每一个颜色分配了 12 或者更多的比特位。
- 现在,帧缓冲区已经支持用浮点数表示颜色值,可以更好的支持 HDR
在简单的图形系统中,帧缓冲区只存储屏幕上要显示的像素的颜色值。但在目前的图形系统中,帧缓冲区存储的信息其实非常多,比如还有生成 3D 图像所需要的深度信息等等。
输出设备
计算显示设备的历史可以追溯到阴极射线管(Cathode Ray Tube,CRT)。二十多年前那种带着长长尾巴的电视机就是基于这种原理。
![](https://img-blog.csdnimg.cn/df6d218b0dea4b10916c2fd0eeabc4bc.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA6LC36Zuo44Gu5qKm,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
CRT 因为电子轰击涂在管子上的磷光物质而发光。电子束的方向由两对偏转板控制。计算机的输出由数模转换器转换成 x 偏转板和 y 偏转板上的电压值。当强度足够大的电子束轰击到磷光物质上,CRT 屏幕就会发光。
如果控制电子束偏转的电压以恒定的速率改变,那么电子束就会扫过一条直线轨迹。这样的设备称为随机扫描(random-scan)显示器。
磷光物质被电子束激发以后,典型的 CRT 的发光只能持续很短的时间,一般是几毫秒。为了让人看到稳定的、不闪烁的图像,电子束必须以足够高的速率重复扫描相同的路径,这就是刷新(refresh),刷新的速率叫做刷新率。在早期的系统中,刷新的速率由电源的(交流电)频率决定,例如美国是 60 Hz,而其他大多数地方是 50 Hz。当然,目前显示器已经远远不止 60 Hz 了,例如 iPad Pro 可以 120 Hz,一些专业的游戏笔记本可以达到 144 Hz。
- 逐行扫描:像素按照刷新率一行一行地显示
- 隔行扫描:奇数行和偶数行交替刷新
- 60 Hz 的隔行扫描,每秒钟屏幕被完整刷新只有 30 次
后面,CRT 就被平板显示技术取代了。平板显示器也是基于光栅原理,虽然平板的具体物理实现可能不同,比如是发光二极管(LED)、液晶显示器(LCD)、等离子显示屏等,但都使用了二维栅格来寻址每个单独的发光元件。
- LED:由传递到栅格上的电信号控制开启和关闭;
- LCD:电场改变液晶的排列方式,可以控制是否阻挡光线通过;
- 等离子显示器:通过激发玻璃板的中的气体,获得能量的气体变成发光的等离子体;
3D 显示则利用交替刷新周期在左眼和右眼看到图像进行切换显示。观众需要佩戴满足一定刷新周期的特制眼镜。3D 电影放映机则会产生具有不同偏振方向的两个图像(通常称为左右眼),观众佩戴偏振光眼镜,这样每个眼睛只看到两个图像的一个,经过大脑的视觉合成,从而形成 3D 效果。
光学成像模型
因为这里不打算讲物理学(虽然后面如果深入了解光和材质的话,像光度学的概念还是要懂的)。这里一笔带过,现实世界中,我们作为观察者看到的图像是由于环境中光源投射到物体上,然后物体反射到我们的眼睛(当然,还有光源直射你的眼睛、包括物体本身的漫反射等等)。
物体(对象)的材质不同,对光的反射和折射也会不同(目前计算机图形学都是基于几何光学,至于波动光学(诸如衍射、偏振等)是不用于计算机图形学的)。所以材质代表了对光处理模型,例如是完全反射回去(理想镜子),还是会在内部进行不断的散射(蜡烛的次表面散射模型)。
三维绘图的 API
目前计算机三维模型使用的虚拟照相机模型,按照这个模型,API 就需要有下面四种类型的函数:
- 对象
- 观察者(也就是视点、相机)
- 光源
- 材质
流水线体系结构
图形绘制系统的体系结果经过多次反复,但无论如何演变,流水线结构的重要性依然不变。其他的图形绘制方法,包括光线跟踪、辐射度方法和光子映射都不能获得实时的行为。
而现在,流水线体系结构中,顶点处理模块和片元处理模块是可以编程的。
- 顶点着色器可以在顶点经过流水线的时候修改每个顶点的位置或者颜色。这样就可以实现许多光线-材质模型或者创建新型的投影。
- 片元着色器可以用许多新的方式来使用纹理,也可以实现基于每个片元的光照而不是每个顶点的光照。
WebGL 的三角形
初始化着色器
在 WebGL 中,首先需要创建顶点着色器和片段着色器,并且绑定和编译对应的着色器程序。在这之后,需要将这两个着色器程序链接成为一个着色器。
function initShader(gl, vertexShader, fragShader) {
// 创建顶点着色器
const v_shader = gl.createShader(gl.VERTEX_SHADER);
// 绑定顶点着色器代码
gl.shaderSource(v_shader, vertexShader);
// 编译顶点着色器
gl.compileShader(v_shader);
// 创建片段着色器
const f_shader = gl.createShader(gl.FRAGMENT_SHADER)