《WebGL 编程指南》 学习笔记(一):WebGL 入门

6,619 阅读5分钟

样板代码说明

后面的示例入口都是以这个html页面为样板。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Draw a point (1)</title>
  </head>

  <body onload="main()">
    <canvas id="webgl" width="400" height="400">
    Please use a browser that supports "canvas"
    </canvas>

    <script src="../lib/webgl-utils.js"></script>
    <script src="../lib/webgl-debug.js"></script>
    <script src="../lib/cuon-utils.js"></script>
    <script src="HelloPoint1.js"></script>
  </body>
</html>

其中 webgl-utils.jswebgl-debug.jscuon-utils.js 等,都是这本书配套的工具方法。

相关源码我自己做了收藏,可以再这里下载。《WebGL 编程指南》书中涉及的源码

最短的WebGL程序:清空绘图区

function main() {
  // 获取 <canvas> 元素
  var canvas = document.getElementById('webgl');
  // 获取 WebGL 绘图上下文
  var gl = getWebGLContext(canvas);
  if (!gl) {
    console.log('Failed to get the rendering context for WebGL');
    return;
  }

  // 设置背景颜色
  gl.clearColor(0.0, 0.0, 0.0, 1.0);

  // 清空颜色缓冲区
  gl.clear(gl.COLOR_BUFFER_BIT);
}

下面我们看下这里涉及的方法:

获取 webgl 绘图上下文

可以直接使用 canvas.getContext('webgl') 直接获取,不过不同浏览器可能有差异,所以这本书封装了一个 getWebGLContext() 方法去获取。

指定绘图区域的背景色

// 这几个参数都是从0 ~ 1。
gl.clearColor(red, green, bule, alpha);

一旦指定了背景色后,背景色就会保存在WebGL系统中,在下次调用 gl.clearColor() 方法之前都不会进行改变。

清空 canvas

/**
 * 把指定的缓冲区清空为预设的值。若清空的是颜色缓冲区,那么将使用gl.clearColor()指定的预设值。
 * @param {*} buffer 指定待清空的缓冲区,可以用 | 来指定多个。
 *            gl.COLOR_BUFFER_BIT 颜色缓冲区
 *            gl.DEPTH_BUFFER_BIT 深度缓冲区
 *            gl.STENCIL_BUFFER_BIT 模板缓冲区
 */
gl.clear(buffer);

调用该方法以后,就会用之前指定的背景颜色清空绘图区域。只是清空了颜色缓冲区,而不是 canvas 绘图区。

WebGL 有多个缓冲区,而gl.COLOR_BUFFER_BIT指的是颜色缓冲,使用gl.clearColor()指定的颜色;深度缓冲区gl.DEPTH_BUFFER_BIT 在画三维图形的时候会用到; gl.STENCIL_BUFFER_BIT 在这本书上没有涉及。

绘制一个点

在画图之前,我们要知道 webgl 依赖一种叫做着色器(shader)的绘图机制。着色器提供了强大的绘制二维或三维图形的方法,它很强大,不过也很复杂。后面我们会一步一步了解着色器。

// 顶点着色器
var VSHADER_SOURCE = `
  void main() {
    gl_Position = vec4(0.0, 0.0, 0.0, 1.0); // 设置顶点坐标
    gl_PointSize = 10.0;                  // 设置点的大小
  }`;

// 片元着色器
var FSHADER_SOURCE =`
  void main() {
    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // 设置颜色
  }`;

function main() {
  // 获取 <canvas> 元素
  var canvas = document.getElementById('webgl');

  // 获取 WebGL 绘图上下文
  var gl = getWebGLContext(canvas);

  // 初始化着色器
  initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)

  // 设置背景颜色
  gl.clearColor(0.0, 0.0, 0.0, 1.0);
  // 清空颜色缓冲区
  gl.clear(gl.COLOR_BUFFER_BIT);
  
  // 绘制一个点
  gl.drawArrays(gl.POINTS, 0, 1);
}

运行效果

着色器是什么?

我们现在使用的着色器是用字符串形式内嵌到 JavaScript 程序中的。 webgl 需要两种着色器:

  • 顶点着色器 (Vertex shader): 顶点着色器是用来描述顶点相关特性(位置、大小)的程序。其中顶点是指二维或三维空间中的一个点。
  • 片元着色器 (Fragment shader):片元着色器是逐片元处理(比如光照)的程序。片元是webgl的一个术语,可以理解为像素。

我们看一下从执行JavaScript程序到浏览器显示结果的过程:

其结果可描述为:

  1. 运行 JavaScript 程序,调动 webgl 相关方法;
  2. 执行顶点着色器和片元着色器,在颜色缓冲区中进行绘制
  3. 清空缓冲区
  4. 颜色缓冲区的内容展示在canva中

着色器程序

着色器程序是使用类似C的 OpenGL ES (GLSL ES) 来编写的。这本书的案例,都是将着色器程序代码当做字符串存储在变量中。

初始化着色器

initShaders() 是这本书提供的辅助函数,作用是初始化着色器,具体实现在 lib/cuon-utils.js 文件中,有兴趣的话,可以看一下。不过作为初学者可以暂时不用纠结其中的细节,等熟悉了webgl 以后,可以再回过头来详细学习其中涉及的知识。

现在我们只要知道调用这个方法,传入上下文、顶点着色器代码字符串、片段着色器代码字符换,就可以初始化着色器就行了。

初始化着色器完成后,在webgl系统中着色器就建立好了,并随时可以使用。在绘图时,顶点着色器先执行,它对 gl_Position 变量和 gl_PointSize 变量进行赋值,并将它们传入片元着色器,然后片元着色器再执行。实际上片元着色器收到的是经过光栅化处理后的片元值,目前可以简单的理解为这两个变量从顶点着色器传入片元着色器。

注意:着色器程序是运行在 webgl 系统中,而不是JavaScript程序中的

顶点着色器

着色器程序和C语言程序一样,都需要一个 main() 函数。同时我们看到了两个特殊的变量 gl_Positiongl_PointSize 。这两个变量都是内置在顶点着色器中的,其中:

  • gl_Position 表示顶点的位置,这个变量是必须被赋值的,否则着色器没有办法执行,类型为 vec4
  • gl_PointSize 表示点的尺寸,可以不赋值,默认为1.0,类型为 float

GLSL ES 是一种强类型的编程语言:

  • vec4 表示由4个浮点数组成的矢量,可以使用 vec4(v0, v1, v2, v3) 进行创建。
  • float 表示浮点数。

片元着色器

顶点着色器控制着点的位置和大小,片元着色器控制着点的颜色。片元着色器的作用就是处理片元,将其显示在屏幕上。

片元着色器将点的颜色赋值给 gl_FragColor 变量,该变量是片元着色器中唯一内置的变量,它控制着显示在屏幕上像素的最终颜色。其类型是 vec4

绘制操作

gl.drawArray() 是一个强大的方法,它能绘制各种图形。

/**
 * 执行顶点着色器,按照mode参数指定的方式进行绘制
 * @param {*} mode 绘制方式。gl.POINTS gl.LINES gl.LINE_STRIP gl.LINE_LOOP gl.TRIANGLES gl.TRIANGLE_STRIP gl.TRIANGLE_FAN 
 * @param {int} first 从哪个顶点开始绘制
 * @param {int} count 绘制次数
 */
gl.drawArrays(mode, first, count);

在例子里,我们只需要绘制一个点。所以第一个参数设置为 gl.POINTS ;第二个设置为0,表示从第一个顶点开始画;第三个参数设为1,表示仅绘制一次,即一个点。

WEBGL 坐标系

webgl使用的是三维坐标系,有x轴、y轴、z轴:

  • x轴正方向为水平向右,y轴正方向为水平向上,z轴正方向为外。
  • canvas中心点为(0.0, 0.0, 0.0)
  • canvas 上边缘和下边缘为(0.0, 1.0, 0.0) 、 (0.0, -1.0, 0.0);
  • canvas 左边缘和右边缘为(-1.0, 0.0, 0.0) 、 (1.0, 0.0, 0.0);