一、创建缓冲区对象
从OpenGL 3.1开始,所有的名字必须由OpenGL使用glGen*()函数之一来生成,不再接受用户定义的名字。
1、为了让OpenGL分配缓冲区对象标识符,可以调用glGenBuffers()函数。
void glGenBuffers(GLsizei n, GLuint *buffers);
在buffers数组中返回n个当前未使用的名称,表示缓冲区对象。在buffers数组中返回的名称并不需要是连续的整数。
返回的名称被标记为已使用,以便分配给缓冲区对象。但是,当它们被绑定之后,它们只获得一个合法的状态。
零是一个被保留的缓冲区对象名称,从来不会被glGenBuffers()作为缓冲区对象返回。
2、还可以调用glIsBuffer()函数,判断一个标识符是否是一个当前被使用的缓冲区对象的标识符。
GLboolean glIsBuffer(GLuint buffer);
如果buffer是一个已经绑定的缓冲区对象的名称,而且还没有删除,这个函数返回GL_TRUE。
如果buffer为0或者它不是一个缓冲区对象的名称,这个函数返回GL_FALSE。
glBindBuffer()函数:一次用于初始化缓冲区对象以及它的数据,以后的调用要么选择用于渲染的缓冲区对象,要么对缓冲区对象的数据进行更新。
为了禁用缓冲区对象,可以用0作为缓冲区对象的标识符来调用glBindBuffer()函数。这将把OpenGL切换为默认的不使用缓冲区对象的模式。
void glBindBuffer(GLenum target, GLuint buffer);
指定了当前的活动缓冲区对象。target必须设置为GL_ARRAY_BUFFER(表示顶点数据)、GL_ELEMENT_ARRAY_BUFFER(表示索引数据)、GL_PIXEL_PACK_BUFFER(表示从OpenGL获取的像素数据)、GL_PIXEL_UNPACK_BUFFER(表示传递给OpenGL的像素数据)、GL_COPY_READ_BUFFER、GL_COPY_WRITE_BUFFER(表示在缓冲区之间复制数据)、GL_TRANSFORM_ FEEDBACK_ BUFFER(表示执行一个变换反馈着色器的结果)或者GL_UNIFORM_BUFFER(表示统一变量值)。buffer指定了将要绑定的缓冲区对象。
glBindBuffer()完成3个任务之一:①当buffer是一个首次使用的非零无符号整数时,它就创建一个新的缓冲区对象,并把buffer分配给这个缓冲区对象,作为它的名称。②当绑定到一个以前创建的缓冲区对象时,这个缓冲区对象便成为活动的缓冲区对象。③当绑定到一个值为零的buffer时,OpenGL就会停止使用缓冲区对象。
一旦绑定了一个缓冲区对象,就需要保留空间以存储数据,这是通过调用glBufferData()函数实现的。
void glBufferData(GLenum target, GLsizeiptr size, const GLvoid *data,
GLenum usage);
分配size个存储单位(通常是字节)的OpenGL服务器内存,用于存储顶点数据或索引。以前所有与当前绑定对象相关联的数据都将删除。
size是存储相关数据所需要的内存数量。这个值通常是数据元素的个数乘以它们各自的存储长度。data可以是一个指向客户机内存的指针(用于初始化缓冲区对象),也可以是NULL。如果它传递的是一个有效的指针,size个单位的存储空间就从客户机复制到服务器。如果它传递的是NULL,这个函数将会保留size个单位的存储空间供以后使用,但不会对它进行初始化。
usage提供了一个提示, 就是数据在分配之后将如何进行读取和写入。它的有效值包括GL_STREAM_DRAW(表示该缓存区会被频繁更改)、GL_STREAM_READ、GL_STREAM_COPY、GL_STATIC_DRAW(表示该缓存区不会被修改)、GL_STATIC_READ、GL_STATIC_COPY、GL_DYNAMIC_DRAW(表示该缓存区会被周期性更改)、GL_DYNAMIC_READ、GL_DYNAMIC_COPY。
如果请求分配的内存数量超过了服务器能够分配的内存,glBufferData( ) 将返回GL_OUT_OF_MEMORY。如果usage并不是允许使用的值之一,这个函数就返回GL_INVALID_VALUE。glBufferData()首先在OpenGL服务器中分配内存以存储数据。如果请求的内存太多,它会设置GL_OUT_OF_MEMORY错误。如果成功分配了存储空间,并且data参数的值不是NULL,size个存储单位(通常是字节)就从客户机的内存复制到这个缓冲区对象。但是,如果需要在创建了缓冲区对象之后的某个时刻动态地加载数据,可以把data参数设置为NULL,为数据保留适当的存储空间,但不对它进行初始化。
glBufferData()的最后一个参数usage是向OpenGL提供的一个性能提示。根据usage参数指定的值,
OpenGL可能会对数据进行优化,进一步提高性能。它也可以选择忽略这个提示。在缓冲区对象数据
上,可以进行3种类型的操作:
1) 绘图:客户机指定了用于渲染的数据。
2) 读取:从OpenGL缓冲区读取(例如帧缓冲区)数据值,并且在应用程序中用于各种与渲染并不直接相关的计算过程。
3) 复制:从OpenGL缓冲区读取数据值,作为用于渲染的数据。
另外,根据数据更新的频率,有几种不同的操作提示描述了数据的读取频率或在渲染中使用的频率:
流模式:缓冲区对象中的数据常常需要更新,但是在绘图或其他操作中使用这些数据的次数较少。
静态模式:缓冲区对象中的数据只指定1次,但是这些数据被使用的频率很高。
动态模式:缓冲区对象中的数据不仅常常需要进行更新,而且使用频率也非常高。
四、更新缓冲区对象的数据值
1、glBufferSubData()将用我们提供的数据替换被绑定的缓冲区对象的一些数据子集。
void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data);用data指向的数据更新与target相关联的当前绑定缓冲区对象中从offset(以字节为单位)开始的size个字节数据。如果size小于0或者size+offset大于缓冲区对象创建时所指定的大小,glBufferSubData()将产生一个GL_INVALID_VALUE错误。
2、glMapBuffer()返回一个指向缓冲区对象的指针,可以在这个缓冲区对象中写入新值(或简单地读取数据,这取决于内存访问权限),就像对数组进行赋值一样。在完成了对缓冲区对象的数据更新之后,可以调用glUnmapBuffer(),表示已经完成了对数据的更新。glMapBuffer()提供了对缓冲区对象中包含的整个数据集合的访问。如果需要修改缓冲区中的大多数数据,这种方法很有用,但是,如果有一个很大的缓冲区并且只需要更新很小的一部分值,这种方法效率很低。
GLvoid *glMapBuffer(GLenum target, GLenum access);
如果这个缓冲区无法被映射(把OpenGL错误状态设置为GL_OUT_OF_MEMORY)或者它以前已经被映射(把OpenGL错误状态设置为GL_INVALID_OPERATION),glMapBuffer()将返回NULL。
在完成了对数据存储的访问之后,可以调用glUnmapBuffer()取消对这个缓冲区的映射。
GLboolean glUnmapBuffer(GLenum target);3、如果只需要更新缓冲区中相对较少的值(与值的总体数目相比),或者更新一个很大的缓冲区对象中的很小的连续范围的值,使用glMapBufferRange()效率更高。它允许只修改所需的范围内的数据值。
GLvoid *glMapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);使用glMapBufferRange(),可以通过在access中设置额外的位来指定可选的提示。这些标志描述了在映射之前OpenGL服务器需要如何保护缓冲区中原有的数据。这个提示用来帮助OpenGL实现确定需要保留哪些数据值,以及保持这些数据的任何内部拷贝正确和一致需要达到多长时间。
4、GLvoid glFlushMappedBufferRange(GLenum target, GLintptr offset,GLsizeiptr length);
表示一个缓冲区范围中的值已经修改,这可能引发OpenGL服务器更新缓冲区对象的缓存版本
五、在缓冲区对象之间复制数据
在OpenGL 3.1中,glCopyBufferSubData()命令复制数据。
void glCopyBufferSubData(GLenum readbuffer, GLenum writebuffer, GLintptr readoffset, GLintptr writeoffset, GLsizeiptr size);把数据从与readbuffer相关联的缓冲区对象复制到绑定到writebuffer的缓冲区对象。readoffset和size指定了复制到目标缓冲区对象中的数据的数量,会从writeoffset开始替换同样大小的数据。
六、清除缓冲区对象
完成了对缓冲区对象的操作之后,可以释放它的资源,并使它的标识符可以由其他缓冲区对象使用。为此,可以调用glDeleteBuffers()。被删除的当前绑定缓冲区对象的所有绑定都将重置为零。
void glDeleteBuffers(GLsizei n, const GLuint *buffers);
删除n个缓冲区对象,它们的名称就是buffers数组的元素。释放的缓冲区对象可以被复用(例如,通过调用glGenBuffers())。
如果一个缓冲区对象是在绑定时删除的,这个对象的所有绑定都重置为默认的缓冲区对象,就像以0作为指定的缓冲区对象参数调用了glBindBuffer()一样。如果试图删除不存在的缓冲区对象或名称为0的缓冲区对象,这个操作将被忽略,并不会产生错误。
七、使用缓冲区对象存储顶点数据数据
要在缓冲区对象中存储顶点数组数据,需要给应用程序添加如下步骤:
1) 生成缓冲区对象标识符(这个步骤是可选的)。
2) 绑定一个缓冲区对象,确定它是用于存储顶点数据还是索引。
3) 请求数据的存储空间,并且对这些数据元素进行初始化(后一个步骤可选)。
4) 指定相对于缓冲区起始位置的偏移量,对诸如glVertexPointer()这样的顶点数组函数进行初始化。
5) 绑定适当的缓冲区对象,用于渲染。
6) 使用适当的顶点数组渲染函数进行渲染,例如glDrawArrays()或glDrawElements()。
如果想初始化多个缓冲区对象,就需要为每个缓冲区对象重复步骤2)~4)。