4

I have a deferred renderer that only calculates lighting equations when the current fragment is in range of a light source. I do this by calculating the size of a light volume in my application and send this with other light information to the shaders. I then check the distance between the fragment and lightPos (per light) and use the light's volume as a treshold.

For simplicity's sake I use a linear equation (quadratic equations generate way too large light volumes) for light attenuation. All the lighting equations work fine, but when I use multiple lights I sometimes see strange circle borders as if the distance check causes the light calculations to prematurely stop causing a sudden change in lighting. You can see this effect in the image below:

Deferred renderer banding

The fragment shader code is as follows:

vec3 position = texture(worldPos, fs_in.TexCoords).rgb;        
vec3 normal = texture(normals, fs_in.TexCoords).rgb;
normal = normalize(normal * 2.0 - 1.0);
vec3 color = texture(albedo, fs_in.TexCoords).rgb;
float depth = texture(worldPos, fs_in.TexCoords).a;
// Add global ambient value
fragColor = vec4(vec3(0.1) * color, 0.0);
for(int i = 0; i < NR_LIGHTS; ++i)
{
    float distance = abs(length(position - lights[i].Position.xyz));
    if(distance <= lights[i].Size)
    {
        vec3 lighting;                
        // Ambient            
        lighting += lights[i].Ambient * color;            
        // Diffuse
        vec3 lightDir = normalize(lights[i].Position.xyz - position);
        float diffuse = max(dot(normal, lightDir), 0.0);
        lighting += diffuse * lights[i].Diffuse * color;
        // Specular
        vec3 viewDir = normalize(viewPos - position);
        vec3 reflectDir = reflect(-lightDir, normal);
        float spec = pow(max(dot(viewDir, reflectDir), 0.0), 8);
        lighting += spec * lights[i].Specular;

        // Calculate attenuation
        float attenuation = max(1.0f - lights[i].Linear * distance, 0.0);
        lighting *= attenuation;
        fragColor += vec4(lighting, 0.0);
    }
}
fragColor.a = 1.0;    

The attenuation function is a linear function of the distance between the fragment position and each light source.

In this particular scene I use a linear attenuation value of 0.075 of which I generate the light's size/radius as:

Size = 1.0 / Linear;

some observations

When I remove the distance check if(distance <= lights[i].Size) I don't get the weird border issue.

If I visualize the lighting value of a single light source and visualize the distance as distance/lights.Size I get the following 2 images:

Deferred renderer banding

which looks as if the light radius/distance-calculations and light attenuation are similar in radius.

When I change the distance check equation to if(distance <= lights[i].Size * 2.0f) (as to drastically increase the light's radius) I get significantly less border banding, but if I look close enough I do see them from time to time so even that doesn't completely remove the issue.

I have no idea what is causing this and I am out of options at the moment.

ABHAY
  • 221
  • 2
  • 12
  • Are you sure your `lights[i].Size` and/or `.Linear` parameter is actually correct? Can you, as a quick test, just try `if(distance <= 1.0/lights[i].Linear)`, just to make sure you test for the same border that is actually in use for the attenuation term? – derhass Apr 04 '15 at 17:44
  • I tried it with `if(distance <= 1.0/lights[i].Linear)` and it gives the same result. Which is weird since you'd expect the border to be exactly right (since this and the attenuation function are the inverse of each other) – ABHAY Apr 04 '15 at 17:48

1 Answers1

3

This section:

    vec3 lighting;                
    // Ambient            
    lighting += lights[i].Ambient * color;   

You are never initializing lighting before you add to it. I think this can cause undefined behaviour. Try to change it to:

    // Ambient            
    vec3 lighting = lights[i].Ambient * color;   
bofjas
  • 1,186
  • 7
  • 19
  • You're right; always thought GLSL would properly default initialize variables, but I guess I was wrong. This fixed the banding issue. Do you (or others) have any idea how this could possibly cause the banding? Perhaps it initializes it to values below `0.0`? – ABHAY Apr 09 '15 at 07:37
  • I probably starts as 0.0 but it will never be cleared so for every iteration lighting increases. so if three lights overlap the last light would include the lighting from the first two. – bofjas Apr 09 '15 at 12:10