WebGL 是 Web 3D 渲染引擎的基础,它作为非常底层的 API,学习上手难度非常大,这是因为 WebGL 要求的背景知识比较多。而网上的教程一般没有过多介绍直接就介绍 API 开始渲染了,容易让人云里雾里,很容易被劝退,就算学到了 API 使用,也是只懂表面知识,没有了解背后的原理,很容易就忘记了。
《从 0 实现 3D 渲染引擎》系列教程将从最基本知识开始,渐进的讲解 WebGL 使用和 WebGL 背后原理还有必不可少的数学知识,真正的从 0 开始,只要了解 JS 就行,不需要要任何图形学或者数学基础。学完之后除了能够自己从 0 实现自己的 3D 渲染引擎还能熟悉 three.js 的源码,因为本系列文章实现的 3D 渲染引擎和 three.js 很相似。
什么是 WebGL?
WebGL(Web Graphics Library)是一个 Web 标准 JavaScript API,通过 HTML5 的 canvas 元素进行暴露,无需使用插件,即可在浏览器中渲染高性能的交互式 3D 和 2D 图形。目前是由非营利 Khronos Group 设计和维护。
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 基于 OpenGL,是 OpenGL 的子集。WebGL1 基于 OpenGL ES 2.0。WebGL2 基于 OpenGL ES 3.0。
GPU
WebGL 性能高的原因是它使用到了 GPU。GPU 和 CPU 针对的是两种不同的应用场景,大家可以把 CPU 想象为一个切图专家,而 GPU 是一群初级切图仔,现在有一大堆非常简单的页面,大街上随便抓个人都能切。那么对于这个任务不用想就知道一群初级切图仔更快,切图专家当然厉害,但是也奈不了对面人多。所以对于大量简单计算 GPU 的执行速度是远大于 CPU 的。
WebGL 是 Web 3D 渲染引擎的基础,它作为非常底层的 API,学习上手难度非常大,这是因为 WebGL 要求的背景知识比较多。而网上的教程一般没有过多介绍直接就介绍 API 开始渲染了,容易让人云里雾里,很容易被劝退,就算学到了 API 使用,也是只懂表面知识,没有了解背后的原理,很容易就忘记了。
《从 0 实现 3D 渲染引擎》系列教程将从最基本知识开始,渐进的讲解 WebGL 使用和 WebGL 背后原理还有必不可少的数学知识,真正的从 0 开始,只要了解 JS 就行,不需要要任何图形学或者数学基础。学完之后除了能够自己从 0 实现自己的 3D 渲染引擎还能熟悉 three.js 的源码,因为本系列文章实现的 3D 渲染引擎和 three.js 很相似。
什么是 WebGL?
WebGL(Web Graphics Library)是一个 Web 标准 JavaScript API,通过 HTML5 的 canvas 元素进行暴露,无需使用插件,即可在浏览器中渲染高性能的交互式 3D 和 2D 图形。目前是由非营利 Khronos Group 设计和维护。
使用 WebGL 的方式和 canvas 2d 类似,都是通过
getContext
方法获取渲染上下文,如下所示。上面代码中是按照
webgl2
、webgl
、experimental-webgl
的顺序获取 WebGL 渲染上下文。webgl2
是最新版本,它几乎完全兼容 WebGL1。experimental-webgl
用来兼容老浏览器,如 IE 11。兼容性
大多数浏览器都支持 WebGL1。也有很多现代浏览器支持 WebGL2,但是苹果还不支持 WebGL2,所以编写 WebGL 程序时,需要向下降级到 WebGL1。
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 基于 OpenGL,是 OpenGL 的子集。WebGL1 基于 OpenGL ES 2.0。WebGL2 基于 OpenGL ES 3.0。
GPU
WebGL 性能高的原因是它使用到了 GPU。GPU 和 CPU 针对的是两种不同的应用场景,大家可以把 CPU 想象为一个切图专家,而 GPU 是一群初级切图仔,现在有一大堆非常简单的页面,大街上随便抓个人都能切。那么对于这个任务不用想就知道一群初级切图仔更快,切图专家当然厉害,但是也奈不了对面人多。所以对于大量简单计算 GPU 的执行速度是远大于 CPU 的。
上面图片中,第一个是 CPU,第二是 GPU,CPU 只有一杆枪,而 GPU 有一大捆枪。CPU 要一下一下的打,就像切图专家一个一个的切,而 GPU 一次性全打了,就像一群初级切图仔,没人切一个,一次性全切完了。
上图是显卡 3090 的配置参数,我们可以看到它有 1 万多个核心,24G 显存。支持 3D API,DirectX 12 Ultimate 和 OpenGL 4.6 (DirectX 是微软的图形 API)。
坐标系
WebGL 的坐标系和 canvas 2d 中的是不太一样的。因为 WebGL 是 OpenGL 子集,所以 WebGL 坐标系和 OpenGL 坐标系性值一样。
canvas 2d 中的坐标系如下所示。
canvas 2d 的坐标原点在左上角,X 轴和 Y 轴的正值分别向右和向下。
而 WebGL 的坐标系和 OpenGL 一样,它更符合我们的常识一点。
原点在正中间,右边为 X 轴正方向,上面为 Y 轴正方向,就和数学中的一样。
需要注意的是 WebGL 中坐标值的范围是
-1
到1
,而 canvas 2d 是根据 canvas 的宽高大小来的。如果 canvas 宽度为500
,那么 WebGL 中的1
就相当于500
,0.5
就相当于250
,这样的好处是我们无需关心 canvas 的宽高,无论 canvas 多大对于渲染图形来说范围都是-1
到1
。当然 WebGL 中还有一个 Z 轴。Z 轴有两种形式,一种是正值朝外,另一种是正值朝内。
当 Z 轴正值朝外时,我们称为右手坐标系,当 Z 轴正值朝内时称为左手坐标系。可以伸出双手像下图一样比划下,就知道为什么称为左手坐标系和右手坐标系了。
那么 WebGL 是左手坐标系还是右手坐标系呢?答案为都不是。但是在实际开发中是使用 右手坐标系,当然并不是右手坐标系比左手坐标系好,而是右手坐标系是 OpenGL 的惯例。例如微软的 DirectX 中惯用的是左手坐标系。
这里为什么说 WebGL 既不是左手坐标系也不是右手坐标系,原因将在后续文章中讲解,现在只用知道 WebGL 中使用的是右手坐标系,也就是 Z 轴正值朝外。
三角形
WebGL 算是比较底层的图形 API,不同于 canvas 2d,WebGL 只能用它来渲染点,线和三角形。那些复杂的 3D 模型其实都是由一个个三角形组成。
比如上方这辆汽车模型,它其实是由 267300 个三角形组成。
点击这个链接查看模型详情https://sketchfab.com/3d-models/the-argonaut-4982efe9a03e42e6a867c33afd863ca5 。
可能有同学会问了,为什么就是三角形,而不是 5 边形,6 边形呢?
因为三角形有很多的优势,比如三角形一定在一个平面上,任何多边形都可以使用三角形组成等性值。
渲染一个三角形
了解了这么多背景知识,现在让我们来实际使用 WebGL 来渲染一个最简单的三角形吧。
渲染结果如下所示。
代码片段
可以发现 WebGL 的代码非常复杂繁琐,一个非常简单的三角形就需要编写这么多的代码。
上面实例代码中有详细的注释,不过相信大家看了也还是满头问号。我们再来看看 WebGL 渲染的整个流程,一般 WebGL 程序是 JS 提供数据(在 CPU 中运行),然后将数据发送到显存中,交给 GPU 渲染,我们可以使用着色器控制 GPU 渲染管线部分阶段。
上面的伪代码,简单展示了 WebGL 程序的执行流程。OpenGL 中着色器是使用 GLSL 编写,WebGL 中也是使用的 GLSL 着色器语言,它的语法有点类似 C 语言,我们可以通过顶点着色器和片段着色器控制 GPU 渲染的部分环节。
WebGL 中有两个着色器分别是顶点着色器和片段(也可称为“片元”)着色器。顶点着色器用于处理图形的每个点,也就是上面例子中三角形的三个顶点。处理完毕后会进行光栅化,大家可以把光栅化理解成把图形变成一个个像素,我们显示器屏幕是一个个像素组成的,要显示图形就需要计算出图形中的每个像素点。片段着色器可以先理解成像素着色器,也就是将光栅化中的每个像素拿过来,给每个像素计算一个颜色。整个流程如下所示。
上图中顶点数据传送给 GPU 后,顶点着色器计算出每个点的位置,光栅化计算出图形的每个像素,片段着色器计算出每个像素的颜色,然后就可以渲染到显示器上了。(可以忽略上图的几何着色器,WebGL 中没有这个着色器)着色器先简单介绍到这里,还不了解着色器也没有关系,下篇文章会更加详细的讲解。
其实 WebGL 是一个非常大的状态机,它提供的方法都是改变 WebGL 的某个状态。我们需要在 CPU 中使用 JS 设置 WebGL 的状态,准备数据和着色器程序,然后发送给 GPU 执行。
上方代码可以分为如下几步。
例子
上面这个简单的三角形一点都不炫酷,其实 WebGL 可以做出非常炫酷的效果,下面列举一些不错例子,大家感兴趣可以看一看。
ThreeJS
https://threejs.org/
WebGL Samples
http://webglsamples.org/
Experiments with Google
https://experiments.withgoogle.com/
Adult Swim
https://www.adultswim.com/etcetera/
Evan Wallace
http://madebyevan.com/
总结
这篇文章讲解了什么是 WebGL,了解了 WebGL 的大致轮廓,并且完成了一个最简单的 WebGL 应用。