# GLSL Fragment shader lighting lambert term

Just a simple question: Should I or should i not normalize the SurfaceToLight vector to calculate the lambert term on a GLSL lighting shader?

I mean, here:

vec3 CalculateLights ( void )
{
vec3 OverallResult = (AmbientLight.Color * AmbientLight.Intensity * Material.Ambient.xyz );
vec3 SurfaceToCamera = WorldFragPosition - CameraPositionWorld;

// Apply point lights
for ( int LightIndex = 0; LightIndex < PointLightCount; ++LightIndex )
{
vec3 LightResult = vec3 ( 0, 0, 0 );
vec3 SurfaceToLight = PointLights[LightIndex].Position - WorldFragPosition;

float Distance = length ( SurfaceToLight );
if ( Distance > PointLights[LightIndex].Cutoff )
continue;

// Calculate normalized vectors and Lambert term
vec3 NormalizedFragVertexNormalWorld = normalize( fragVertexNormalWorld );
vec3 NormalizedSurfaceToLight = normalize( SurfaceToLight );
//  float LambertTerm = max( dot( NormalizedSurfaceToLight, NormalizedFragVertexNormalWorld ), 0 ); // Should I use normalized here?
float LambertTerm = max( dot( SurfaceToLight, fragVertexNormalWorld ), 0 ); // Should I use normalized here?

// Compute the diffuse term.
vec3 DiffuseResult = LambertTerm * PointLights[LightIndex].Intensity * PointLights[LightIndex].Color * Material.Diffuse.xyz;
LightResult += DiffuseResult;

// Compute specular
//  float SpecularCoefficient = 0.0;
//  if ( LambertTerm > 0.0 )
//      SpecularCoefficient = pow ( max ( 0.0, dot ( SurfaceToCamera, reflect(-SurfaceToLight, NormalizedFragVertexNormalWorld))), Material.Shininess);
//  vec3 SpecularResult = SpecularCoefficient * Material.Specular.xyz * PointLights[LightIndex].Color;
//  LightResult += SpecularResult;

// Compute attenuation
float Attenuation = PointLights[LightIndex].ConstantAttenuation + PointLights[LightIndex].LinearAttenuation * Distance + PointLights[LightIndex].ExponentialAttenuation * pow ( Distance, 2 );

//  float Attenuation = 1.0 / (1.0 + PointLights[LightIndex].ConstantAttenuation * pow(Distance, 2));
LightResult /= Attenuation;
OverallResult += LightResult;
}

return OverallResult;
}


As you can see, i've been trying out some different formulas on this shader. Specular still does not work. Here is the normalized and non-normalized versions of the image

Of course you should normalize it - it isn't the Lambert term if it isn't normalized.

Lambert term = max(cos(angle between direction to light and surface normal),0)

And (in HLSL-ish pseudocode): dot(A,B) = cos(angle(A,B)) * length(A) * length(B)

So to get dot(A,B) = cos(angle(A,B)), length of both vectors must be equal to 1 (which is what normalization does - divides a vector by its length).

https://en.wikipedia.org/wiki/Lambert's_cosine_law

• ok, just checking, because the image looks a whole lot better without normalization... thanks. – Joao Pincho Jul 2 '15 at 12:51
• @RhiakathFlanders, you can always increase light intensity and experiment with attenuation equations, it could produce similar results while still making more sense mathematically. – snake5 Jul 2 '15 at 12:57
• @RhiakathFlanders Also, that light difference might be due to gamma vs linear space. http.developer.nvidia.com/GPUGems3/gpugems3_ch24.html – Felipe Lira Jul 2 '15 at 13:11
• @PhilLira, it has nothing to do with color spaces. Please read the code given by OP. – snake5 Jul 2 '15 at 13:13
• @snake5 I worded my comment wrong. The light difference I mean would be to achieve a brighter effect with normalized vectors. He didn't posted the fragment output section and as a side note I'm stating that if he doesn't use either sRGB frambuffer extension or convert final color to gamma space in shader his light computation will be wrong. – Felipe Lira Jul 2 '15 at 13:21

Yes, you should always normalize light computation vectors.

dot(N, L) = cos(angle(N, L)) * length(N) * length(L), so, if vectors are normalized max(0.0, dot(N,L)) will get you values in the range of [0,1] which is exacly what you want.

0 being light perpendicular to surface normal, 1 being light pointing exaclyt at the same direction of normal (thus giving most contribution). Negative values means light is hit the back of surface, thus the need o max(0.0, dot()).

A few things worth noting:

1. Linear and Gamma Space

Monitors use a non-linear color space (gamma), so you should be aware of this or your light will appear darker. Textures that come from programs are already written in gamma space and you should convert them to linear space to compute lighting and after lighting done you should convert final result to gamma again.

Now you either use an sRGB texture and framebuffer extension to do this automatically for you or you need to do this in your shader.

So, for instance, you'd have to do:

vec3 finalCol = do_all_lighting_and_shading();
return vec4(pow(finalCol, 1.0 / 2.2), pixelAlpha);


For more information on linear and gamma space check this link: The Importance of Being Linear, and this is how to add sRGB extension to do gamma/linear conversion automatically for you: Using sRGB color space with OpenGL.

2. A word on Specular

I see you're using Reflect vector to compute specular. A better approach is to use Half vector. Check this: https://en.wikipedia.org/wiki/Blinn%E2%80%93Phong_shading_model

• why is half vector better? on that like you've sent, it mentions that calculating the half vector is slower. – Joao Pincho Jul 2 '15 at 20:56
• I think you misread it. Read the Efficiency section. – Felipe Lira Jul 2 '15 at 21:19
• ok, i'll try that implementation. just to be sure: I have two matrices being passed unto the shader. the ModelViewProjection ( CameraPerspective * CameraViewMatrix * ModelMatrix ) and the ModelMatrix by itself. Is my ModelMatrix what they mean by modelview? Or should I multiply it by the camera view matrix? – Joao Pincho Jul 3 '15 at 14:09