WebGL 作为十分底层的 API,学习上手难度十分大,这是由于 WebGL 要求的布景常识比较多。而网上的教程一般没有过多介绍直接就介绍 API 开端烘托了,容易让人云里雾里,很容易被劝退,就算学到了 API 运用,也是只懂表面常识,没有了解背面的原理,很容易就忘记了。

《零根底玩转 WebGL》系列教程将从最基本常识开端,渐进的解说 WebGL 运用和 WebGL 背面原理还有必不可少的数学常识。让你学完之后不光能够开发 WebGL 运用,还能轻松阅览烘托引擎源码,如 Three.js,而且能够开发自己的 3D 烘托引擎库。

这是《零根底玩转 WebGL》的榜首篇文章,系列文章目录请检查:零根底玩转 WebGL – 目录

什么是 WebGL?

WebGL(Web Graphics Library)是一个 Web 规范 JavaScript API,经过 HTML5 的 canvas 元素进行暴露,无需运用插件,即可在浏览器中烘托高性能的交互式 3D 和 2D 图形。目前是由非营利 Khronos Group 设计和保护。

运用 WebGL 的方式和 canvas 2d 相似,都是经过 getContext 办法获取烘托上下文,如下所示。

const canvas = document.createElement('canvas')
const gl = (
  canvas.getContext('webgl2') ||
  canvas.getContext('webgl') ||
  canvas.getContext('experimental-webgl')
)

上面代码中是按照 webgl2webglexperimental-webgl 的顺序获取 WebGL 烘托上下文。webgl2 是最新版本,它简直彻底兼容 WebGL1。experimental-webgl 用来兼容老浏览器,如 IE 11。

兼容性

零根底玩转 WebGL - 快速上手

大多数浏览器都支撑 WebGL1。也有许多现代浏览器支撑 WebGL2,可是苹果还不支撑 WebGL2,所以编写 WebGL 程序时,需求向下降级到 WebGL1。

零根底玩转 WebGL - 快速上手

OpenGL

在深入 WebGL 之前,咱们还需求先了解 OpenGL,由于 WebGL 是依据 OpenGL 的。OpenGL(Open Graphics Library) 是用于烘托2D、3D矢量图形的跨言语、跨渠道的运用程序编程接口,常用于CAD、虚拟现实、科学可视化程序和电子游戏开发。OpenGL 通常是显卡生产商依据规范来实现的。

OpenGL 前身是 SGI 的 IRIS GL API 它在其时被认为是最先进的科技并成为事实上的行业规范,后由 SGI 转变为一项敞开规范 OpenGL。1992年 SGI 创立 OpenGL架构审查委员会,2006年将 OpenGL API 规范的操控权交给 Khronos Group。

OpenGL 是跨渠道的,在移动设备上是运用 OpenGL ES(OpenGL for Embedded Systems), 它是 OpenGL 的子集。下图展现了 OpenGL 和 OpenGL ES 的时间线。

零根底玩转 WebGL - 快速上手

WebGL 依据 OpenGL,是 OpenGL 的子集。WebGL1 依据 OpenGL ES 2.0。WebGL2 依据 OpenGL ES 3.0。

GPU

WebGL 性能高的原因是它运用到了 GPU。GPU 和 CPU 针对的是两种不同的运用场景,咱们能够把 CPU 想象为一个切图专家,而 GPU 是一群初级切图仔,现在有一大堆十分简略的页面,大街上随意抓个人都能切。那么对于这个任务不用想就知道一群初级切图仔更快,切图专家当然厉害,可是也奈不了对面人多。所以对于许多简略核算 GPU 的执行速度是远大于 CPU 的。

零根底玩转 WebGL - 快速上手

零根底玩转 WebGL - 快速上手

上面图片中,榜首个是 CPU,第二是 GPU,CPU 只要一杆枪,而 GPU 有一大捆枪。CPU 要一下一下的打,就像切图专家一个一个的切,而 GPU 一次性全打了,就像一群初级切图仔,没人切一个,一次性全切完了。

零根底玩转 WebGL - 快速上手

上图是显卡 3090 的装备参数,咱们能够看到它有 1 万多个中心,24G 显存。支撑 3D API,DirectX 12 Ultimate 和 OpenGL 4.6 (DirectX 是微软的图形 API)。

坐标系

WebGL 的坐标系和 canvas 2d 中的是不太相同的。由于 WebGL 是 OpenGL 子集,所以 WebGL 坐标系和 OpenGL 坐标系性值相同。

canvas 2d 中的坐标系如下所示。

const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')

零根底玩转 WebGL - 快速上手

canvas 2d 的坐标原点在左上角,X 轴和 Y 轴的正值别离向右和向下。

而 WebGL 的坐标系和 OpenGL 相同,它更符合咱们的常识一点。

零根底玩转 WebGL - 快速上手

原点在正中间,右边为 X 轴正方向,上面为 Y 轴正方向,就和数学中的相同。

需求留意的是 WebGL 中坐标值的规模是 -11,而 canvas 2d 是依据 canvas 的宽高巨细来的。假如 canvas 宽度为 500,那么 WebGL 中的 1 就相当于 5000.5 就相当于 250,这样的好处是咱们无需关怀 canvas 的宽高,无论 canvas 多大对于烘托图形来说规模都是 -11

当然 WebGL 中还有一个 Z 轴。Z 轴有两种形式,一种是正值朝外,另一种是正值朝内。

当 Z 轴正值朝外时,咱们称为右手坐标系,当 Z 轴正值朝内时称为左手坐标系。能够伸出双手像下图相同比划下,就知道为什么称为左手坐标系和右手坐标系了。

零根底玩转 WebGL - 快速上手

那么 WebGL 是左手坐标系仍是右手坐标系呢?答案为都不是。可是在实际开发中是运用 右手坐标系,当然并不是右手坐标系比左手坐标系好,而是右手坐标系是 OpenGL 的常规。例如微软的 DirectX 中惯用的是左手坐标系。

这儿为什么说 WebGL 既不是左手坐标系也不是右手坐标系,原因将在后续文章中解说,现在只用知道 WebGL 中运用的是右手坐标系,也便是 Z 轴正值朝外。

三角形

WebGL 算是比较底层的图形 API,不同于 canvas 2d,WebGL 只能用它来烘托点,线和三角形。那些杂乱的 3D 模型其实都是由一个个三角形组成。

零根底玩转 WebGL - 快速上手

比方上方这辆汽车模型,它其实是由 267300 个三角形组成。

点击这个链接检查模型详情sketchfab.com/3d-models/t…。

可能有同学会问了,为什么便是三角形,而不是 5 边形,6 边形呢?

由于三角形有许多的优势,比方三角形一定在一个平面上,任何多边形都能够运用三角形组成等性值。

烘托一个三角形

了解了这么多布景常识,现在让咱们来实际运用 WebGL 来烘托一个最简略的三角形吧。

const canvas = document.createElement('canvas')
canvas.width = canvas.height = 300
document.body.append(canvas)
const gl = canvas.getContext('webgl')
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height)
// 设置 webgl 视口,将 -1 到 1 映射为 canvas 上的坐标
const vertexShader = gl.createShader(gl.VERTEX_SHADER) // 创立一个极点着色器
gl.shaderSource(vertexShader, `
  attribute vec4 a_position;
  void main() {
    gl_Position = a_position; // 设置极点方位
  }
`) // 编写极点着色器代码
gl.compileShader(vertexShader) // 编译着色器
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER) // 创立一个片元着色器
gl.shaderSource(fragmentShader, `
  precision mediump float;
  uniform vec4 u_color;
  void main() {
    gl_FragColor = u_color; // 设置片元色彩
  }
`) // 编写片元着色器代码
gl.compileShader(fragmentShader) // 编译着色器
const program = gl.createProgram() // 创立一个程序
gl.attachShader(program, vertexShader) // 增加极点着色器
gl.attachShader(program, fragmentShader) // 增加片元着色器
gl.linkProgram(program) // 衔接 program 中的着色器
gl.useProgram(program) // 告诉 webgl 用这个 program 进行烘托
const colorLocation = gl.getUniformLocation(program, 'u_color') // 获取 u_color 变量方位
gl.uniform4f(colorLocation, 0.93, 0, 0.56, 1) // 设置它的值
const positionLocation = gl.getAttribLocation(program, 'a_position') 
// 获取 a_position 方位
const positionBuffer = gl.createBuffer() 
// 创立一个极点缓冲目标,回来其 ID,用来放三角形极点数据,
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer) 
// 将这个极点缓冲目标绑定到 gl.ARRAY_BUFFER
// 后续对 gl.ARRAY_BUFFER 的操作都会映射到这个缓存
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
    0, 0.5,
    0.5, 0,
    -0.5, -0.5
]),  // 三角形的三个极点
     // 由于会将数据发送到 GPU,为了省去数据解析,这儿运用 Float32Array 直接传送数据
gl.STATIC_DRAW // 表明缓冲区的内容不会常常更改
)
// 将极点数据参加的刚刚创立的缓存目标
gl.enableVertexAttribArray(positionLocation);
// 敞开 attribute 变量,使极点着色器能够拜访缓冲区数据
gl.vertexAttribPointer( // 告诉 OpenGL 怎么从 Buffer 中获取数据
    positionLocation, // 极点特点的索引
    2, // 组成数量,有必要是1,2,3或4。咱们只供给了 x 和 y
    gl.FLOAT, // 每个元素的数据类型
    false, // 是否归一化到特定的规模,对 FLOAT 类型数据设置无效
    0, // stride 步长 数组中一行长度,0 表明数据是严密的没有空隙,让OpenGL决议具体步长
    0 // offset 字节偏移量,有必要是类型的字节长度的倍数。
)
gl.clearColor(0, 1, 1, 1) // 设置清空色彩缓冲时的色彩值
gl.clear(gl.COLOR_BUFFER_BIT) // 清空色彩缓冲区,也便是清空画布
gl.drawArrays( // 从数组中绘制图元
    gl.TRIANGLES, // 烘托三角形
    0,  // 从数组中哪个点开端烘托
    3   // 需求用到多少个点,三角形的三个极点
)

烘托结果如下所示。

能够发现 WebGL 的代码十分杂乱繁琐,一个十分简略的三角形就需求编写这么多的代码。

上面实例代码中有具体的注释,不过信任咱们看了也仍是满头问号。咱们再来看看 WebGL 烘托的整个流程,一般 WebGL 程序是 JS 供给数据(在 CPU 中运转),然后将数据发送到显存中,交给 GPU 烘托,咱们能够运用着色器操控 GPU 烘托管线部分阶段。

// CPU
const vertexShader = `shader source code` // 极点着色器代码
const fragmentShader = `shader source code` // 片段着色器代码
const points = [{ x: 1, y: 1, z: 1 }/* ... */]  // 准备数据
gl.draw(points, vertexShader, fragmentShader) // 将数据和着色器发送给 GPU
// GPU
const positions = data.map(point => vertexShader(point)) // 运转极点着色器
const frags = Rasterization(positions) // 光栅化
const colors = frags.map(frag => fragmentShader(frag)) // 运转片段着色器
Display(colors) // 烘托到屏幕

上面的伪代码,简略展现了 WebGL 程序的执行流程。OpenGL 中着色器是运用 GLSL 编写,WebGL 中也是运用的 GLSL 着色器言语,它的语法有点相似 C 言语,咱们能够经过极点着色器和片段着色器操控 GPU 烘托的部分环节。

WebGL 中有两个着色器别离是极点着色器和片段(也可称为“片元”)着色器。极点着色器用于处理图形的每个点,也便是上面比如中三角形的三个极点。处理完毕后会进行光栅化,咱们能够把光栅化了解成把图形变成一个个像素,咱们显示器屏幕是一个个像素组成的,要显示图形就需求核算出图形中的每个像素点。片段着色器能够先了解成像素着色器,也便是将光栅化中的每个像素拿过来,给每个像素核算一个色彩。整个流程如下所示。

零根底玩转 WebGL - 快速上手

上图中极点数据传送给 GPU 后,极点着色器核算出每个点的方位,光栅化核算出图形的每个像素,片段着色器核算出每个像素的色彩,然后就能够烘托到显示器上了。(能够疏忽上图的几许着色器,WebGL 中没有这个着色器)着色器先简略介绍到这儿,还不了解着色器也没有关系,下篇文章会更加具体的解说。

其实 WebGL 是一个十分大的状况机,它供给的办法都是改动 WebGL 的某个状况。咱们需求在 CPU 中运用 JS 设置 WebGL 的状况,准备数据和着色器程序,然后发送给 GPU 执行。

上方代码能够分为如下几步。

  1. 由于 WebGL 的坐标是 -1 到 1,所以首先咱们运用 viewport 设置视口巨细信息。
  2. 创立极点和片段着色器(关于着色器情况下篇文章),然后创立一个程序,来衔接极点和片段着色器。
  3. 然后获取着色器中的变量,设置怎么将值传递给着色器。三角形是由 3 个极点组成,所以准备了 3 个点的坐标。
  4. 设置清屏色彩,并清屏,和坐标相似,WebGL 中的色彩是 0 到 1,而不是 0 到 255。
  5. 将数据发送给 GPU 来烘托三角形

比如

上面这个简略的三角形一点都不炫酷,其实 WebGL 能够做出十分炫酷的作用,下面列举一些不错比如,咱们感兴趣能够看一看。

ThreeJS

零根底玩转 WebGL - 快速上手

threejs.org/

WebGL Samples

零根底玩转 WebGL - 快速上手

webglsamples.org/

Experiments with Google

零根底玩转 WebGL - 快速上手

experiments.withgoogle.com/

Adult Swim

零根底玩转 WebGL - 快速上手

www.adultswim.com/etcetera/

Evan Wallace

零根底玩转 WebGL - 快速上手

madebyevan.com/

总结

这篇文章解说了什么是 WebGL,了解了 WebGL 的大致轮廓,而且完成了一个最简略的 WebGL 运用。下篇文章将具体解说十分重要的 WebGL 着色器。

假如觉得文章还不错欢迎点赞重视支撑,我会尽快更新系列教程的下一篇文章。

零根底玩转 WebGL 系列文章目录请检查:零根底玩转 WebGL – 目录