基本流程
- 创建缓冲区对象 gl.createBuffer()
- 绑定缓冲区对象 gl.bindBuffer()
- 将数据写入缓冲区对象 gl.bufferData()
- 将缓冲区对象分配给一个attribute变量 gl.vertexAttribPointer()
- 开启attribute变量 gl.enableVertexAttribArray()
绘制 gl.drawArray() 或 gl.drawElements()
下面分开详解这些API。
createBuffer():WebGLBuffer
本函数对应OpenGL中的glGenBuffers(), 用于创建一个指代缓冲区对象的名字,用WebGLBuffer对象作为代理。这个函数并不会真正在GPU显存上开辟空间,只是返回了一个类似于指针的对象。
bindBuffer(target: uint, buffer: WebGLBuffer):void
- target: 绑定的目标(OpenGL内部会采取一种最优的内存管理策略,根据缓存对象完成绑定的情况来分配它对应的内存,这些可用的缓存结合点称为目标),指定buffer是存储顶点属性数据还是数据的索引。gl.ARRAY_BUFFER, gl.ELEMENT_ARRAY_BUFFER
- buffer: createBuffer创建的对象, 如果buffer为null,则禁用对target的绑定
本函数将createBuffer创建的buffer指代的缓冲区绑定为当前缓冲区(current buffer)。WebGL的绘制是单通道的,一次只能绘制一组数据,每次绘制的就是当前缓冲区buffer中的数据。后续所有将数据放入缓冲区的函数操作的都是这个当前缓冲区。
bindBuffer主要完成以下几点任务:1.如果buffer是第一次被绑定且它非空,则WebGL将创建一个与该buffer对应的缓冲区对象VBO,且置其为当前缓冲区对象;2.如果绑定一个已经创建的缓冲区对象,则将本对象置为当前缓冲区对象;3.如果buffer为null,则不再对当前target应用任何缓冲对象。bufferData(target: uint, data: TypedArray, usage: uint)
- target 同上,需要和bindBuffer使用的target相同
- data 类型化数组,存储顶点数据
usage 设置分配数据之后的读取和写入方式,用于指导WebGL如何使用这些存储的数据。本参数帮助WebGL做优化,所以,即便你传入了错误的值,也不会终止程序,但可能导致性能下降。这个参数对能否达到最优性能至关重要。枚举值有:
gl.STATIC_DRAW
数据存储内容只写入一次,然后多次绘制
STATIC_开头,就是说数据的变动非常有限,或者根本就没有——因为它本质上是静态数据。这类标识符显然需要用于那些只修改过一次就不再变动的数据类型。OpenGl会在内部对数据重新处理,以保证它在内存中的布置更为合理,或者使用最优的数据格式。虽然这一步操作代价可能很大,但是只进行一次,整体还是理想的。gl.DYNAMIC_DRAW
数据存储内容会被反复写入和反复使用
DYNAMIC_开头,说明数据变动非常频繁,而变动过程中对数据的使用也非常频繁。例如一个建模程序,它所使用的数据可能被用户所编辑,此时有必要使用这个标识符。这时,一个可能的情况是数据在多帧内被持续使用,然后被修改,然后再次被多帧使用,如此反复。gl.STREAM_DRAW
数据存储内容只写入一次,不会被频繁绘制
与gl.DYNAMIC_DRAW相反,它的缓存修改是有规律的,并且每次修改数据后只会少量地加以使用。这种时候,OpenGL甚至可能不会讲数据拷贝到快速的图像内存中,而是直接在原地进行访问。这种情形通常发生在CPU端执行应用程序诸如物理仿真的操作时,此时每一帧都会给出一些新的数据集,供程序调取。
本函数有两个任务:1.为顶点数据分配GPU存储空间(显存);2.将数据从前端的RAM拷贝至分配的存储空间中,这需要执行一次IO行为。
vertexAttribPointer(location: int, size: unit, type: unit, normalized: boolean, stride: int, offset: int)
- location
attribute变量在shader中的索引,通过gl.getAttribLocation()获得
- size
attribute变量占有缓冲区数组中每个顶点的元素个数(1-4),如果size比attribute变量需要的数据小,则自动补全为0
- type
数据格式
- normalized
是否将非浮点数的数据归一化为[0, 1]或[-1, 1]区间
- stride
指定相邻两个顶点之间的字节数,以字节为单位
- offset
指定缓冲区对象中的偏移量,以字节为单位
本函数用于告诉WebGL如何使用顶点数组中的数据:每个attribute变量如何从类型化数组中分配数据。它可以将缓冲区中的数据一次性的分配给某个attribute变量。
var verticesSizes = new Float32Array([ // 顶点坐标和点的尺寸 0.0, 0.5, 10.0, // 第一个点 -0.5, -0.5, 20.0, // 第二个点 0.5, -0.5, 30.0 // 第三个点 ]); var FSIZE = verticesSizes.BYTES_PER_ELEMENT; ... gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 3, 0); gl.enableVertexAttribArray(a_Position); ... gl.vertexAttribPointer(a_Size, 1, gl.FLOAT, false, FSIZE * 3, 2); gl.enableVertexAttribArray(a_Size);
- location
enableVertexAttribArray(location: int)
- location
同上
本函数告诉WebGL顶点缓存中记录了哪些顶点属性数据。在GPU从顶点缓存中读取数据之前,必须使用gl.enableVertexAttribArray()来开启attribute变量对应的顶点属性数组。如果不启用的话,WebGL会使用静态顶点属性。每个顶点的静态顶点属性都是一个默认值。每个顶点的静态顶点属性可以通过gl.vertexAttrib*()系列函数来设置。
- location
gl.drawArray or gl.drawElements
gl.drawArray(mode, first, count)
- mode 表示构建图元的类型,必须是gl.TRIANGLES,gl.LINE_LOOP,gl.LINES, gl.POINTS等其中之一
- first 数组中的起始位置,表示从本顶点开始绘制
- count 绘制的顶点数
使用数组元素建立连续的的几何图元序列,每个启用的数组中起始位置为first,结束位置为first + count - 1.
gl.drawElements(mode, count, type, offset)
- mode 同drawArray
- count
- type 索引数组中元素的类型,必须是gl.UNSIGNED_BYTE, gl.UNSIGNED_SHORT, gl.UNSIGNED_INT其中之一
- offset
使用count个元素来定义一系列几何图元,而元素的索引值保存在一个绑定到gl.ELEMENT_ARRAY_BUFFER的缓存中(元素数组缓存)。offset定义了索引数组中的绘制起点,单位是字节。