- OpenGL顶点规范和绘图命令 Vertex Specification and Drawing Commands
- 图元类型 Primitive Types
- 当前顶点属性值 Current Vertex Attribute Values
- 顶点数组 Vertex Arrays
- 通用顶点属性的数组 Specifying Arrays for Generic Vertex Attributes
- 顶点属性除数 Vertex Attribute Divisors
- 使用顶点数组绘制命令 Drawing Commands Using Vertex Arrays
- 条件渲染 Conditional Rendering
在OpenGL中,为了设置顶点数组对象(VAO)中通用顶点属性数组的组织结构,以及如何将缓冲对象绑定到VAO以便提供顶点数据,您可以使用以下函数:
-
设置顶点属性格式:
void VertexAttribFormat( uint attribindex, int size, enum type, boolean normalized, uint relativeoffset ); void VertexAttribIFormat( uint attribindex, int size, enum type, uint relativeoffset ); void VertexAttribLFormat( uint attribindex, int size, enum type, uint relativeoffset ); void VertexArrayAttribFormat( uint vaobj, uint attribindex, int size, enum type, boolean normalized, uint relativeoffset ); void VertexArrayAttribIFormat( uint vaobj, uint attribindex, int size, enum type, uint relativeoffset ); void VertexArrayAttribLFormat( uint vaobj, uint attribindex, int size, enum type, uint relativeoffset );
VertexAttribFormat
、VertexAttribIFormat
和VertexAttribLFormat
这三个函数用于指定每个顶点属性数组的布局。它们分别对应浮点数、整数和双精度浮点数类型的属性。- 同样地,对于特定VAO,有对应的
VertexArrayAttribFormat
、VertexArrayAttribIFormat
和VertexArrayAttribLFormat
函数。
这些函数的参数包括:
attribindex
:要配置的顶点属性索引。size
:每个顶点中该属性所包含的值的数量(如3表示一个三维向量)。type
:存储在数组中的数据类型(如GL_FLOAT、GL_UNSIGNED_INT等)。normalized
(仅适用于某些类型):如果为true,则表示整型数据应归一化到[0, 1]或[-1, 1]区间。relativeoffset
:相对于关联的顶点缓冲开始位置的偏移量(以字节计)。
// 生成并绑定顶点数组对象 (VAO) GLuint vao; glGenVertexArrays(1, &vao); glBindVertexArray(vao); // 生成并绑定顶点缓冲对象 (VBO) GLuint vbo; glGenBuffers(1, &vbo); glBindBuffer(GL_ARRAY_BUFFER, vbo); // 顶点数据 GLfloat vertices[] = { 0.0f, 0.5f, 0.0f, // 顶点1坐标 -0.5f, -0.5f, 0.0f, // 顶点2坐标 0.5f, -0.5f, 0.0f // 顶点3坐标 }; // 将顶点数据传递到顶点缓冲中 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 配置顶点属性 // 在这里,我们使用通用顶点属性0表示顶点位置,每个顶点有3个分量 (x, y, z),浮点型数据 glVertexAttribFormat(0, 3, GL_FLOAT, GL_FALSE, 0); // 启用顶点属性数组 glEnableVertexAttribArray(0); // 设置顶点缓冲绑定点,此处为0 glBindVertexBuffer(0, vbo, 0, 3 * sizeof(GLfloat)); // 解绑顶点数组对象和顶点缓冲对象 glBindVertexArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); // 渲染循环中的绘制代码...
// 生成并绑定顶点数组对象 (VAO) GLuint vao; glCreateVertexArrays(1, &vao); // 生成并绑定顶点缓冲对象 (VBO) GLuint vbo; glCreateBuffers(1, &vbo); // 顶点数据 GLfloat vertices[] = { 0.0f, 0.5f, 0.0f, // 顶点1坐标 -0.5f, -0.5f, 0.0f, // 顶点2坐标 0.5f, -0.5f, 0.0f // 顶点3坐标 }; // 将顶点数据传递到顶点缓冲中 glNamedBufferData(vbo, sizeof(vertices), vertices, GL_STATIC_DRAW); // 配置顶点属性 // 在这里,我们使用通用顶点属性0表示顶点位置,每个顶点有3个分量 (x, y, z),浮点型数据 glVertexArrayAttribFormat(vao, 0, 3, GL_FLOAT, GL_FALSE, 0); // 启用顶点属性数组 glEnableVertexArrayAttrib(vao, 0); // 设置顶点缓冲绑定点,此处为0 glVertexArrayVertexBuffer(vao, 0, vbo, 0, 3 * sizeof(GLfloat)); // 解绑顶点数组对象和顶点缓冲对象 // 注意:此处不再需要调用 glBindVertexArray 和 glBindBuffer // Vertex Array Object (VAO) 包含了顶点缓冲的状态,因此在渲染时只需绑定 VAO 即可 // Vertex Buffer Object (VBO) 也包含在 VAO 绑定状态中 // 不同于 glBindBuffer,glBindVertexArray 不再需要
-
绑定顶点缓冲:
void BindVertexBuffer( uint bindingindex, uint buffer, intptr offset, sizei stride ); void VertexArrayVertexBuffer( uint vaobj, uint bindingindex, uint buffer, intptr offset, sizei stride );
- 使用
BindVertexBuffer
或VertexArrayVertexBuffer
命令将一个缓冲对象绑定到VAO的特定绑定点,作为顶点数据源。bindingindex
:指定顶点缓冲区的绑定点。buffer
:要绑定的缓冲对象名称,若为0,则会解除当前绑定。offset
:从缓冲起始位置的偏移量。stride
:连续两个顶点之间的字节数。
- 使用
-
同时绑定多个顶点缓冲:
void BindVertexBuffers( uint first, sizei count, const uint *buffers, const intptr *offsets, const sizei *strides ); void VertexArrayVertexBuffers( uint vaobj, uint first, sizei count, const uint *buffers, const intptr *offsets, const sizei *strides );
-
BindVertexBuffers
和VertexArrayVertexBuffers
函数可以一次绑定多个缓冲对象到不同的顶点缓冲区绑定点。for (i = 0; i < count; i++) { if (buffers == NULL) { BindVertexBuffer(first + i, 0, 0, 16); } else { BindVertexBuffer(first + i, buffers[i], offsets[i], strides[i]); } } for (i = 0; i < count; i++) { if (buffers == NULL) { VertexArrayVertexBuffer(vaobj, first + i, 0, 0, 16); } else { VertexArrayVertexBuffer(vaobj, first + i, buffers[i], offsets[i], strides[i]); } }
// 生成并绑定顶点数组对象 (VAO) GLuint vao; glCreateVertexArrays(1, &vao); // 生成并绑定顶点缓冲对象 (VBO1) GLuint vbo1; glCreateBuffers(1, &vbo1); // 生成并绑定顶点缓冲对象 (VBO2) GLuint vbo2; glCreateBuffers(1, &vbo2); // 顶点数据1 GLfloat vertices1[] = { 0.0f, 0.5f, 0.0f, // 顶点1坐标 -0.5f, -0.5f, 0.0f, // 顶点2坐标 0.5f, -0.5f, 0.0f // 顶点3坐标 }; // 顶点数据2 GLfloat vertices2[] = { 1.0f, 0.0f, 0.0f, // 顶点4坐标 0.0f, -1.0f, 0.0f, // 顶点5坐标 1.0f, -1.0f, 0.0f // 顶点6坐标 }; // 将顶点数据传递到 VBO1 中 glNamedBufferData(vbo1, sizeof(vertices1), vertices1, GL_STATIC_DRAW); // 将顶点数据传递到 VBO2 中 glNamedBufferData(vbo2, sizeof(vertices2), vertices2, GL_STATIC_DRAW); // 配置顶点属性1 glVertexArrayAttribFormat(vao, 0, 3, GL_FLOAT, GL_FALSE, 0); glEnableVertexArrayAttrib(vao, 0); glVertexArrayVertexBuffer(vao, 0, vbo1, 0, 3 * sizeof(GLfloat)); // 配置顶点属性2 glVertexArrayAttribFormat(vao, 1, 3, GL_FLOAT, GL_FALSE, 0); glEnableVertexArrayAttrib(vao, 1); glVertexArrayVertexBuffer(vao, 1, vbo2, 0, 3 * sizeof(GLfloat)); // 使用 BindVertexBuffers 一次性绑定多个 VBO GLuint buffers[] = {vbo1, vbo2}; GLintptr offsets[] = {0, 0}; GLsizei strides[] = {3 * sizeof(GLfloat), 3 * sizeof(GLfloat)}; // 0 和 1 表示两个顶点数组对象的索引 glBindVertexBuffers(0, 2, buffers, offsets, strides); // 使用 VertexArrayVertexBuffers 一次性绑定多个 VBO // 这里使用 0 和 1 表示两个顶点数组对象的索引 glVertexArrayVertexBuffers(vao, 0, 2, buffers, offsets, strides);
-
-
关联顶点属性与缓冲绑定:
void VertexAttribBinding( uint attribindex, uint bindingindex ); void VertexArrayAttribBinding( uint vaobj, uint attribindex, uint bindingindex );
-
使用
VertexAttribBinding
或VertexArrayAttribBinding
命令来指定哪个顶点属性应该从哪个缓冲绑定点读取数据。// 生成并绑定顶点数组对象 (VAO) GLuint vao; glCreateVertexArrays(1, &vao); // 生成并绑定顶点缓冲对象 (VBO) GLuint vbo; glCreateBuffers(1, &vbo); // 顶点数据 GLfloat vertices[] = { 0.0f, 0.5f, 0.0f, // 顶点1坐标 -0.5f, -0.5f, 0.0f, // 顶点2坐标 0.5f, -0.5f, 0.0f // 顶点3坐标 }; // 将顶点数据传递到 VBO 中 glNamedBufferData(vbo, sizeof(vertices), vertices, GL_STATIC_DRAW); // 绑定 VBO 到指定的绑定点 glVertexArrayVertexBuffer(vao, 0, vbo, 0, 3 * sizeof(GLfloat)); // 配置顶点属性 glVertexArrayAttribFormat(vao, 0, 3, GL_FLOAT, GL_FALSE, 0); glEnableVertexArrayAttrib(vao, 0); // 将顶点属性绑定到指定的绑定点 glVertexArrayAttribBinding(vao, 0, 0); // 此时,顶点属性数组已经绑定到了顶点缓冲对象的绑定点 // ... 绘制代码 ... // 在这个例子中,我们首先创建了一个顶点数组对象(VAO)和一个顶点缓冲对象(VBO)。 // 使用 glNamedBufferData 将顶点数据传递到 VBO 中。 // 然后,通过 glVertexArrayVertexBuffer 将 VBO 绑定到 VAO 的指定绑定点(这里是 0)。 // 接下来,配置了顶点属性,包括属性的格式和启用属性。 // 最后,使用 glVertexArrayAttribBinding 函数将顶点属性绑定到指定的绑定点,这里是将顶点属性数组 0 绑定到绑定点 0。 //这样,顶点属性数组就成功地绑定到了顶点缓冲对象的指定绑定点,可以在绘制时使用这个顶点数组对象了。
-
-
设置顶点属性指针:
void VertexAttribPointer( uint index, int size, enum type, boolean normalized, sizei stride, const void *pointer ); void VertexAttribIPointer( uint index, int size, enum type, sizei stride, const void *pointer ); void VertexAttribLPointer( uint index, int size, enum type, sizei stride, const void *pointer );
-
VertexAttribPointer
、VertexAttribIPointer
和VertexAttribLPointer
函数实际上同时设置了顶点属性状态、顶点缓冲区绑定及两者间的映射关系。当调用这些函数时,系统会根据给定的大小、类型、是否归一化、步长和指针地址自动设置相关状态,并且绑定相应的缓冲对象。- index: 顶点着色器中顶点属性的位置索引。
- size: 每个顶点属性的分量数量(例如,3 表示三维坐标)。
- type: 数据类型(例如,GL_FLOAT 表示浮点数)。
- normalized: 是否需要归一化数据。
- stride: 相邻两个顶点属性之间的字节偏移量。
- pointer: 第一个顶点属性的起始内存偏移量。
// VertexAttribPointer // 等价于 VertexAttrib*Format(index, size, type, {normalized, }, 0); VertexAttribBinding(index, index); if (stride != 0) { effectiveStride = stride; } else { compute effectiveStride based on size and type; } VERTEX_ATTRIB_ARRAY_STRIDE[index] = stride; // This sets VERTEX_BINDING_STRIDE to effectiveStride VERTEX_ATTRIB_ARRAY_POINTER[index] = pointer; BindVertexBuffer(index, buffer bound to ARRAY_BUFFER, (char *)pointer - (char *)NULL, effectiveStride);
-
-
启用/禁用顶点属性:
void EnableVertexAttribArray( uint index ); void EnableVertexArrayAttrib( uint vaobj, uint index ); void DisableVertexAttribArray( uint index ); void DisableVertexArrayAttrib( uint vaobj, uint index );
- 使用
EnableVertexAttribArray
和DisableVertexAttribArray
(或者针对特定VAO的EnableVertexArrayAttrib
和DisableVertexArrayAttrib
)来启用或禁用某个通用顶点属性数组参与渲染过程。
- 使用
总的来说,在OpenGL编程中,开发者需要通过以上API详细地配置顶点数组对象,从而定义出正确格式化的顶点数据流,方便GPU高效地处理并渲染图形。