OpenGL系列教程之八:OpenGL頂點緩衝區對象(VBO)

來源:互聯網
上載者:User

相關主題:頂點數組,顯示列表,像素緩衝區對象

下載:vbo.zip, vboSimple.zip

建立VBO 繪製VBO 更新VBO 例子
GL_ARB_vertex_buffer_object擴充通過提供頂點數組和顯示列表的優點並且避免它們的不足提高了OpenGL的效能。頂點緩衝區對象(vertex buffer object,VBO)允許頂點數組儲存在位於伺服器端的高效能的圖形記憶體中,並且提升了資料轉送的效率。如果緩衝區對象用來儲存像素資料,那麼它叫做像素緩衝區對象(PBO)。
使用頂點資料可以減少函數調用的次數和共用頂點的冗餘使用,但是,頂點數組的缺點是頂點數組中的函數位於用戶端並且數組中的資料在它每次被引用時都需要發送到伺服器端一次。
相反,顯示列表位於伺服器端,因此它不需要忍受資料轉送的開銷。但是,當一個顯示列表編譯後,顯示列表裡的資料就不能夠再修改了。
頂點緩衝區對象(VBO)在伺服器端高效能的記憶體中為頂點屬性創造了一個“緩衝區對象”,並且提供了一些函數來引用在頂點數組中使用的數組,如glVertexPointer(),glNormalPointer(),glTexCoordPointer()等。
頂點緩衝區對象(VBO)中的記憶體管理根據使用者定義的"目標(target)"和“用法(usage)”模式,將緩衝區對象放置在記憶體中最合適的地方。因此,記憶體管理通過在3中不同的記憶體(系統記憶體,AGP,顯卡記憶體)中協調能最佳化緩衝區。
不像顯示列表,通過將緩衝區映射到用戶端的記憶體地區中頂點緩衝區中的資料可以被讀取或者更新。
頂點緩衝區對象(VBO)的另外一個優點是緩衝區對象可以被多個用戶端共用,像顯示列表和紋理。由於頂點緩衝區對象(VBO)位於伺服器端,多個用戶端使用相關聯的標示符可以訪問相同的緩衝區。

建立VBO
建立VBO需要3步: 使用glGenBuffersARB()產生一個緩衝區對象; 使用glBindBufferARB()綁定一個緩衝區對象; 使用glBufferDataARB()將頂點資料複製到緩衝區對象中。

glGenBuffersARB() glGenBuffersARB()建立一個緩衝區對象並且返回這個緩衝區對象的標示。它需要兩個參數:第一個參數指示要建立的緩衝區對象的個數,第二個參數指示存放返回一個或多個緩衝區標示的GLuint類型變數或數組的地址。

<span style="white-space:pre"></span>void glGenBuffersARB(GLsizei n, GLuint* ids)


glBindBufferARB() 當緩衝區對象建立以後,在使用緩衝區對象之前我們需要將緩衝區對象的標示綁定。glBindBufferARB()需要兩個參數:target和ID。
<span style="white-space:pre"></span>void glBindBufferARB(GLenum target, GLuint id)

target是告訴VBO這個緩衝區對象是用來儲存頂點數組資料還是用來儲存索引數組資料的:GL_ARRAY_BUFFER_ARB或 GL_ELEMENT_ARRAY_BUFFER_ARB 。任何頂點屬性,如頂點座標,紋理座標,法向量,和顏色資訊需要使用GL_ARRAY_BUFFER_ARB作為target。而像glDraw[Range]Elements()函數使用的索引數組則需要與GL_ELEMENT_ARRAY_BUFFER_ARB綁定。注意這個target標示協助VBO決定頂點緩衝區的最佳位置,例如,有些系統將會將索引數組放置在AGP或系統記憶體中,而將頂點數組放置在顯卡記憶體中。
當glBindBufferARB()被首次調用的時候,VBO使用大小為0的記憶體區初始化這個緩衝區並且設定了這個VBO的初始狀態,例如usage和訪問屬性。

glBufferDataARB() 當緩衝區初始化以後你可以使用glBufferDataARB()將數組複製到緩衝區對象中。
<span style="white-space:pre"></span>void glBufferDataARB(GLenum target, GLsizei size, const void* data, GLenum usage)

第一個參數target可能是GL_ARRAY_BUFFER_ARB或GL_ELEMENT_ARRAY_BUFFER_ARB。size是將要傳輸的資料的位元組數。第3個參數是指向資料來源的指標,如果這個指標是NULL,那麼VBO將會只保留指定大小的儲存空間。最後一個參數usage是另一個VBO中的績效參數,指示這個緩衝區會被怎麼使用,static(靜態),dynamic(動態),stream(流),read(讀),copy(複製),draw(繪製)。
VBO為usage標示指定了9中枚舉的值:
GL_STATIC_DRAW_ARBGL_STATIC_READ_ARBGL_STATIC_COPY_ARBGL_DYNAMIC_DRAW_ARBGL_DYNAMIC_READ_ARBGL_DYNAMIC_COPY_ARBGL_STREAM_DRAW_ARBGL_STREAM_READ_ARBGL_STREAM_COPY_ARB

“static”意味著VBO中的資料不能被改變(指定一次使用多次),“dynamic”意味著資料將會頻繁地改變(指定多次使用多次),“stream”意味著資料在每一幀中都會被改變(指定一次使用一次)。“draw”意味著資料被傳輸至GPU渲染(從應用程式到OpenGL),“read”意味著資料被用戶端的應用程式所讀取(從OpenGL到應用程式),“copy0”意味著既可以用來“draw”也可以用來“read”。
注意只有"draw"標示可以被VBO使用,“copy”和“read”標示對像素緩衝區對象(PBO)和框架緩衝區對象(FBO)才有意義。
VBO記憶體管理會根據usage的值為緩衝區對象選擇最合適的記憶體位置,例如,GL_STATIC_DRAW_ARB 和GL_STREAM_DRAW_ARB將會選擇顯卡記憶體,GL_DYNAMIC_DRAW_ARB將會選擇AGP記憶體。任何與_READ_相關的緩衝區既可以使用系統記憶體也可以是用AGP記憶體,因為資料應該很容易被訪問到。

glBufferSubDataARB()
<span style="white-space:pre"></span>void glBufferSubDataARB(GLenum target, GLint offset, GLsizei size, void* data)

和glBufferDataARB()比較類似,glBufferSubDataARB()用來將資料複製到VBO中,但是它只將部分資料複製到已經存在的緩衝區中,從給定的位移量開始。(緩衝區的總大小必須在使用glBufferSubDataARB()之前使用glBufferDataARB()設定)

glDeleteBuffersARB()
<span style="white-space:pre"></span>void glDeleteBuffersARB(GLsizei n, const GLuint* ids)

你可以使用glDeleteBuffersARB()函數來刪除一個或多個不再使用的VBO。緩衝區對象被刪除掉後,它的內容會丟失掉。
下面的代碼是一個為頂點座標建立一個簡單的頂點緩衝區對象(VBO)的例子。注意當你把所有的資料複製到VBO中之後你可以刪除在你的應用程式中為頂點數組分配的記憶體。
GLuint vboId;                              // VBO的IDGLfloat* vertices = new GLfloat[vCount*3]; // 建立頂點數組...// 產生一個新的頂點緩衝區對象並得到相關聯的IDglGenBuffersARB(1, &vboId);// 綁定頂點緩衝區對象glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboId);// 將資料複製到頂點緩衝區對象中glBufferDataARB(GL_ARRAY_BUFFER_ARB, dataSize, vertices, GL_STATIC_DRAW_ARB);// 將資料複製到頂點緩衝區對象中後就可以刪除頂點數組了delete [] vertices;...// 程式終止時刪除頂點緩衝區對象glDeleteBuffersARB(1, &vboId);


繪製VBO
因為VBO是基於頂點數組實現的,渲染VBO和使用頂點數組比較類似。唯一的不同點是指向頂點資料的指標現在變成了指向現在綁定的緩衝區對象的位移量。因此,除了glBindBufferARB()之外繪製VBO不需要額外的API。
// 為頂點數組和索引數組綁定VBOglBindBufferARB(GL_ARRAY_BUFFER_ARB, vboId1);         // 為頂點數組glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, vboId2); // 為索引數組// 除了指標不同外其他和頂點數組操作一樣glEnableClientState(GL_VERTEX_ARRAY);             // 啟用頂點數組glVertexPointer(3, GL_FLOAT, 0, 0);               // 最後一個參數是位移// 使用索引數組的位移值繪製6個面glDrawElements(GL_QUADS, 24, GL_UNSIGNED_BYTE, 0);glDisableClientState(GL_VERTEX_ARRAY);            // 禁用頂點數組// 綁定0,這樣將返回到正常的指標操作glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);

最後一行將緩衝區對象綁定到0將關閉VBO操作。這是一個使用VBO之外關閉VBO的好方法,這樣正常的頂點數組操作將會被再次啟用。

更新VBO
VBO相比於顯示列表的優點是用戶端可以讀取和更改緩衝區對象中的資料,而顯示列表不能。最簡單的更新VBO中的資料的方法是使用glBufferDataARB()或glBufferSubDataARB()函數將資料一遍又一遍地複製到你所綁定的VBO中。對這種情況,你的應用程式需要一直有一個有效頂點數組。這意味著你需要有頂點數組的兩份拷貝:一份位於你的應用程式中,另一份位於VBO中。
另一個修改緩衝區對象的方式是將緩衝區對象映射到用戶端記憶體中,然後用戶端可以使用指向映射到緩衝區的指標更新資料。下面顯示了如何將一個VBO映射到用戶端記憶體中並怎樣訪問被映射的資料。


glMapBufferARB() VBO使用glMapBufferARB()來將緩衝區對象映射到用戶端記憶體中。

<span style="white-space:pre"></span>void* glMapBufferARB(GLenum target, GLenum access)
如果OpenGL能將緩衝區映射到用戶端的記憶體中,glMapBufferARB()將返回指向緩衝區的指標,否則返回NULL。
第一個參數,target和上面的glBindBufferARB()一樣,第二個參數access指定了對那些映射的資料的操作:讀,寫或者讀寫都可以。
GL_READ_ONLY_ARBGL_WRITE_ONLY_ARBGL_READ_WRITE_ARB

注意glMapBufferARB()將會導致一個同步的問題。如果GPU依然在頂點緩衝區中工作,那麼glMapBufferARB()函數將會在GPU結束在指定的緩衝區的工作之後才返回。
為了避免等待,你可以首先使用一個null 指標調用glBufferDataARB()函數,然後調用glMapBufferARB()。在這種情況下,之前的資料將會被捨棄,glMapBufferARB()將立即返回一個新分配地區的指標,即使GPU依然在之前的資料上工作。
然而,這種方法只有在你需要更新整個資料集的時候才有效,因為這樣將會捨棄之前的資料。如果你只是希望改變部分資料或者只是希望讀取資料,你最好不要捨棄之前的資料。

glUnmapBufferARB()

<span style="white-space:pre"></span>GLboolean glUnmapBufferARB(GLenum target)

修改完VBO的資料之後,必須解除掉緩衝區對象和用戶端記憶體的映射。glUnmapBufferARB()成功時會返回GL_TRUE。當它返回GL_FALSE時VBO的內容會被破壞。破壞的原因是視窗解析度的改變或者系統時間發生了。在這種情況下,資料需要被再次提交。
下面是一個使用映射的方法修改VBO的例子:
// 綁定並映射VBOglBindBufferARB(GL_ARRAY_BUFFER_ARB, vboId);float* ptr = (float*)glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);// 如果映射成功,則更新VBOif(ptr){    updateMyVBO(ptr, ...);                 // 修改緩衝區資料    glUnmapBufferARB(GL_ARRAY_BUFFER_ARB); // 解除映射}// 使用新的VBO繪圖...


例子
這個例子使VBO沿著法向量擺動,它將VBO映射到了用戶端並在每一幀中改變。你可以將它與傳統的頂點數組的方式比較效能。
它使用了兩個頂點緩衝區:一個給頂點座標和法向量使用,另一個只用來儲存索引。
下載源檔案和可執行檔: vbo.zip, vboSimple.zip
vboSimple是一個非常簡單的使用VBO和頂點數組繪製立方體的例子。你可以非常簡單地看出頂點數組和VBO的相同點和不同點。
我同樣在src檔案夾下也包含了在linux下啟動並執行makefile檔案(Makefile.linux),你也可以在linux下使用下面的方式產生可執行檔:
 <span style="white-space:pre"></span>make -f Makefile.linux

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.