关联属性与缓冲区对象
WebGL:将着色器属性与缓冲区对象关联
Section titled “WebGL:将着色器属性与缓冲区对象关联”顶点着色器(Vertex shaders)按顶点处理数据。这些数据(如位置、颜色、纹理坐标、法线)通过 WebGL 缓冲区对象(Buffer Objects)(特别是 VBO)从你的 JavaScript 应用程序提供。你需要明确告诉 WebGL 如何将 VBO 中的数据连接到顶点着色器中声明的对应 attribute(GLSL 1.00)或 in(GLSL 3.00)变量。
这种关联涉及三个主要步骤,通常在着色器链接和缓冲区填充后,在初始化阶段执行一次:
- **获取属性位置(Get Attribute Location):**找到着色器程序分配给你的属性变量的数值索引(位置)。
- **将属性指向缓冲区(Point Attribute to Buffer):**指定哪个缓冲区包含该属性的数据,并描述数据在该缓冲区中的布局(类型、大小、步长、偏移量)。
- **启用属性(Enable Attribute):**激活属性,以便 WebGL 在渲染时从中缓冲区拉取数据。
1. 获取属性位置
Section titled “1. 获取属性位置”链接着色器程序后,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 floatsconst normalize = false; // Don't normalize floatsconst stride = 0; // 0 = move forward size * sizeof(type) each vertexconst offset = 0; // Start at the beginning of the buffer
gl.vertexAttribPointer( positionAttributeLocation, numComponents, type, normalize, stride, offset);3. 启用属性
Section titled “3. 启用属性”最后,你必须启用顶点属性数组。这告诉 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 应用程序,强烈建议使用它们。