OpenGL中glVertex、顯示列表(glCallList)、頂點數組(Vertex array)、VBO及VAO區別


OpenGL中glVertex、顯示列表(glCallList)、頂點數組(Vertex array)、VBO及VAO區別

1.glVertex

  最原始的設置頂點方法,在glBegin和glEnd之間使用。OpenGL3.0已經廢棄此方法。每個glVertex與GPU進行一次通信,十分低效。

glBegin(GL_TRIANGLES);
	glVertex(0, 0);
	glVertex(1, 1);
	glVertex(2, 2);
glEnd();

2.顯示列表(glCallList)

  每個glVertex調用都與GPU進行一次通信,顯示列表是收集好所有的頂點,一次性的發送給GPU。缺點是在繪制之前就要把要傳給GPU的頂點准備好,傳后就不能修改了。

1 GLuint glassList;
2 glNewList(glassList, GL_COMPILE);
3     DrawGlass();
4 glEndList();
5 
6 glCallList(glassList); //DrawGlass();

3.頂點數組(Vertex Array)

  頂點數組也是收集好所有的頂點,一次性發送給GPU。不過數據不是存儲於GPU中的,繪制速度上沒有顯示列表快,優點是可以修改數據。

顯示列表和頂點數組都是過時的東西了,下面的VBO和VAO才是重點

#define MEDIUM_STARS   40
M3DVector2f vMediumStars[MEDIUM_STARS];
//在這做點vMediumStars的設置//
glVertexPointer(2, GL_FLOAT, 0, vMediumStars);
glDrawArrays(GL_POINTS, 0, MEDIUM_STARS);

4.VBO(Vertex Buffer Object)頂點緩沖區對象

  VBO,全稱為Vertex Buffer Object,與FBO,PBO並稱,但它實際上老不少。就某種意義來說,他就是VA(Vertex Array)的升級版。VBO出現的背景是人們發現VA和顯示列表還有讓人不滿足的地方。一般,在OpenGL里,提高頂點繪制的辦法:

 (1)顯示列表:把常規的glBegin()-glEnd()中的代碼放到一個顯示列表中(通常在初始化階段完成),然后每遍渲染都調用這個顯示列表。

 (2)VA:使用頂點數組,把頂點以及頂點屬性數據作為數組,渲染的時候直接用一個或幾個函數調動這些數組里的數據進行繪制,形式上是減少函數調用的次數(告別glVertex),提高繪制效率。

  但是,這兩種方法都有缺點。VA是在客戶端設置的,所以執行這類函數(glDrawArray或glDrawElement)后,客戶端還得把得到的頂點數據向服務端傳輸一次(所謂的“二次處理”),這樣一來就有了不必要的動作了,降低了效率——如果我們寫的函數能直接把頂點數據發送給服務端就好了——這正是VBO的特性之一。顯示列表的缺點在於它的古板,一旦設定就不容許修改,所以它只適合對一些“固定”的東西的繪制進行包裝。(我們無辦法直接在硬件層改頂點數據,因為這是脫離了流水線的事物)。而VBO直接把頂點數據交到流水線的第一步,與顯示列表的效率還是有差距,但它這樣就得到了操作數據的彈性——渲染階段,我們的VBO繪制函數持續把頂點數據交給流水線,在某一刻我們可以把該幀到達了流水線的頂點數據取回客戶端修改(Vertex mapping),再提交回流水線(Vertex unmapping),或者用glBufferData或glBufferSubData重新全部或buffer提交修改了的頂點數據,這是VBO的另一個特性。

      VBO結合了VA和顯示列表這個說法不太妥當,應該說它結合了兩者的一些特性,繪制效率在兩者之間,且擁有良好的數據更改彈性。這種折衷造就了它一直為目前最高的地位。

//創建VBO及VBO賦值
glGenBuffers(1, &m_nPositionVBO);
glBufferData(GL_ARRAY_BUFFER, 
	sizeof(posData), posData, GL_STREAM_DRAW);

glGenBuffers(1, &m_nTexcoordVBO);
glBufferData(GL_ARRAY_BUFFER,
	sizeof(texData), texData, GL_STREAM_DRAW);

glGenBuffers(1, &m_nIndexVBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, 
	sizeof(indexData), indexData, GL_STATIC_DRAW);

//代碼一,不使用shader VBO已經創建好了
glBindBuffer(GL_ARRAY_BUFFER, m_nPositionVBO);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, NULL);

glBindBuffer(GL_ARRAY_BUFFER, m_nTexcoordVBO);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, 0, NULL);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_nIndexVBO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, NULL);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, NULL);

glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);

glBindBuffer(GL_ARRAY_BUFFER, NULL);

//代碼二,使用shader
glBindBuffer(GL_ARRAY_BUFFER, m_nPositionVBO);
glEnableVertexAttribArray(VAT_POSITION);
glVertexAttribPointer(VAT_POSITION, 2, GL_INT, GL_FALSE, 0, NULL);

glBindBuffer(GL_ARRAY_BUFFER, m_nTexcoordVBO);
glEnableVertexAttribArray(VAT_TEXCOORD);
glVertexAttribPointer(VAT_TEXCOORD, 2, GL_INT, GL_FALSE, 0, NULL);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_nIndexVBO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, NULL);

glDisableVertexAttribArray(VAT_POSITION);
glDisableVertexAttribArray(VAT_TEXCOORD);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, NULL);
glBindBuffer(GL_ARRAY_BUFFER, NULL);

5.VAO(Vertex Array Object)頂點數組對象

  VBO將頂點信息放到GPU中,GPU在渲染時去緩存中取數據,二者中間的橋梁是GL-Context。GL-Context整個程序一般只有一個,所以如果一個渲染流程里有兩份不同的繪制代碼,GL-context就負責在他們之間進行切換。這也是為什么要在渲染過程中,在每份繪制代碼之中會有glBindbuffer、glEnableVertexAttribArray、glVertexAttribPointer。那么優化的方法來了,把這些都放到初始化時候完成吧!VAO記錄該次繪制所需要的所有VBO所需信息,把它保存到VBO特定位置,繪制的時候直接在這個位置取信息繪制。

  VAO的全名是Vertex Array Object,首先,它不是Buffer-Object,所以不用作存儲數據;其次,它針對“頂點”而言,也就是說它跟“頂點的繪制”息息相關。(VAO和VA沒有任何關系)

  VAO記錄的是一次繪制中所需要的信息,這包括“數據在哪里glBindBuffer”、“數據的格式是怎么樣的glVertexAttribPointer”、shader-attribute的location的啟用glEnableVertexAttribArray。

glGenBuffers(1, &m_nQuadPositionVBO);  
glBindBuffer(GL_ARRAY_BUFFER, m_nQuadPositionVBO);  
glBufferData(GL_ARRAY_BUFFER, sizeof(fQuadPos), fQuadPos, GL_STREAM_DRAW);  
 
glGenBuffers(1, &m_nQuadTexcoordVBO);  
glBindBuffer(GL_ARRAY_BUFFER, m_nQuadTexcoordVBO);  
glBufferData(GL_ARRAY_BUFFER, sizeof(fQuadTexcoord), fQuadTexcoord, GL_STREAM_DRAW);  

glGenBuffers(1, &m_nQuadIndexVBO);  
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_nQuadIndexVBO);  
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(nQuadIndex), nQuadIndex, GL_STREAM_DRAW);  

//VAO 初始化部分
glGenVertexArrays(1, &m_nQuadVAO);
glBindVertexArray(m_nQuadVAO);

//開始保存狀態
glBindBuffer(GL_ARRAY_BUFFER, m_nQuadPositionVBO);  
glEnableVertexAttribArray(VAT_POSITION);  
glVertexAttribPointer(VAT_POSITION, 2, GL_INT, GL_FALSE, 0, NULL);
  
glBindBuffer(GL_ARRAY_BUFFER, m_nQuadTexcoordVBO);  
glEnableVertexAttribArray(VAT_TEXCOORD);  
glVertexAttribPointer(VAT_TEXCOORD, 2, GL_INT, GL_FALSE, 0, NULL);  

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_nQuadIndexVBO);   
//保存結束
glBindVertexArray(NULL);

glBindBuffer(GL_ARRAY_BUFFER, NULL);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, NULL);

  以上就是VAO的使用方法了。VAO可以理解為一個狀態容器,記錄VBO的狀態。

參考:

1.學一學,VBO

2.AB是一家,VAO與VBO

3.OpenGL超級寶典(第4版)

4.OpenGL ES 3.0 編程指南

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM