VAO VBO 和 IBO
曾經有一個老師在教我們的時候,給我們說了一句很實在的話,當你接觸一個新的領域的時候,什麼最壓人?名詞最壓人,隨便一個專有名詞都夠你吃一壺的。事實上確實如此。
學習OpenGL首先,先要弄明白的三個名詞就是VAO,VBO和IBO。我們學習一個東西的時候,往往被過多,過詳細的數據,淹沒真正核心的那幾句話,這可能也是不同人學習能力不一樣的原因把,我應該屬於學習能力比較弱的那種,總是要一邊叮囑自己,關鍵那幾句話在哪?一邊看東西,才能勉強找到關鍵那幾句話。
本篇不是教程,不會說明這三個玩意的來龍去脈以及用法,只是在你已具備相應基礎的前提下,加深一下認知。
VAO:頂點數組對象,Vertex Array Object
VBO:頂點緩存對象,Vertex Buffer Object,VBO
IBO:頂點索引對象,Element Buffer Object,EBO或Index Buffer Object,IBO
借用一下LearnOpenGL網站上的圖。
最讓我迷惑的不是IBO,而是VAO的作用,其實這VAO和IBO一樣,都是爲了“複用”。而VBO關鍵作用在於“打包”上傳。
從上圖看來,VAO,其實是VBO的約束器,解讀器。它管理一類VBO的配置。VAO1,只用到一個屬性,VAO2則有兩個屬性,位置和顏色。同時還要用AttributePointer去描述VBO。
1、VAO
頂點數組對象:
使用VAO的好處——複用屬性設置。
1、VAO是可複用的
2、VAO可以綁定一系列頂點屬性
3、VAO只能和匹配的VBO綁定,比如你VAO只描述了位置,而VBO包含了頂點和顏色信息,他們不能綁定,否則數據就錯亂了
4、VAO解綁後可以綁定給其他匹配的VBO,這就是“複用”。
2、VBO
頂點緩存對象:
使用VBO的好處——批量數據傳輸。
具體來講:使用VBO好處是可以一次發送大量頂點到顯卡,而不是每次只發送一個頂點。
這裏額外思考一下,既然IO是相當費時的,那麼如果內存和顯卡可以異步傳輸數據是不是能節省掉一部分時間?比如先傳A數據,然後渲染A的同時傳B的樹呢?這應該就是多線程渲染做的事情,並不是真的多線程渲染,而是把渲染和IO交錯以節省時間。當然如果瓶頸不是IO,而是渲染本事,那多線程渲染也無力迴天了。
3、IBO
先對來講IBO是最好理解的,假定一個四邊形由兩個三角形組成,共六個頂點,實際上有兩對頂點共享,所以只有四個頂點,IBO用來告訴你,如何複用這些頂點。
比如0,1,3是一個三角形,1,2,3是另一個三角形。
這就是IBO的複用。
依據以上總結,在回頭來看這段源代碼,就思路就相對清晰了。
//聲明頂點數據,實際應用的時候,應該是從FBX等文件裏面讀取的。
//很少自己生成
float vertices[] = {
-0.5f, -0.5f, 0.0f, // left
0.5f, -0.5f, 0.0f, // right
0.0f, 0.5f, 0.0f // top
};
//創建頂點數組對象和頂點緩存對象
unsigned int VBO, VAO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
//綁定,或者說“選用”這個頂點緩存對象。
glBindVertexArray(VAO);
//綁定VBO對象,並拷貝數據到VBO
//注意數據不是拷貝給VAO的,VAO裏面不存頂點數據
//GL_STATIC_DRAW,這是不是Unity區分Static和non-static物件的原因?
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices),
vertices, GL_STATIC_DRAW);
//描述頂點,並把它在0號位置激活,一個VAO可以有多個描述對象。
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
//相當於解除綁定。注意是解綁,不是刪除。
glBindBuffer(GL_ARRAY_BUFFER, 0);
//簡單來講,按照綁定的反順序進行解綁就對了。先解綁VBO,再解綁VAO
glBindVertexArray(0);
//到此位置,該送入GPU的數據以及都“送”進去了
//開始進入渲染主循環
while (!glfwWindowShouldClose(window))
{
//處理輸入
processInput(window);
//清屏
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
//選擇shader,這裏不是本篇要講的內容,略過
glUseProgram(shaderProgram);
//因爲沒有其他數據,所以不用綁定解綁也沒事,這麼做是爲了規範。
//思考:當VBO有多個的時候,這裏要怎麼做?
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
glfwSwapBuffers(window);
glfwPollEvents();
}
//這裏纔是刪除緩存
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);