三、缓冲区对象
缓冲区对象是WebGL系统中的一块内存区域,可以一次性地向缓冲区对象中填充大量的顶点数据,然后将这些数据保存在其中,供顶点着色器使用。
使用缓冲区对象,我们可以绘制多个点,或者是多个面
1.类型化数组
在 webgl 中,需要处理⼤量的相同类型数据,所以引入类型化数组,这样程序就可以预知到数组中的数据类型, 提⾼性能。
| 类型化数组类型 | 每个元素占字节数 | 描述 |
|---|---|---|
| Int8Array | 1 | 8位整型 |
| UInt8Array | 1 | 8位无符号整型 |
| Int16Array | 2 | 16位整型 |
| UInt16Array | 2 | 16位无符号整型 |
| Int32Array | 4 | 32位整型 |
| UInt32Array | 4 | 32位无符号整型 |
| Float32Array | 4 | 单精度32位浮点型 |
| Float64Array | 8 | 单精度64位浮点型 |
类型化数组的方法、属性和常量
| 方法、属性、常量 | 描述 |
|---|---|
| get(index) | 获取第index个元素值 |
| set(index,value) | 设置第index个元素的值为value |
| set(array,offset) | 从第offset个元素开始将array中的值填充进去 |
| length | 数组的长度 |
| BYTES_PER_ELEMENT | 数组中每个元素所占的字节数 |
注意:
- 类型化数组不支持
push()、pop()方法 - 类型化数组只能通过
new关键字创建
2.使用缓冲区对象步骤

2.1 创建顶点数据
js
const points = new Float32Array([
-0.5, -0.5,
0.5, -0.5,
-0.5, 0.5
]);2.2 创建缓冲区对象
js
const buffer = gl.createBuffer()通过deleteBuffer(buffer)删除缓冲区
2.3 绑定缓冲区对象
js
gl.bindBuffer(target,buffer)target:缓冲区存储的数据类型,可以是以下两个选项gl.ARRAY_BUFFER: 表示缓冲区存储的是顶点的数据gl.ELEMENT_ARRAY_BUFFER: 表示缓冲区存储的是顶点的索引值
buffer:缓冲区对象
2.4 将顶点数据传入缓冲区
js
gl.bufferData(target, data, type)target:缓冲区存储的数据类型,一定要跟bindBuffer()的target类型相同gl.ARRAY_BUFFER: 表示缓冲区存储的是顶点的数据gl.ELEMENT_ARRAY_BUFFER: 表示缓冲区存储的是顶点的索引值
data:需要写入缓冲区的顶点数据,如Float32Array定义的数组type:表示如何使⽤缓冲区对象中的数据,分为以下⼏类gl.STATIC_DRAW: 静态绘制模式,写⼊⼀次,绘制一次gl.STREAM_DRAW: 流式绘制模式gl.DYNAMIC_DRAW: 动态绘制模式,写⼊多次,绘制多次
2.5 缓冲区数据传入位置变量
gl.vertexAttribPointer()用于将缓冲区的顶点数据传入顶点着色器
js
gl.vertexAttribPointer(location, size, type, normalized, stride, offset)location:attribute变量的存储位置,即通过getAttribLocation()获取的数据size:指定每个顶点所使⽤数据的个数type:顶点数据类型顶点数据类型 描述 对应类型数组 gl.FLOAT 浮点型 Float32Array gl.UNSIGNED_BYTE 无符号字节 Uint8Array gl.SHORT 短整型 Int16Array gl.UNSIGNED_SHORT 无符号短整型 Uint16Array gl.INT 整型 Int32Array gl.UNSIGNED_INT 无符号整型 Uint32Array
normalized:表示是否将数据归⼀化到[0, 1],[-1, 1]这个区间stride:两个相邻顶点之间的字节数,每个顶点的数据占几个字节offset:数据偏移量
2.6 允许数据传递
js
gl.enableVertexAttribArray(location)- location: attribute 变量的存储地址
禁⽤attribute变量
js
gl.disableVertexAttribArray(location);3.缓冲区执行过程

4.绘制多个顶点
使用缓冲区对象绘制多个顶点
js
const canvas = document.querySelector("#canvas");
const gl = canvas.getContext("webgl");
// 顶点着色器
const VERTEX_SHADER_SOURCE = `
attribute vec4 aPosition;
void main() {
gl_Position = aPosition;
gl_PointSize = 30.0;
}
`;
const FRAGMENT_SHADER_SOURCE = `
void main() {
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}
`;
// 初始化着色器
const program = initShader(gl,VERTEX_SHADER_SOURCE,FRAGMENT_SHADER_SOURCE);
// 获取attribute变量,aPosition的存储位置
const aPosition = gl.getAttribLocation(program, "aPosition");
// 创建顶点数据,三个顶点,每两个数据分别位x,y坐标
const points = new Float32Array([
-0.5, -0.5,
0.5, -0.5,
-0.5, 0.5
]);
// 创建缓冲区对象
const buffer = gl.createBuffer();
// 绑定缓冲区对象
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
// 将顶点数据写入缓冲区
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);
// 将缓冲区的顶点数据写入顶点着色器
gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0);
// 开启attribute变量
gl.enableVertexAttribArray(aPosition);
// 绘制,3个顶点
gl.drawArrays(gl.POINTS, 0, 3);5.多缓冲区
使用缓冲区对象绘制多个顶点,并给每个顶点设置不同尺寸

js
const canvas = document.querySelector("#canvas");
const gl = canvas.getContext("webgl");
// 顶点着色器
const VERTEX_SHADER_SOURCE = `
attribute vec4 aPosition;
attribute float aPointSize;
void main() {
gl_Position = aPosition;
gl_PointSize = aPointSize;
}
`;
const FRAGMENT_SHADER_SOURCE = `
void main() {
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}
`;
const program = initShader(
gl,
VERTEX_SHADER_SOURCE,
FRAGMENT_SHADER_SOURCE
);
const aPosition = gl.getAttribLocation(program, "aPosition");
const aPointSize = gl.getAttribLocation(program, "aPointSize");
const points = new Float32Array([
-0.5, -0.5,
0.5, -0.5,
-0.5, 0.5
]);
// 顶点位置数据的缓冲区
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);
gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(aPosition);
// 顶点尺寸数据的缓冲区
const sizes = new Float32Array([10.0,20.0,30.0]);
const sizeBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, sizeBuffer);
gl.bufferData(gl.ARRAY_BUFFER, sizes, gl.STATIC_DRAW);
gl.vertexAttribPointer(aPointSize, 1, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(aPointSize);
gl.drawArrays(gl.POINTS, 0, 3);6.缓冲区数据偏移
在上述案例中,我们创建了两个缓冲区对象和两个Float32Array数据,分别控制顶点的位置和尺寸。
通过缓冲区数据偏移,我们可以使用一个Float32Array数据和一个缓冲区对象,同时控制多个attribute变量

6.1 获取每个数据字节数
通过BYTES_PER_ELEMENT属性,获取强类型数组中每个元素所占用的字节数
js
// 每一行的前两个数据表示位置,最后一个数据表示尺寸
const points = new Float32Array([
-0.5, -0.5, 30.0,
0.5, -0.5, 20.0,
-0.5, 0.5, 10.0,
]);
const BYTES = points.BYTES_PER_ELEMENT;
onsole.log(BYTES) // 46.2 设置偏移量
通过修改gl.vertexAttribPointer(location, size, type, normalized, stride, offset)的stride和offset,控制不同数据的偏移量
- 上述points中,每个顶点用3个数据表示,所以
stride为BYTES * 3 - 每个顶点的位置信息从当前行第一个开始,所以位置信息的偏移量为
BYTES * 0,也就是0 - 每个顶点的尺寸信息为当前行第三个数据,所以尺寸信息的偏移量为
BYTES * 2
js
// 顶点的位置数据
gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, BYTES * 3, 0);
gl.enableVertexAttribArray(aPosition);
// 顶点的尺寸数据
gl.vertexAttribPointer(aPointSize,1,gl.FLOAT,false,BYTES * 3,BYTES * 2);
gl.enableVertexAttribArray(aPointSize);