原文地址
目录
创建VBO
画出VBO
更新VBO
例子
使用顶点数据可以减少函数调用次数及复用共享顶点,然而,顶点数组的缺点是顶点函数及顶点数据在客户端(me:对于OpenGL来说,显卡为服务端,其它为客户端),每次引用顶点数组时,都必须将顶点数据从客户端(me:内存)发送到服务端(显卡)。另一方面,显示列表是服务端的函数,它不会再重头传送数据。但是,一旦显示列表被编译了,显示列表中的数据就不能修改了。
Vertex buffer object (VBO) 为顶点创建创建了一个缓冲区对象。缓冲区对象在服务端的高性能内存中,并提供了相同的函数,引用这些数组,如glVertexPointer(), glNormalPointer(), glTexCoordPointer(), 等等.顶点缓冲区内存管理器将缓冲区对象放在储存器中最佳的位置。这依赖了用户输入的模式:"target"模式和"usage"模式。因此,储存管理器可以优化缓冲区,平衡三种内存:system ,AGP, video memory。与显示列表不同的是,在顶点缓冲区对象中的数据可以读也可以将它映射到服务端的内存空间中,然后更新它的数据。
VBO另一个重要的优点是,可以在许多客户端中共享缓冲区对象,就像显示列表和纹理那样。由于VBO在服务端,多个客户端可以通过对应的标识符访问同一个缓冲区。
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或系统内存中可能更佳,顶点数组在显卡上可能更好。
一旦glBindBufferARB()被首次调用后,VBO初始化缓冲区对象,设置VBO的状态,如使用和访问属性,但缓冲区对象的内存大小为0。
glBufferDataARB()GLuint vboId; // ID of VBO GLfloat* vertices = new GLfloat[vCount*3]; // create vertex array ... // generate a new VBO and get the associated ID glGenBuffersARB(1, &vboId); // bind VBO in order to use glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboId); // upload data to VBO glBufferDataARB(GL_ARRAY_BUFFER_ARB, dataSize, vertices, GL_STATIC_DRAW_ARB); // it is safe to delete after copying data to VBO delete [] vertices; ... // delete VBO when program terminated glDeleteBuffersARB(1, &vboId);<me>
绘制VBO
由于VBO是基于以实现了的vertex array方法,渲染VBO的方法几乎于渲染vertex array的方法相同。唯一不同的是,原来指向顶点数组的指针,现在是一个偏移值(me:此值最为关键,将n个顶点buffer合并成一个大buffer,这个偏移值指定了第i个buffer在大buffer中的起始位置). 因此,除了调用glBindBufferARB()函数外,绘制VBO不需要额外的API。
// bind VBOs for vertex array and index array glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboId1); // for vertex coordinates glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, vboId2); // for indices // do same as vertex array except pointer glEnableClientState(GL_VERTEX_ARRAY); // activate vertex coords array glVertexPointer(3, GL_FLOAT, 0, 0); // last param is offset, not ptr // draw 6 quads using offset of index array glDrawElements(GL_QUADS, 24, GL_UNSIGNED_BYTE, 0); glDisableClientState(GL_VERTEX_ARRAY); // deactivate vertex array // bind with 0, so, switch back to normal pointer operation glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); 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到客户端的内存上,以及如休访问被映射的数据。// bind then map the VBO glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboId); float* ptr = (float*)glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB); // if the pointer is valid(mapped), update VBO if(ptr) { updateMyVBO(ptr, ...); // modify buffer data glUnmapBufferARB(GL_ARRAY_BUFFER_ARB); // unmap it after use } // you can draw the updated VBO ...
例子
这个例子创建了一个VBO。并摆动其法向量。映射了一个VBO并在每一帧使用映射到缓冲区对象的指针,更新顶点,你可以与传统vertex array对比其性能。它使用了两个顶点缓冲区:一个储存了顶点坐标及法向量,另一个只储存索引。