OpenGL頂點緩衝區對象(VBO)

轉載c++

http://blog.csdn.net/dreamcs/article/details/7702701數組

建立VBO
        GL_ARB_vertex_buffer_object 擴展能夠提高OpenGL的性能。它提供了頂點數組和顯示列表,這避免了低效實現這些功能。Vertex buffer object (VBO) 容許頂點數據儲存在高性能顯卡上,即服務端的內存中,改善數據傳輸效率。若是緩衝區對象保存了像素數據,它就被稱作Pixel Buffer Object (PBO)。app

使用頂點數據能夠減小函數調用次數及複用共享頂點,然而,頂點數組的缺點是頂點函數及頂點數據在客戶端(me:對於OpenGL來講,顯卡爲服務端,其它爲客戶端),每次引用頂點數組時,都必須將頂點數據從客戶端(me:內存)發送到服務端(顯卡)。另外一方面,顯示列表是服務端的函數,它不會再重頭傳送數據。可是,一旦顯示列表被編譯了,顯示列表中的數據就不能修改了。ide

        Vertex buffer object (VBO) 爲頂點建立建立了一個緩衝區對象。緩衝區對象在服務端的高性能內存中,並提供了相同的函數,引用這些數組,如glVertexPointer(), glNormalPointer(), glTexCoordPointer(), 等等.頂點緩衝區內存管理器將緩衝區對象放在儲存器中最佳的位置。這依賴了用戶輸入的模式:"target"模式和"usage"模式。所以,儲存管理器能夠優化緩衝區,平衡三種內存:system ,AGP, video memory。與顯示列表不一樣的是,在頂點緩衝區對象中的數據能夠讀也能夠將它映射到服務端的內存空間中,而後更新它的數據。函數

       VBO另外一個重要的優勢是,能夠在許多客戶端中共享緩衝區對象,就像顯示列表和紋理那樣。因爲VBO在服務端,多個客戶端能夠經過對應的標識符訪問同一個緩衝區。性能


       建立VBO
       建立VBO的三個步驟:
      1.使用glGenBuffersARB()獲得一個新的緩衝區對象。
      2.Bind the buffer object with 使用glBindBufferARB()綁定一個緩衝區對象。
      3.使用glBufferDataARB()複製頂點數據到緩衝區對象。


     glGenBuffersARB()
     glGenBuffersARB()建立緩衝區對象並返回緩衝區對象的標識符。它須要兩個參數:第一個參數是要建立緩衝區的個數,第二個參數是一個GLuint變量的地址或保存ID的數組地址。void glGenBuffersARB(GLsizei n, GLuint* ids)

     glBindBufferARB()
     一旦緩衝區對象被建立,咱們要在使用緩衝區對象前將緩衝區對象與綁定ID。glBindBufferARB()有兩個參數:target和ID.void glBindBufferARB(GLenum target, GLuint id)

優化

      Targe告訴VBO是將儲存頂點數組(me:頂點數組的種類有頂點座標數組,頂點法向量數組,頂點紋理數組,頂點顏色數組等)仍是索引數組。儲存頂點數組時Targe爲GL_ARRAY_BUFFER_ARB,索引數組時爲GL_ELEMENT_ARRAY_BUFFER_ARB. 任何一種頂點屬性,如頂點座標,紋理座標,法向量和顏色數組將使用GL_ARRAY_BUFFER_ARB.索引數組用於glDraw[Range]Elements()函數,它將使用GL_ELEMENT_ARRAY_BUFFER_ARB參數.注意target標識幫助VBO決定緩衝區對象的最佳位置。如在一些系統中,索引數組在AGP或系統內存中可能更佳,頂點數組在顯卡上可能更好。ui

      一旦glBindBufferARB()被首次調用後,VBO初始化緩衝區對象,設置VBO的狀態,如使用和訪問屬性,但緩衝區對象的內存大小爲0。spa

     glBufferDataARB()
     在初始化時,可以使用glBufferDataARB函數,複製數據到緩衝區對象中。

      void glBufferDataARB(GLenum target, GLsizei size, const void* data, GLenum usage)
      第一個參數,target能夠是GL_ARRAY_BUFFER_ARB或者是GL_ELEMENT_ARRAY_BUFFER_ARB.
      Size是將被傳送的數據的字節數。
      第三個參數是源數據數組的指針。若是data是NULL指針,那麼VBO按數據大小分配儲存空間。
      最後一個參數,"usage"標誌是一個能夠提高性能的參數。VBO經過它知道緩衝區對象的使用策略:static,dynamic或stream,和 read,copy或draw.

      VBO指定了這個標誌的9個枚舉值
      GL_STATIC_DRAW_ARB
      GL_STATIC_READ_ARB
      GL_STATIC_COPY_ARB
      GL_DYNAMIC_DRAW_ARB
      GL_DYNAMIC_READ_ARB
      GL_DYNAMIC_COPY_ARB
      GL_STREAM_DRAW_ARB
      GL_STREAM_READ_ARB
      GL_STREAM_COPY_ARB


      static代表在VBO中的數據不會改變了。(指定一次數據,使用屢次)
      dynamic代表數據將被頻繁改變
      stream代表數據將在每一幀中被改變(指定一次,使用一次)
      draw代表數據將被髮送到GPU用於繪製圖形(程序到GL) read代表數據被客戶端讀取(GL到程序)
      copy代表數據便可用於drawing也可用於(GL到GL)

      注意只用draw標籤用於VBO。copy和read標籤只用於pixel/frame buffer object (PBO or FBO).VBO內存管理器將基於usage標誌爲緩衝區對象選擇最佳的內存位置。例如,GL_STATIC_DRAW_ARB和GL_STREAM_DRAW_ARB 可能在顯卡上,GL_DYNAMIC_DRAW_ARB可能使用AGP內存。任何與_READ_有關的緩衝區都是有益的,由於這樣作,訪問數組更容易。


      glBufferSubDataARB()
      void glBufferSubDataARB(GLenum target, GLint offset, GLsizei size, void* data).與glBufferDataARB()類似,glBufferSubDataARB()複製數據到VBO中。但它只代替指定範圍內的在緩衝區中的數據。指定範圍由給出的偏移值開始。 (在使用glBufferSubDataARB前,必須先使用glBufferDataARB()設置緩衝區對象的大小。

      glDeleteBuffersARB()
      void glDeleteBuffersARB(GLsizei n, const GLuint* ids)
      你可使用glDeleteBuffersARB刪除一個或多個VBO,若是它們再也不被使用了。當緩衝區對象被刪除後,它其中的內容就沒了。下面的代碼給出了這樣一個例子:建立一個頂點座標VBO。注意在你複製程序中的頂點數組到VBO後,你能夠釋放頂點數組的內存。.net

 1 GLuint vboId;                              // ID of VBO
 2 GLfloat* vertices = new GLfloat[vCount*3]; // create vertex array
 3 ...
 4 
 5 
 6 // generate a new VBO and get the associated ID
 7 glGenBuffersARB(1, &vboId);
 8 
 9 
10 // bind VBO in order to use
11 glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboId);
12 
13 
14 // upload data to VBO
15 glBufferDataARB(GL_ARRAY_BUFFER_ARB, dataSize, vertices, GL_STATIC_DRAW_ARB);
16 
17 
18 // it is safe to delete after copying data to VBO
19 delete [] vertices;
20 ...
21 
22 
23 // delete VBO when program terminated
24 glDeleteBuffersARB(1, &vboId);

<me>
glGenBuffersARB 做用是獲得一個沒有使用的緩衝區對象的ID。
glBindBufferARB 至關於聲明ID對應的對象的數據類型。至關於c++中數據類型聲明。
glBufferDataARB 至關於分對象分配內存。此內存應該是顯卡中。
</me>

繪製VBO

        因爲VBO是基於以實現了的vertex array方法,渲染VBO的方法幾乎於渲染vertex array的方法相同。惟一不一樣的是,原來指向頂點數組的指針,如今是一個偏移值(me:此值最爲關鍵,將n個頂點buffer合併成一個大buffer,這個偏移值指定了第i個buffer在大buffer中的起始位置). 所以,除了調用glBindBufferARB()函數外,繪製VBO不須要額外的API。

 1 // bind VBOs for vertex array and index array
 2 glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboId1);         // for vertex coordinates
 3 glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, vboId2); // for indices
 4 
 5 
 6 // do same as vertex array except pointer
 7 glEnableClientState(GL_VERTEX_ARRAY);             // activate vertex coords array
 8 glVertexPointer(3, GL_FLOAT, 0, 0);               // last param is offset, not ptr
 9 
10 
11 // draw 6 quads using offset of index array
12 glDrawElements(GL_QUADS, 24, GL_UNSIGNED_BYTE, 0);
13 
14 
15 glDisableClientState(GL_VERTEX_ARRAY);            // deactivate vertex array
16 
17 
18 // bind with 0, so, switch back to normal pointer operation
19 glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
20 glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);

以0綁定一個緩衝區對象,即關閉了VBO操做。在使用VBO後,關閉VBO是一個好主意。so normal vertex array operations with absolute pointers will be re-activated.

更新VBO

        VBO勝於顯示列表的一個優勢是,用戶能夠讀取和修改緩衝區對象中的數據,但顯示列表卻不能。更新VBO最簡單的方法是使用。在此例中,你的程序有一個在任什麼時候候都有效的頂點數組。即你必須老是有兩份頂點數據:一份在你的程序中,另外一份在VBO中。另外一個方法是映射一個緩衝區對象到客戶端的內存中(me:即程序使用的系統內存)。緩衝區對象映射到了一個數組指針上,客戶端使用此指針更新數據,近而更新了緩衝區對象中的數據。下面描述瞭如何映射VBO到客戶端的內存上,以及如休訪問被映射的數據。
glMapBufferARB()
VBO提供glMapBufferARB()函數,映射一個緩衝區對象到客戶端內存中。
void* glMapBufferARB(GLenum target, GLenum access)
若是OpenGL映射一個緩衝區對象到客戶端的地址空間中,那麼glMapBufferARB()返回一個指針,指向此緩衝區對象。不然返回NULL。
        第一個參數targe與先前提到的glBindBufferARB()的第一個參數相同,第二個參數,是一個訪問標識。指定如何操做映射到的數據:讀、寫或者是讀寫
        GL_READ_ONLY_ARB
        GL_WRITE_ONLY_ARB
        GL_READ_WRITE_ARB
        注意glMapBufferARB()會引起一個同步操做。若是GPU仍舊使用此緩衝區對象,glMapBufferARB()不會返回,直到GPU完成在此緩衝區上的工做。

        爲了不等待,你能夠首先調用glBufferDataARB並傳入空指針,而後調用glMapBufferARB()。此例中,先前的數據將被清除,glMapBufferARB()返回一個新分配的指針,即便GPU仍舊使用先前的數據。然而,此方法只在你想所有更新數據時有效,由於你要清除先前的數據。若是你想部分改變數據或讀數據,你最後是別清除之前的數據(-_-清除了還怎麼部分改變)
glUnmapBufferARB()
GLboolean glUnmapBufferARB(GLenum target)
         在修改VBO中的數據以後,必需要解除在客戶內存中映射的緩衝區對象。若是glUnmapBufferARB()調用成功,返回GL_TRUE。當返回GL_FALSE時,VBO中的內容corrupted當緩衝區對象被映射後。 The corruption results from screen resolution change or window system specific events. 在此例中,數據必須從新提交。
下面的代碼,簡單地使用了映射方法來修改VBO中的數據。

 1 // bind then map the VBO
 2 glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboId);
 3 float* ptr = (float*)glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
 4 
 5 
 6 // if the pointer is valid(mapped), update VBO
 7 if(ptr)
 8 {
 9     updateMyVBO(ptr, ...);                 // modify buffer data
10     glUnmapBufferARB(GL_ARRAY_BUFFER_ARB); // unmap it after use
11 }
12 
13 
14 // you can draw the updated VBO
15 ...

例子

 

 這個例子建立了一個VBO。並擺動其法向量。映射了一個VBO並在每一幀使用映射到緩衝區對象的指針,更新頂點,你能夠與傳統vertex array對比其性能。它使用了兩個頂點緩衝區:一個儲存了頂點座標及法向量,另外一個只儲存索引。 


下載源碼及exe: vbo.zip, vboSimple.zip.vboSimple是一個很簡單的例子。它使用VBO和Vertex Array畫出一個立方體。從中你能夠輕鬆看出VBO和VA之間的相同及不一樣之處。

相關文章
相關標籤/搜索