顶点缓冲对象
在上一节中,我们只是简单提了一下,封装了两个函数 bindArrayBuffer 和 activateAttribute 用于绘制一个三角形。实际上这对应着 OpenGL 中的一个核心概念——顶点缓冲对象(Vertex Buffer Object, 简称VBO)。它用于在 GPU 中存储顶点数据,包括坐标、颜色、法线等描述顶点的信息。 不用每次绘制时都从 CPU 向 GPU 搬运数据,极大提高了渲染效率。
1. 构建缓冲对象
bindArrayBuffer(data) {
let gl = this.gl;
let vbo = gl.createBuffer();
if (!vbo)
return null;
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
gl.bufferData(gl.ARRAY_BUFFER, data, gl.DYNAMIC_DRAW);
return vbo;
}
在 WebGL 中对应着数据类型 WebGLBuffer,该类型的对象没有定义任何方法或者属性,我们必须通过渲染上下文来操作它。 上面的函数是我们在上一节中封装的函数 bindArrayBuffer,它大体上做了三件事情:
- 创建缓冲对象
gl.createBuffer
。显然,在使用 VBO 之前需要先创建一个对象出来。函数 createBuffer 用于构建一个新的 VBO 对象, 如果构建成功将返回一个 WebGLBuffer 对象,否则返回 null。我们将用局部变量 vbo 保存对象。对应的还有 gl.deleteBuffer(vbo) 用于删除构建出来的缓冲对象。 - 绑定缓冲对象
gl.bindBuffer
。该函数用于指定刚构建出来的 vbo 对象作用的缓冲类型。 这里我们选择的是ARRAY_BUFFER
,用于存储顶点数据,如位置、法线、颜色等信息。渲染时,顶点着色器会根据这个缓冲区确定各个顶点的属性。 除此之外,还有ELEMENT_ARRAY_BUFFER
,COPY_READ_BUFFER
,COPY_WRITE_BUFFER
等多种选择,详细可以参见 文档。 - 搬运数据到缓冲对象中
gl.bufferData
。最后,我们还需要通过 bufferData 接口完成数据的搬运工作。第二个参数就是我们要搬运的带有类型的数据, 第一个参数选择ARRAY_BUFFER
就会将数据搬运到刚刚构建的 VBO 中。最后一个参数gl.DYNAMIC_DRAW
表示该 VBO 将多次写入数据并多次渲染。除此之外,还有一种只指定缓存大小的接口gl.bufferData(target, size, usage)
。 后续可以再通过bufferSubData
来填充数据。
2. 应用缓冲对象
完成了数据的搬运操作之后,我们就需要告知 WebGL 将顶点缓冲对象中的数据填充到 attribute 修饰的变量中,以完成图形渲染的工作。
在下面的代码的第 3 行,我们通过接口 useProgram
指定当前的渲染上下文中使用的着色器程序,包括了顶点着色器和片段着色器。
activateAttribute(attrib, itemSize, type, stride = 0, offset = 0) {
let gl = this.gl; let program = this.program;
gl.useProgram(program);
let attr = gl.getAttribLocation(program, attrib);
gl.vertexAttribPointer(attr, itemSize, _gl_type_map_[type], false, stride, offset)
gl.enableVertexAttribArray(attr);
}
然后通过接口 getAttribLocation
获取指定名称的 attribute 变量在 WebGL 中的索引。当 WebGL 成功找到目标变量后,
就会返回一个32位的有符号整型(GLint)数据,否则将返回 -1。
上一节的例程调用 render.activateAttribute("a_position", 2, "float")
通过参数 "a_position" 指定顶点着色器通过上述构建的 VBO 对象获取顶点位置数据。
接着,我们通过接口 vertexAttribPointer 告知 WebGL 应该如何使用缓冲中的数据, 关于该接口的参数的详细设置可以参考文档。 最后,通过调用接口 enableVertexAttribArray 开启 attribute 变量,使得顶点着色器能够访问缓冲中的数据。 此外,我们还可以通过接口 gl.disableVertexAttribArray 来关闭 attribute 变量。
3. 完成渲染
完成上述的数据搬运和 attribute 变量的指定之后,我们就可以调用 gl.drawArrays
完成几何图形的绘制。
有了 VBO 的顶点缓存对象的加持,我们可以先把大量的数据搬运到内存中,再通过 gl.bindBuffer
在各个VBO之间切换,绘制多个几何图形。
上图是例程3的截图。我们先通过封装的函数 bindArrayBuffer 构建了两个缓冲对象 vbo0 和 vbo1,
它们分别是构成右上角和左下角两个三角形的顶点坐标。在接下来的程序中,我们在函数 useArrayBuffer 中,
直接调用接口gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
完成缓冲对象的切换。如此就可以先渲染右上角的绿三角,再渲染左下角的红三角。