WebGL提供了一种很方便的机制,即缓冲区对象(buffer object),它可以一次性地向着色器传入多个顶点的数据。
缓冲区对象是WebGL系统中的一块内存区域,我们可以一次性地向缓冲区对象中填充大量的顶点数据,然后将这些数据保存在其中,供顶点着色器使用。
使用缓冲区对象向顶点着色器传入多个顶点的数据,需要遵循以下5个步骤:
- 创建缓冲区对象
gl.createBuffer()
- 绑定缓冲区对象
gl.bindBuffer()
- 将数据写入缓冲区对象
gl.bufferData()
- 将缓冲区对象分配给一个
attribute
变量gl.vertexAttribPointer()
- 开始
attribute
变量gl.enableVertexAttribArray()
- 绘制三角形的3个顶点
// Vertex shader program
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'void main() {\n' +
' gl_Position = a_Position;\n' +
' gl_PointSize = 10.0;\n' +
'}\n';
// Fragment shader program
var FSHADER_SOURCE =
'void main() {\n' +
' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' +
'}\n';
function main() {
// Retrieve <canvas> element
var canvas = document.getElementById('webgl');
// Get the rendering context for WebGL
var gl = getWebGLContext(canvas);
if (!gl) {
console.log('Failed to get the rendering context for WebGL');
return;
}
// Initialize shaders
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('Failed to intialize shaders.');
return;
}
// Write the positions of vertices to a vertex shader
var n = initVertexBuffers(gl);
if (n < 0) {
console.log('Failed to set the positions of the vertices');
return;
}
// Specify the color for clearing <canvas>
gl.clearColor(0, 0, 0, 1);
// Clear <canvas>
gl.clear(gl.COLOR_BUFFER_BIT);
// Draw three points
gl.drawArrays(gl.POINTS, 0, n);
}
function initVertexBuffers(gl) {
var vertices = new Float32Array([
0.0, 0.5, -0.5, -0.5, 0.5, -0.5
]);
var n = 3; // The number of vertices
// 创建缓冲区对象
var vertexBuffer = gl.createBuffer();
if (!vertexBuffer) {
console.log('Failed to create the buffer object');
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('Failed to get the storage location of a_Position');
return -1;
}
// 将缓冲区对象分配给a_Position变量
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
// 连接a_Position变量与分配给它的缓冲区对象
gl.enableVertexAttribArray(a_Position);
return n;
}
- 创建缓冲区对象
gl.createBuffer()
执行该方法的结果就是,WebGL系统中多了一个新创建出来的缓冲区对象。返回值为null表示创建失败。
相应地,
gl.deleteBuffer(buffer)
函数可以用来删除被gl.createBuffer()
创建出来的缓冲区对象
- 绑定缓冲区
gl.bindBuffer(target,buffer)
创建缓冲区的第二步就是将缓冲区对象绑定到WebGL系统中已经存在的“目标”上。这个“目标”表示缓冲区对象的用途。允许使用buffer表示的缓冲区对象绑定到target表示的目标上。
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
其中,
gl.ARRAY_BUFFER
表示缓冲区对象中包含了顶点的数据
- 向缓冲区对象中写入数据
gl.bufferData(target,data,usage)
第三步,开辟空间并向缓冲区中写入数据。
var vertices = new Float32Array([
0.0,0.5,-0.5,-0.5,0.5,0.5
])
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW)
该方法的效果是,将第2个参数vertices中的数据写入绑定到第1个参数
gl.ARRAY_BUFFER
上的缓冲区对象。我们不能直接向缓冲区写入数据,而只能向"目标"写入数据,所以要向缓冲区写数据,必须先绑定。
其中,参数usage表示程序将如何使用存储在缓冲区对象中的数据。该参数将帮助WebGL优化操作,但即便传入了错误的值,也不会终止程序(仅仅是降低程序的效率)
上面,我们使用了Float32Array对象,而不是JS更常见的Array对象。这是因为,JS中的数组Array是一种通用的类型,既可以存储数字也可以存储字符串,而并没有对“大量元素都是同一种类型”优化。为了解决这个问题,WebGL引入了类型化的数组,Float32Array就是其中之一。
- WebGL使用的各种类型化数组
数组类型 | 每个元素所点字节数 | 描述(C语言中的数据类型) |
---|---|---|
Int8Array | 1 | 8位整型(singed char) |
UInt8Array | 1 | 8位无符号整型(unsinged char) |
Int16Array | 2 | 16位整型(singed short) |
UInt16Array | 2 | 16位无符号整型(unsinged short) |
Int32Array | 4 | 32位整型(singed int) |
UInt32Array | 4 | 32位无符号整型(unsinged int) |
Float32Array | 4 | 单精度32位浮点数(float) |
Float64Array | 8 | 双精度64位浮点数(double) |
注意: 与普通的Array数组不同,类型化数组不支持push()和pop()方法;创建类型化数组的唯一方法就是使用new运算符,不能使用[]运算符。
- 类型化数组的方法、属性和常量
方法、属性和常量 | 描述 |
---|---|
get(index) | 获取第index个元素值 |
set(index,value) | 设置第index个元素的值为value |
set(array,offset) | 从第offset个元素开始将数组array中的值填充进去 |
length | 数组的长度 |
BYTES_PER_ELEMENT | 数组中每个元素所占的字节数 |
- 将缓冲区对象分配给attribute变量
gl.vertexAttribPointer(location,size,type,normalized,stride,offset)
将绑定到
gl.ARRAY_BUFFER
的缓冲区对象分配给由location指定的attribute变量。
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
参数location指定待分配attribute变量的存储位置;
size指定缓冲区中每个顶点的分量个数(1到4),若size比attribute变量需要的分量数小,缺失分量将按照与
gl.vertexAttrib[1234]f()
相同的规则补全。(比如:size为1,那么第2、3分量自动设为0,第4分量为1)type为数据类型
normalize传入true或false,表明是否将非浮点型的数据归一化到[0,1]或[1,1]区间
stride指定相邻两个顶点间的字节数,默认为0
offset指定缓冲区对象中的偏移量
- 开启attribute变量
gl.enableVertexAttribArray()
gl.enableVertexAttribArray(a_Position)
注意:虽然函数的名称似乎表示该函数是用来处理“顶点数组”的,但实际上它处理的对象是缓冲区。这是由于历史原因(从OpenGL中继承)造成的。
开启attribute变量后,就不能再用gl.vertexAttrib[1234]f()向它传数据了,除非你显示地关闭该attribute变量。实际上,你无法(也不应该)同时使用这两个函数。
gl.drawArrays(mode,first,count)
mode指定绘制的方式,可接收:gl.POINTS、gl.LINES、gl.LINES_STRIP、gl.LINE_LOOP、gl.TRIANGLES、gl.TRIANGLE_STRIP、gl.TRINGLE_FAN
first指定从哪个顶点开始绘制(整数)
count 指定绘制需要用到多少个顶点(整数)
gl.drawArrays(gl.POINTS,0,n) //n为3
实际上,顶点着色器执行了n(3)次,我们通过存储在缓冲区中的顶点坐标数据被依次传给attribute变量。
- 连接三个顶点,填充三角形
基于上面顶点程序的改动有两处:
1.在顶点着色器中,去掉指定点的尺寸
gl_PointSize = 10.0;
,该语句只有在绘制单个点的时候才起作用。2.
gl.drawArrays()
方法的第1个参数从gl.POINTS
被改为了gl.TRINGLES
,就相当于告诉WebGL,从缓冲区中的第1个顶点开始,使顶点着色器执行3次(n为3),用这3个点绘制出一个三角形。
- WebGL可以绘制的基本图形
- 对应效果
WebGL有点上头,暂时告一段落……