无法将简单的帧缓冲区渲染到四边形并显示

Posted

技术标签:

【中文标题】无法将简单的帧缓冲区渲染到四边形并显示【英文标题】:Not able to get a simple framebuffer rendered to a quad and displayed 【发布时间】:2019-04-01 19:22:55 【问题描述】:

这是我第一次尝试使用帧缓冲区而不是使用默认帧缓冲区(然后将帧缓冲区纹理绘制到默认帧缓冲区上)。我一路上做错了什么,我不确定是什么。我将列出我正在执行的每个步骤,因为这个示例非常接近 MCVE。

我想要的所有 glEnable() 状态都设置正确,根据 RenderDoc,这不是剔除面问题。我已经用glDebugMessageCallback 注册了一个回调并且我没有收到任何错误。我还附加了一个深度/模板纹理,但在此示例中未使用它(但在我解决此问题时将使用它)。

我的代码基本上如下(一切都是顺序的,所以如果在步骤X 有绑定,那么它也绑定到步骤X + 1):

0) 创建用于绘制 FBO 纹理的 VBO/VAO/shader 程序

// This is fine according to RenderDoc

1) 使用我们将渲染到的纹理和深度/模板纹理创建帧缓冲区:

glGenFramebuffers(1, &fbo);

// Indenting added to this MCVE only
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    glBindTexture(GL_TEXTURE_2D, texture);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glBindTexture(GL_TEXTURE_2D, 0);

    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);

    [same as above but now with a depth24/stencil8 texture]

    assert GL_FRAMEBUFFER_COMPLETE (which returns true)
glBindFramebuffer(GL_FRAMEBUFFER, 0);

2) 渲染阶段,清除默认帧缓冲区

glClearColor(0.1, 0.1, 0.1, 1.0)
glClear(GL_COLOR_BUFFER_BIT)
glViewport(0, 0, w, h)

3) 在绘制四边形之前绑定新的 FBO 并清除它

glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glClearColor(0.0, 0.0, 0.0, 0.0)
glClear(color/stencil/depth bits)
glViewport(0, 0, w, h)

4) 用 2 个三角形在新的帧缓冲区中绘制一个四边形

// Not needed in the MCVE but will be required in the actual
// application, so I'll put it in here in the odd case this
// is somehow important
glClearDepth(1.0);

// Do I need to bind the depth/stencil texture here as well?
glBindTexture(GL_TEXTURE_2D, texture);
    bind shader program
        set mvp uniform
        bind vbo
            upload 2 triangles to vbo
            // My goal is to draw the quad on the second
            // framebuffer here
            glDrawArrays(...)
        unbind vbo
    unbind shader
glBindTexture(GL_TEXTURE_2D, 0);

5) 将 FB 纹理绘制到默认帧缓冲区上

glBindFramebuffer(GL_FRAMEBUFFER, 0);

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
    // QUESTION: Do I also need to bind the depth/stencil here
    //           if I was planning to use it? Right now I do not
    //           and I'm wondering if that's a mistake
    bind second shader program
        create vbo/vao
        bind vbo
            fill up vbo with a quad to draw the framebuffer texture
            glDrawArrays(...)
        unbind vbo
    unbind second shader program
glBindTexture(GL_TEXTURE_2D, 0);

最后一步没有设置制服,因为我给它 NDC 坐标并且不需要转换。

当我运行程序并调出帧缓冲区时,我可以看出它正在做一些事情,因为我的 FPS 从 4000-5000 fps 下降到大约 2000 fps,并且我开始出现线圈嗡嗡声,并且当我切换帧缓冲区渲染时,它会恢复到 4k-5k fps,并且线圈的呜呜声消失了。

但是没有什么吸引...

我在 RenderDoc 中打开它以查看发生了什么,这就是它告诉我的:

1) 我所有的数据(VBO、视口、清晰的颜色等)都完全符合我的要求。如果您将看到的数据有任何错误,那是我对 OpenGL 的误解,而不是代码错误。

2) 第二个帧缓冲区和默认帧缓冲区网格结果似乎都超出了截锥体,我做错了什么吗?

我的第一步是将内容绘制到第二个帧缓冲区上(我在 800x600 视口上渲染一个四边形,我想在该视口上填充上半部分(所以 0,0 到 800,300,正交矩阵被设计为具有左上角是原点)),然后我会在上面绘制这个“第二帧缓冲区”的纹理。所以对于第二个帧缓冲区......这就是我所看到的:

实际上,白框(我假设是视锥)之间有一个间隙,这让我想知道我是否在错误的 Z 处绘制了两个逆时针三角形。Renderdoc 说我在绘制它们NDC Z 坐标为 -0.60,所以我认为这是在范围内,因为 NDC 立方体在所有 3 个轴上都是从 -1 到 +1。

四边形本身与第二张图片中看到的一致,但我的一部分想知道我的正交矩阵是否做错了什么......但是如果 NDC Z 值为 -0.60 就可以了我假设转换正在做我想做的事情。

RenderDoc 说四边形是根据帧缓冲区纹理渲染的,这很好。为了调试它,我做了:

// Force any draw we have to be red and max alpha to debug it
fragColor.x = 1.0;
fragColor.w = 1.0;

我们根据 RenderDoc 将其作为第二个帧缓冲区的输出:

那就太好了!我似乎正确地在第二个帧缓冲区上绘制了四边形。

那我的问题一定是我没有正确渲染到默认的帧缓冲区,但我不知道为什么。

为了尝试调试它,我对使用默认帧缓冲区的着色器做了同样的事情(除了我将在片段着色器中使用绿色而不是红色)。然而没有画出绿色,但 RenderDoc 说我做的其他一切都是正确的。这是信息:

顶点着色器:

    #version 440 core

    layout(location = 0) in vec3 pos;
    layout(location = 1) in vec2 uv;

    out vec2 uvFrag;

    void main() 
        uvFrag = uv;
        gl_Position = vec4(pos, 1.0);
    

片段着色器:

    #version 440

    in vec2 uvFrag;

    out vec4 fragColor;

    uniform sampler2D framebufferTexture;

    void main() 
        fragColor = texture(framebufferTexture, uvFrag);

        // Debug whether its actually even drawing the quad
        fragColor.y = 1.0;
        fragColor.w = 1.0;
    

网格输出:

我不确定我在哪里搞砸了。 NDC Z坐标为-0.90好吗?如果它们是,那么构成四边形的两个三角形正在到达那里......但它们没有被着色。由于在 1.0 时使用绿色/alpha 进行调试,即使纹理有某种错误,我也应该至少得到 something 被绘制。

管道状态很好,交换缓冲区适用于其他事情,所以我很确定我在那里没有做错任何事情。未发现 GL 错误。

关于为什么三角形没有被默认帧缓冲区拾取的任何猜测?我只在默认帧缓冲区上获得了清晰的颜色,这让我想知道我是否在错误的地方绑定了东西,或者我搞砸了重新绑定默认帧缓冲区,或者我发布的最后一张图像中的 NDC 坐标是错了,或者是我不知道的其他问题。

【问题讨论】:

能否提供renderdoc捕获文件?此外,“示例非常接近 MCVE” - 正确的 MCVE 会有很大帮助,它可以让我们尝试调试您的代码。 @HolyBlackCat 我在帖子末尾上传了一个渲染文档捕获。遗憾的是,我无法发布完整的代码,因为它是大型代码库的一部分。回想起来,我应该刚刚完成了一个完全独立的 MCVE。如果您(或其他任何人)找不到问题所在,我将使用独立的 MCVE 重新发布一个新问题。它可能与您在这篇文章中看到的内容以及 renderdoc 文件中的内容极为相似。 @Water 渲染第二遍时是否禁用深度测试? @Rabbid76 它已启用第二遍,或者更确切地说,我从未禁用深度测试。我唯一一次使用深度是在 glClear() 中重置它时,以及在渲染第二个帧缓冲区时将其设置为等于 1.0f 的时候。我应该尝试禁用它,还是我不应该使用它?我可以禁用它并尝试,因为我只渲染一个帧缓冲区(而且我知道无论如何禁用深度的顺序不应该影响我的最终结果,所以如果你想要我可以试试)。 @Water 你没有清除默认帧缓冲区的深度缓冲区,所以你必须禁用深度测试。 【参考方案1】:

在第二遍渲染之前,必须禁用深度测试

glDisable(GL_DEPTH_TEST);

或者必须清除渲染目标的深度缓冲区(默认帧缓冲区):

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

注意,默认情况下深度函数是GL_LESS,所以如果深度缓冲区没有被“清除”,深度测试将失败1.0。即使在第一帧深度测试成功,在第二帧颜色缓冲区被清除,深度测试将失败并且根本没有渲染任何东西。

【讨论】:

【参考方案2】:
// Do I need to bind the depth/stencil texture here as well?
glBindTexture(GL_TEXTURE_2D, texture);

实际上你根本不应该在那里绑定任何东西。如果将纹理绑定到纹理单元,则可能会创建反馈循环(如果纹理单元是从着色器中采样的)。如果管道配置不会从中读取,则允许保留渲染目标纹理绑定,但在“不会渲染”的情况下,最好确保渲染目标纹理不处于“可读”状态时画到。

【讨论】:

我是否正确地说,一旦我调用了glFramebufferTexture2D(...),在使用帧缓冲区时我不再需要绑定它,直到我必须在最终渲染到帧缓冲区时使用它? @Water:是的,因为glBindTexture 就是选择纹理作为数据源。

以上是关于无法将简单的帧缓冲区渲染到四边形并显示的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL 帧缓冲区缺失面

在 OpenGL 中显示帧缓冲区

我是不是需要将渲染缓冲区附加到已经附加了纹理的帧缓冲区?

将模板渲染缓冲区绑定到opengl中的帧缓冲区

opengl - 比我设置的更小的帧缓冲纹理?

渲染到纹理时出现黑屏