在执行渲染之前,需要将各种顶点相关的数据传递到顶点着色器中,如位置、法线、UV等
顶点数据传递流程
flowchart TB
subgraph EBO["EBO"]
e1["glGenBuffers"] --> e2["glBindBuffer"] --> e3["glBufferData"] --> e4["glBindBuffer(TARGET, 0)"]
end
subgraph VBO["VBO"]
b1["glGenBuffers"] --> b2["glBindBuffer"] --> b3["glBufferData"]
b4["glBindBuffer(TARGET, 0)"]
end
subgraph VAO["VAO"]
a1["glGenVertexArrays"] --> a2["glBindVertexArray"] --> a3["glVertexAttribPointer"] --- a4["glEnableVertexAttribArray"] --> a5["glBindVertexArray(0)"]
end
subgraph delete["Delete"]
d1["glDeleteVertexArrays"]
d2["glDeleteBuffers"]
end
b3 --> a3
a5 --> b4 --> d2
e4 --> d2
a5 --> d1
style a5 stroke-width:2px,stroke-dasharray: 2
style b4 stroke-width:2px,stroke-dasharray: 2
style e4 stroke-width:2px,stroke-dasharray: 2
完整示例
unsigned int VAO, VBO, EBO;
struct VertexInfo {
glm::vec3 Position; // 顶点位置
glm::vec3 Normal; // 法线
glm::vec2 TexCoord; // 纹理坐标
}
// @param vbi 顶点信息数据
// @param ebi 索引数据
Mesh(const std::vector<VertexInfo> &vbi, const std::vector<unsigned int> &ebi) {
// 计算顶点信息数据大小
const auto vbi_size = vbi.size() * sizeof(VertexInfo);
// 计算索引数据大小
const auto indices_size = ebi.size() * sizeof(unsigned int);
/// 生成VAO
glGenVertexArrays(1, &VAO);
// 绑定VAO
glBindVertexArray(VAO);
// 处理顶点信息数据
/// 生成VBO
glGenBuffers(1, &VBO);
// 绑定VBO
glBindBuffer(GL_ARRAY_BUFFER, VBO);
/// 传入VBO数据
glBufferData(GL_ARRAY_BUFFER, vbi_size, vbi.data(), GL_STATIC_DRAW);
/// 设置锚定点
//// 位置 Position
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexInfo), reinterpret_cast<void *>(offsetof(VertexInfo, Position)));
glEnableVertexAttribArray(0);
//// 法线 Normal
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(VertexInfo), reinterpret_cast<void *>(offsetof(VertexInfo, Normal)));
glEnableVertexAttribArray(1);
//// 纹理坐标 TexCoord
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(VertexInfo), reinterpret_cast<void *>(offsetof(VertexInfo, TexCoord)));
glEnableVertexAttribArray(2);
// 处理顶点信息数据结束
// 处理索引数据
/// 生成EBO
glGenBuffers(1, &EBO);
// 绑定EBO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
/// 传入EBO数据
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices_size * sizeof(unsigned int), ebi.data(), GL_STATIC_DRAW);
// 解绑VAO
glBindVertexArray(0);
}
~Mesh() override {
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &EBO);
}缓冲区对象 (Buffer Object)
我们可以通过缓冲区对象将一大批数据一次性地发送到GPU上,从CPU把数据发送到GPU相对较慢,所以只要可能,我们都要尝试尽量一次性发送尽可能多的数据。
glGenBuffers
用于生成指定数量的缓冲区对象
void glGenBuffers(GLsizei n, GLuint *buffers)- 参数:
n—— 要生成的VBO数量buffers—— 用于存储VBO ID的变量的指针,生成多个VBO时需要传入数组指针
- 无返回值
glBindBuffer
将指定的缓冲区对象绑定到当前上下文中的某个目标(Target)
void glBindBuffer(GLenum target, GLuint buffer)- 参数:
target—— 指定缓冲区的绑定目标,将决定缓冲区的用途 ^aa4639GL_ARRAY_BUFFER—— 即VBO,存储顶点属性数据(如顶点坐标、法线、颜色等)GL_ELEMENT_ARRAY_BUFFER—— 即EBO,存储顶点索引数据GL_UNIFORM_BUFFER—— 即UBO,存储Uniform块数据...
buffer—— 缓冲区对象。如果为0,则解绑指定的目标。
- 无返回值
备注
- 可以对多个缓冲区目标同时分别绑定,不会相互影响
- 可以调用该函数并对参数
buffer传入0以解绑缓冲区目标,但通常不解绑,绑定可以被覆盖
glBufferData / glNamedBufferData
为缓冲区对象创建一个新的数据存储,原有的数据存储会被删除
void glBufferData(
GLenum target,
GLsizeiptr size,
const void *data,
GLenum usage,
)
void glNamedBufferData(
GLuint buffer,
GLsizeiptr size,
const void *data,
GLenum usage,
)- 参数:
target/buffer—— 缓冲区目标(^aa4639) / 缓冲区对象size—— 要存储数据的大小(单位:字节)data—— 一个指向数据的指针,该数据将被复制到数据存储中,数据的内存空间要连续usage—— 希望GPU如何管理给定的数据GL_STATIC_DRAW—— 数据不会或几乎不会改变GL_DYNAMIC_DRAW—— 数据会经常改变GL_STREAM_DRAW—— 数据每次绘制时都会改变
- 无返回值
glDeleteBuffers
用于删除缓冲区对象,并释放资源。注意:如果当前绑定的缓冲区对象被删除,则对应目标会自动解绑,绑定状态恢复为0。
void glDeleteBuffers(GLsizei n, const GLuint * buffers)参数参考glGenBuffers
顶点数组对象 VAO(Vertex Array Object)
VAO是一个用于管理顶点属性配置的对象,它并不直接存储顶点数据(如坐标、颜色、纹理等),而是存储如何从缓冲对象中解析顶点数据的配置。
glGenVertexArrays
用于生成指定数量的VAO
void glGenVertexArrays(GLsizei n, GLuint *arrays)- 参数:
n—— 要生成的VAO数量buffers—— 用于存储VAO ID的变量的指针,生成多个VAO时需要传入数组指针
- 无返回值
glBindVertexArray
用于绑定并使用VAO
void glBindVertexArray(GLuint array);- 参数:
array—— VAO ID
- 无返回值
备注
- 参数传入
0时,表示解除绑定VAO - 在实践中,VAO使用完毕后应当马上解除绑定
glVertexAttribPointer
定义一个顶点属性点(锚定点)
void glVertexAttribPointer(
GLuint index,
GLint size,
GLenum type,
GLboolean normalized,
GLsizei stride,
const void * pointer,
);- 参数:
index—— 顶点属性索引,与顶点着色器中in变量的location值对应size—— 一个顶点属性有几个值,如vec3的size为3,以此类推type—— 顶点属性中的值的数据类型,常见的有GL_FLOAT、GL_INT等normalized—— 是否要将传入的数据标准化stride—— 传入的数据中,每个顶点属性之间的字节偏移量pointer—— 数据中第一个元素的指针
- 无返回值

glEnableVertexAttribArray / glDisableVertexAttribArray
启用/禁用顶点属性,所有顶点属性默认禁用;当不再需要某个顶点属性时,最好禁用以优化性能
void glEnableVertexArrayAttrib([GLuint vaobj], GLuint index);
void glDisableVertexArrayAttrib([GLuint vaobj], GLuint index);- 参数:
vaobj—— (可省略)VAO ID,若不填写该参数请先使用glBindVertexArray绑定VAOindex—— 顶点属性索引,与顶点着色器中in变量的location值对应
- 无返回值
glDeleteVertexArrays
用于删除VAO,并释放资源。注意:如果当前绑定的VAO被删除,则会自动解绑,绑定状态恢复为0。
void glDeleteVertexArrays(GLsizei n, const GLuint *arrays);