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

问题

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

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

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

在绑定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 =真

use_fbo = FALSE

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


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

  • 纹理格式

    您正在使用:

    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

  • 深度目标

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

  • 纹理大小

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

  • 如果它有帮助,那么我的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和init框架的内容
    scr.x0,y0,xs,ys是目标窗口的视口
    scr.txrs是纹理系统类(处理所有纹理),比如add新纹理加载/保存/从文件, CPU / GPU之间的转换等等。

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


    List<double> xxx;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
    

    请看这里的具体例子:

  • 使用OpenGL渲染填充具有大量顶点的复杂多边形
  • 对于那些坚持使用旧款英特尔高清显卡的用户来说 ,粗糙对于那些由于驱动程序中的错误而不期望这会起作用。 看到这个缓慢的解决方法:

  • OpenGL规模单像素线
  • 链接地址: http://www.djcxy.com/p/33931.html

    上一篇: Render OpenGL scene to texture using FBO in fixed function pipeline drawing

    下一篇: How to use Multisampling with OpenGL FBOs