9.7 - Light Attenuation

A basic property of light is that it loses its intensity the further it travels from its source. That is why Venus and Mercury are much hotter than the Earth and Mars is much colder. The intensity of light from the sun changes in proportion to the distance from the sun. The technical name for this is light attenuation.

This lesson explains how to include light attenuation in a light model.

Light Attenuation

Light becomes weaker the further is travels from its source. In the physical world the attenuation is proportional to 1/d2, where d is the distance between the light source and an object. Using the function 1/d2 causes light to decrease very rapidly and so it is common to make attenuation be proportional to 1/d. Notice that if d is greater than 1, both equations calculate a fraction between 0.0 and 1.0. Attenuation simply calculates a percentage of the original light that is used to color a pixel.

The example fragment shader code below performs the following tasks:

  • Calculate a vector from the fragment’s location to the light source.
  • Calculate the length of the vector, which is the distance to the light source.
  • Calculate the amount of attenuation. (For this example, the function 10.0 / d is used.)
  • Clamps the attenuation to be a percentage between 0% and 100%. (For the equation 10.0 / d, any object closer than 10 units will receive full light, while objects more than 10 units away will receive less than full light. Obviously the value of 10 is arbitrary and would change based on a specific application’s needs.)
  • The final color of the fragment is a percentage of its calculated color.
to_light = u_Light_position - v_Vertex;
d = length( to_light );
attenuation = clamp( 10.0 / d, 0.0, 1.0);
// ...
color = attenuation * (ambient_color + diffuse_color + specular_color);

If you wanted an attenuation factor that could be changed during run-time, you could make the constant in your attenuation equation be a uniform variable that is assigned a value at run-time. However, using different attenuation values for different models in a scene would be uncommon.

In the original OpenGL lighting model, the equation 1.0/(c1 + c2*d + c3*d^2) was used to give programmers control over attenuation. You could set the values for c1, c2, and c3 to create a large number of possible attenuation functions. Since programmable shaders were introduced, you can implement the exact attenuation function that meets your application’s needs.

In the literature you typically see an attenuation equation like 10.0 / d written with the proportional constant in the denominator like this, 1.0 / 0.1*d. You get the same results with either equation, but perhaps one of the equations is more intuitive to you.

If you don’t want the attenuation to abruptly “kick in” at some distance from the light source, you can add a one to the denominator to guarantee that the denominator is always larger than the numerator. This eliminates the need for the clamping function in the above example and provides a smooth attenuation for any distance from the light source. You can experiment with various values for the constants c1 and c2 in the plot of the attenuation function 1.0/(1.0 + c1*d + c1*d^2) below.


1.0 / (1.0 + 1.0*d + 1.0*d2
c1: 0.0 4.0
c2: 0.0 4.0

Summary

Have you noticed that all lighting and color calculations are percentages!

Glossary

attenuation
The decrease in intensity of electromagnetic wave energy as it travels away from its generating source.
Next Section - 9.8 - Types of Light Sources