Vertex Buffer Object(VBO)
一個openGL系統中可以有多個VBO,我們利用VBO來管理顯存,VBO不是存儲空間,一個VBO在顯存中映射一塊用於存儲頂點數據的空間,並使用VBO中存儲的數據來描述和管理這塊空間,因此VBO是對象/結構體。系統中每次只能綁定一個VBO,綁定後就能訪問到這個VBO對應的空間。綁定VBO就相當於在操作系統中更換頁表,保證對存儲空間訪問的獨立性。
Vertex Array Object(VAO)
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的。