10.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 its distance from the sun. The technical name for this is light attenuation.
This lesson explains how to include light attenuation in a lighting 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 a surface.
Using the function 1/d2 causes light to decrease very rapidly and so it is
common for CGI applications 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.
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 any 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. You can experiment below
with various values for the constants c1 and c2 in the plot of the attenuation function
1.0/(1.0 + c1*d + c2*d^2)
. In many scenarios, setting c1
to 0.1
and c2
to 0.0
gives good results.
attenuation = 1.0 / (1.0 + 1.00*d + 1.00*d2
c1: 0.0 3.0
c2: 0.0 3.0
Example WebGL Program¶
Experiment with the following WebGL program by varying the constants in the attenuation
function 1.0/(1.0 + c1*d + c2*d^2)
.
Experiment with Light Attenuation
Virtual World Ambient, Diffuse, Specular w/ Attenuationcamera eye (0.0, 0.0, 5.0) | camera center (0.0, 0.0, 0.0) |
X: -5.0 +5.0 | X: -5.0 +5.0 |
Y: -5.0 +5.0 | Y: -5.0 +5.0 |
Z: -5.0 +5.0 | Z: -5.0 +5.0 |
light position(3.0, 0.0, 5.0) | light color (1.00, 1.00, 1.00) | |
X: -5.0 +5.0 | Red: | 0.0 1.0 |
Y: -5.0 +5.0 | Green: | 0.0 1.0 |
Z: -5.0 +5.0 | Blue: | 0.0 1.0 |
ambient intensities (0.30, 0.30, 0.30) |
attenuation 1.0/(1.0 + 0.10*d + 0.00*d^2) |
||
Red: | 0.0 1.0 | c1: | 0.0 3.0 |
Green: | 0.0 1.0 | c2: | 0.0 3.0 |
Blue: | 0.0 1.0 | Change all intensities at once. |
shininess = 30.0 | |||
0.1 128.0 | |||
Red Cube Red X |
Green Cube Green Y |
Blue Cube Blue Z |
White Cube |
Change the shininess of all models. |
Open this webgl demo program in a new tab or window
Please insure you make the following observations about attenuation:
- Setting
c1
to0.0
andc2
to1.0
is the attenuation for real world lighting: e.g.,1/d^2
. However, this typically makes a CGI scene too dark. - Setting
c1
to0.1
andc2
to0.0
gives good visual results for the example scene above.
Fragment Shader¶
The following fragment shader program implements light attenuation.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | // Fragment shader program
precision mediump int;
precision mediump float;
// Light model
uniform vec3 u_Light_position;
uniform vec3 u_Light_color;
uniform float u_Shininess;
uniform vec3 u_Ambient_intensities;
uniform float u_c1, u_c2; // Attenuation constants: 1/(1 + c1*d + c2*d^2)
// Data coming from the vertex shader
varying vec3 v_Vertex;
varying vec4 v_Color;
varying vec3 v_Normal;
void main() {
vec3 ambient_color;
vec3 specular_color;
vec3 diffuse_color;
vec3 to_light;
float distance_from_light;
vec3 fragment_normal;
vec3 reflection;
vec3 to_camera;
float cos_angle;
float attenuation;
vec3 color;
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// AMBIENT calculations
ambient_color = u_Ambient_intensities * vec3(v_Color);
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// General calculations needed for both specular and diffuse lighting
// Calculate a vector from the fragment location to the light source
to_light = u_Light_position - v_Vertex;
distance_from_light = length( to_light);
to_light = normalize( to_light );
// The fragment's normal vector is being interpolated across the
// geometric primitive which can make it un-normalized. So normalize it.
fragment_normal = normalize( v_Normal);
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// DIFFUSE calculations
// Calculate the cosine of the angle between the vertex's normal
// vector and the vector going to the light.
cos_angle = dot(fragment_normal, to_light);
cos_angle = clamp(cos_angle, 0.0, 1.0);
// Scale the color of this fragment based on its angle to the light.
diffuse_color = vec3(v_Color) * u_Light_color * cos_angle;
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// SPECULAR calculations
// Calculate the reflection vector
reflection = 2.0 * dot(fragment_normal,to_light) * fragment_normal
- to_light;
reflection = normalize( reflection );
// Calculate a vector from the fragment location to the camera.
// The camera is at the origin, so just negate the fragment location
to_camera = -1.0 * v_Vertex;
to_camera = normalize( to_camera );
// Calculate the cosine of the angle between the reflection vector
// and the vector going to the camera.
cos_angle = dot(reflection, to_camera);
cos_angle = clamp(cos_angle, 0.0, 1.0);
cos_angle = pow(cos_angle, u_Shininess);
// If this fragment gets a specular reflection, use the light's color,
// otherwise use the objects's color
specular_color = u_Light_color * cos_angle;
attenuation = 1.0/
(1.0 + u_c1*distance_from_light + u_c2*pow(distance_from_light,2.0));
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// COMBINED light model
color = ambient_color + attenuation*(diffuse_color + specular_color);
color = clamp(color, 0.0, 1.0);
gl_FragColor = vec4(color, v_Color.a);
}
|
Line(s) | Description |
---|---|
10 | The constants for the attenuation function are declared as
uniform variables so they can be modified by the example WebGL
program. Typically you would just hard code appropriate constants
in the equation used in lines 81-82. |
40 | The length of the “to the light” vector gives the distance between the surface and the light source. The length must be calculated before the vector is normalized. |
81-82 | The attenuation percentage is calculated. |
86 | The color of the fragment is determined by adding the ambient, diffuse and specular colors. The ambient light comes from unknown light sources and since the distance to those light sources is not known, the ambient light should not be attenuated. Therefore, only the diffuse and specular light is attenuated. |
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.
Self Assessment¶
- be darker.
- Correct. Because the objects are receiving less light.
- be brighter.
- Incorrect. The exact opposite is true.
- have more contrast.
- Incorrect. They get less light, which decreases contrast.
- have more detail.
- Incorrect. They get less light, which decreases visual detail.
Q-231: The visual effect of light attenuation is to make objects further from a light source to …
- 1/(d^2)
- Correct. One divided by the distance squared.
- 1/d
- Incorrect.
- 1.0/(1 + d + d^2)
- Incorrect.
- 20/d
- Incorrect.
Q-232: In the real world, what is the formula for calculating light attenuation, where d
is the distance a surface is from a light source?
- proportional to 1/d
- Correct. Proportional to one divided by the distance.
- 1/(d^2)
- Incorrect.
- 1.0/(1 + d + d^2)
- Incorrect.
- 20/d
- Incorrect.
Q-233: CGI scenes often get better results from which attenuation formula below?
- ambient light
- Incorrect. Ambient light has no known source, so there is no “distance to the light” to allow for attenuation.
- diffuse light from a point light source
- Correct. The distance between the light source and the surface is known.
- specular light from a point light source
- Correct. The distance between the light source and the surface is known.
- sun light
- Incorrect. If the sun is being modeled, it is so far away from the scene that all objects whould have the same attenuation.
Q-234: Using a combined light model, which of the following types of light should be attenuated? (Select all that apply.)