无法将简单的帧缓冲区渲染到四边形并显示
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
就是选择纹理作为数据源。以上是关于无法将简单的帧缓冲区渲染到四边形并显示的主要内容,如果未能解决你的问题,请参考以下文章