延迟着色器纹理从FBO显示为黑色。

3
我将尝试使用延迟渲染实现SSAO,但是在延迟片段着色器中访问纹理时遇到了问题。代码使用C++/Qt5编写,并利用Coin3D生成其他UI(但这在这里并不重要)。
延迟渲染的片段着色器如下:
#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();
}

我知道纹理在几何缓冲区创建时已经正确处理,因为我可以将它们转储到文件中并获得预期结果。
延迟渲染通道无法工作。着色器编译正确,但我在屏幕上得到以下结果:

Bad result

我的代码的最后一部分(DeferredPassBegin/Debug)是将FBO绘制到屏幕上(如截图所示),以证明GBuffer是正确的。

当前的结果似乎意味着纹理没有正确地绑定到它们各自的uniform上,但我知道内容是有效的,因为我将纹理转储到文件中并得到了与上面显示的相同的结果。

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,这仅适用于固定功能着色。在核心配置文件中,启用它实际上会创建无效的枚举错误。 - Andon M. Coleman
好的,我预期GL_FRAMEBUFFER包括READ和DRAW,但我不确定(特别是考虑到常量在值上似乎没有关联,GL_FRAMEBUFFER!= GL_READ_FRAMEBUFFER | GL_WRITE_FRAMEBUFFER)。 - Alexandre Kaspar
是的,它不是位域,所以那样做行不通。如果是的话,这些名称将以“_BIT”结尾。这在GL中实际上有点不寻常,但这种行为在“glBindFramebuffer(...)”的手册页面中有记录-类似于“GL_FRONT_AND_BACK”。你能展示一下“DeferredPassBegin”和“DeferredPassDebug”的代码吗?我对它们应该做什么有一个很好的想法,但它们不在你的问题中。 - Andon M. Coleman
虽然这部分着色器被禁用,但是我已经编辑了代码,因此它不应该与不起作用的部分交互。主要是为了展示FBO纹理内容(我期望从颜色组件中出现白色兔子在背景上,但现在我只得到了纹理坐标梯度,所以片段中的color=0,但当我将其转储到文件时,纹理数据显示color!=0)。 - Alexandre Kaspar
1个回答

0

啊咳!!!

所以我原本以为纹理参数不是必须的,但当我查看一些代码后,我尝试指定我的纹理参数。现在在生成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,如果我切换到可视化法线/位置,同样也会得到正确的结果)。
结论:必须指定纹理参数才能使延迟着色工作(至少与我的应用程序/设备的图形设置相对应)。

Correct bunny


1
啊,我相信我可以为此提供一些帮助。默认情况下,缩小过滤器是GL_NEAREST_MIPMAP_LINEAR,这意味着当您尝试纹理映射它时,由于它没有mipmaps,您会得到一个不完整的纹理。您在发布的代码中所做的是更改缩小过滤器以删除mipmap过滤。您也可以调用glGenerateMipmap(GL_TEXTURE_2D),但完全避免mipmap过滤更加明智。 - Andon M. Coleman
为什么默认设置会导致不完整状态?这不是一个糟糕的默认选择吗?背后的原因是什么?如果预计默认使用mimaps,那么它们不应该默认打开吗? - Alexandre Kaspar
我可以诚实地说我不知道为什么这是默认设置。它会导致很多问题。同样奇怪的是,OpenGL中的纹理默认带有1000个LOD,直到您进去更改(非mipmapped纹理应该设置为1)。但你学会了适应OpenGL的怪癖。 - Andon M. Coleman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接