Skip to content

关联属性与缓冲区对象

WebGL:将着色器属性与缓冲区对象关联

Section titled “WebGL:将着色器属性与缓冲区对象关联”

顶点着色器(Vertex shaders)按顶点处理数据。这些数据(如位置、颜色、纹理坐标、法线)通过 WebGL 缓冲区对象(Buffer Objects)(特别是 VBO)从你的 JavaScript 应用程序提供。你需要明确告诉 WebGL 如何将 VBO 中的数据连接到顶点着色器中声明的对应 attribute(GLSL 1.00)或 in(GLSL 3.00)变量。

这种关联涉及三个主要步骤,通常在着色器链接和缓冲区填充后,在初始化阶段执行一次:

  1. **获取属性位置(Get Attribute Location):**找到着色器程序分配给你的属性变量的数值索引(位置)。
  2. **将属性指向缓冲区(Point Attribute to Buffer):**指定哪个缓冲区包含该属性的数据,并描述数据在该缓冲区中的布局(类型、大小、步长、偏移量)。
  3. **启用属性(Enable Attribute):**激活属性,以便 WebGL 在渲染时从中缓冲区拉取数据。

链接着色器程序后,WebGL 会为每个活动的属性分配一个索引(一个位置)。你需要此位置来配置属性。

使用 gl.getAttribLocation(program, name) 方法:

  • program:已链接的 WebGL 着色器程序对象。
  • name:一个字符串,包含顶点着色器源代码中属性变量的精确名称。

如果找到并激活了属性,它返回一个整数位置(>= 0),否则返回 -1(例如,如果属性名称拼写错误或属性因未使用而被优化掉)。

// Assume 'shaderProgram' is your linked program
// Vertex shader contains: attribute vec3 aVertexPosition;
const positionAttributeLocation = gl.getAttribLocation(shaderProgram, 'aVertexPosition');
if (positionAttributeLocation === -1) {
console.error("Could not find attribute 'aVertexPosition'");
} else {
console.log("Attribute 'aVertexPosition' location:", positionAttributeLocation);
}

(注意:在 GLSL ES 3.00 (WebGL 2) 中,你可以使用着色器中的 layout(location = N) 显式分配位置,如果你手动管理位置,则无需使用 getAttribLocation。)

2. 将属性指向 VBO (vertexAttribPointer)

Section titled “2. 将属性指向 VBO (vertexAttribPointer)”

这是核心步骤,你在此描述特定属性的数据如何在当前绑定的 gl.ARRAY_BUFFER(VBO)中布局。

使用 gl.vertexAttribPointer(location, size, type, normalized, stride, offset) 方法:

  • location:从 getAttribLocation 获取的整数位置。
  • size:每个顶点属性的分量数量(例如,vec3 为 3,vec2 为 2)。必须是 1、2、3 或 4。
  • type:缓冲区中每个分量的数据类型(例如,gl.FLOAT、gl.UNSIGNED_BYTE、gl.SHORT)。
  • normalized:一个布尔值。如果为 true,整数数据类型在着色器中作为浮点数访问时,将被归一化到 [-1, 1](对于有符号类型)或 [0, 1](对于无符号类型)的范围。对于浮点类型,设置为 false。
  • stride:连续顶点属性之间的字节偏移量。如果你的数据是紧密打包的(例如,只有位置:x,y,z, x,y,z,...),这可以是 0。如果你的数据是交错的(例如,位置和颜色:x,y,z, r,g,b, x,y,z, r,g,b,...),步长是一个顶点完整数据的总字节数(例如,(3 + 3) * bytesPerFloat)。值为 0 表示属性按顺序打包。
  • offset:缓冲区中该属性数据开始的字节偏移量。如果缓冲区仅包含该属性的数据,通常为 0;如果数据是交错的,则为偏移量。

至关重要的是,在调用 vertexAttribPointer 之前,你必须使用 gl.bindBuffer 将包含此属性数据的正确 VBO 绑定到 gl.ARRAY_BUFFER 目标。

示例(简单、非交错的位置数据):

Section titled “示例(简单、非交错的位置数据):”
// Assume 'positionAttributeLocation' holds the location of 'aVertexPosition'
// Assume 'positionBuffer' is a VBO containing tightly packed vec3 float positions
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); // Bind the correct VBO
const numComponents = 3; // vec3 has 3 components (x, y, z)
const type = gl.FLOAT; // Data is 32-bit floats
const normalize = false; // Don't normalize floats
const stride = 0; // 0 = move forward size * sizeof(type) each vertex
const offset = 0; // Start at the beginning of the buffer
gl.vertexAttribPointer(
positionAttributeLocation,
numComponents,
type,
normalize,
stride,
offset
);

最后,你必须启用顶点属性数组。这告诉 WebGL 在调用 gl.drawArrays 或 gl.drawElements 时,根据你刚刚使用 vertexAttribPointer 提供的规范,从缓冲区拉取数据。

使用 gl.enableVertexAttribArray(location) 方法:

  • location:要启用的属性的整数位置。
gl.enableVertexAttribArray(positionAttributeLocation);

如果需要,你也可以使用 gl.disableVertexAttribArray(location) 禁用属性。

顶点数组对象(VAOs)和属性状态

Section titled “顶点数组对象(VAOs)和属性状态”

如前所述,设置属性涉及多次调用(bindBuffer、vertexAttribPointer、enableVertexAttribArray)。顶点数组对象(Vertex Array Object,VAO)提供了一种捕获整个配置状态的方式。

绑定 VAO(gl.bindVertexArray(vao))时,所有后续对 gl.vertexAttribPointer、gl.enableVertexAttribArray、gl.disableVertexAttribArray 以及 gl.bindBuffer(当目标为 gl.ELEMENT_ARRAY_BUFFER 时)的调用都会被记录为此 VAO 状态的一部分。

稍后,在渲染过程中,只需绑定 VAO(gl.bindVertexArray(vao))即可立即恢复所有相关的属性配置和元素数组缓冲区绑定。这比为每个要绘制的对象单独重新应用所有设置要高效得多。

VAO 在 WebGL 1 中通过 OES_vertex_array_object 扩展可用,在 WebGL 2 中是标准的、必需的功能。对于任何非简单的 WebGL 应用程序,强烈建议使用它们。