[OpenGL 超级宝典][笔记] 8.1 PBO介绍与用法

来源:互联网 发布:数据库设置主键的语句 编辑:程序博客网 时间:2024/06/01 09:11

像素缓冲区对象(PixelBufferObject)

在上一节中,我们介绍了通用的缓冲区的创建,使用,删除。
当通用的缓冲区对象绑定到PBO缓冲点时,一个缓冲区才成为一个像素缓冲区。

绑定点 未绑定的性质 绑定后性质 GL_PIXEL_PACK_BUFFER 包装缓冲区绑定点 1.OpenGL读取像素的操作,如glReadPixels, glGetTexImage, glGetCompressedTexImage,会从像素缓冲区中读取数据。2.通常是从帧缓冲区或纹理中抽取数据,读到CPU内存中 当绑定到包装缓冲区绑定点时,像素数据在GPU内存中,不会下载到CPU内存中. GL_PIXEL_UNPACK_BUFFER 解包缓冲区绑定点 1.OpenGL绘制像素的操作,如glDraw等会将绘制得到的数据放入像素缓冲区中。glTexImage, glTexSubImage, glCompressedTexImage, glCompressedTexSubImage 2.将数据或纹理从本地CPU内存读取到帧缓冲区中。 当绑定到解包缓冲区绑定点时,读取操作会从GPU内存中,而不是CPU内存中。

优点:

  • PBO存放数据时,需要先通过glBufferData 申请GPU内存。
  • 操作都是在GPU管线中进行,不需要等待所有的活动停止后,就可以将新数据发送到GPU中。提高性能。

Example1:

ReadPixels 将数据从GPU拷贝到CPU端内存块data

// Code 1void* data = (void*)malloc(pixelDataSize);glReadBuffer(GL_BACK_LEFT);glReadPixels(0, 0, GetWidth(), GetHeight(), GL_RGB,GL_UNSIGNED_BYTE, NULL);

使用了GL_PIXEL_PACK_BUFFER绑定点后,ReadPixels 将数据从GPU拷贝到GPU端的名为pixBufferObjs[0]的Buffer

// Code2glReadBuffer(GL_BACK_LEFT);glBindBuffer(GL_PIXEL_PACK_BUFFER, pixBufferObjs[0]);glReadPixels(0, 0, GetWidth(), GetHeight(), GL_RGB,GL_UNSIGNED_BYTE, NULL);

Code2相对Code1, 对于需要将绘制结果回拷到GPU的应用场景,会有极大的性能提升,并且代码简单。


Example2:

运动模糊 (Motion Blur) 效果有助于显示场景中哪些对象是运动的,以及它们运动有多快。
程序中的一种简单的处理方式是:将绘制出的前端的几帧结果存储并与当前帧混合到一起。

下面的示例中,采用二种方式,分别实现Motion Blur的效果:一种是将GPU绘制的数据拷贝到CPU中,再拷贝到GPU中进行运动模糊。另一种是直接使用PBO的方式,如示例代码Code2中,直接将GPU绘制的结果,拷贝到GPU的PBO中,进行运动模糊。

示例代码如下:
纹理与PBO的初始化

glGenTextures(6, blurTextures); // Create Textures// 分配像素缓冲区来对纹理和PBO初始化pixelDataSize = GetWidth() * GetHeight() * 3 * sizeof(unsigned byte);void* data = (void*)malloc(pixelDataSize);memset(data, 0x00, pixelDataSize);// 设置纹理单元并初始化纹理数据for(int i=0; i<6; i++){    glActiveTexture(GL_TEXTURE1 + i);    glBindTexture(GL_TEXTURE_2D, blurTextures[i]);    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, GetWidth(), GetHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, data);}// 为像素的复制分配空间,避免每次进行绘制时都调用函数原形glGenBuffers(1, pixBuffObjects);glBindBuffer(GL_PIXEL_PACK_BUFFER, pixBuffObjs[0]);glBufferData(GL_PIXEL_PACK_BUFFER, pixelDataSize, pixelData, GL_DYNAMIC_COPY);glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);

将绘制结果作为纹理,通常有二种方式,如下:
- 传统的方式,是使用glReadPixels来得到像素数据,调用glTexImage2D将像素数据复制GPU的纹理对象上去。
- 使用PBO的方式时,绑定到GL_PIXEL_PACK_BUFFER, 调用glReadPixels时,这些像素将重新定向到PBO而不是再次回到CPU。解除绑定后,绑定到GL_PIXEL_UNPACK_BUFFER, 调用glTexImage2D时,缓冲区中的数据将加载到纹理上。

if(bUsePBOPatch){    //绑定到包装绑定点,然后将像素读取到PBO中    glBindBuffer(GL_PIXEL_PACK_BUFFER, pixBuffObjs[0]);    glReadPixels(0, 0, GetWidth(), GetHeight(), GL_RGB, GL_UNSIGNED_BYTE, NULL);    glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);    // 绑定到解包绑定点上,然后将像素读取到PBO中,再将像素放入纹理    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pixBuffObjs[0]);    // 为新模糊设置纹理,每一帧都有增加    glActiveTexture(GL_TEXTURE0 + GetBlurTarget0());    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, GetWidth(), GetHeight(),    0, GL_RGB, GL_UNSIGNED_BYTE, NULL);    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);}else{    // 获取屏幕像素并复制客户端内存     glReadPixels(0, 0, GetWidth(), GetHeight(), GL_RGB, GL_UNSIGNED_BYTE, pixelData);    //将像素从客户端内存中移到纹理中    //为新的模糊设置纹理单元,每一帧都增加    glActiveTexture( GL_TEXTURE0 + GetBlurTarget0() );    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, GetWidth(), GetHeight(),    0, GL_RGB, GL_UNSIGNED_BYTE, pixelData);}

Shader 片段着色器

#version 150in vec2 vTexCoord;uniform sampler2D textureUnit0;uniform sampler2D textureUnit1;uniform sampler2D textureUnit2;uniform sampler2D textureUnit3;uniform sampler2D textureUnit4;uniform sampler2D textureUnit5;void main(void){    vec4 blur0 = texture(textureUnit0, vTexCoord);    vec4 blur1 = texture(textureUnit1, vTexCoord);    vec4 blur2 = texture(textureUnit2, vTexCoord);    vec4 blur3 = texture(textureUnit3, vTexCoord);    vec4 blur4 = texture(textureUnit4, vTexCoord);    vec4 blur5 = texture(textureUnit5, vTexCoord);    vec4 summedBlur = blur0 + blur1 + blur2 + blur3 + blur4 + blur5;    gl_FragColor = summedBlur / 6.0;}

结论:

PBO的方式,比传统的方式性能上快很多。

原创粉丝点击