Vertex Buffer Object

关于GL_ARB_vertex_buffer_object扩展<转>


  GL_ARB_vertex_buffer_object,一般简称为VBO,这是OpenGL里的一个千呼万唤始出来的扩展,它可以根据实际情况决定把顶点数据放到显存、AGP内存或系统内存中。

  没有这个扩展的时候,偶们用vertex array时,用glVertexPointer / glNormalPointer 来指定顶点数据,这时顶点数据是放在系统内存中的,每次渲染时,都要把数据从系统内存拷贝到显存,消耗不少时间。

  实际上很多拷贝都是不必要的,比如静态对象的顶点数据是不变的,如果能把它们放到显存里面,那么每次渲染时都不需要拷贝操作,可以节约不少时间。

  另外现在的显卡大多数是AGP的,系统会在系统内存中开辟一块区域作为AGP内存,显卡可以通过DMA来直接访问AGP内存,把数据传到显卡,速度很快,并且在传数据时不需要CPU干涉,显卡可以和CPU并行运算。我们可以把一些动态对象的顶点数据放在AGP内存中,更新对象顶点数据后能利用AGP的快速传输能力,把数据传到显卡,这样比从系统内存传到显存要快。

  GL_ARB_vertex_buffer_object的使用很简单,和纹理的用法有点相近,下面几个函数和纹理的函数很相近:   值得注意的是glBufferDataARB的最后一个参数usage,它有如下取值:

glBindBufferARB
glDeleteBuffersARB
glGenBuffersARB
glIsBufferARB
glBufferDataARB
glBufferSubDataARB
glGetBufferSubDataARB

GL_STREAM_DRAW_ARB
GL_STREAM_READ_ARB
GL_STREAM_COPY_ARB
GL_STATIC_DRAW_ARB
GL_STATIC_READ_ARB
GL_STATIC_COPY_ARB
GL_DYNAMIC_DRAW_ARB
GL_DYNAMIC_READ_ARB
GL_DYNAMIC_COPY_ARB

其中:

STREAM表示只赋一次值,只用一次或很少的几次,这部种数据很可能放在系统内存中

STATIC表示只赋一次值,重复使用很多次,这种数据很可能放在显存中

DYNAMIC表示多次赋值,重复使用,这种数据很可能放在AGP内存中

DRAW 表示赋给buffer object的数据来自用户程序,buffer object作为绘制函数的数据源

COPY 表示赋给buffer object的数据来自OpenGL,buffer object作为绘制函数的数据源

READ 表示赋给buffer object的数据来自OpenGL,buffer object作为用户程序的数据源

  目前只有DRAW有意义。  驱动程序会以usage为参考,根据多种条件决定是把数据放在显存、AGP内存还是系统内存中。比如如果还有足够的空余显存,usage指定为GL_STATIC_DRAW_ARB时,数据会被放在显存中,而如果空间不够,就会被放到AGP或系统内存中。
  我们要修改一个buffer object的数据有两种办法,

一种是通过glBufferDataARB/glBufferSubDataARB函数指定数据

另一种是通过glMapBufferARB获得修改数据的指针,通过指针修改数据,修改完成后通过glUnmapBufferARB来提交数据
  我们来看一个例子,在这个例子里面,index数据是不变的,而顶点和颜色则是动态的:

Vertex arrays using a mapped buffer object for array data and an
unmapped buffer object for indices:

// Create system memory buffer for indices
indexdata = malloc(400);

// Fill system memory buffer with 100 indices

// GL_ELEMENT_ARRAY_BUFFER_ARB

// Define arrays (and create buffer object in first pass)
BindBufferARB(ARRAY_BUFFER_ARB, 1);


// vertex array、color array放在一个VBO中
VertexPointer(4, FLOAT, 0, BUFFER_OFFSET(0));
ColorPointer(4, UNSIGNED_BYTE, 0, BUFFER_OFFSET(256));
BindBufferARB(ELEMENT_ARRAY_BUFFER_ARB, 2); // 绑定index

// Enable arrays
EnableClientState(VERTEX_ARRAY);
EnableClientState(COLOR_ARRAY);

// Initialize data store of buffer object
BufferDataARB(ARRAY_BUFFER_ARB, 320, NULL, STREAM_DRAW_ARB);

// Map the buffer object
float *p = MapBufferARB(ARRAY_BUFFER_ARB, WRITE_ONLY);

// 用指针p修改顶点和颜色数据
// Compute and store data in mapped buffer object

// Unmap buffer object and draw arrays
if (UnmapBufferARB(ARRAY_BUFFER_ARB)) {
DrawElements(TRIANGLE_STRIP, 100, UNSIGNED_INT,
BUFFER_OFFSET(0));}

// Disable arrays
DisableClientState(VERTEX_ARRAY);
DisableClientState(COLOR_ARRAY);

// Other rendering commands…
}

// Delete buffer objects
int buffers[2] = {1, 2};
DeleteBuffersARB(1, buffers);

演示程序(vbo.zip,40.8KB)   程序是针对32M显存的,程序中一个buffer object大概占1M字节,如果是更大的显存,可以把MAX_BUFFER改大。
GL_ARB_vertex_buffer_object扩展规范
  只要机器支持VBO,就尽量使用它,因为就算使用VBO在有些情况下(比如显存、AGP内存空间不够时,数据放在系统内存中)速度不会提升,也不会比原来的vertex array慢。而一旦数据放在显存、AGP内存中,性能将会得到很大的提升。偶试了下,在填充率不是瓶颈时,使用VBO比不使用VBO的帧数要快一倍。为 index buffer专用
// 其它的vertex\normal\color等应该使用GL_ARRAY_BUFFER_ARB
// Create index buffer object
BindBufferARB(ELEMENT_ARRAY_BUFFER_ARB, 2);// 为buffer object分配空间,并把数据拷贝到新空间。
// 因为index是不变的,所以应该放到显存里面去,这里指定STATIC_DRAW_ARB,可能是显存
BufferDataARB(ELEMENT_ARRAY_BUFFER_ARB, 400, indexdata, STATIC_DRAW_ARB);// Free system memory buffer
// 已经创建了buffer object,并把数据拷贝过去,所以indexdata没必要再存在
free(indexdata);// Frame rendering loop
while (…) {

补充:来自opengl programming guide 6th Edition

利用这项技术,可以将一些固定的数据存放在 opengl server 端(我觉得可以理解为显存),这样就不用在每次调用时将数据从 client 端(可以理解为内存)传到 server 端。 Opengl 被设计为 client-server 的调用模式。在 1.5 以前的版本中,数据一般都存放在 client 端,在需要时才从 client 端拷贝到 server 端。 Buffer objects 是在 1.5 版本中被加入到 opengl 的标准中。

Buffer objects 的创建与纹理对象比较类似 .

首先是调用函数: glGenBuffers (GLsizei n, GLuint* buffers); 该函数的作用是让 opengl 来自动返回 n 个未被使用的 buffer objects 名字,名字是正整数,存放在缓存 buffers 中。 0 是默认的被保留的名字,不会被返回。

然后需要调用函数: glBindBuffer(GLenum target, GLuint buffer) 来激活某个 buffers objects 。和纹理对象类似,该函数也可以做三件事情。当 buffer 对应的名字第一次被绑定时,该函数就会生成一个新的 buffer objects 。如果不是第一次调用,那么就激活名字对应的对象。如果是名字是 0 ,那么就停止使用 buffer objects Target 的取值可以是下面的某个:

GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, GL_PIXEL_PACK_BUFFER, GL_PIXEL_UNPACK_BUFFER

接着就要调用函数: glBufferData(GLenum target, GLsizeipter size, const GLvoid* data, GLenum usage) 来存储数据。 Target 指定 buffer object 对应的目标,取值和上面一致。 Size 是在 server 端分配的存储空间的大小(一般以 byte 为基准), data client 端的数据指针,如果为 NULL ,那么就不进行初始化,后续动态的进行更新。 Usage 表示该存储块如何被使用和更新。

Buffer objet 的更新方法:

1 、调用函数: glBufferSubData(GLenum target, GLintpter offset, GLsizeipter size, const void* data) 。该函数用 data[offset-1] - data[offset+size-1] 这段数据来对绑定到 target 上的 buffer object 进行更新。

2 、调用函数: GLvoid* glMapBuffer(GLenum target, GLenum access) 。该函数可以返回一个指针,指针和 target 的数据块对应,对指针对应的地址进行操作就可以修改 target 数据,进而修改绑定到 target buffer object 。这种方法更加的灵活。

标签:,
上一篇: opengl course || 下一篇:Vertex Buffer Object
News, OpenGL

相关日志

If you enjoyed this post, please consider to leave a comment or subscribe to the feed and get future articles delivered to your feed reader.

Leave Comment

(必填)

(必填)