在固定功能管道绘图中使用FBO将OpenGL场景渲染为纹理

时间:2017-05-10 18:10:00

标签: opengl textures fbo render-to-texture

问题

我在开源游戏torcs(http://torcs.sourceforge.net/)上工作。 游戏的图形管道仍在使用OpenGL 1.3的固定功能管道(FFP)。

我尝试将游戏场景渲染到FBO(Framebuffer对象)中的纹理,以便对渲染的纹理进行一些后处理。我使用的是OpenGL 3.3。在我的机器上。

目前,我已经在GL_COLOR_ATTACHMENT0&1设置了附加纹理的FBO(2为了在着色器中有两个连续的帧可读),并在GL_DEPTH_ATTACHMENT设置了附加的渲染缓冲区。

绑定FBO后,执行游戏渲染功能。当我之后取消绑定FBO并进行验证时,通过着色器程序将纹理写回窗口缓冲区,场景不完整。更具体地说,仅渲染汽车的轮廓,轮胎的滑动痕迹和一些烟雾也是如此。这表明某些内容会呈现给FBO的纹理,但不是所有内容。其中没有纹理(树木,房屋,草等)被渲染到FBO中的纹理。这表明我的纹理设置不正确,但不幸的是我对OpenGL的了解有限,这就是为什么我希望得到你的帮助。

值得注意的另一件事是,如果我在绘图发生之前省略了行glActiveTexture(GL_TEXTURE0);,那么将显示一个纹理(即将被写入FBO并写回窗口系统帧缓冲而不是汽车大纲。

守则

以下代码显示了FBO的初始化(来自https://en.wikibooks.org/wiki/OpenGL_Programming/Post-Processing):

int screen_width = 640; 
int screen_height = 480;
/* Texture A*/
glGenTextures(1, &fbo_texture);
glBindTexture(GL_TEXTURE_2D, fbo_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, screen_width, screen_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D, 0);

/* Texture B*/
//glActiveTexture(GL_TEXTURE1);
glGenTextures(1, &fbo_texture_a);
glBindTexture(GL_TEXTURE_2D, fbo_texture_a);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, screen_width, screen_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D, 0);

/* Depth buffer */
glGenRenderbuffers(1, &rbo_depth);
glBindRenderbuffer(GL_RENDERBUFFER, rbo_depth);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, screen_width, screen_height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);

/* Framebuffer to link everything together */
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo_texture, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, fbo_texture_a, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo_depth);
GLenum status;
if ((status = glCheckFramebufferStatus(GL_FRAMEBUFFER)) != GL_FRAMEBUFFER_COMPLETE) {
    fprintf(stderr, "glCheckFramebufferStatus: error 0x%x", status);
    return 0;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);

/* Compile and link shaders */
...

以下代码显示了绘图的位置: 修改:如果use_fbo=false,那么所有内容都会像以前一样直接呈现给屏幕。我所做的唯一更改是在括号内。

if (use_fbo) 
{
    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    glPushAttrib(GL_VIEWPORT_BIT);
    glViewport(0,0,grWinw, grWinh);

    if (fbo_a) // drawing to fbo_texture_a
    {               
        glDrawBuffer(GL_COLOR_ATTACHMENT1);
        glActiveTexture(GL_TEXTURE0+11);
        glEnable(GL_TEXTURE_2D);
        glBindTexture(GL_TEXTURE_2D, fbo_texture_a);
    }
    else
    {
        glDrawBuffer(GL_COLOR_ATTACHMENT0);
        glActiveTexture(GL_TEXTURE0+12);
        glEnable(GL_TEXTURE_2D);
        glBindTexture(GL_TEXTURE_2D, fbo_texture);
    }
    glActiveTexture(GL_TEXTURE0);
    glEnable(GL_TEXTURE_2D);
}

glClearColor(0.7f, 0.1f, 0.1f, 1.0f); //clear with red to see what is drawn to the fbo
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

grScreens[0]->update(s, grFps);//THIS IS WHERE THE DRAWING HAPPENS unchanged from original drawing in TORCS

if (use_fbo) 
{
    glPopAttrib();
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    glEnable(GL_TEXTURE_2D);
    glDrawBuffer(GL_BACK);

    glClearColor(1.0, 1.0, 1.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

    glUseProgram(program_postproc);

    if (fbo_a) // drawn to fbo_texture_a
    {
        glUniform1i(uniform_fbo_texture, 11);        
        glUniform1i(uniform_fbo_texture_a, 12);        
        fbo_a=!fbo_a;
    }
    else
    {
        glUniform1i(uniform_fbo_texture, 12);        
        glUniform1i(uniform_fbo_texture_a, 11);        
        fbo_a=!fbo_a;
    }

    glEnableVertexAttribArray(attribute_v_coord_postproc);

    glBindBuffer(GL_ARRAY_BUFFER, vbo_fbo_vertices);
    glVertexAttribPointer(
            attribute_v_coord_postproc, 2, GL_FLOAT, GL_FALSE, 0, 0);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    glDisableVertexAttribArray(attribute_v_coord_postproc);
    glUseProgram(0); 
}

我希望我能为您提供足够的信息来帮助我。任何建议表示赞赏。

编辑:我再次检查了我的着色器代码和FBO实现(将其简化为只有一个颜色附件等,并使用简化的图纸)并且一切正常。我认为麻烦的是用于绘图的固定功能管道和我对FBO的实现......

编辑:以下是use_fbo = true与false结果的两张图片: (注意:绑定FBO后,红色是清晰的颜色,看看有什么呈现给fbo:除了阴影和防滑标记之外什么都没有)

use_fbo=true

use_fbo=false

我还试图可视化深度缓冲区(将实现更改为深度的纹理附件),即使我线性化,也没有信息。我想深度也没有正确写入FBO。

1 个答案:

答案 0 :(得分:0)

当我将您的代码与我的工作引擎进行比较时,我会看到这些差异,因此请逐一尝试:

  1. 纹理格式

    你正在使用:

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, screen_width, screen_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
    

    将你所有的东西合并到:

    GL_COLOR_ATTACHMENT0: GL_RGBA,GL_RGBA,GL_UNSIGNED_BYTE
    GL_COLOR_ATTACHMENT1: GL_RGBA,GL_RGBA,GL_UNSIGNED_BYTE
    

    我正在使用:

    GL_COLOR_ATTACHMENT0 : GL_RGBA           , GL_RGBA8            , GL_UNSIGNED_BYTE
    GL_DEPTH_ATTACHMENT  : GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT16, GL_UNSIGNED_BYTE
    GL_STENCIL_ATTACHMENT: GL_STENCIL_INDEX  , GL_STENCIL_INDEX8   , GL_UNSIGNED_BYTE
    

    您需要更改纹理的内部像素格式以指定位宽。如果我在编写代码时(几年前)我的记忆效果很好,那么只有GL_RGBA,GL_RGBA才能使用。

  2. 深度目标

    我使用depthstencil纹理的方式与color attachment相同。我不使用任何RenderBuffer次调用。这并不意味着你的代码是错误的,但我的代码是经过测试和运作的。

  3. 纹理尺寸

    这很可能不再有效,因为大多数gfx卡支持矩形纹理扩展,但 OpenGL 纹理应该是2分辨率的功率。因此,对于初学者来说,请尝试使用512x512而不是640x480并在代码运行时进行更改(只是为了确保......)。

  4. 如果它有帮助,我的 C ++ FBO class取自我的引擎,所以你有比较的东西(不会单独工作,因为它使用纹理和引擎的东西) :

    //------------------------------------------------------------------------------
    //--- Open GL FBO object ver 2.31 ----------------------------------------------
    //------------------------------------------------------------------------------
    #ifndef _OpenGL_FBO_h
    #define _OpenGL_FBO_h
    //------------------------------------------------------------------------------
    class OpenGL_FBO
        {
    public:
        GLuint fbo;
        int xs,ys;
        struct _dst
            {
            GLint txr;      // texture id
            GLenum dst;     // GL_DEPTH_COMPONENT, GL_COLOR_ATTACHMENT0, ...
            _dst()          { txr=-1; dst=GL_COLOR_ATTACHMENT0; }
            _dst(_dst& a)   { *this=a; }
            ~_dst()         {}
            _dst* operator = (const _dst *a) { *this=*a; return this; }
            //_dst* operator = (const _dst &a) { ...copy... return this; }
            };
        List<_dst> dst;
    
        OpenGL_FBO() { fbo=0xFFFFFFFF; xs=1; ys=1; dst.reset(); }
        OpenGL_FBO(OpenGL_FBO& a)   { fbo=0xFFFFFFFF; dst.reset(); *this=a; }
        ~OpenGL_FBO() { if (fbo!=0xFFFFFFFF) glDeleteFramebuffers(1,&fbo); }
        OpenGL_FBO* operator = (const OpenGL_FBO *a) { *this=*a; return this; }
        //OpenGL_FBO* operator = (const OpenGL_FBO &a) { ...copy... return this; }
    
        void resize(OpenGLscreen &scr,int _xs=-1,int _ys=-1)
            {
            int i;
            _dst *d;
            if (_xs<=0) _xs=scr.xs;
            if (_ys<=0) _ys=scr.ys;
    //      for (xs=1;xs<_xs;xs<<=1);
    //      for (ys=1;ys<_ys;ys<<=1);
            xs=_xs; ys=_ys; // ****
    
            if (fbo==0xFFFFFFFF) glGenFramebuffers(1,&fbo);
            glBindFramebuffer(GL_FRAMEBUFFER,fbo);
            for (d=dst.dat,i=0;i<dst.num;i++,d++)
                {
                scr.txrs.bind(d->txr);
                scr.txrs.resize(d->txr,xs,ys,1);
    //          glFramebufferTexture2D(GL_FRAMEBUFFER,t->dst,GL_TEXTURE_2D,scr.txrs.names[d->txr],0);
                glFramebufferTexture(GL_FRAMEBUFFER,d->dst,scr.txrs.names[d->txr],0);
    //          glCheckFramebufferStatus(GL_FRAMEBUFFER);
                }
            scr.txrs.unbind();
            glBindFramebuffer(GL_FRAMEBUFFER,0);
            }
        int add(OpenGLscreen &scr,int _dest=GL_COLOR_ATTACHMENT0)   // add txr to fbo
            {
            _dst d;
            OpenGL_TXR tmp;
            // colro atachments
            tmp.pixelformat =GL_RGBA;
            tmp.pixeliformat=GL_RGBA8;
            tmp.pixeltype=GL_UNSIGNED_BYTE;
            tmp.mag=GL_NEAREST;
            tmp.min=GL_NEAREST;
            if (_dest==GL_DEPTH_ATTACHMENT)
                {
                tmp.pixelformat =GL_DEPTH_COMPONENT;
                tmp.pixeliformat=GL_DEPTH_COMPONENT16;
    //          tmp.pixeltype=GL_FLOAT;
                tmp.pixeltype=GL_UNSIGNED_BYTE;
                }
            if (_dest==GL_STENCIL_ATTACHMENT)
                {
                tmp.pixelformat =GL_STENCIL_INDEX;
                tmp.pixeliformat=GL_STENCIL_INDEX8;
                tmp.pixeltype=GL_UNSIGNED_BYTE;
                }
            tmp.xs=xs;
            tmp.ys=ys;
            tmp.zs=1;
            tmp._mipmap=0;
            tmp.txrtype=GL_TEXTURE_2D;
            d.txr=scr.txrs.add(tmp);
            d.dst=_dest;
            dst.add(d);
            return d.txr;
            }
        void bind(OpenGLscreen &scr)    // init fbo >> txr
            {
            // init and resize
            if (fbo==0xFFFFFFFF) glGenFramebuffers(1,&fbo);
            glBindFramebuffer(GL_FRAMEBUFFER,fbo);
            glViewport(0,0,xs,ys);
            scr.cls();
            }
        void unbind(OpenGLscreen &scr)
            {
            glBindFramebuffer(GL_FRAMEBUFFER,0);
            glViewport(scr.x0,scr.y0,scr.xs,scr.ys);
            }
        };
    //------------------------------------------------------------------------------
    #endif
    //------------------------------------------------------------------------------
    //--- end. ---------------------------------------------------------------------
    //------------------------------------------------------------------------------
    

    <强>其中:


    OpenGLscreen scr是我的渲染引擎
    scr.cls()只是glClear和初始框架的东西
    scr.x0,y0,xs,ys是目标窗口的视口
    scr.txrs是纹理系统类(处理所有纹理),如add新纹理加载/从文件保存, CPU / GPU 之间的转换等等更多。

    我也使用我的动态列表模板:


    List<double> xxx;double xxx[];相同
    xxx.add(5);5添加到列表的末尾
    xxx[7]访问数组元素(安全)
    xxx.dat[7]访问数组元素(不安全但快速直接访问)
    xxx.num是数组的实际使用大小
    xxx.reset()清除数组并设置xxx.num=0
    xxx.allocate(100)100项目预分配空间

    典型用法是:

    // [globals and init]
    OpenGLScreen scr; // can ignore this
    OpenGL_FBO fbo;
    scr.init(window_handle); // init OpenGL stuff can ignore this
    fbo.add(scr,GL_COLOR_ATTACHMENT0);
    fbo.add(scr,GL_DEPTH_ATTACHMENT);
    fbo.resize(scr,512,512);
    
    // [render loop]
    fbo.bind(scr);
    // here render
    fbo.unbind(scr);
    // here you can use the textures fbo.dst[].txr
    

    在这里查看具体示例:

    对于那些坚持使用较旧的英特尔高清显卡的人来说,粗略的,不要指望这会因驱动程序中的错误而起作用。看到这个缓慢的解决方法: