Skip to content

三、缓冲区对象

缓冲区对象是WebGL系统中的一块内存区域,可以一次性地向缓冲区对象中填充大量的顶点数据,然后将这些数据保存在其中,供顶点着色器使用。

使用缓冲区对象,我们可以绘制多个点,或者是多个面

1.类型化数组

在 webgl 中,需要处理⼤量的相同类型数据,所以引入类型化数组,这样程序就可以预知到数组中的数据类型, 提⾼性能。

类型化数组类型每个元素占字节数描述
Int8Array18位整型
UInt8Array18位无符号整型
Int16Array216位整型
UInt16Array216位无符号整型
Int32Array432位整型
UInt32Array432位无符号整型
Float32Array4单精度32位浮点型
Float64Array8单精度64位浮点型

类型化数组的方法、属性和常量

方法、属性、常量描述
get(index)获取第index个元素值
set(index,value)设置第index个元素的值为value
set(array,offset)从第offset个元素开始将array中的值填充进去
length数组的长度
BYTES_PER_ELEMENT数组中每个元素所占的字节数

注意

  • 类型化数组不支持push()pop()方法
  • 类型化数组只能通过new关键字创建

2.使用缓冲区对象步骤

image-20231006165519491

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.缓冲区执行过程

image-20231006165616787

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.多缓冲区

使用缓冲区对象绘制多个顶点,并给每个顶点设置不同尺寸

image-20231006172658297

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变量

image-20231006172707627

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)  // 4

6.2 设置偏移量

通过修改gl.vertexAttribPointer(location, size, type, normalized, stride, offset)的stride和offset,控制不同数据的偏移量

  • 上述points中,每个顶点用3个数据表示,所以strideBYTES * 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);

MIT Licensed