最原始的設置頂點方法,在glBegin和glEnd之間使用。OpenGL3.0已經廢棄此方法。每一個glVertex與GPU進行一次通訊,十分低效。html
glBegin(GL_TRIANGLES); glVertex(0, 0); glVertex(1, 1); glVertex(2, 2); glEnd();
每一個glVertex調用都與GPU進行一次通訊,顯示列表是收集好全部的頂點,一次性的發送給GPU。缺點是在繪製以前就要把要傳給GPU的頂點準備好,傳後就不能修改了。編程
1 GLuint glassList; 2 glNewList(glassList, GL_COMPILE); 3 DrawGlass(); 4 glEndList(); 5 6 glCallList(glassList); //DrawGlass();
頂點數組也是收集好全部的頂點,一次性發送給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);
VBO,全稱爲Vertex Buffer Object,與FBO,PBO並稱,但它實際上老很多。就某種意義來講,他就是VA(Vertex Array)的升級版。VBO出現的背景是人們發現VA和顯示列表還有讓人不知足的地方。通常,在OpenGL裏,提升頂點繪製的辦法:app
(1)顯示列表:把常規的glBegin()-glEnd()中的代碼放到一個顯示列表中(一般在初始化階段完成),而後每遍渲染都調用這個顯示列表。函數
(2)VA:使用頂點數組,把頂點以及頂點屬性數據做爲數組,渲染的時候直接用一個或幾個函數調動這些數組裏的數據進行繪製,形式上是減小函數調用的次數(告別glVertex),提升繪製效率。優化
可是,這兩種方法都有缺點。VA是在客戶端設置的,因此執行這類函數(glDrawArray或glDrawElement)後,客戶端還得把獲得的頂點數據向服務端傳輸一次(所謂的「二次處理」),這樣一來就有了沒必要要的動做了,下降了效率——若是咱們寫的函數能直接把頂點數據發送給服務端就行了——這正是VBO的特性之一。顯示列表的缺點在於它的古板,一旦設定就不允許修改,因此它只適合對一些「固定」的東西的繪製進行包裝。(咱們無辦法直接在硬件層改頂點數據,由於這是脫離了流水線的事物)。而VBO直接把頂點數據交到流水線的第一步,與顯示列表的效率仍是有差距,但它這樣就獲得了操做數據的彈性——渲染階段,咱們的VBO繪製函數持續把頂點數據交給流水線,在某一刻咱們能夠把該幀到達了流水線的頂點數據取回客戶端修改(Vertex mapping),再提交回流水線(Vertex unmapping),或者用glBufferData或glBufferSubData從新所有或buffer提交修改了的頂點數據,這是VBO的另外一個特性。ui
VBO結合了VA和顯示列表這個說法不太穩當,應該說它結合了二者的一些特性,繪製效率在二者之間,且擁有良好的數據更改彈性。這種折衷造就了它一直爲目前最高的地位。spa
//建立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);
VBO將頂點信息放到GPU中,GPU在渲染時去緩存中取數據,兩者中間的橋樑是GL-Context。GL-Context整個程序通常只有一個,因此若是一個渲染流程裏有兩份不一樣的繪製代碼,GL-context就負責在他們之間進行切換。這也是爲何要在渲染過程當中,在每份繪製代碼之中會有glBindbuffer、glEnableVertexAttribArray、glVertexAttribPointer。那麼優化的方法來了,把這些都放到初始化時候完成吧!VAO記錄該次繪製所須要的全部VBO所需信息,把它保存到VBO特定位置,繪製的時候直接在這個位置取信息繪製。code
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
3.OpenGL超級寶典(第4版)
4.OpenGL ES 3.0 編程指南