2

I've just implemented deferred rendering and am having trouble getting my skybox working. I try rendering my skybox at the very end of my rendering loop and all I get is a black screen. Here's the rendering loop:

    //binds the fbo
    gBuffer.Bind();

    //the shader that writes info to gbuffer
    geometryPass.Bind();

    glDepthMask(GL_TRUE);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_DEPTH_TEST);
    glDisable(GL_BLEND);

    //draw geometry
    geometryPass.SetUniform("model", transform.GetModel());
    geometryPass.SetUniform("mvp", camera.GetViewProjection() * transform.GetModel());

    mesh3.Draw();

    geometryPass.SetUniform("model", transform2.GetModel());
    geometryPass.SetUniform("mvp", camera.GetViewProjection() * transform2.GetModel());
    sphere.Draw();

    glDepthMask(GL_FALSE);
    glDisable(GL_DEPTH_TEST);

    glEnable(GL_BLEND);
    glBlendEquation(GL_FUNC_ADD);
    glBlendFunc(GL_ONE, GL_ONE);

    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    glClear(GL_COLOR_BUFFER_BIT);

    //shader that calculates lighting
    pointLightPass.Bind();
    pointLightPass.SetUniform("cameraPos", camera.GetTransform().GetPosition());

    for (int i = 0; i < 2; i++)
    {
        pointLightPass.SetUniformPointLight("light", pointLights[i]);
        pointLightPass.SetUniform("mvp", glm::mat4(1.0f));
        //skybox.GetCubeMap()->Bind(9);
        quad.Draw();
    }

    //draw skybox
    glEnable(GL_DEPTH_TEST);
    skybox.Render(camera);

    window.Update();
    window.SwapBuffers();

The following is the skybox's render function

glCullFace(GL_FRONT);
glDepthFunc(GL_LEQUAL);

m_transform.SetPosition(camera.GetTransform().GetPosition());
m_shader->Bind();

m_shader->SetUniform("mvp", camera.GetViewProjection() * m_transform.GetModel());
m_shader->SetUniform("cubeMap", 0);

m_cubeMap->Bind(0);
m_cubeMesh->Draw();

glDepthFunc(GL_LESS);
glCullFace(GL_BACK);

And here is the skybox's vertex shader:

layout (location = 0) in vec3 position;

out vec3 TexCoord;

uniform mat4 mvp;

void main()
{
    vec4 pos = mvp * vec4(position, 1.0);
    gl_Position = pos.xyww;
    TexCoord = position;
}

The skybox's fragment shader just sets the output color to texture(cubeMap, TexCoord). As you can see from the vertex shader, I'm setting the position's z component to be w so that it will always have a depth of 1. I am also setting the depth function to be GL_LEQUAL so that it will fail the depth test. Should this not only draw the skybox in places where other objects weren't already drawn? Why does it result in a black screen?

I know I have set up the skybox correctly because if I just draw the skybox by itself it shows up just fine.

theonewhoknocks
  • 203
  • 2
  • 8
  • I guess your last sentence says it all. It works without drawing stuff previously. Either the previous draw calls are changing the GL state, such as enabling `GL_DEPTH_TEST` for the lighting pass, or drawing your geometry is changing the depth values everywhere. I haven't spotted anything obvious. I'd set `gl_FragDepth` rather than `gl_Position.z` because I would have assumed it'd mess with clipping, but if it works it works. – jozxyqk Mar 29 '15 at 13:16
  • I don't know about the geometry changing the depth values everywhere but the lighting pass or any pass after the geometry pass does not change `GL_DEPTH_TEST`. I also tried setting `gl_FragDepth` but that still results in a black screen. I don't really know why it's not working. If I don't enable `GL_DEPTH_TEST` before rendering the skybox, it shows up but none of the other geometry shows because the skybox is on top of everything. – theonewhoknocks Mar 30 '15 at 01:17
  • Oh, right. The thing about the black screen is pretty odd (I assumed you meant the background was black). If you draw something that overwrites previous values with black then something is indeed drawing. If you see sky without depth testing, I can only assume the sky overwrites the black geometry. Though `TexCoord` should ensure you don't get black assuming your texture isn't and is bound, unless the coord is zero (which would put the geometry behind the camera anyway). I'd try to narrow down what's causing the black. Maybe start by setting a constant colour in the fragment shader. – jozxyqk Mar 30 '15 at 02:53
  • Even if I set the color to a constant color in the fragment shader it still results in a black screen. None of my geometry is black and the skybox is a bluish texture. – theonewhoknocks Mar 30 '15 at 23:37
  • This is quite strange. I'm still not sure about `pos.xyww`, but unless there's some weird blending operations happening, a fragment should either set or not set the pixel. No idea how it turns black. Could you add the full veiw matrix to the skybox to place it as an object in the world and see how it renders then? – jozxyqk Mar 31 '15 at 14:01
  • If I don't set the camera to be in the center of the skybox and use the model view projection matrix then it is still a black screen. If I don't enable `GL_DEPTH_TEST` before drawing the skybox then I see only the skybox and none of my other geometry. But I would think `GL_DEPTH_TEST` should be enabled before drawing the skybox. I should also note that if I don't enable the depth test when drawing the skybox, I can briefly see for a split second the geometry that should be drawn before the skybox is drawn on top of everything. – theonewhoknocks Mar 31 '15 at 22:01
  • 1
    Well, I'm afraid I'm out of ideas. See if my answer helps, I've explained a little. If you do find the issue, I'd be interested to know what could have caused your problems! – jozxyqk Apr 01 '15 at 05:49

1 Answers1

3

I can briefly see for a split second the geometry that should be drawn before the skybox is drawn on top of everything.

Since you're using double buffering, seeing different things must be due to a different frame being drawn. The depth buffer in the default framebuffer isn't being cleared, which I believe is the cause of the temporal instability at least.

In your case, you want the default depth buffer to be the same as the GBuffer when you draw the skybox. A quick way to achieve this is with glBlitFramebuffer, also avoiding the need to clear it:

glBindFramebuffer(GL_READ_FRAMEBUFFER, gbuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBlitFramebuffer(..., GL_DEPTH_BUFFER_BIT, ...);

Now to explain the black screen when the skybox fills the screen. Without the depth test, of course the skybox just draws. With the depth test, the skybox still draws on the first frame, but shortly after the second frame clears only the colour buffer. The depth buffer still contains stale skybox values so it does not get re-draw for this frame and you're left with black...

However your geometry pass draws without depth testing enabled, so this should still be visible even if the skybox isn't. Also this would only happen with GL_LESS and you have GL_LEQUAL. And you have glDepthMask false, which means nothing should write to the default depth buffer in your code. This points to the depth buffer containing other values, perhaps uninitialized, but in my experience it's initially zero. Also this still happens when the skybox doesn't fill the screen, drawn as a cube away from the camera, which blows away that argument. Now, perhaps if the geometry failed to draw in the second frame that would explain it. For that matter blatant driver bugs would too, but I'm not seeing any problems in the given code.

TLDR: Many unexplained things, so **I tried it myself and can't reproduce your problem...

Here's a quick example based on your code and it works fine for me...

(green sphere is the geometry, red cube is the skybox)

gl_Position = pos:
enter image description here

Note the yellow from additive blending even if the skybox is drawn over the top. I would have thought you'd be seeing this too.

gl_Position = pos.xyww:
enter image description here

Now for the code...

//I haven't enabled back face culling, but that shouldn't affect anything

//binds the fbo
fbo.bind();

//the shader that writes info to gbuffer
//geometryPass.Bind(); //fixed pipeline for now

glDepthMask(GL_TRUE);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glDisable(GL_BLEND);

glColor3f(0,1,0);
fly.uploadCamera(); //glLoadMatrixf
sphere.draw();

glDepthMask(GL_FALSE);
glDisable(GL_DEPTH_TEST);

glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_ONE, GL_ONE);

fbo.unbind(); //glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClear(GL_COLOR_BUFFER_BIT);

//shader that calculates lighting
drawtex.use();
//pointLightPass.SetUniform("cameraPos", camera.GetTransform().GetPosition());
drawtex.set("tex", *(Texture2D*)fbo.colour[0]);

for (int i = 0; i < 2; i++)
{
    //pointLightPass.SetUniformPointLight("light", pointLights[i]);
    //pointLightPass.SetUniform("mvp", glm::mat4(1.0f));
    //skybox.GetCubeMap()->Bind(9);
    drawtex.set("modelviewMat", mat44::identity());
    quad.draw();
}

drawtex.unuse();

//draw skybox
glEnable(GL_DEPTH_TEST);

glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBlitFramebuffer(0, 0, fbo.size.x, fbo.size.y, 0, 0, fbo.size.x, fbo.size.y, GL_DEPTH_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER, 0);

//glCullFace(GL_FRONT);
glDepthFunc(GL_LEQUAL);

//m_transform.SetPosition(camera.GetTransform().GetPosition());
skybox.use();

skybox.set("mvp", fly.camera.getProjection() * fly.camera.getInverse() * mat44::translate(1,0,0));
//m_shader->SetUniform("mvp", camera.GetViewProjection() * m_transform.GetModel());
//m_shader->SetUniform("cubeMap", 0);

//m_cubeMap->Bind(0);
cube.draw();
skybox.unuse();

glDepthFunc(GL_LESS);
//glCullFace(GL_BACK);

//window.Update();
//window.SwapBuffers();
jozxyqk
  • 16,424
  • 12
  • 91
  • 180
  • Thanks a lot for the help! I really appreciate it. Using `glBlitFramebuffer` to copy the depth buffer did indeed solve my problem. I'm wondering if there is a more suitable place to draw the skybox so I don't have to copy over the depth buffer from the gbuffer to the default framebuffer. For now though I will use this fix. Thanks again for the help. – theonewhoknocks Apr 01 '15 at 23:27
  • @theonewhoknocks No worries, glad it worked. I think blitting is fine. To avoid the copy you want to use a texture as the default depth buffer which isn't possible. Trying to render the skybox first or into the GBuffer isn't going be clean. If later down the track you do some post processing and are rendering to another FBO, you could use the GBuffer's depth texture directly there, but a blit is so fast compared to other things you'll be doing I don't think you'd notice any difference. – jozxyqk Apr 02 '15 at 02:03