1
votes

I'm trying to implement deferred shading using the OpenGL, I render positions and normals to the textures. When I use only 1 light source without blending everything works well, but after I turn on blending, so the other light sources could contribute, I get a transparency. Does anyone had the same problem and a solution to it?

Here is my fragment shader for lighting:

#version 330 

uniform vec2 ScreenSize;
uniform vec3 AmbientColor;
uniform vec3 DiffuseColor;
uniform vec3 SpecColor;
uniform vec3 LightPosition;
uniform sampler2D PositionMap;
uniform sampler2D NormalMap;

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

out vec4 FragColor;

void main()
{
    vec2 PixelPos = CalcTexCoord();
    vec3 WorldPos = texture(PositionMap, PixelPos).xyz;
    vec3 Normal = texture(NormalMap, PixelPos).xyz;

    vec3 LightDir = normalize(LightPosition - WorldPos);

    float Lambertian = max(dot(LightDir, Normal), 0.0);
    float Specular = 0.0;

    if(Lambertian > 0.0)
    {
        vec3 ViewDir = normalize(-WorldPos);

        // this is blinn phong
        vec3 HalfDir = normalize(LightDir + ViewDir);
        float SpecAngle = max(dot(HalfDir, Normal), 0.0);
        Specular = pow(SpecAngle, 16.0);

        vec3 ReflectDir = reflect(-LightDir, Normal);
        SpecAngle = max(dot(ReflectDir, ViewDir), 0.0);
        // note that the exponent is different here
        Specular = pow(SpecAngle, 4.0);
    }

    FragColor = vec4(AmbientColor+Lambertian*DiffuseColor+Specular*SpecColor, 1.0);
}

The geometry pass:

glDisable(GL_BLEND);
glDepthMask(GL_TRUE);
glEnable(GL_DEPTH_TEST);

glBindFramebuffer(GL_FRAMEBUFFER, Fbo);

glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glViewport(0, 0, Camera.Viewport.X, Camera.Viewport.Y);

UpdateInput(Window, &Camera);
UpdateMatrices(&Camera, &RenderState);

Meshes[1].ModelMat = Translate(M4(1.0f), LightPos);

UseShader(&RenderState, &GeometryShader);

for(uint8 i = 0; i < ArrayCount(Meshes); ++i)
{
`RenderMesh(&GeometryShader, &Meshes[i]);
}

glDepthMask(GL_FALSE);
glDisable(GL_DEPTH_TEST);

The light pass:

glBindFramebuffer(GL_READ_FRAMEBUFFER, Fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);

glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0, 0, Camera.Viewport.X, Camera.Viewport.Y);

// Binding Position and Normal maps.
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, Targets[0].Id);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, Targets[2].Id);

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

UseShader(&RenderState, &LightShader);

LoadVec2(&LightShader, "ScreenSize", V2(Camera.Viewport.X, Camera.Viewport.Y));
LoadVec3(&LightShader, "AmbientColor", V3(0.2f, 0.2f, 0.2f));
LoadVec3(&LightShader, "DiffuseColor", V3(0.1f, 0.1f, 0.1f));
LoadVec3(&LightShader, "SpecColor", V3(0.3f, 0.3f, 0.3f));
LoadVec3(&LightShader, "LightPosition", V3(5.0f, 3.0f, 3.0f));
LoadSampler2D(&LightShader, "PositionMap", 0);
LoadSampler2D(&LightShader, "NormalMap", 1);

for(uint8 i = 0; i < ArrayCount(Meshes); ++i)
{
   RenderMesh(&LightShader, &Meshes[i]);
}

LoadVec3(&LightShader, "LightPosition", LightPos);

for(uint8 i = 0; i < ArrayCount(Meshes); ++i)
{
   RenderMesh(&LightShader, &Meshes[i]);
}
glDisable(GL_BLEND);

The blending that I'm applying:

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

Textures format:

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, Width, Height, 0, GL_RGBA, GL_FLOAT, Pixels);

Depth texture format:

glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, Width, Height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);

Position and Normal maps: http://postimg.org/image/qctx0smj7/

Blending is off: http://postimg.org/image/7nqlz3sbr/

Blending is on: http://postimg.org/image/99h7x2p5t/

1
Where do you enable blending, and when do you disable it again? It is totally unclear what you are doing here.derhass
I enable blending after geometry pass is done. And disable it after the light pass is over.user3696459

1 Answers

1
votes

You have to ensure that a fragment that gets blended to the final fbo is a topmost one. Otherwise you add up light in overlapping regions. If you disable blending, the problem is still the same, but you don't see it since fragments behind the topmost one are overwritten. Conclusion: The speed advantage of deferred shading is completely wasted since you are drawing in the second pass the same number of fragments that would have been drawn in normal forward rendering.

Solutions

Most engines render the second pass not to the backbuffer but to another fbo, mainly because of post-processing. But this allows to use the same depthbuffer in both renderpathes. In the first pass depth reads and writes are performed as already done by you. In the second pass, depth write is disabled, but depth testing is still done (with GL_LESS_OR_EQUAL). This discards all fragments that are behind the topmost one. Sometimes it might be necessary to draw the objects in the second path a little bit nearer to the camera to prevent z-fighting issues.

If you cannot use a second fbo, you can do two things: Copy the depth buffer of the first pass to the default depth buffer (in general horrible performance), or you can do the depth testing in your fragment shader.