OpenGL顶点缓冲区对象(VBO)

OpenGL顶点缓冲区对象(VBO)

原文地址

目录

创建VBO

画出VBO

更新VBO

例子



创建VBO
        GL_ARB_vertex_buffer_object 扩展可以提升OpenGL的性能。它提供了顶点数组和显示列表,这避免了低效实现这些功能。Vertex buffer object (VBO) 允许顶点数据储存在高性能显卡上,即服务端的内存中,改善数据传输效率。如果缓冲区对象保存了像素数据,它就被称做Pixel Buffer Object (PBO)。

使用顶点数据可以减少函数调用次数及复用共享顶点,然而,顶点数组的缺点是顶点函数及顶点数据在客户端(me:对于OpenGL来说,显卡为服务端,其它为客户端),每次引用顶点数组时,都必须将顶点数据从客户端(me:内存)发送到服务端(显卡)。另一方面,显示列表是服务端的函数,它不会再重头传送数据。但是,一旦显示列表被编译了,显示列表中的数据就不能修改了。

        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或系统内存中可能更佳,顶点数组在显卡上可能更好。

      一旦glBindBufferARB()被首次调用后,VBO初始化缓冲区对象,设置VBO的状态,如使用和访问属性,但缓冲区对象的内存大小为0。

     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后,你可以释放顶点数组的内存。
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>
glGenBuffersARB 作用是得到一个没有使用的缓冲区对象的ID。
glBindBufferARB 相当于声明ID对应的对象的数据类型。相当于c++中数据类型声明。
glBufferDataARB 相当于分对象分配内存。此内存应该是显卡中。
</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到客户端的内存上,以及如休访问被映射的数据。
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中的数据。
// 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
...

例子

 OpenGL顶点缓冲区对象(VBO)_第1张图片

 这个例子创建了一个VBO。并摆动其法向量。映射了一个VBO并在每一帧使用映射到缓冲区对象的指针,更新顶点,你可以与传统vertex array对比其性能。它使用了两个顶点缓冲区:一个储存了顶点坐标及法向量,另一个只储存索引。 


下载源码及exe: vbo.zip, vboSimple.zip.
vboSimple是一个很简单的例子。它使用VBO和Vertex Array画出一个立方体。从中你可以轻松看出VBO和VA之间的相同及不同之处。

你可能感兴趣的:(Stream,object,delete,buffer,float,Pointers)