PDA

View Full Version : Rendering a skybox after drawing post process effects to a screen quad



Niruz91
10-20-2015, 05:28 AM
So I've been implementing a deferred shader and I've run into some troubles trying to get a skybox working. My idea here is that after a HDR/Bloom pass to calculate the final picture I render it using a forward shader using a full screen quad. After this I'm trying to render a skybox but I can't get it to work. Almost all online sources usually blit the depth buffer from the geometry pass into the main FBO, however after failing to do so and getting GL_INVALID_OPERATION errors and after asking on stackoverflow it seems that the default framebuffer is user defined and that's what causing my errors. So Instead I've been using the stored depth texture from the geometry pass which I'm trying to use to determine if a fragment should have the skybox rendered to it. Essentially I draw the final image to a full screen quad like this



ShaderMan.bindShader(FINAL_BLUR_SHADER);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
//Enables the depth texture to be written into
glDepthMask(GL_TRUE);
glEnable(GL_DEPTH_TEST);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glUniform1i(ShaderMan.getActiveShader().getUniform Location("hdrTexture"), 0);
glUniform1i(ShaderMan.getActiveShader().getUniform Location("bloomTexture"), 1);

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, mHDRTexture);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, pingpongTextures[!horizontal]);

glBindVertexArray(mQuadVAO);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindVertexArray(0);

glBindTexture(GL_TEXTURE_2D, 0);

ShaderMan.unbindShader();

Which works fine, and looks like this:

2160

This should mean that the quad's depth value is almost 0, or mapped to the near plane in [0,1] coordinates

Now after this I want to render the skybox and check if the stored depth value in the texture is 0, then I should render the skybox on that fragment. Now if I after the geometry pass just try to render the skybox wherever a depth of 0 is detected using these shaders:

VS

#version 430

layout (location = 0) in vec3 position;

//3D cubemap
out vec3 v2fTexcoords;

uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;

void main()
{
//Trick the depth test since w/w will be 1 so it's always at max depth value
vec4 pos = projectionMatrix * viewMatrix * vec4(position, 1.0);
pos = pos*-1.0f;
gl_Position = pos.xyww;
v2fTexcoords = position;
}
FS

#version 430

in vec3 v2fTexcoords;

out vec4 fragColor;

uniform samplerCube skybox;
uniform sampler2D depthTexture;

uniform vec2 screenSize;

vec2 CalcTexCoord()
{
return gl_FragCoord.xy / screenSize;
}

void main()
{
vec2 TexCoord = CalcTexCoord();
float depthValue = texture2D(depthTexture,TexCoord).r;
if(depthValue == 0)
fragColor = texture(skybox, v2fTexcoords);
}

I get this image, as expected, also the pos*-1.0f was to change the direction of the textures since they were the wrong way at first, since z is set to w it should still map to 1 after w/w

2161


Now, as I can clearly render the final image to a full screen quad and I can render the skybox and check if the depth value is 0 and then render the texture on that fragment everything should be fine right? Well no, I can't get the skybox to render and I'm not sure where I'm doing something wrong, here's the complete code for rendering the quad and then the skybox at the same time, which just results with the quad being drawn



ShaderMan.bindShader(FINAL_BLUR_SHADER);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
//Enables the depth texture to be written into
glDepthMask(GL_TRUE);
glEnable(GL_DEPTH_TEST);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glUniform1i(ShaderMan.getActiveShader().getUniform Location("hdrTexture"), 0);
glUniform1i(ShaderMan.getActiveShader().getUniform Location("bloomTexture"), 1);

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, mHDRTexture);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, pingpongTextures[!horizontal]);

glBindVertexArray(mQuadVAO);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindVertexArray(0);

glBindTexture(GL_TEXTURE_2D, 0);

ShaderMan.unbindShader();

drawSkybox();

And the drawSkyBox() function


glDepthFunc(GL_LEQUAL);
ShaderMan.bindShader(SKY_BOX_SHADER);

glm::mat4 view = glm::mat4(glm::mat3(CameraMan.getActiveCamera()->getViewMatrix())); // Remove any translation component of the view matrix
glm::mat4 projection = CameraMan.getActiveCamera()->getProjectionMatrix();
glUniformMatrix4fv(ShaderMan.getActiveShader().get UniformLocation("viewMatrix"), 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(ShaderMan.getActiveShader().get UniformLocation("projectionMatrix"), 1, GL_FALSE, glm::value_ptr(projection));
// skybox cube
glUniform1i(ShaderMan.getActiveShader().getUniform Location("skybox"), 0);
glUniform1i(ShaderMan.getActiveShader().getUniform Location("depthTexture"), 1);
glUniform2f(ShaderMan.getActiveShader().getUniform Location("screenSize"), mWindowWidth, mWindowHeight);

glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, mStoredDepthTexture);
mSkybox.render();

glDepthFunc(GL_LESS); // Set depth function back to default
ShaderMan.unbindShader();

I've tried setting the depthfunc to always pass in the skybox pass dut I still cant get it right.

nostalgic
10-22-2015, 06:04 AM
Are you aware that rendering a skybox can be implemented as a screen-space effect?

Vertex Shader:


#version 450

out vec3 vsViewDirection;

uniform mat4 invView;
uniform mat4 invProj;

void main()
{
gl_Position = vec4(((gl_VertexID & 1) << 2) - 1, (gl_VertexID & 2) * 2 - 1, 0.0, 1.0);
vsViewDirection = mat3(invView) * (invProj * gl_Position).xyz;
}

Fragment Shader:

#version 450

in vec3 vsViewDirection;

out vec4 fragment_color;

uniform samplerCube cubeMapTexture;

void main()
{
fragment_color = texture(cubeMapTexture, vsViewDirection);
}

Host code:

glDepthMask(GL_FALSE);

mSkyboxProgram->bind();
mSkyboxProgram->setTexture("cubeMapTexture", mCubeMap);

glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

mSkyboxProgram->unbind();

glDepthMask(GL_TRUE);

No need to use actual vertex positions for the box here. But that is just a general remark. To address your problem


This should mean that the quad's depth value is almost 0

First, why should it be 0 and not 1 as usual? Sticking to the term "depth", the range is usually defined as [0, 1] where 0 means near plane and 1 means far plane. So you should clear your depth buffer with glClearDepth(1.0) and set the depth test to glDepthFunc(GL_LEQUAL).
Second, why "almost"? If you did not write anything else to the depth buffer, this is the exact value that you used for clearing. So if 0 is your clear depth, you can just draw the skybox as full-screen quad, enable the depth test, set the depth function to glDepthFunc(GL_EQUAL), set the depth range to glDepthRange(0.0, 0.0) (or add gl_FragDepth = 0.0; to the fragment shader) and draw. The depth test will fail everywhere where the depth is not the clear value, i.e. where geometry has been drawn.

Niruz91
11-13-2015, 10:57 AM
That solution actually worked better than what I was doing. Also I was confusing myself with the depth value, since it was never written to it would be 0 and then you can render the sky box there. Thanks for clearing some things up