在固定功能管线绘图中使用FBO将OpenGL场景渲染为纹理 [英] Render OpenGL scene to texture using FBO in fixed function pipeline drawing

查看:177
本文介绍了在固定功能管线绘图中使用FBO将OpenGL场景渲染为纹理的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我致力于开源游戏torcs( http://torcs.sourceforge.net/) . 游戏的图形流水线仍使用OpenGL 1.3的固定功能流水线(FFP).

I work on the open source game torcs (http://torcs.sourceforge.net/). The game's graphic pipeline is still using the fixed function pipeline (FFP) of OpenGL 1.3.

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

I try to render the game scenes to textures in a FBO (Framebuffer Object) in order to do some post-processing on the rendered textures. I use OpenGL 3.3. on my machine.

当前,我已在FBO上设置了附着纹理,在GL_COLOR_ATTACHMENT0&1(2,以便在着色器中可以读取两个连续的帧),并在GL_DEPTH_ATTACHMENT附着了渲染缓冲区.

Currently I have set up the FBO with attached textures at GL_COLOR_ATTACHMENT0&1 (2 in order to have two consecutive frames readable in the shader) and an attached renderbuffer at GL_DEPTH_ATTACHMENT.

绑定FBO之后,将执行游戏渲染功能.然后,当我解除绑定FBO并进行验证时,通过着色器程序将纹理写回到窗口缓冲区,则场景是不完整的.更具体地说,仅绘制了汽车的轮廓,轮胎的打滑痕迹和一些烟雾也被绘制.这表明某些东西已渲染到FBO的纹理上,但不是全部.除其他外,FBO中没有将纹理(树木,房屋,草等)渲染为该纹理.这表明我的纹理设置不正确,但是不幸的是,我对OpenGL的了解有限,这就是为什么希望得到您的帮助.

After binding the FBO the game rendering function is executed. When I afterwards unbind the FBO and for validation write back the texture via a shader program to the window buffer the scene is incomplete. To be more specific, only the outline of the car is rendered, so are skidmarks of the tires and some smoke. This shows that something is rendered to the texture of the FBO, but not everything. Among others no textures (for trees, houses, grass etc.) are rendered to the texture in the FBO. This suggests that my texture set up is incorrect, but unfortunately my knowledge of OpenGL is limited, which is why I hope for your help.

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

One other thing worth noting is, that if I leave out the line glActiveTexture(GL_TEXTURE0); before the drawing happens then one texture will be displayed (i.e. will be written to the FBO and written back to the window system framebuffer instead of the car outline.

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

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,则所有内容将像以前一样直接呈现到屏幕上.我所做的唯一更改是在方括号内.

The following code shows where the drawing happens: Edit: if use_fbo=false then everything will be rendered to screen directly as before. The only changes I made are within the brackets.

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); 
}

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

I hope I provided enough information for you to help me with this. Any advice is appreciated.

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

I checked my shader code and FBO implementation again (simplified it to just one color attachment etc. with a simplified drawing) and it all worked. I think the trouble is the mix of fixed function pipeline for drawing and my implementation of the FBO...

编辑:以下是两幅有关use_fbo = true与false发生的情况的图像: (注意:红色是FBO绑定后的透明颜色,以查看向FBO渲染的内容:除了阴影和防滑痕迹之外没有其他内容)

here are two images of what happens with use_fbo=true vs. false: (Note: the red color is the clear color after the FBO is bound, to see what gets rendered to the fbo: nothing apart from the shadow and skid marks)

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

I also tried to visualize the depth buffer (changed the implementation to a texture attachment for depth) and even though I linearized, there was no information. I suppose the depth is not correctly written to the FBO either.

推荐答案

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

When I compared your code with mine working engine I see these differences so try them one by one:

  1. 纹理格式

您正在使用:

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

因此将您的所有合并到:

so merging all your to:

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.

You need to change internal pixel format of textures to specify bit width. If my memory serves well when I code this (some years back) it did not work with just GL_RGBA,GL_RGBA.

深度目标

我使用depthstencil纹理的方式与color attachment相同,我不使用任何RenderBuffer调用.那并不意味着您的代码是错误的,而是我的代码已经过测试并且可以工作.

I am using depth and stencil textures the same way as color attachment I do not use any RenderBuffer calls. That does not mean your code is wrong but mine is tested and works.

纹理大小

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

This is most likely not valid anymore as most gfx cards support rectangle texture extension but OpenGL textures should be power of 2 resolution. So for starters try 512x512 instead of your 640x480 And change back when your code is working (just to be sure ...).

如果这对我的 C ++ FBO class有帮助,那么您可以从中进行比较(因为它使用了引擎中的纹理和内容,因此无法单独使用):

In case it helps here is my C++ FBO class taken from mine engine so you got something to compare to (will not work alone as it uses textures and stuff from the engine):

//------------------------------------------------------------------------------
//--- 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 之间进行转换等等.


OpenGLscreen scr is my rendering engine
scr.cls() is just glClear and stuff to init frame
scr.x0,y0,xs,ys is viewport of target window
scr.txrs is texture system class (handles all the textures) like add new texture loading/saving from/to file, conversion between CPU/GPU and much much more.

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

I also use mine dynamic list template so:


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个项目预分配空间


List<double> xxx; is the same as double xxx[];
xxx.add(5); adds 5 to end of the list
xxx[7] access array element (safe)
xxx.dat[7] access array element (unsafe but fast direct access)
xxx.num is the actual used size of the array
xxx.reset() clears the array and set xxx.num=0
xxx.allocate(100) preallocate space for 100 items

典型用法是:

// [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

在这里查看具体示例:

对于那些被较旧的 Intel HD图形卡住的人来说,由于驱动程序中的错误,他们并不希望这种方法能正常工作.请参阅以下缓慢的解决方法:

Of coarse for those of you that are stuck with older Intel HD graphics do not expect that this will work due to bug in drivers. See this slow workaround:

这篇关于在固定功能管线绘图中使用FBO将OpenGL场景渲染为纹理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆