OpenGL笔记之缓冲区对象(基础篇上)

来源:互联网 发布:盐和避难所剧情 知乎 编辑:程序博客网 时间:2024/05/22 03:35

OpenGL笔记

缓冲区对象是一个强大的概念,它允许应用程序快速方便地将数据从一个渲染管线移动到另一个渲染管线,以及从一个对象绑定到另一个对象。

缓冲区有许多不同的用途,它能保存顶点数据、像素数据、纹理数据、着色器处理的输入、或者不同着色器阶段的输出。
缓冲区保存在GPU内存中,它们提供高速和高效的访问。在OpenGL有缓冲区对象之前,应用程序只有有限的选择可以在GPU中存储数据。另外,在GPU中更新数据常常需要重新加载整个对象,而在系统内存和GPU内存之间来回移动数据常常是一个缓慢的过程,所以缓冲区对象应运而生。

1. 创建缓冲区
我们可以一次性创建任意数量的缓冲区:
GLuint buffers[n]; //n代表缓冲区数量
glGenBuffers(n, buffers);
执行此函数后,buffers数组中就存放着n个缓冲区对象的标识符(非0的无符号整数)。接下来就可以将这些缓冲区标识符(如buffers[0])进行绑定来使用缓冲区。
在OpenGL中有许多不同的绑定点(binding point),每个绑定点有着不同的作用。我们可以将每个结合点或绑定点看做一个在同一时刻只能结合一个对象的槽。
绑定点如下:
GL_ARRAY_BUFFER:数组缓冲区存储颜色、位置、纹理坐标等顶点属性,或者其它自定义属性
GL_COPY_READ_BUFFER:缓冲区用作通过glCopyBufferSubData进行复制的数据源
GL_COPY_WRITE_BUFFER:缓冲区用作通过glCopyBufferSubData进行复制的目标
GL_ELEMENT_ARRAY_BUFFER:索引数组缓冲区用于保存glDrawElements、glDrawRangeElements和glDrawElementsInstanced的索引
GL_PIXEL_PACK_BUFFER:glReadPixels之类像素包装操作的目标缓冲区
GL_PIXEL_UNPACK_BUFFER:glTexImageXD、glTexSubImageXD之类的纹理更新函数的源缓冲区
GL_TEXTURE_BUFFER:着色器可以通过纹理单元拾取来访问的缓冲区
GL_TRANSFORM_FEEDBACK_BUFFER:变换反馈顶点着色器写入的缓冲区
GL_UNIFORM_BUFFER:着色器访问的uniform值

2. 绑定缓冲区
函数原型:glBindBuffer(GLenum target, GLuint buffer);
target参数为上面说到的绑定点,buffer参数为缓冲区对象的标识符。
如果要解除绑定点的绑定,则再调用一次glBindBuffer参数,而参数buffer置为0即可。
另外,其它合法的缓冲区也可以绑定到同一个绑定点上。
当使用完缓冲区后,我们需要调用glDeleteBuffers(GLsizei n, const GLuint* buffers)函数对其进行清除。

3.填充缓冲区
即将数据传递到缓冲区,函数原型:
glBufferData(GLenum target, GLsizeiptrARB size, const void *data, GLenum usage);
其中GLsizeiptrARB 类型等于ptrdiff_t。
target: 绑定点
size: 上传数据大小
data: 数据。如果只是想分配指定大小的缓冲区,而不对它进行填充,则置为NULL
usage: 这个参数告诉OpenGL我们打算如何使用此缓冲区,一般来说为GL_DYNAMIC_DRAW(缓冲区内容将经常会由应用程序进行更新,并且经常用于绘制或复制到其他图像)。其它的可选值还包括:
GL_STREAM_DRAW: 缓冲区内容将由应用程序进行一次设置,并且经常用于绘制
GL_STREAM_READ:缓冲区的内容将作为一条OpenGL命令的输出来进行一次设置,并且经常用于绘制
GL_STREAM_COPY:缓冲区的内容将作为一条OpenGL命令的输出来进行一次设置,并且不经常用于绘制或复制到其他图像
GL_STATIC_DRAW:缓冲区内容将由应用程序进行一次设置,并且经常用于绘制或复制到其他图像
GL_STATIC_READ:缓冲区的内容将作为一条OpenGL命令的输出来进行一次设置,并且由应用程序进行多次查询
GL_STATIC_COPY:缓冲区的内容将作为一条OpenGL命令的输出来进行一次设置,并且经常用于绘制或复制到其他图像
GL_DYNAMIC_READ:缓冲区的内容将作为一条OpenGL命令的输出经常进行更新,并且由应用程序进行多次查询
GL_DYNAMIC_COPY:缓冲区的内容将作为一条OpenGL命令的输出经常进行更新,并且经常用于绘制或复制到其他图像
另外,我们可以使用glBufferSubData函数对缓冲区的一部分进行更新。函数原型如下:
void glBufferSubData(GLenum target, intptr offset, sizeiptr size, const void *data);
这个函数不能更改缓冲区的使用方式,因为缓冲区内存已经被分配了。

4.PBO(Pixel Buffer Object)
像素缓冲区对象存储在GPU中。PBO的绑定点为:
1.GL_PIXEL_PACK_BUFFER:当一个PBO绑定到这个目标时,任何读取像素的OpenGL操作都会从PBO中获取他们的数据,包括glReadPixels、glGetTexImage和glGetCompressedTexImage。这样的话,这些读取像素的操作就不必将从帧缓冲区或纹理中抽取的数据读回到客户端内存中。
2.GL_PIXEL_UNPACK_BUFFER:当绑定到此目标时,任何绘制像素的OpenGL操作都会向一个绑定的PBO中放入他们的数据。如glTexImageXD、glTexSubImageXD、glCompressedTexImageXD等。
PBO在处理需要经常访问、修改或者更新像素数据时有着巨大的优势。它可以暂存GPU本地像素数据,前提是需要先为他们分配存储空间。它也经常用来存储来自一个渲染目标的2D图像、纹理或其它数据源。
从缓冲区中读取像素数据
void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels);
此函数用于从当前启用的读取缓冲区的指定位置获取像素,然后将他们复制到CPU内存中。
使用缓冲区,可以避免当我们向客户端内存进行写入操作时,整个管线经常需要被清空而可能带来的性能问题。

5.TBO(texBO)
一个纹理主要包含两个部分:纹理采样状态和包含纹理值得数据缓冲区。我们可以将一个缓冲区对象绑定到GL_TEXTURE_BUFFER缓冲区中的一个纹理绑定点。
TBO有两个特性:
1.纹理缓冲区能直接填充来自其他渲染结果(如变换反馈、像素读取操作或顶点数据)的数据,这样可以节省不少时间。
2.宽松的纹理大小限制。
我们可以通过下面的方法来创建、使用纹理缓冲区:
GLuint texBO;
glGenBuffer(1, texBO);
glBindBuffer(GL_TEXTURE_BUFFER, texBO);
//上传数据
glBufferData(GL_TEXTURE_BUFFER, sizeof(float) * count, fileData, GL_DYNAMIC_DRAW);
//绑定到一个纹理单元
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_BUFFER, texBOTexture);
//绑定
glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, texBO);
但是,纹理缓冲区不能在着色器中用普通的采样器-sampler1D、sampler2D来进行访问,而是使用samplerBuffer,采样函数也相应地变为了texelFetch:
uniform samplerBuffer sampler;
void main() {
......
int offset = int(vColor.r * (1024 - 1));
factor.r = texelFetch(sampler, offset).r;
......
}


原创粉丝点击