FBO

FBO一个最常见的应用就是:渲染到纹理(render to texture),通过这项技术可以实现发光效果,环境映射,阴影映射等很炫的效果。


OpenGL中的Frame Buffer Object(FBO)扩展,被推荐用于把数据渲染到纹理对像。相对于其它同类技术,如数据拷贝或交换缓冲区等,使用FBO技术会更高效并且更容易实现。


在OpenGL渲染管线中,几何数据和纹理最终都是以2d像素绘制到屏幕上。最后一步的渲染目标在OpenGL渲染管线中被称为帧缓存(frame buffer)。帧缓存是颜色缓存、深度缓存、模板缓存、累积缓存的集合。默认情况下, OpenGL使用的帧缓存是由窗体系统创建和管理的。


在OpenGL扩展中,GL_EXT_framebuffer_object扩展提供了一个创建额外帧缓存对象(FBO)的接口。这个帧缓存的创建和控制完全是由OpenGL完成的,有别于窗体系统创建的默认的帧缓存。与系统默认的帧缓存类似,一个FBO也是颜色缓存、深度缓存、模板缓存的集合(FBO不包括累积缓存),然后OpenGL程序就可以把渲染重定向到FBO中。


这里有一个新的概念需要注意,那就是renderbuffer object。这个对象是通过GL_EXT_framebuffer_object扩展创建。它被用来在渲染过程中为一个2D图像提供渲染目标。


下图展示了FBO和renderbuffer object与texture object之间的关系。从图中我们可以看出:多个renderbuffer object和texture object可以通过挂接点挂接到FBO上。需要主要的是FBO并没有实际存储数据的地方,它只是一个数据的壳,它只有挂接点。




一个FBO对象包含多个颜色挂接点和一个深度挂接点以及一个模板挂接点。不同的显卡支持的颜色挂接点的数目是不同的,可以通过查询GL_MAX_COLOR_ATTACHMENTS_EXT获取支持的最大的挂接点的数目。支持多个颜色挂接点的原因是FBO可以在同一时间内将颜色缓存渲染到多个目标中去,这种能力被称为MRT(multiple render targets)。通过GL_ARB_draw_buffers扩展可以实现该功能。


帧缓冲提供了一种有效的切换机制,使得挂接和卸载一个可挂接的图像非常之迅速。FBO使用glFramebufferTexture2DEXT()来进行texturebuffer对象的切换,使用glFramebufferRenderbufferEXT()来进行renderbuffer对象的切换。


让我们看一下其使用过程:

1.FBO对象的创建与销毁

void glGenFramebuffersEXT(GLsizei n, GLuint* ids);

void glDeleteFramebuffersEXT(GLsizei n, const GLuint* ids);

第一个参数是要创建的FBO的个数,第二个保存创建的FBO的ID。


2.一旦FBO对象创建完毕,即要进行绑定

void glBindFramebufferEXT(GLenum target, GLuint id);

第一个参数必须是GL_FRAMEBUFFER_EXT

第二个参数是第一步创建FBO对象中获取的id号,该id号是一个非0值,因为系统默认的FBO的id号是0,所以如果你想取消FBO的绑定,将id等于0作为id参数传递给该函数即可。


3.创建Renderbuffer Object

void glGenRenderbuffersEXT(GLsizei n, GLuint* ids);

void glDeleteRenderbuffersEXT(GLsizei n, const GLuint* ids);


4.同样的,Renderbuffer Object创建好之后,要记得绑定。

void glBindRenderbufferEXT(GLenum target, GLuint id)


5.确定Renderbuffer Object的数据格式和尺寸。

  这一步很重要,因为我们只是创建了Renderbuffer Object,还没有为它提供一个存储数据的地方,也没有指定其存储数据的地方存储什么格式的数据,所以接下来就要做这个事情了。

void glRenderbufferStorageEXT(GLenum target, GLenum internalFormat, GLsizei width, GLsizei height);

第一个参数必须是GL_RENDERBUFFER_EXT

第二个参数可以是可渲染的颜色格式,如(GL_RGB, GL_RGBA, etc.),可渲染的深度格式(GL_DEPTH_COMPONENT)或者是可渲染的模板格式(GL_STENCIL_INDEX)。

最后两个参数就是以像素为单位指定数据所要占用内存的大小。


这里需要注意两点:

1。 指定的内存区域的宽和高应该都小于GL_MAX_RENDERBUFFER_SIZE_EXT,否则将产生GL_INVALID_VALUE错误。

2。 所有的texture object也好,renderbuffer object也好,其宽和高都是严格一致的。


6.指定挂接的对象

glFramebufferTexture2DEXT函数挂接一个texture图像到FBO

glFramebufferRenderbufferEXT函数挂接一个Renderbuffer图像到FBO


7.检测FBO状态

该挂接的对象都挂接好了,在使用FBO之前,必须要检查FBO的状态是否处于完成状态。如果FBO处于未完成状态,那么绘制操作就会失败。如何获取FBO的完成状态呢?使用

glCheckFramebufferStatusEXT函数。


至此,关于FBO的概念及其使用就算告一段落了。下面给出一个完整的例子,该例子是从国外一个网站上找到的,代码写得简单而漂亮,对FBO的演示也很完善。







帧缓冲是显卡内存中的一块,保存在该内存区块中的图像数据会实时地在显示器上显示出来,帧缓存中的数据最大最小值会被限定在一个范围内,也就是 [0/255; 255/255]

有一个叫 EXT_framebuffer_object 的OpenGL的扩展, 允许我们把一个离屏缓冲区作为我们渲染运算的目标。我们通常把这一技术叫FBO,也就是 Frame Buffer Object的缩写。

要使用一个离屏缓冲区作我们的渲染运算区,只要很少的几行代码便可以实现了。当使用数字0来绑定一个FBO的时候,它会还原window系统的特殊帧缓冲区。因此可以用FBO实现在GPU上进行运算


2. FBO用法

分三步: 创建,使用,停止

创建:glGenFrameBuffersEXT, glBindFrameBuffersEXT, 绑定纹理

使用:glFrameBufferTexutre2DEXT(), glFrameBuffer2DEXT(...,0)

停止:glDeleteFrameBufferEXT


3. 示例(来自http://blog.chinaunix.net/u/15845/showart_315485.html)

/**

使用GPU的计算

1. 使用glTexSubImage2D将数据放到GPU的纹理内存里

2. 使用绘制将运算结果放到FBO里

3. 使用glReadPixels将结果读出到CPU里

*/

#include <stdio.h>

#include <stdlib.h>

#include <GL/glew.h>

#include <GL/glut.h>


int main(int argc, char **argv) {

    // 这里声明纹理的大小为:teSize;而数组的大小就必须是texSize*texSize*4

    int texSize = 2;

    int i;


    // 生成测试数组的数据

    float* data = (float*)malloc(4*texSize*texSize*sizeof(float));

    float* result = (float*)malloc(4*texSize*texSize*sizeof(float));

    for (i=0; i<texSize*texSize*4; i++)

        data[i] = (i+1.0)*0.01F;


    // 初始化OpenGL的环境

    glutInit (&argc, argv);

    glutCreateWindow("TEST1");

    glewInit();//使用了OpenGL扩展,所以必须glewInit

    // 视口的比例是 1:1 pixel=texel=data 使得三者一一对应

    glMatrixMode(GL_PROJECTION);

    glLoadIdentity();

    // glFrustum(-1,1,-1,1,1,-1);//测试变换

    gluOrtho2D(0.0,texSize,0.0,texSize);

    glMatrixMode(GL_MODELVIEW);

    glLoadIdentity();

    glViewport(0,0,texSize,texSize);


    

    // 生成并绑定FBO,也就是生成一个离屏渲染对像

    GLuint fb;

    glGenFramebuffersEXT(1,&fb); 

    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,fb);

    // 生成两个纹理,一个(tex)是<b>输入</b>的纹理,一个(fboTex)是<b>运算结果</b>的纹理

    GLuint tex,fboTex;

    glGenTextures (1, &tex);

    glGenTextures (1, &fboTex);


    //设置运算结果fboTex

    glBindTexture(GL_TEXTURE_RECTANGLE_ARB,fboTex);

    glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, 

                    GL_TEXTURE_MIN_FILTER, GL_NEAREST);

    glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, 

                    GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, 

                    GL_TEXTURE_WRAP_S, GL_CLAMP);

    glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, 

                    GL_TEXTURE_WRAP_T, GL_CLAMP);

    glTexImage2D(GL_TEXTURE_RECTANGLE_ARB,0,GL_RGBA32F_ARB,//申请内存

                 texSize,texSize,0,GL_RGBA,GL_FLOAT,0);


    //设置输入tex

    glBindTexture(GL_TEXTURE_RECTANGLE_ARB,tex);

    glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, 

                    GL_TEXTURE_MIN_FILTER, GL_NEAREST);

    glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, 

                    GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, 

                    GL_TEXTURE_WRAP_S, GL_CLAMP);

    glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, 

                    GL_TEXTURE_WRAP_T, GL_CLAMP);

    glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_COLOR,GL_DECAL);

    glTexImage2D(GL_TEXTURE_RECTANGLE_ARB,0,GL_RGBA32F_ARB,//申请内存

                 texSize,texSize,0,GL_RGBA,GL_FLOAT,0);


    //绑定FBO与运算结果,开始计算

    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, 

                              GL_COLOR_ATTACHMENT0_EXT, 

                              GL_TEXTURE_RECTANGLE_ARB,fboTex,0);


    //Step1. 将输入存入显卡

    glBindTexture(GL_TEXTURE_RECTANGLE_ARB,tex);

    glTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB,0,0,0,texSize,texSize,

                    GL_RGBA,GL_FLOAT,data);



    //--------------------begin-------------------------

    //以下代码是渲染一个大小为texSize * texSize矩形,

    //其作用就是把纹理中的数据,经过处理后,保存到帧缓冲中去,

    //由于用到了离屏渲染,这里的帧缓冲区指的就是FBO纹理。

    //在这里,只是简单地把数据从纹理直接传送到帧缓冲中,

    //没有对这些流过GPU的数据作任何处理,但是如果我们会用CG、

    //GLSL等高级着色语言,对显卡进行编程,便可以在GPU中

    //截获这些数据,并对它们进行任何我们所想要的复杂运算。

    //这就是GPGPU技术的精髓所在。

    //Step2 GPU->FBO, 运算应该发生在这里, 可以Enable Cg的Profile,实现运算

    glBindTexture(GL_TEXTURE_RECTANGLE_ARB,tex);

    glEnable(GL_TEXTURE_RECTANGLE_ARB);

            glBegin(GL_QUADS);

                glTexCoord2f(0.0, 0.0); 

                glVertex2f(0.0, 0.0);

                glTexCoord2f(texSize, 0.0); 

                glVertex2f(texSize, 0.0);

                glTexCoord2f(texSize, texSize); 

                glVertex2f(texSize, texSize);

                glTexCoord2f(0.0, texSize); 

                glVertex2f(0.0, texSize);

            glEnd();


    //--------------------end------------------------


    //Step3 从显卡的FBO将数据读出到CPU

    glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);

    glReadPixels(0, 0, texSize, texSize,GL_RGBA,GL_FLOAT,result);


    //打印输出结果

    printf("Data before roundtrip:\n");


    for (i=0; i<texSize*texSize*4; i++)

        printf("%f\n",data[i]);

    printf("Data after roundtrip:\n");

    for (i=0; i<texSize*texSize*4; i++)

        printf("%f\n",result[i]);

    // 释放本地内存

    free(data);

    free(result);


    // 释放显卡内存

    glDeleteFramebuffersEXT (1,&fb);

    glDeleteTextures (1,&tex);

    glDeleteTextures(1,&fboTex);

    return 0;

}

转载于:https://my.oschina.net/amoyai/blog/106750

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值