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

2023-12-09

问题

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

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

目前我已经在以下位置设置了带有附加纹理的 FBO:GL_COLOR_ATTACHMENT0&1(2是为了在着色器中具有两个连续的帧可读)和一个附加的渲染缓冲区GL_DEPTH_ATTACHMENT.

绑定FBO后,执行游戏渲染函数。当我随后解除 FBO 的绑定并通过着色器程序将纹理写回到窗口缓冲区进行验证时,场景不完整。更具体地说,只渲染了汽车的轮廓,还有轮胎的擦痕和一些烟雾。这表明有些东西被渲染到 FBO 的纹理,但不是所有东西。其中,没有纹理(树木、房屋、草地等)被渲染到 FBO 中的纹理。这表明我的纹理设置不正确,但不幸的是我对OpenGL的了解有限,这就是为什么我希望得到你的帮助。

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

The Code

下面的代码展示了FBO的初始化 (from 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 */
...

以下代码显示了绘图发生的位置: Edit: if 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); 
}

我希望我为您提供了足够的信息来帮助我解决这个问题。任何建议表示赞赏。

EDIT:我再次检查了我的着色器代码和 FBO 实现(使用简化的绘图将其简化为仅一种颜色附件等),并且一切正常。我认为问题在于用于绘图的固定功能管道和我对 FBO 的实现的混合......

EDIT:以下两张图片展示了 use_fbo=true 与 false 时发生的情况: (注:红色是FBO绑定后的清晰颜色,看看渲染到FBO上的是什么:除了阴影和防滑痕迹之外什么都没有)

use_fbo=true

use_fbo=false

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


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

  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. 深度目标

    我在用depth and stencil纹理的方式与color attachment我不使用任何RenderBuffer来电。这并不意味着您的代码是错误的,但我的代码已经过测试并且可以工作。

  3. 纹理尺寸

    这很可能不再有效,因为大多数 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. ---------------------------------------------------------------------
//------------------------------------------------------------------------------

where:


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

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


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

具体示例请看这里:

  • 使用 OpenGL 渲染具有大量顶点的填充复杂多边形

对于那些坚持较旧的人来说很粗糙英特尔高清显卡由于驱动程序中的错误,不要指望这会起作用。请参阅这个缓慢的解决方法:

  • OpenGL 比例单像素线
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • 没有着色器的 OpenGL

    我已经阅读了一些教程来编写以下代码 唯一的区别是原始教程使用 SDL 而不是 GLEW 我不明白这段代码有什么问题 它可以编译 但我没有看到三角形 教程也没有使用着色器 include
  • 使用 Opengl 绘制立方体 3D

    我想使用 OpenGL 绘制 3D 立方体这是我的代码如何纠正错误 float ver 8 3 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
  • (定义一个宏)方便OpenGL命令调试?

    有时插入条件打印和检查需要很长时间glGetError 使用二分搜索的形式来缩小范围 其中第一个函数调用是 OpenGL 首先报告错误 我认为如果有一种方法可以构建一个宏 我可以包装所有可能失败的 GL 调用 并有条件地调用 那就太酷了gl
  • 如何用opengl制作2D地形?

    我想制作一个简单的二维地形 只有一些颠簸和高度变化 我想过只使用随机数来描述某个顶点的高度 但我不知道如何从中制作一个网格 我正在寻找一种方法来查找地形的顶点和索引缓冲区 我该怎么做呢 您可以仅将 GL POLYGON 与所有顶点的列表一起
  • 三角形未在 OSX 上的 OpenGL 2.1 中绘制

    我正在学习有关使用 OpenGL 在 Java 中创建游戏引擎的教程 我正在尝试在屏幕上渲染一个三角形 一切运行良好 我可以更改背景颜色 但三角形不会显示 我还尝试运行作为教程系列的一部分提供的代码 但它仍然不起作用 教程链接 http b
  • OpenGL:调试“单通道线框渲染”

    我正在尝试实现论文 单通道线框渲染 它看起来很简单 但它给了我所期望的厚暗值 论文没有给出计算海拔高度的确切代码 所以我按照自己认为合适的方式进行了操作 代码应该将三个顶点投影到视口空间中 获取它们的 高度 并将它们发送到片段着色器 片段着
  • gluPerspective 与 gluOrtho2D

    我查看了 MSDN 上关于这两个函数的文档 但是 我不太明白这两个功能之间的区别 一个是用于设置 3D 相机视图 另一个是用于设置 2D 相机视图 如果能得到解答就太好了 预先感谢您的评论 正交投影基本上是没有透视的 3D 投影 本质上 这
  • 为什么我的 FPS 相机一劳永逸地滚动?

    如果我忽略四元数代数的肮脏细节 我想我理解了旋转和平移变换背后的数学 但仍然不明白我做错了什么 为什么我的相机一劳永逸地滚动 更具体地说 我应该如何从相机的方向 旋转矩阵 计算相机视图矩阵 我正在用 Python 编写一个简约的 3d 引擎
  • 如何安装适用于 Windows C++ 的最新版本 OpenGL?

    我正在使用 Visual Studio 2010 运行 Windows 7 包含的 OpenGL 版本 include 是版本 1 1 我希望使用合理的当前版本 某种版本 3 或 4 我需要做什么才能达到该状态 OpenGL SDK 页面位
  • 如何将点光源转换为卵形/椭圆形?

    我希望通过具有不同 x 和 y 值的 vec2 半径将当前的圆形光变成椭圆形 有没有办法根据我当前在片段着色器中的代码来做到这一点 uniform struct Light vec4 colour vec3 position vec2 ra
  • lwjgl 3 , glUniformMatrix4 导致 jre 崩溃

    我正在使用 lwjgl 3 并学习现代 opengl 3 我想将统一矩阵发送到顶点着色器 以便我可以应用转换 我尝试过 但程序因此错误而崩溃 A fatal error has been detected by the Java Runti
  • 使用 GLSL 着色器在同一片段着色器中定义的多个子例程类型无法正常工作

    我正在开发一个使用 GLSL 着色器的程序 我编写了 2 种不同的方法来用 2 种不同的方法计算 ADS 环境光 漫反射 镜面反射 着色 为了正确完成这项工作 我使用子例程来使用一种或另一种方法来计算 ADS 着色 这是片段着色器代码的一部
  • Eclipse 标记 OpenGL 函数无法解析

    我尝试在 Eclipse C C 中使用一些 OpenGL 函数 一些 标准 函数如 GlClear 可以被 eclipse 识别 而其他函数如 glBindBuffer 和 glEnableVertexAttribArray 则不能 它们
  • 使用draw()而不是eventloop时的pyglet

    我正在尝试用 pyglet 画一个圆 但当我使用 draw 函数而不是 app run 循环时 它是不可见的 有什么建议我可以做什么吗 谢谢 from math import from pyglet gl import window pyg
  • Android OpenGLES 渲染到纹理

    我为 iPhone 编写图形应用程序 并且希望将我最新的应用程序 Layers 移植到 Android 平台 Layers 是一款绘画应用程序 允许用户在屏幕上绘画并使用不同的画笔 颜色等创建多层绘画 并导出到 PSD 它有桌面同步 涂抹工
  • 更改 Qt OpenGL 窗口示例以使用 OpenGL 3.3

    我正在尝试更改 Qt OpenGL 示例以使用更现代的 opengl 版本 330 似乎合适 所以我做了 在 main cpp 上设置版本和配置文件 设置着色器版本 更改着色器以使用统一 它现在构建没有任何错误 但我只看到一个空白窗口 我错
  • Visual Studio 2010 中的 SOIL 设置

    我无法得到SOIL http www lonesock net soil html正确使用 Visual Studio 2010 我远非 VS 专家 但据我所知 只需执行以下步骤即可使环境正常运行 属性 gt gt C C gt 常规 gt
  • Android 纹理仅显示纯色

    我正在尝试在四边形上显示单个纹理 我有一个可用的 VertexObject 它可以很好地绘制一个正方形 或任何几何对象 现在我尝试扩展它来处理纹理 但纹理不起作用 我只看到一种纯色的四边形 坐标数据位于 arrayList 中 the ve
  • 三角形纹理映射OpenGL

    我正在开发一个使用 Marching Cubes 算法并将数据更改为 3D 模型的项目 现在我想在 OpenGL 中为我的 3D 模型使用纹理映射 我首先尝试了一个简单的示例 它将图片映射到三角形上 这是我的代码 int DrawGLSce
  • 使用 JOGL 和 Android OpenGL 编写可移植 Java 应用程序

    我计划编写一款可以在 PC 和 Android 上运行的 Java 3D 游戏 不幸的是 这两个平台似乎没有通用的 OpenGL API API 是否有显着差异 有没有办法在两个版本中使用相同的 3D 代码 这是不是一个好主意 Androi

随机推荐