Deferred Shading - Multiple Lights (OpenGL/GLSL)

1.2k Views Asked by At

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...

2

There are 2 best solutions below

0
On BEST ANSWER

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.

0
On

I don't see any distance based calculations but this is required otherwise everything in the scene is influenced by each of the lights in the same way.

There are different ways to calculate distance based lights the most common is inverse square root which is basically:

 intensity = 1 / sqrt(distance * distance)

You can also take a look at this article on GameDev. Of course you can get are much better look if you go with PBS ( Physical based shading; For example: Unreal Engine 4 shading ).