WebGL 绘制三角形

该文章于一天前发表在 github,若有问题可提至 github

在上一篇文章WebGL 初探中,我们熟悉了 WebGL 的简单背景以及如何绘制一个简单的点。但是只绘制点我们是无法进入三维世界的,本篇章将讲解如何使用 WebGL 绘制三角形,因为很多 3D 图形都是使用三角形为基础进行渲染的,所以有些对 GPU 性能指标的评价就是渲染三角形的能力。

attribute 变量

前一篇文章中,绘制一个点直接将一个硬编码的矢量赋给了位置属性。但是,显然这样写是不够灵活的。所以在这篇文章的前一部分会使用变量重些前一篇文章的例子。我们的目标是将位置信心从 JavaScript 程序中传递到顶点着色器,而 attribute 变量就传输的是那些和顶点相关的数据。下图展示了如何使用 attribute 变量传输数据:

WebGL 绘制三角形

var VSHADER_SOURCE =
  'attribute vec4 a_Position;\n' +
  'void main() {\n' +
  '  gl_Position = a_Position;\n' +
  '  gl_PointSize = 10.0;\n' +
  '}\n';

如上所示,前一篇文章中,直接将使用矢量进行赋值 gl_Position = vec4(0.0, 0.0, 0.0, 1.0)。而这里使用 attribute vec4 a_Position 声明一个 attribute 变量。关键字 attribute 被称为存储限定符,表示声明一个 attribute 变量,而且 attribute 变量必须声明成全局变量,数据将从着色器外传入。gl_Position = a_Position 将 attribute 变量 a_Position 赋值给 gl_Position。

// 获得 attribute 变量 a_Position 的存储位置
  var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
  if (a_Position < 0) {
    console.log('Failed to get the storage location of a_Position');
    return;
  }

  // 将顶点位置传递给 attribute 变量
  gl.vertexAttrib3f(a_Position, 0.0, 0.0, 0.0);

上面,我们使用 vertexAttrib3f(location, v0, v1, v2) 方法将数据 (v0, v1, v2) 传递给 location 参数指向的 attribute 变量。而这里的 location 是要修改的 attribute 变量的存储位置。所以在这之前我们需要获取 attribute 变量的存储位置。这里,可以通过 getAttribLocation(program, name) 获取 attribute 变量的存储位置。进行完这些更改之后,运行会发现和上一篇文章相同的效果。

缓冲区对象

到现在,我们一次只能绘制一个点,无法一次性绘制多个点,甚至是线和面。想要一次绘制多个点,我们需要借助一个叫做缓冲区对象的东西。缓冲区对象是 WebGL 中的一块存储区域,其中保存了大量的顶点数据,可以一次性的向顶点着色器传入多个顶点到 attribute 变量。下图展示了如何使用缓冲区对象:

WebGL 绘制三角形

var vertices = new Float32Array([
    0, 0.5,   -0.5, -0.5,   0.5, -0.5
  ]);

  // 创建缓冲区对象
  var vertexBuffer = gl.createBuffer();
  if (!vertexBuffer) {
    console.log('创建缓冲区对象失败。');
    return -1;
  }
  // 绑定缓冲区对象到目标
  gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);

 // 将数据写入缓冲区对象
  gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

  var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
  if (a_Position < 0) {
    console.log('获得 attriute 变量 a_Position 失败');
    return -1;
  }
  // 将缓冲区对象赋值给 attriute 变量 a_Position
  gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);

  // 开启 attribute 变量
  gl.enableVertexAttribArray(a_Position);

首先,是通过 createBuffer() 方法创建一个缓冲区对象,对应的方法还有 deleteBuffer() 用于删除一个缓冲区对象。接着,使用 bindBuffer(target, buffer) 将缓冲区对象绑定到 WebGL 已经存在的目标上,以通知 WebGL 正确的处理其内容。紧接着,使用 bufferData(target, data, useage) 方法将数据写入缓冲区,这里实际是将数据写入到绑定的目标对象上,所以在第二步中进行了缓冲区对象的绑定。而第二个参数是一个定义好的类型化数组。这里的第三个参数,STATIC_DRAW 代表只会想缓冲区写入一次,绘制多次,其它用法参数可查相关 API。然后,使用 vertexAttribPointer(location, size, type, normalized, stride, offset) 将缓冲区对象赋值给 attribute 变量。这里的第二个参数制定缓冲区对象的每个顶点的分量(1到4之间),如果 size 比 attribute 变量需要的分量小,则按照一定规则补全。第三个参数指定了数据格式。第四个参数表示是否将非浮点型数据归一到 [0, 1] 或[-1,1] 之间。最后两个参数一般默认为0,此处不做具体介绍。最后通过 enableVertexAttribArray(a_Position) 方法开启缓冲区对象和 attribute 变量的连接。

绘制三角形

最后,我么可通过 drawArrays(gl.TRIANGLES, 0, n) 绘制三角形。但是,前一篇文章中提到 drawArrays 函数功能非常强大。现在,我们就看一下该函数有哪些强大的特性。该函数中第一个参数是指定绘制方式,第二个参数指定从哪个顶点开始绘制,第三个参数指定绘制需要多少个顶点。下表展示了第一个参数对应的绘制方式:

参数基本图形呈现效果
gl.POINTS一系列的点绘制在v0,v1,v2等处。
gl.LINES线段一系列单独线段。
gl.LINE_STRIP连线一个相连的线段。
gl.LINE_LOOP回路一个相连接的回路。
gl.TRIANGLES三角形一系列单独的三角形。
gl.TRIANGLE_STRIP三角带一个相邻三角形组成的图形。

下图是 new Float32Array([0, 0.5, -0.5, -0.5, 0.0, 0.0, 0.5, -0.5]) 矢量的一个测试:

WebGL 绘制三角形

结语

这篇文章主要讲了如何利用缓冲区对象在空间中绘制点、线、面。下一篇文章将讲解矩阵变换,有了这两个知识的积累,理论上可以画出我们想要的 3D 图形。WebGL 的 API 看似复杂,但基本都很类似。当我们对这一套熟悉之后,我们会愈发轻车熟路。

系列文章

相关推荐