FBO 的延迟着色器纹理显示为黑色
Posted
技术标签:
【中文标题】FBO 的延迟着色器纹理显示为黑色【英文标题】:Deferred shader textures from FBO display as black 【发布时间】:2015-02-23 00:31:08 【问题描述】:我正在尝试使用延迟着色来实现 SSAO,但在延迟片段着色器中访问我的纹理时遇到问题。代码在 C++/Qt5 中,并利用 Coin3D 生成 UI 的其余部分(但这在这里应该无关紧要)。
deferred pass的片段着色器为:
#version 150 compatibility
uniform sampler2D color;
uniform sampler2D position;
uniform sampler2D normal;
uniform vec3 dim;
uniform vec3 camPos;
uniform vec3 camDir;
void main()
// screen position
vec2 t = gl_TexCoord[0].st;
// the color
vec4 c = texture2D(color, t);
gl_FragColor = c + vec4(1.0, t.x, t.y, 1.0);
运行延迟传递的代码是
_geometryBuffer.Unbind();
// push state
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glPushAttrib(GL_DEPTH_BUFFER_BIT |
GL_COLOR_BUFFER_BIT |
GL_LIGHTING_BIT |
GL_SCISSOR_BIT |
GL_POLYGON_BIT |
GL_CURRENT_BIT);
glDisable(GL_DEPTH_TEST);
glDisable(GL_ALPHA_TEST);
glDisable(GL_LIGHTING);
glDisable(GL_COLOR_MATERIAL);
glDisable(GL_SCISSOR_TEST);
glDisable(GL_CULL_FACE);
// bind shader
// /!\ IMPORTANT to do before specifying locations
_deferredShader->bind();
_CheckGLErrors("deferred");
// specify positions
_deferredShader->setUniformValue("camPos", ...);
_deferredShader->setUniformValue("camDir", ...);
_geometryBuffer.Bind(GBuffer::TEXTURE_TYPE_NORMAL, 2);
_deferredShader->setUniformValue("normal", GLint(2));
_geometryBuffer.Bind(GBuffer::TEXTURE_TYPE_POSITION, 1);
_deferredShader->setUniformValue("position", GLint(1));
_geometryBuffer.Bind(GBuffer::TEXTURE_TYPE_DIFFUSE, 0);
_deferredShader->setUniformValue("color", GLint(0));
_CheckGLErrors("bind");
// draw screen quad
glBegin(GL_QUADS);
glTexCoord2f(0, 0);
glColor3f(0, 0, 0);
glVertex2f(-1, -1);
glTexCoord2f(1, 0);
glColor3f(0, 0, 0);
glVertex2f( 1, -1);
glTexCoord2f(1, 1);
glColor3f(0, 0, 0);
glVertex2f( 1, 1);
glTexCoord2f(0, 1);
glColor3f(0, 0, 0);
glVertex2f(-1, 1);
glEnd();
_deferredShader->release();
// for debug
_geometryBuffer.Unbind(2);
_geometryBuffer.Unbind(1);
_geometryBuffer.Unbind(0);
_geometryBuffer.DeferredPassBegin();
_geometryBuffer.DeferredPassDebug();
// pop state
glPopAttrib();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
我知道纹理已在几何缓冲区创建中得到正确处理,因为我可以将它们转储到文件中并获得预期的结果。
延期通行证不起作用。着色器正确编译,我在屏幕上得到以下结果:
我的代码的最后一部分(DeferredPassBegin/Debug)是将 FBO 绘制到屏幕上(如屏幕截图所示),以证明 GBuffer 是正确的。
当前结果似乎意味着纹理没有正确绑定到它们各自的制服,但我知道内容是有效的,因为我将纹理转储到文件并得到了与上图相同的结果。
我在 GBuffer 中的绑定函数是:
void GBuffer::Unbind()
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
void GBuffer::Bind(TextureType type, uint32_t idx)
glActiveTexture(GL_TEXTURE0 + idx);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, _textures[static_cast<uint32_t>(type)]);
void GBuffer::Unbind(uint32_t idx)
glActiveTexture(GL_TEXTURE0 + idx);
glDisable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
最后,纹理是 512/512,我在 GBuffer 中创建了它们:
WindowWidth = WindowHeight = 512;
// Create the FBO
glGenFramebuffers(1, &_fbo);
glBindFramebuffer(GL_FRAMEBUFFER, _fbo);
const uint32_t NUM = static_cast<uint32_t>(NUM_TEXTURES);
// Create the gbuffer textures
glGenTextures(NUM, _textures);
glGenTextures(1, &_depthTexture);
for (unsigned int i = 0 ; i < NUM; i++)
glBindTexture(GL_TEXTURE_2D, _textures[i]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, WindowWidth, WindowHeight, 0, GL_RGBA, GL_FLOAT, NULL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i + _firstIndex, GL_TEXTURE_2D, _textures[i], 0);
// depth
glBindTexture(GL_TEXTURE_2D, _depthTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, WindowWidth, WindowHeight, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, _depthTexture, 0);
GLenum buffers[NUM];
for(uint32_t i = 0; i < NUM; ++i)
buffers[i] = GLenum(GL_COLOR_ATTACHMENT0 + i + _firstIndex);
glDrawBuffers(NUM, buffers);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
printf("FB error, status: 0x%x\n", status);
return _valid = false;
// unbind textures
glBindTexture(GL_TEXTURE_2D, 0);
// restore default FBO
glBindFramebuffer(GL_FRAMEBUFFER, 0);
在这个阶段如何进一步调试? 我知道纹理数据是有效的,但我似乎无法将其正确绑定到着色器(但我有其他着色器使用从文件加载的纹理并且工作正常)。
--- 编辑 1 ---
如被问及,DeferredPassBegin/Debug 的代码(主要来自 this tutorial)
void GBuffer::DeferredPassBegin()
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindFramebuffer(GL_READ_FRAMEBUFFER, _fbo);
void GBuffer::DeferredPassDebug()
GLsizei HalfWidth = GLsizei(_texWidth / 2.0f);
GLsizei HalfHeight = GLsizei(_texHeight / 2.0f);
SetReadBuffer(TEXTURE_TYPE_POSITION);
glBlitFramebuffer(0, 0, _texWidth, _texHeight,
0, 0, HalfWidth, HalfHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
SetReadBuffer(TEXTURE_TYPE_DIFFUSE);
glBlitFramebuffer(0, 0, _texWidth, _texHeight,
0, HalfHeight, HalfWidth, _texHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
SetReadBuffer(TEXTURE_TYPE_NORMAL);
glBlitFramebuffer(0, 0, _texWidth, _texHeight,
HalfWidth, HalfHeight, _texWidth, _texHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
【问题讨论】:
您知道,glBindFramebuffer(GL_FRAMEBUFFER, 0);
等同于 GBuffer:Unbind()
中的两个调用。无论何时使用GL_FRAMEBUFFER
,它都适用于读取和绘制缓冲区目标。此外,您不要在可编程管道中启用或禁用GL_TEXTURE_2D
,这仅适用于固定功能着色。在核心配置文件中,启用它实际上会创建一个无效的枚举错误。
好吧,我预计 GL_FRAMEBUFFER 包括 READ 和 DRAW,但我不确定(特别是考虑到常量的值似乎不相关,GL_FRAMEBUFFER != GL_READ_FRAMEBUFFER | GL_WRITE_FRAMEBUFFER)。
是的,它不是位域,所以它不起作用。如果是,这些名称将以_BIT
结尾。对于 GL,这实际上有点不寻常,但这种行为记录在 glBindFramebuffer (...)
的手册页中 - 类似于 GL_FRONT_AND_BACK
。你能显示DeferredPassBegin
和DeferredPassDebug
的代码吗?我很清楚那些应该做什么,但它们不在你的问题中。
用代码编辑,虽然这部分不应该与那个不起作用的部分交互,因为在此之前着色器被禁用。这里主要是展示FBO贴图内容(我希望颜色成分中的白兔出现在背景上,但现在我只得到贴图坐标渐变,所以片段中color=0
,但是贴图数据显示color!=0
当我将它转储到文件时)。
【参考方案1】:
啊!!!
所以我希望纹理参数不是强制性的,但是当我查看一些代码时,我只是尝试指定我的纹理参数。在生成 FBO 纹理时,我现在使用
for (unsigned int i = 0 ; i < NUM; i++)
glBindTexture(GL_TEXTURE_2D, _textures[i]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, WindowWidth, WindowHeight, 0, GL_RGBA, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_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);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i + _firstIndex, GL_TEXTURE_2D, _textures[i], 0);
通过此更改,我得到了预期的结果(片段着色器中只有 c
,如果我切换到可视化法线 / 位置,则会得到类似的正确结果)。
结论:必须指定纹理参数才能使延迟着色起作用(至少对于我的应用程序/机器的图形设置而言)。
【讨论】:
啊,我相信我可以对此有所了解。默认情况下,缩小过滤器是GL_NEAREST_MIPMAP_LINEAR
,这意味着当您尝试对其进行纹理映射时,因为它没有 mipmap,您有一个 incomplete 纹理。您在发布的代码中所做的是更改缩小过滤器以删除 mipmap 过滤。您也可以调用 glGenerateMipmap (GL_TEXTURE_2D)
,但完全避免 mipmap 过滤更为明智。
为什么默认设置会导致状态不完整?这不是一个糟糕的默认选择吗?背后的原因是什么?如果希望默认使用 mimap,那么它们不应该默认启用吗?
老实说,我不知道为什么这是默认设置。它会导致很多问题。同样奇怪的是,OpenGL 中的纹理默认具有 1000 个 LOD,直到您进入并更改它(对于非 mipmapped 纹理,它应该设置为恰好 1)。但是你要学会忍受 OpenGL 的怪癖。以上是关于FBO 的延迟着色器纹理显示为黑色的主要内容,如果未能解决你的问题,请参考以下文章