

G. Palmer, 1797 T. Young, 1801
Additive colors

Subtractive colors

A pixel is formed from subregions or subpixels, each of which displays one of three colors
glColor3f( 1.0, 0.0, 0.0 ); //full red, no green, no blue
glBegin( GL_POINT );

Figure A lit and an unlit sphere
(a) Wireframe teapot (b) Teapot drawn with solid color but no lighting or shading
(c) flat shading with only ambient and diffuse lighting
(e) flat shading with ambient, diffuse, and specular lighting 
Simplest and by far the most popular, lighting and shading model
c_{a} = reflectivity coefficient
The intensity of outgoing light is ( see next section )
In addition, a surface can also be given an emissive intensity constant I_{e} which is equal to the light emitted by the surface
Sphere with only ambient light 
where cos θ = L^{.}N, c_{d} = diffuse reflectivity coefficient
Diffuse light distribution 
Sphere with only diffuse light 
Diffuse reflection causes color bleeding as light striking a surface carries the surface's color into the environment. 
where α = angle between perfect reflection and the observing
reflection
f = experimentally adjusted positive constant
c_{s} = specular reflectivity coefficient
Specular light distribution 
Sphere with only specular light 
Specular highlights on a pair of spheres. 
Fundamental vectors of the Phong lighting model
Specular Reflection
r = al + bn  ( 1 ) 
n⋅r = an⋅l + bn⋅n
or cos θ = a cos θ + b 
( 2 ) 
1 = r⋅r = a^{2} + 2 a b l⋅n + b^{2}
or 1 = a^{2} + 2 a b cos θ + b^{2} 
( 3 ) 
Phong model
I  = I_{a} + I_{d} + I_{s} + I_{e} 
= c_{a}I_{a}^{in} + c_{d}I_{d}^{in}( l ⋅ n ) + c_{s}I_{s}^{in}( r ⋅ v )^{α} + I_{e} 
BRIDF ( l, v, λ ) = I^{λ,out} / I^{λ,in}
Treat light as being composed of ambient, diffuse and specular components.
I  =  I_{a} + I_{d} + I_{s} 
=  ρ_{a}I_{a}^{in} + ρ_{d}I_{d}^{in}(l⋅n) + I_{s} 
Almost same as Phong model, except
n  =  unit normal to the surface 
s  =  scalar constant 
D  =  D(l,v) measures the distribution of the microfacets 
G  =  G(l,v) measures the reduction of reflected light due to shadowing and masking. 
F  =  F(l,v, λ) is the Fresnel coefficient, measuring the percentage of incidence light being reflected 
(n⋅v) I_{s} measures the amount of light leaving the unit area
The Microfacet Distribution Term
Assumes light incident from the direction of l is specularly reflected independently
by each individual microfacet.
Define halfway vector h
v + l h =  v + lFor perfect microfacet reflection,
Some suggested forms for D:
The Geometric Surface Occlusion Term
Geometric Attenuation is a product of two effects shadowing and masking.
Fundamentally they are the same effect, but one caters for incoming energy and
the other for outgoing energy.
To derive the geometric term G, the microfacets are modeled as symmetric Vshaped
grooves with the tops of the grooves all at the same height:
Specularly reflected light occlusion may occur:
Selfshadowing:
Masking:
Shadowing without masking does not reduce the intensity of the reflected light:
Lemma: Consider the following figure where the edges of AC and AD form a symmetric groove, and AC = AD. h and h' are normals to the sides of the groove.
BC 2(n⋅h)(n⋅v)  =  AC (h⋅v)
One can prove the lemma using the law of sines. From the lemma, one can derive the geometric term G as follows:
The Fresnel Term
An important part of the CookTorrance model is that the reflected specular
light is not a constant colour; rather it is viewdependent, which can be modeled by
the Fresnel term.
Full Fresnel equation is very complicated.
For realtime computer graphics it is generally more suitable to use an
approximation presented by Christophe Schlick:
Implementation
The following code, adopted from "gamedev.net", shows an implementation of the Cooktorrance model
in a shading language:
float4 cook_torrance ( in float3 normal, in float3 viewer, in float3 light, uniform int roughness_mode ) { // Compute any aliases and intermediary values //  float3 half_vector = normalize( light + viewer ); //saturate function clamps values to range [0, 1] float NdotL = saturate( dot( normal, light ) ); float NdotH = saturate( dot( normal, half_vector ) ); float NdotV = saturate( dot( normal, viewer ) ); float VdotH = saturate( dot( viewer, half_vector ) ); float r_sq = roughness_value * roughness_value; // Evaluate the geometric term //  float geo_numerator = 2.0f * NdotH; float geo_denominator = VdotH; float geo_b = (geo_numerator * NdotV ) / geo_denominator; float geo_c = (geo_numerator * NdotL ) / geo_denominator; float geo = min( 1.0f, min( geo_b, geo_c ) ); // Now evaluate the roughness term //  float roughness; if( ROUGHNESS_LOOK_UP == roughness_mode ) { // texture coordinate is: float2 tc = { NdotH, roughness_value }; // Remap the NdotH value to be 0.01.0 // instead of 1.0..+1.0 tc.x += 1.0f; tc.x /= 2.0f; // look up the coefficient from the texture: roughness = texRoughness.Sample( sampRoughness, tc ); } if( ROUGHNESS_BECKMANN == roughness_mode ) { float roughness_a = 1.0f / ( 4.0f * r_sq * pow( NdotH, 4 ) ); float roughness_b = NdotH * NdotH  1.0f; float roughness_c = r_sq * NdotH * NdotH; roughness = roughness_a * exp( roughness_b / roughness_c ); } if( ROUGHNESS_GAUSSIAN == roughness_mode ) { // This variable could be exposed as a variable // for the application to control: float c = 1.0f; float alpha = acos( dot( normal, half_vector ) ); roughness = c * exp( ( alpha / r_sq ) ); } // Next evaluate the Fresnel value //  float fresnel = pow( 1.0f  VdotH, 5.0f ); fresnel *= ( 1.0f  ref_at_norm_incidence ); fresnel += ref_at_norm_incidence; // Put all the terms together to compute // the specular term in the equation //  float3 Rs_numerator = ( fresnel * geo * roughness ); float Rs_denominator = NdotV * NdotL; float3 Rs = Rs_numerator/ Rs_denominator; // Put all the parts together to generate // the final colour //  float3 final = max(0.0f, NdotL) * (cSpecular * Rs + cDiffuse); // Return the result //  return float4( final, 1.0f ); } 
The above image shows the Beckmann distribution (top) and Gaussian distribution (bottom) with roughness values of 0.2 (left), 0.4, 0.6, 0.8 and 1.0 (right). Whilst the Gaussian form is noticeably different to the Beckmann form it is worth noting that there is an arbitrary constant controlling the Gaussian distribution. Experimentation with this coefficient can generate closer or more visually acceptable results. 
glShadeModel ( GL_SMOOTH ); //on
Gouraud interpolation is named after H. Gourand, who proposed linear interpolation in 1971 as a method of blending colors across polygons. )
glNormal3f( x, y, z ); //(x, y, z) is the normal glMaterial*(...); //multiple glMaterial command O.K. glVertex*(...); //vertex position
float color[4] = { r, g, b, a }; glLightModelfv ( GL_LIGHT_MODEL_AMBIENT, color );The alpha component is for blending ( see next chapter ), which is usually set to 1.
float pos[4] = { x, y, z, w }; glLightfv ( GL_LIGHTi, GL_POSITION, pos );if w ≠ 0, => positional light at position ( x/w, y/w, z/w )
if w = 0, => directional light ( light source at infinity ), (x, y, z ) is the direction of the source
float color[4] = { r, g, b, a }; glLightfv( GL_LIGHTi, GL_AMBIENT, color );
float color[4] = {r, g, b, a}; glMaterialfv ( GL_FRONT, GL_AMBIENT, color }; or glEnable( GL_COLOR_MATERIAL ); then the glColor*() will affect material properties. You then code as glNormal3f( x, y, z ); glColor3f( r, g, b ); //change reflectivity parameters glVertex* ( .... );
//light.cpp #include <GL/glut.h> #include <stdlib.h> /* Initialize material property, light source, lighting model, * and depth buffer. */ void init(void) { GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 }; GLfloat mat_shininess[] = { 50.0 }; GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 }; GLfloat light[] = { 1.0, 0.2, 0.2 }; GLfloat lmodel_ambient[] = { 0.1, 0.1, 0.1, 1.0 }; glClearColor (0.0, 0.0, 0.0, 0.0); glShadeModel (GL_SMOOTH); glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess); glLightfv(GL_LIGHT0, GL_POSITION, light_position); glLightfv(GL_LIGHT0, GL_DIFFUSE, light ); glLightfv(GL_LIGHT0, GL_SPECULAR, light ); glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_DEPTH_TEST); } void display(void) { glClear (GL_COLOR_BUFFER_BIT  GL_DEPTH_BUFFER_BIT); glutSolidSphere (1.0, 20, 16); glFlush (); } void reshape (int w, int h) { glViewport (0, 0, (GLsizei) w, (GLsizei) h); glMatrixMode (GL_PROJECTION); glLoadIdentity(); if (w <= h) glOrtho (1.5, 1.5, 1.5*(GLfloat)h/(GLfloat)w, 1.5*(GLfloat)h/(GLfloat)w, 10.0, 10.0); else glOrtho (1.5*(GLfloat)w/(GLfloat)h, 1.5*(GLfloat)w/(GLfloat)h, 1.5, 1.5, 10.0, 10.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode (GLUT_SINGLE  GLUT_RGB  GLUT_DEPTH); glutInitWindowSize (500, 500); glutInitWindowPosition (100, 100); glutCreateWindow (argv[0]); init (); glutDisplayFunc(display); glutReshapeFunc(reshape); glutMainLoop(); return 0; } 
void glLight{if}(GLenum light, GLenum pname, TYPE param);
void glLight{if}v(GLenum light, GLenum pname, TYPE *param); 
Creates the light specified by light, which can be GL_LIGHT0, GL_LIGHT1, ... , or GL_LIGHT7. The characteristic of the light being set is defined by pname, which specifies a named parameter (see Table below). param indicates the values to which the pname characteristic is set; it's a pointer to a group of values if the vector version is used, or the value itself if the nonvector version is used. The nonvector version can be used to set only singlevalued light characteristics. 
Parameter Name  Default Value  Meaning  

GL_AMBIENT  (0.0, 0.0, 0.0, 1.0)  ambient RGBA intensity of light  
GL_DIFFUSE  (1.0, 1.0, 1.0, 1.0)  diffuse RGBA intensity of light  
GL_SPECULAR  (1.0, 1.0, 1.0, 1.0)  specular RGBA intensity of light  
GL_POSITION  (0.0, 0.0, 1.0, 0.0)  (x, y, z, w) position of light  
GL_SPOT_DIRECTION  (0.0, 0.0, 1.0)  (x, y, z) direction of spotlight  
GL_SPOT_EXPONENT  0.0 
spotlight exponent
(how concentrated is the light) 

GL_SPOT_CUTOFF  180.0  spotlight cutoff angle  
GL_CONSTANT_ATTENUATION  1.0  constant attenuation factor  
GL_LINEAR_ATTENUATION  0.0  linear attenuation factor  
GL_QUADRATIC_ATTENUATION  0.0  quadratic attenuation factor 
Position
The fourth component of light_position is 0.0 which signifies
the light is directional light.
By default, GL_POSITION is (0, 0, 1, 0), which defines a
directional light that points along the negative zaxis.
GLfloat light_position[] = { 5.0, 10.0, 2.0, 1.0 };
glLightfv(GL_LIGHT0, GL_POSITION, light_position); 
The location is transformed by the model view
matrix and stored in the eye coordinate system
(i.e. relative to the eye )
Note that by default (i.e. gluLookAt() is not called ), the
camera ( eye ) is situated at the origin, pointing down the
negative zaxis.
Spotlights
glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 45.0);
GLfloat spot_direction[] = { 1.0, 1.0, 0.0 }; glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, spot_direction); 
Attenuation
attenuation factor =  1 k_{c} + k_{l}d + k_{q}d^{2} 
d = distance between the light's position and the vertex k_{c} = GL_CONSTANT_ATTENUATION k_{l} = GL_LINEAR_ATTENUATION k_{q} = GL_QUADRATIC_ATTENUATION
By default, k_{c} = 1.0, k_{l} = k_{q} = 0.0.
You can give these parameters different values:
glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 2.0); glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 1.0); glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 0.5); 
Multiple lights
Can have up to 8 light sources.
Example: Second light source
GLfloat light1_ambient[] = { 0.2, 0.2, 0.2, 1.0 }; GLfloat light1_diffuse[] = { 1.0, 1.0, 1.0, 1.0 }; GLfloat light1_specular[] = { 1.0, 1.0, 1.0, 1.0 }; GLfloat light1_position[] = { 2.0, 2.0, 1.0, 1.0 }; GLfloat spot_direction[] = { 1.0, 1.0, 0.0 }; glLightfv(GL_LIGHT1, GL_AMBIENT, light1_ambient); glLightfv(GL_LIGHT1, GL_DIFFUSE, light1_diffuse); glLightfv(GL_LIGHT1, GL_SPECULAR, light1_specular); glLightfv(GL_LIGHT1, GL_POSITION, light1_position); glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION, 1.5); glLightf(GL_LIGHT1, GL_LINEAR_ATTENUATION, 0.5); glLightf(GL_LIGHT1, GL_QUADRATIC_ATTENUATION, 0.2); glLightf(GL_LIGHT1, GL_SPOT_CUTOFF, 45.0); glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, spot_direction); glLightf(GL_LIGHT1, GL_SPOT_EXPONENT, 2.0); glEnable(GL_LIGHT1); 