简体   繁体   中英

Weird behaviour of Z-Buffer

I'm using Deferred Shading method to render the scene, but I have a problem with a Skybox technique due to weird behaviour of Z-Buffer. I've created additional Framebuffer and attached 5 textures, one of which is being used as a depth buffer. First pass is a geometry pass, which fills the textures with needed data, second pass renders the final objects with textures and lightning applied to the main framebuffer, the last pass renders Skybox. The problem is Skybox being ignored (Z-Buffer check fails, so it is invisible).

When rendering a Skybox, I map the secondary framebuffer as a GL_READ_FRAMEBUFFER and main framebuffer (screen) as a GL_DRAW_FRAMEBUFFER, so the depth buffer of the secondary framebuffer can be used, then I set the depth function to GL_LEQUAL. And only if I change depth function to GL_GREATER (or GL_ALWAYS) the Skybox can be seen, however it is being rendered on top of the objects.

The Vertex Shader:

#version 330

layout (location = 0) in vec3 Position;

uniform mat4 M;
uniform mat4 V;
uniform mat4 P;

out vec3 TexCoord0;

void main()
{
    vec4 MVP_Pos = P * V * M * vec4(Position, 1.0);
    gl_Position = MVP_Pos.xyww;
    TexCoord0 = Position;
}

Model matrix is a product of translation of the Skybox to the camera coordinates.

glDepthFunction set to GL_LEQUAL: GL_LEQUAL

glDepthFunction set to GL_GREATER: GL_GREATER

Contents of the Z-Buffer: Z缓冲

UPDATE 1: I've tried to use glBlitFramebuffer like that:

glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo); //Additional framebuffer
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glDepthMask(GL_TRUE);
glBlitFramebuffer(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, GL_DEPTH_BUFFER_BIT, GL_NEAREST);

But it doesn't work. Main depth buffer contains array of black pixels.

UPDATE 2:

//FBO initialization
glGenFramebuffers(1, &m_fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo);

// Create gbuffer textures
glGenTextures(ARRAY_SIZE_IN_ELEMENTS(m_textures), m_textures);
glGenTextures(1, &m_depthTexture);

for (unsigned int i = 0; i < ARRAY_SIZE_IN_ELEMENTS(m_textures); i++) {
    glBindTexture(GL_TEXTURE_2D, m_textures[i]);

    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, WindowWidth, WindowHeight, 0, GL_RGB, GL_FLOAT, NULL);

    glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, m_textures[i], 0);
    }

// depth buffer
glBindTexture(GL_TEXTURE_2D, m_depthTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, WindowWidth, WindowHeight, 0, GL_DEPTH_COMPONENT, GL_FLOAT,
        NULL);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_depthTexture, 0);

GLenum DrawBuffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3 };
glDrawBuffers(ARRAY_SIZE_IN_ELEMENTS(DrawBuffers), DrawBuffers);

GLenum Status = glCheckFramebufferStatus(GL_FRAMEBUFFER);

if (Status != GL_FRAMEBUFFER_COMPLETE) {
    fprintf(stderr, "FB error, status: 0x%x\n", Status);
    return false;
}

Skybox buffers:

GLfloat cube_vertices[] = {
        // front
        -1.0, -1.0, 1.0,
        1.0, -1.0, 1.0,
        1.0, 1.0, 1.0,
        -1.0, 1.0, 1.0,
        // back
        -1.0, -1.0, -1.0,
        1.0, -1.0, -1.0,
        1.0, 1.0, -1.0,
        -1.0, 1.0, -1.0,
    };

    GLushort cube_elements[] = {
        // front
        0, 1, 2,
        2, 3, 0,
        // top
        3, 2, 6,
        6, 7, 3,
        // back
        7, 6, 5,
        5, 4, 7,
        // bottom
        4, 5, 1,
        1, 0, 4,
        // left
        4, 0, 3,
        3, 7, 4,
        // right
        1, 5, 6,
        6, 2, 1,
    };

When rendering a Skybox, I map the secondary framebuffer as a GL_READ_FRAMEBUFFER and main framebuffer (screen) as a GL_DRAW_FRAMEBUFFER , so the depth buffer of the secondary framebuffer can be used,

That is not how things work. The GL_READ_FRAMEBUFFER is not relevant for the depth test. It is used as the source for opeations like glReadPixels or glBlitFramebuffer . But for any kind of rendering, only the GL_DRAW_FRAMEBUFFER is relevant. This includes the depth test.

Unfortuanly, you can't mix user-defined render targets like render buffers or textures with window-system provided buffers, so you just can't render to the final screen while using the specific depth test. So you either have to do an additional render pass to an FBO using the custom depth buffer, and blit the result to the screen, or you can blit the contents of the custom depth buffer to the window-system provided one and render with that.

I found a solution. Additional framebuffer had 32 bits/pixel, but main framebuffer had only 24, that prevented glBlitFramebuffer from working. So the proper Framebuffer initialization would look like that:

glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, WindowWidth, WindowHeight, 0, GL_DEPTH_COMPONENT, GL_FLOAT,
        NULL);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM