WebGL 绘制三角形
该文章于一天前发表在 github,若有问题可提至 github。
在上一篇文章WebGL 初探中,我们熟悉了 WebGL 的简单背景以及如何绘制一个简单的点。但是只绘制点我们是无法进入三维世界的,本篇章将讲解如何使用 WebGL 绘制三角形,因为很多 3D 图形都是使用三角形为基础进行渲染的,所以有些对 GPU 性能指标的评价就是渲染三角形的能力。
attribute 变量
前一篇文章中,绘制一个点直接将一个硬编码的矢量赋给了位置属性。但是,显然这样写是不够灵活的。所以在这篇文章的前一部分会使用变量重些前一篇文章的例子。我们的目标是将位置信心从 JavaScript 程序中传递到顶点着色器,而 attribute 变量就传输的是那些和顶点相关的数据。下图展示了如何使用 attribute 变量传输数据:
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 变量。下图展示了如何使用缓冲区对象:
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])
矢量的一个测试:
结语
这篇文章主要讲了如何利用缓冲区对象在空间中绘制点、线、面。下一篇文章将讲解矩阵变换,有了这两个知识的积累,理论上可以画出我们想要的 3D 图形。WebGL 的 API 看似复杂,但基本都很类似。当我们对这一套熟悉之后,我们会愈发轻车熟路。