WebGLRenderingContext: vertexAttribPointer() 方法
WebGL API 的 **WebGLRenderingContext.vertexAttribPointer()
** 方法将当前绑定到 gl.ARRAY_BUFFER
的缓冲区绑定到当前顶点缓冲区对象的通用顶点属性,并指定其布局。
语法
vertexAttribPointer(index, size, type, normalized, stride, offset)
参数
index
-
一个
GLuint
,指定要修改的顶点属性的索引。 size
-
一个
GLint
,指定每个顶点属性的组件数。必须为 1、2、3 或 4。 type
-
一个
GLenum
,指定数组中每个组件的数据类型。可能的值gl.BYTE
:带符号 8 位整数,值在 [-128, 127] 范围内gl.SHORT
:带符号 16 位整数,值在 [-32768, 32767] 范围内gl.UNSIGNED_BYTE
:无符号 8 位整数,值在 [0, 255] 范围内gl.UNSIGNED_SHORT
:无符号 16 位整数,值在 [0,65535] 范围内gl.FLOAT
:32 位 IEEE 浮点数
当使用 WebGL 2 上下文 时,以下值也可用
gl.HALF_FLOAT
:16 位 IEEE 浮点数gl.INT
:32 位带符号二进制整数gl.UNSIGNED_INT
:32 位无符号二进制整数gl.INT_2_10_10_10_REV
:32 位带符号整数,值在 [-512, 511] 范围内gl.UNSIGNED_INT_2_10_10_10_REV
:32 位无符号整数,值在 [0, 1023] 范围内
normalized
-
一个
GLboolean
,指定在转换为浮点数时是否应将整数数据值规范化到某个范围内。- 对于类型
gl.BYTE
和gl.SHORT
,如果为真,则将值规范化到 [-1, 1]。 - 对于类型
gl.UNSIGNED_BYTE
和gl.UNSIGNED_SHORT
,如果为真,则将值规范化到 [0, 1]。 - 对于类型
gl.FLOAT
和gl.HALF_FLOAT
,此参数无效。
- 对于类型
stride
-
一个
GLsizei
,指定连续顶点属性开始位置之间的偏移量(以字节为单位)。不能为负数或大于 255。如果 stride 为 0,则假定属性紧密打包,即属性不交错,但每个属性都在一个单独的块中,并且下一个顶点的属性紧跟在当前顶点之后。 offset
-
一个
GLintptr
,指定顶点属性数组中第一个组件的偏移量(以字节为单位)。必须是type
的字节长度的倍数。
返回值
无 (undefined
)。
异常
- 如果
stride
或offset
为负数,则会抛出gl.INVALID_VALUE
错误。 - 如果
stride
和offset
不是数据类型大小的倍数,则会抛出gl.INVALID_OPERATION
错误。 - 如果没有 WebGLBuffer 绑定到 ARRAY_BUFFER 目标,则会抛出
gl.INVALID_OPERATION
错误。 - 当使用 WebGL 2 上下文 时,如果此顶点属性在顶点着色器中定义为整数(例如
uvec4
或ivec4
,而不是vec4
),则会抛出gl.INVALID_OPERATION
错误。
描述
假设我们要渲染一些 3D 几何图形,为此我们需要将顶点提供给顶点着色器。每个顶点都有一些属性,例如位置、法线向量或纹理坐标,这些属性在 ArrayBuffer
中定义,并将提供给顶点缓冲区对象 (VBO)。首先,我们需要将我们要使用的 WebGLBuffer
绑定到 gl.ARRAY_BUFFER
,然后,使用此方法 gl.vertexAttribPointer()
,我们指定属性存储的顺序以及它们的数据类型。此外,我们需要包含步长,它是单个顶点所有属性的总字节长度。此外,我们还需要调用 gl.enableVertexAttribArray()
来告诉 WebGL 此属性应使用我们的数组缓冲区中的数据填充。
通常,您的 3D 几何图形已采用某种二进制格式,因此您需要阅读该特定格式的规范以了解内存布局。但是,如果您自己设计格式,或者您的几何图形位于文本文件中(例如 Wavefront .obj 文件)并且必须在运行时转换为 ArrayBuffer
,则您可以自由选择如何构建内存。为了获得最高的性能,交错 属性并使用仍然能够准确表示几何图形的最小数据类型。
顶点属性的最大数量取决于显卡,您可以调用 gl.getParameter(gl.MAX_VERTEX_ATTRIBS)
来获取此值。在高端显卡上,最大值为 16,在低端显卡上,该值将更低。
属性索引
对于每个属性,您必须指定其索引。这与数组缓冲区内的位置无关,因此您可以按与数组缓冲区中存储顺序不同的顺序发送属性。您有两个选择
- 您可以自己指定索引。在这种情况下,您需要调用
gl.bindAttribLocation()
将顶点着色器中的命名属性连接到您想要使用的索引。这必须在调用gl.linkProgram()
之前完成。然后,您可以将相同的索引提供给gl.vertexAttribPointer()
。 - 或者,您可以使用图形卡在编译顶点着色器时分配的索引。根据图形卡的不同,索引会有所不同,因此您必须调用
gl.getAttribLocation()
来查找索引,然后将此索引提供给gl.vertexAttribPointer()
。如果您使用的是WebGL 2,则可以在顶点着色器代码中自己指定索引并覆盖图形卡使用的默认值,例如layout(location = 3) in vec4 position;
将"position"
属性设置为索引3。
整数属性
虽然ArrayBuffer
可以填充整数和浮点数,但属性在发送到顶点着色器时始终会被转换为浮点数。如果您需要在顶点着色器代码中使用整数,则可以在顶点着色器中将浮点数强制转换为整数(例如(int) floatNumber
),或者使用WebGL 2中的gl.vertexAttribIPointer()
。
默认属性值
顶点着色器代码可能包含多个属性,但我们不需要为每个属性指定值。相反,我们可以提供一个默认值,该值对于所有顶点都相同。我们可以调用gl.disableVertexAttribArray()
告诉WebGL使用默认值,而调用gl.enableVertexAttribArray()
将从数组缓冲区读取值,如gl.vertexAttribPointer()
中指定的那样。
类似地,如果我们的顶点着色器期望例如具有vec4
的4个分量属性,但在我们的gl.vertexAttribPointer()
调用中我们将size
设置为2
,则WebGL将根据数组缓冲区设置前两个分量,而第三和第四个分量将取自默认值。
默认值为vec4(0.0, 0.0, 0.0, 1.0)
,但我们可以使用gl.vertexAttrib[1234]f[v]()
指定不同的默认值。
例如,您的顶点着色器可能正在使用位置和颜色属性。大多数网格在每个顶点级别都指定了颜色,但有些网格是统一色调的。对于这些网格,没有必要将每个顶点的相同颜色放入数组缓冲区中,因此您可以使用gl.vertexAttrib4fv()
设置常量颜色。
查询当前设置
您可以调用gl.getVertexAttrib()
和gl.getVertexAttribOffset()
获取属性的当前参数,例如数据类型或是否应规范化属性。请记住,这些WebGL函数的性能较慢,最好将状态存储在您的JavaScript应用程序中。但是,这些函数非常适合调试WebGL上下文,而无需触及应用程序代码。
示例
此示例演示如何将顶点属性发送到着色器程序。我们使用一个虚拟的数据结构,其中每个顶点的属性与每个顶点长度为20字节交错存储
- 位置:我们需要存储X、Y和Z坐标。为了获得最高的精度,我们使用32位浮点数;总共使用12字节。
- 法线向量:我们需要存储法线向量的X、Y和Z分量,但由于精度不是那么重要,因此我们使用8位有符号整数。为了获得更好的性能,我们通过还存储第四个零值分量将数据对齐到32位,从而使总大小达到4字节。此外,我们告诉WebGL规范化值,因为我们的法线始终在[-1, 1]范围内。
- 纹理坐标:我们需要存储U和V坐标;对于此16位无符号整数提供了足够的精度,总大小为4字节。我们还告诉WebGL将值规范化到[0, 1]。
例如,以下顶点
{
"position": [1.0, 2.0, 1.5],
"normal": [1.0, 0.0, 0.0],
"texCoord": [0.5, 0.25]
}
将在数组缓冲区中按以下方式存储
创建数组缓冲区
首先,我们使用DataView
从JSON数据动态创建数组缓冲区。请注意使用了true
,因为WebGL期望我们的数据为小端序。
// Load geometry with fetch() and Response.json()
const response = await fetch("assets/geometry.json");
const vertices = await response.json();
// Create array buffer
const buffer = new ArrayBuffer(20 * vertices.length);
// Fill array buffer
const dv = new DataView(buffer);
vertices.forEach((vertex, i) => {
dv.setFloat32(20 * i, vertex.position[0], true);
dv.setFloat32(20 * i + 4, vertex.position[1], true);
dv.setFloat32(20 * i + 8, vertex.position[2], true);
dv.setInt8(20 * i + 12, vertex.normal[0] * 0x7f);
dv.setInt8(20 * i + 13, vertex.normal[1] * 0x7f);
dv.setInt8(20 * i + 14, vertex.normal[2] * 0x7f);
dv.setInt8(20 * i + 15, 0);
dv.setUint16(20 * i + 16, vertex.texCoord[0] * 0xffff, true);
dv.setUint16(20 * i + 18, vertex.texCoord[1] * 0xffff, true);
});
为了获得更高的性能,我们也可以在服务器端(例如使用Node.js)进行先前的JSON到ArrayBuffer转换。然后,我们可以加载二进制文件并将其解释为数组缓冲区
const response = await fetch("assets/geometry.bin");
const buffer = await response.arrayBuffer();
使用WebGL使用数组缓冲区
首先,我们创建一个新的顶点缓冲区对象(VBO)并向其提供我们的数组缓冲区
//Bind array buffer to a Vertex Buffer Object
const vbo = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
gl.bufferData(gl.ARRAY_BUFFER, buffer, gl.STATIC_DRAW);
然后,我们指定数组缓冲区的内存布局,要么通过自己设置索引
//Describe the layout of the buffer:
//1. position, not normalized
gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 20, 0);
gl.enableVertexAttribArray(0);
//2. normal vector, normalized to [-1, 1]
gl.vertexAttribPointer(1, 4, gl.BYTE, true, 20, 12);
gl.enableVertexAttribArray(1);
//3. texture coordinates, normalized to [0, 1]
gl.vertexAttribPointer(2, 2, gl.UNSIGNED_SHORT, true, 20, 16);
gl.enableVertexAttribArray(2);
//Set the attributes in the vertex shader to the same indices
gl.bindAttribLocation(shaderProgram, 0, "position");
gl.bindAttribLocation(shaderProgram, 1, "normal");
gl.bindAttribLocation(shaderProgram, 2, "texUV");
//Since the attribute indices have changed, we must re-link the shader
//Note that this will reset all uniforms that were previously set.
gl.linkProgram(shaderProgram);
或者我们可以使用图形卡提供的索引而不是自己设置索引;这避免了重新链接着色器程序。
const locPosition = gl.getAttribLocation(shaderProgram, "position");
gl.vertexAttribPointer(locPosition, 3, gl.FLOAT, false, 20, 0);
gl.enableVertexAttribArray(locPosition);
const locNormal = gl.getAttribLocation(shaderProgram, "normal");
gl.vertexAttribPointer(locNormal, 4, gl.BYTE, true, 20, 12);
gl.enableVertexAttribArray(locNormal);
const locTexUV = gl.getAttribLocation(shaderProgram, "texUV");
gl.vertexAttribPointer(locTexUV, 2, gl.UNSIGNED_SHORT, true, 20, 16);
gl.enableVertexAttribArray(locTexUV);
规范
规范 |
---|
WebGL规范 # 5.14.10 |
浏览器兼容性
BCD表仅在浏览器中加载