This Article covers the most basic form of lighting as present in the fixed function pipeline of OpenGL – only this time in the form of GLSL shaders.
The ADS Lighting Model is probably the most well know lighting model in CG today – ADS standds for Ambient, Diffuse and Specular. These three Components add up to the resulting Color of a given position at the lighted object.
Lighting Basics
The final color of a vertex is calculated by multiplying the material color with the coefficient calculated by the current lighting type and summing up the components:

Ambient Lighting
Ambient Lighting is by far the most simple one and can be seen as a global brightening factor applied to the geometry lit. Imagine light which comes from no particular location because it has bounced off the surface of objects often – you can also say it comes from everywhere. This light is relatively low in terms of its contributen. It just makes absolutely dark places a little more bright.
For example, take a Vertex which has a ambient material color of pure red:
![vec{Vertex_Ambient} = delim{[}{matrix{1}{3}{1 0 0}}{]} vec{Vertex_Ambient} = delim{[}{matrix{1}{3}{1 0 0}}{]}](http://www.matrix44.net/cms/wp-content/plugins/wpmathpub/phpmathpublisher/img/math_981.5_63be7bb83198bc227f02e2c8cf73f40c.png)
If your Ambient Lighting Color is:
![vec{Light_Ambient} = delim{[}{matrix{1}{3}{0.2 0.2 0.2}}{]} vec{Light_Ambient} = delim{[}{matrix{1}{3}{0.2 0.2 0.2}}{]}](http://www.matrix44.net/cms/wp-content/plugins/wpmathpub/phpmathpublisher/img/math_981.5_ce5fae45524f6f2eb951ff068d4c63f8.png)
To get the Ambient Color for the Vertex, just multiply those two component wise:
![vec{Ambient} = delim{[}{matrix{1}{3}{1 * 0.2 0 * 0.2 0 * 0.2}}{]} = delim{[}{matrix{1}{3}{0.2 0 0}}{]} vec{Ambient} = delim{[}{matrix{1}{3}{1 * 0.2 0 * 0.2 0 * 0.2}}{]} = delim{[}{matrix{1}{3}{0.2 0 0}}{]}](http://www.matrix44.net/cms/wp-content/plugins/wpmathpub/phpmathpublisher/img/math_990.5_5a5dd47b79f11e4b7200374e3bdedfcb.png)
Because a pictures says more then thousand words, take a look a the following:

With a little imagination you could see that this is a Torus. Each vertex of the torus has a ambient material color of red (as shown above). The ambient lighting color is 0.2 (also shown above) and the result is – you probably know already – this dark red (also shown above in the equation)
Diffuse Lighting
A little bit more complex then ambient lighting is diffuse lighting. Imagine a light source which is at a unlimited (or really far away) location like our real sun is. Each light ray is (nearly) parallel when a object is hit:

The diffuse coefficient is proportional to the input angle of the incoming rays – at 90° to the vertex it’s value is 1, at 0° or 180° its 0. To get the angle of the incoming ray we need the vertex normal (which is perpendicular to the surface). Take a look at the following image:

If we take a look at our torus we can clearly see far more details – this time the torus is diffusely lit only:
To calculate the final diffuse color of the vertex, use the following equation:

The actual math and how to do this in GLSL is explained later when building the shader.
Specular Lighting
This is the last of the three light types discussed in this article. Imagine something made of glossy plastic. If you look at it, you will see spots reflecting the sun (they’re white probably). This property leads us to the conclusion that specular lighting is a highly view angle and light position dependent property. Take a look:

So, here we have more properties to take care off:
- L is the vector from the Light source to the vertex
- N is the vertex normal
- R is the reflected incoming Light Ray
- C is the vector from the vertex to the viewer position
- The shininess exponent (S), ranging from 0 (maximum reflection) to 128 (no reflection); this one isn’t shown on the graphic
To calculate the Specular coefficient use the following equation:

In the equation above you need to know the Reflection R, which is calculated by:

This type of specular light calculation is the so called “Phong” Model (there are others, but this one’s the simplest)
Seems a little bit complicated but thankfully, GLSL supports us with built-in functions to calculate the reflection of a vector about another one (“reflect”). Last but not least, here’s a image of our torus with just the specular components:

Fully Lit Torus
If we combine Ambient, Diffuse and Specular Components we get something like:

Which looks kinda good in my opinion
Building the Shader
To use the theoretical knowledge from above in a useful way, let’s build a GLSL Shader which implements our ADS Lighting model. Below you will find the entire source to the shader. I’ve documented nearly every line to make clear what’s going on there:
Vertex Shader:
#version 130
// Our Model, View and Projection matrices
// we need them to transform the incoming vertices and for lighting
// calculation
uniform mat4 mModel;
uniform mat4 mView;
uniform mat4 mProjection;
// Position of the "Camera" and the Light
// in Model space
uniform vec4 vEyePosition;
uniform vec4 vLightPosition;
// The Colors of the material
uniform vec4 vAmbientMaterial;
uniform vec4 vDiffuseMaterial;
uniform vec4 vSpecularMaterial;
// Vertex properties
in vec4 vVertexPosition;
in vec3 vVertexNormal;
// Final color of the vertex we pass on to the next stage
smooth out vec4 vVaryingColor;
// Returns the specular component of the color
vec4 GetSpecularColor()
{
// Transform the Vertex and corresponding Normal into Model space
vec4 vTransformedNormal = mModel * vec4( vVertexNormal, 1 );
vec4 vTransformedVertex = mModel * vVertexPosition;
// Get the directional vector to the light and to the camera
// originating from the vertex position
vec4 vLightDirection = normalize( vLightPosition - vTransformedVertex );
vec4 vCameraDirection = normalize( vEyePosition - vTransformedVertex );
// Calculate the reflection vector between the incoming light and the
// normal (incoming angle = outgoing angle)
// We have to use the invert of the light direction because "reflect"
// expects the incident vector as its first parameter
vec4 vReflection = reflect( -vLightDirection, vTransformedNormal );
// Calculate specular component
// Based on the dot product between the reflection vector and the camera
// direction.
//
// hint: The Dot Product corresponds to the angle between the two vectors
// hint: if the angle is out of range (0 ... 180 degrees) we use 0.0
float spec = pow( max( 0.0, dot( vCameraDirection, vReflection )), 32 );
return vec4( vSpecularMaterial.r * spec, vSpecularMaterial.g * spec, vSpecularMaterial.b * spec, 1.0 );
}
// Ambient color component of vertex
vec4 GetAmbientColor()
{
return vAmbientMaterial * vec4( 0.2, 0.2, 0.2, 1.0 );
}
// Diffuse Color component of vertex
vec4 GetDiffuseColor()
{
// Transform the normal from Object to Model space
// we also normalize the vector just to be sure ...
vec4 vTransformedNormal = normalize( mModel * vec4( vVertexNormal, 1 ));
// Get direction of light in Model space
vec4 vLightDirection = normalize( vLightPosition - vTransformedNormal );
// Calculate Diffuse intensity
float fDiffuseIntensity = max( 0.0, dot( vTransformedNormal, vLightDirection ));
// Calculate resulting Color
vec4 vDiffuseColor;
vDiffuseColor.xyz = vDiffuseMaterial.rgb * fDiffuseIntensity;
vDiffuseColor.a = 1.0;
return vDiffuseColor;
}
void main(void)
{
vec4 ambientColor = GetAmbientColor();
vec4 diffuseColor = GetDiffuseColor();
vec4 specularColor = GetSpecularColor();
// Combine into final color
vVaryingColor = ambientColor + diffuseColor + specularColor;
// Transform the vertex to MVP Space
gl_Position = mProjection * mView * mModel * vVertexPosition;
}
Fragment Shader:
#version 130
out vec4 vFragColor;
in vec4 vVaryingColor;
void main(void)
{
// Just use the interpolated color as our output
// (Colors between vertices are interpolated)
vFragColor = vVaryingColor;
}
To test out everything and play around a little bit you can download the source to the entire project – it’s compilable on Linux and Windows (please not that you need the FreeGLUT Libraries installed):
