openglglsllightingdeferred-renderingdeferred-shading

Deferred Shading - Multiple Lights (OpenGL/GLSL)


I'm working on a deferred shading program and now I have to set up 50 different lights in the scene. To do so, I'm randomly generating its attributes (position, diffuse color, specular color) with this piece of code:

void FBORender::BuildLights()
{
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_real_distribution<> dis(0.0, 0.1);

    for (int i = 0; i < NUM_LIGHTS; i++)
    {
        float dc_r = (float) dis(gen);
        float dc_g = (float) dis(gen);
        float dc_b = (float) dis(gen);

        printf("%f\n", dc_r);

        float lp_x = (float)(rand() % 40 - 20);
        float lp_y = (float)(rand() % 100 + 10);
        float lp_z = (float)(rand() % 40 - 20);

        DC[i * NUM_LIGHTS] = dc_r;
        DC[i * NUM_LIGHTS + 1] = dc_g;
        DC[i * NUM_LIGHTS + 2] = dc_b;

        LP[i * NUM_LIGHTS] = lp_x;
        LP[i * NUM_LIGHTS + 1] = lp_y;
        LP[i * NUM_LIGHTS + 2] = lp_z;
    }
}

However, I'm not so sure how to perform the lighting with multiple lights. I was said that the ambient light should be equal the diffuse light and the specular light should be white.

Adapting the shader that I had to perform Phong Ilumination, I have this:

#version 410 core

#define numLights 5

uniform sampler2D tDiffuse; 
uniform sampler2D tPosition;
uniform sampler2D tNormals;

uniform vec4 specularColor;
uniform vec3 diffuseColor[numLights];
uniform vec3 vLightPosition[numLights];

in vec2 texCoord;

out vec4 fragColor;


void main( void )
{
    vec3 tex = texture( tDiffuse, texCoord.st ).xyz;
    vec3 vPosition = texture( tPosition, texCoord.st ).xyz;
    vec3 vNormal = normalize( texture( tNormals, texCoord.st ).xyz * 2 - 1 );

    vec3 vVaryingNormal = vNormal;

    for (int i = 0; i < numLights; i++)
    {
        vec3 vVaryingLightDir = normalize( vLightPosition[i] - vPosition );

        float diff = max( 0.0, dot( normalize( vVaryingNormal ), normalize( vVaryingLightDir ) ) );
        vec4 partialColor = diff * vec4(diffuseColor[i], 1.0);
        partialColor = vec4( mix( partialColor.rgb, tex, 0.5 ), partialColor.a );
        partialColor += vec4( diffuseColor[i], 1.0 );
        vec3 vReflection = normalize( reflect( -normalize( vVaryingLightDir ), normalize( vVaryingNormal )));
        float spec = max( 0.0, dot( normalize( vVaryingNormal ), vReflection ));
        if( diff != 0 )
        {
            float fSpec = pow( spec, 128.0 );
            partialColor.rgb += vec3( fSpec, fSpec, fSpec );
        }

        fragColor += partialColor;
    }
}

However, I'm getting ugly results (if not wrong too), as the following pictures show:

enter image description here enter image description here

And this results are using just 2 lights. As I have to use 50, I think that all that I will see is a white screen...


Solution

  • In deferred rendering you usually render each light separately and let the GPU blend the result together. This reduces shader complexity.

    Let's take a closer look at your lighting calculations.

    partialColor = vec4( mix( partialColor.rgb, tex, 0.5 ), partialColor.a );
    

    Hm... What is this line supposed to do? At that time, partialColor contains the diffusely shaded color (assuming a white material). If you really want to calculate the material color by mixing (which seems a bit strange), you should do the shading afterwards:

    vec3 materialColor = mix( vec3(1.0, 1.0, 1.0), tex, 0.5 );
    vec4 partialColor = vec4(diffuseColor[i] * diff * materialColor, 1.0);
    

    Next line in your code:

    partialColor += vec4( diffuseColor[i], 1.0 );
    

    I have no clue what this is supposed to do. Leave it away.

    Next lines:

    vec3 vReflection = normalize( reflect( -normalize( vVaryingLightDir ), normalize( vVaryingNormal )));
    float spec = max( 0.0, dot( normalize( vVaryingNormal ), vReflection ));
    

    You are mixing Phong and Blinn-Phong. Phong requires the dot product of the reflected light vector and the eye vector. Blinn-Phong requires the dot product of the normal and the half vector between light and eye vector.

    Next line:

    partialColor.rgb += vec3( fSpec, fSpec, fSpec );
    

    This should be multiplied by the light's specular color. Also, instead of checking diff != 0.0, you should check for spec > 0.

    If you still experience problems, turn off specular lighting and see if diffuse lighting is correct.

    Additionally, your lights seem a bit bright. The expected value of the light color is (0.05, 0.05, 0.05). 50 of those make up for a total of (2.5, 2.5, 2.5), which is far brighter than just a white light.