一个openGL系统中可以有多个VBO,我们利用VBO来管理显存,VBO不是存储空间,一个VBO在显存中映射一块用于存储顶点数据的空间,并使用VBO中存储的数据来描述和管理这块空间,因此VBO是对象/结构体。系统中每次只能绑定一个VBO,绑定后就能访问到这个VBO对应的空间。绑定VBO就相当于在操作系统中更换页表,保证对存储空间访问的独立性。
VAO相当于一个进程控制块,主要用于保存进程上下文。当我们要切换进程时(更换渲染对象),我们需要切换EBO、顶点属性指针配置等,为了将这些切换操作进行封装,我们将这些需要切换的内容封装在VAO。于是所有的切换操作简化为一次VAO的bind。从VAO绑定到VAO解绑为止,glVertexAttribPointer、glEnableVertexAttribArray、glEnableVertexAttribArray、glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO)、glDisableVertexAttribArray产生作用的结果都会保存在VAO中。
注意:VAO不直接保存绑定的VBO,而是通过glVertexAttribPointer的调用,更新VAO中存储的指向显存的指针,及顶点数据存储的方式(stride)。当调用glVertexAttribPointer时,由于已经处在某个VBO的上下文中,所访问到的地址必定是当前VBO所对应的地址,直接将这个地址存储在指针中,不需要另外存储VBO。可以通过修改代码验证VAO的存储方式。
///create a vertex buffer obj
unsigned int VBO;
glGenBuffers(1, &VBO);
///bind the obj to mem
glBindBuffer(GL_ARRAY_BUFFER, VBO);
///cp data
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
///create a vertex array obj
unsigned int VAO;
glGenVertexArrays(1, &VAO);
///进入VAO的上下文 在此之前已进入VBO,使用VBO对应的存储空间
glBindVertexArray(VAO);
///create a element buffer obj
unsigned int EBO;
glGenBuffers(1, &EBO);
///绑定EBO 在VAO中记录EBO 用于下次绑定VAO时,绑定EBO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
///设置指针属性,并保存至VAO,注意此时仍使用之前绑定的VBO,因此指针指向VBO对应的存储区
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
///在解绑VAO之前解绑VBO不影响VAO的设置
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
结果:
修改解绑VBO的时间,如下:
///在设置指针属性之前解绑VBO
glBindBuffer(GL_ARRAY_BUFFER, 0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
//glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
结果:
在设置指针属性之前解绑VBO,导致不能正确的访问到VBO对应的存储区,不能为VAO的指针设置正确的地址,导致出错。
修改绑定VBO的时间,如下:
///create a vertex buffer obj
unsigned int VBO;
glGenBuffers(1, &VBO);
///bind the obj to mem
//glBindBuffer(GL_ARRAY_BUFFER, VBO);
///cp data
//glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
///create a vertex array obj
unsigned int VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
///create a element buffer obj
unsigned int EBO;
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
///修改绑定VBO的时间
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
结果:
只要在设置指针属性之前绑定VBO就能正确设置指针。
在解绑VAO之前解绑EBO,如下:
glBindBuffer(GL_ARRAY_BUFFER, 0);
///解绑EBO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArray(0);
结果:
VAO保存的是在解绑解绑VAO之前,最后的EBO状态,因此,VAO保存的EBO id是0,导致错误。由于VAO不直接存储VBO,在解绑VAO之前,是可以解绑VBO的。