Over-saturation when using multiple lights

Hey All,

So I want to write a simple diffuse shader for multiple lights sources (64). I obtain the light source directions and colours by doing a Median Cut on an HDR evironment map. (That part works ok and the light intensities ranges from ca. 0.008 to 12.). The directions and colours are stored as two arrays, which are sent to the vertex shader.
In the vertex shader I do a simple N.L and sum the values, like this:


#define NUM_LIGHTS	64			

uniform vec3 lightPos[ NUM_LIGHTS ];	// Stored in World Space
uniform vec3 lightCol[ NUM_LIGHTS ];
uniform float multiplier;

varying vec3 diffuse;

void main()
{
    vec3 normal = normalize( gl_NormalMatrix * gl_Normal );

    diffuse = vec3(0.);

    for( int i = 0; i < NUM_LIGHTS; i++ )
    {
        vec3 ecLightDir = vec3( gl_ModelViewMatrix * vec4(lightPos[i], 1.) );

	float intensity = max( 0., dot(normal, normalize(ecLightDir))) * multiplier;
		
	diffuse += lightCol[i] * intensity;
    }

    gl_Position = ftransform();
}

In the fragment shader I just multiply material colour with this diffuse term. This should be correct?

However, the result does not resemble a reference shader that uses a simple cube mapped irradience map. The bright parts are too bright and the more shadowy parts are too dark, and thus fail to achieve that smooth diffuse look from image-based lighting.

I can post some screen shots if you like…

Is there something I’m missing? Any ideas?

What value do you use for the ‘multiplier’ uniform ?

“light intensities ranges from ca. 0.008 to 12.)”
12 is very strong, will completely saturate nearly half of your cube with white.

I am not sure if you median cut accurately discretize the HDR env map. Each texel on the original env map encodes lighting as “light power for 1 pixel worth of solid angle”. After your median cut, I guess the unit of surface for each of the 64 lights is no more constant. You should multiply each light power by its own solid angle.

Wow, that was a quick response! Thanks :slight_smile:

Each texel on the original env map encodes lighting as “light power for 1 pixel worth of solid angle”. After your median cut, I guess the unit of surface for each of the 64 lights is no more constant. You should multiply each light power by its own solid angle.

Ah, yes, I think you are spot on! I have forgotten to do this rescaling.

What value do you use for the ‘multiplier’ uniform ?

The multiplier was set around 0.001 and was an attempt (hack) to tweak the values prior to actual tonemapping. (I’m using the ‘standard’ Uffizi map from Debevec, which has such a bright sky.)
But of course the rescaling of the light intensities wrt. solid angle is the correct way to handle this.

Edit:

Yes indeed, after rescaling everything looks correct. Thanks for your suggestion

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.