Clockworkcoders Tutorials

 




Per Fragment Lighting

Introduction

In OpenGL Shading language you can access built in OpenGL lighting states. Implementing an advanced lightning model would be compatible with standard OpenGL statements.

Accessing Lighting States

You can access all OpenGL Lighting states and some derived states. The following tables can also be found in the OpenGL Shading Language specification:

Light Source Parameters

gl_LightSource[] is a built-in array you can access for all lights. gl_LightSource is defined this way:

struct gl_LightSourceParameters 
{   
   vec4 ambient;              // Aclarri   
   vec4 diffuse;              // Dcli   
   vec4 specular;             // Scli   
   vec4 position;             // Ppli   
   vec4 halfVector;           // Derived: Hi   
   vec3 spotDirection;        // Sdli   
   float spotExponent;        // Srli   
   float spotCutoff;          // Crli                              
                              // (range: [0.0,90.0], 180.0)   
   float spotCosCutoff;       // Derived: cos(Crli)                 
                              // (range: [1.0,0.0],-1.0)   
   float constantAttenuation; // K0   
   float linearAttenuation;   // K1   
   float quadraticAttenuation;// K2  
};    


uniform gl_LightSourceParameters gl_LightSource[gl_MaxLights];
        
Light Source Parameters Definition

Material Parameters

You can access the values you set in C++ with glMaterial using the GLSL built-in variables gl_FrontMateral and gl_BackMaterial.

struct gl_MaterialParameters  
{   
   vec4 emission;    // Ecm   
   vec4 ambient;     // Acm   
   vec4 diffuse;     // Dcm   
   vec4 specular;    // Scm   
   float shininess;  // Srm  
};  


uniform gl_MaterialParameters gl_FrontMaterial;  
uniform gl_MaterialParameters gl_BackMaterial;        
Material Parameters

 

Derived State from Products of Light and Material

struct gl_LightModelProducts  
{    
   vec4 sceneColor; // Derived. Ecm + Acm * Acs  
};  


uniform gl_LightModelProducts gl_FrontLightModelProduct;  
uniform gl_LightModelProducts gl_BackLightModelProduct;      


struct gl_LightProducts 
{   
   vec4 ambient;    // Acm * Acli    
   vec4 diffuse;    // Dcm * Dcli   
   vec4 specular;   // Scm * Scli  
};  


uniform gl_LightProducts gl_FrontLightProduct[gl_MaxLights];  
uniform gl_LightProducts gl_BackLightProduct[gl_MaxLights];
        
Derived State from Products of Light and Material

Shading Models

A shading model is a mathematical representation of the surface characteric of an object.

Per Vertex Lighting using fixed function pipeline (without GLSL)

Per Fragment Lighting using phong shading model (phong fragment shader)

 

Diffuse Reflection (Lambert's cosine law)


Image: Diffuse Term

The angle between the two vectors, is called the angle of incidence. Lambert's law states that the amount of reflected light is proportional to the cosine of the angle of incidence (dot product). A surface is illuminated by a light source only if the angle of incidence is between 0 and 90 degrees.

varying vec3 N;
varying vec3 v; void main(void) { v = vec3(gl_ModelViewMatrix * gl_Vertex); N = normalize(gl_NormalMatrix * gl_Normal);
   gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
          
Vertex Shader Source Code

The current vertex position is transformed to eye space. This is done by multiplying the modelview matrix with the vertex position. The normal is passed to the fragment shader.

varying vec3 N;
varying vec3 v; void main(void) { vec3 L = normalize(gl_LightSource[0].position.xyz - v); vec4 Idiff = gl_FrontLightProduct[0].diffuse * max(dot(N,L), 0.0); Idiff = clamp(Idiff, 0.0, 1.0); gl_FragColor = Idiff; }
Fragment Shader Source Code

Lambert's Law is calculated for every fragment. Please note that the normal is an interpolated value and is may not be normalized, this is disregarded because the error is small.

Specular Reflection

Image: Specular Term

Specular reflection is the direct reflection of light by a surface. Shiny surfaces reflect almost all incident light and therefore have bright specular highlights or hot spots. The location of a highlight moves as you move your eye, while keeping the light source and the surface at the stationary positions. In order to make a surface mirror-like, decrease the diffuse reflection and increase the specular reflection.

Ambient Reflection

Image: Ambient Term

Ambient reflection is a gross approximation of multiple reflections from indirect light sources (e.g., the surfaces of walls and tables in a room that reflect off the lights from light sources). Ambient reflection produces a constant illumination on all surface, regardless of their orientation. If you look at the faces of a cube to which only ambient reflection is applied, all the faces are illuminated by the same amount of light. Ambient reflection itself produces very little realism in images.

Some famous shading models are:

Phong Shading Model

Bui Tuong Phong published his illumination model in 1973: "Illumination for Computer-Generated Images".

Blinn-Phong Shading Model

This model was introduces by Blinn, James F. Models of Light Reflection for Computer Synthesized Pictures. Computer Graphics (SIGGRAPH 77 Proceedings) 11(2) July 1977, p. 192-198.

Cook-Torrance Shading Model

Robert L. Cook, Kenneth E. Torrance, A reflectance model for computer graphics, 1982.

Schlick Shading Model

This lighting model was created by Christophe Schlick, A Customizable Reflectance Model for Everyday Rendering, Fourth Eurographics Workshop on Rendering, 1993.

Implementing Phong Shader (for one Point-Light)

 

varying vec3 N;
varying vec3 v;
void main(void)  
{     
   v = vec3(gl_ModelViewMatrix * gl_Vertex);       
   N = normalize(gl_NormalMatrix * gl_Normal);
   gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;  
}
          
Vertex Shader Source Code

varying vec3 N;
varying vec3 v;
void main (void)  
{  
   vec3 L = normalize(gl_LightSource[0].position.xyz - v);   
   vec3 E = normalize(-v); // we are in Eye Coordinates, so EyePos is (0,0,0)  
   vec3 R = normalize(-reflect(L,N));  

//calculate Ambient Term: vec4 Iamb = gl_FrontLightProduct[0].ambient; //calculate Diffuse Term: vec4 Idiff = gl_FrontLightProduct[0].diffuse * max(dot(N,L), 0.0); Idiff = clamp(Idiff, 0.0, 1.0); // calculate Specular Term: vec4 Ispec = gl_FrontLightProduct[0].specular * pow(max(dot(R,E),0.0),0.3*gl_FrontMaterial.shininess); Ispec = clamp(Ispec, 0.0, 1.0);
   // write Total Color:  
   gl_FragColor = gl_FrontLightModelProduct.sceneColor + Iamb + Idiff + Ispec;     
}
          
Fragment Shader Source Code

Using More Lights

To support more than one light you can simply add a loop to the fragment shader. For reasons of performance it is not a good idea to use a uniform variable for the loop, therefore the best approach is to compile different shaders for different numbers of lights. (Only the fragment shader changes, the vertex shader is same as above.)

varying vec3 vN;
varying vec3 v; 
#define MAX_LIGHTS 3 
void main (void) 
{ 
   vec3 N = normalize(vN);
   vec4 finalColor = vec4(0.0, 0.0, 0.0, 0.0);
   
   for (int i=0;i<MAX_LIGHTS;i++)
   {
      vec3 L = normalize(gl_LightSource[i].position.xyz - v); 
      vec3 E = normalize(-v); // we are in Eye Coordinates, so EyePos is (0,0,0) 
      vec3 R = normalize(-reflect(L,N)); 
   
      //calculate Ambient Term: 
      vec4 Iamb = gl_FrontLightProduct[i].ambient; 
      //calculate Diffuse Term: 
      vec4 Idiff = gl_FrontLightProduct[i].diffuse * max(dot(N,L), 0.0);
      Idiff = clamp(Idiff, 0.0, 1.0); 
   
      // calculate Specular Term:
      vec4 Ispec = gl_FrontLightProduct[i].specular 
             * pow(max(dot(R,E),0.0),0.3*gl_FrontMaterial.shininess);
      Ispec = clamp(Ispec, 0.0, 1.0); 
   
      finalColor += Iamb + Idiff + Ispec;
   }
   
   // write Total Color: 
   gl_FragColor = gl_FrontLightModelProduct.sceneColor + finalColor; 
}
Fragment Shader Source Code

 

Example Project

In this example project the phong shader with 1 light is included. You can use this as base to implement it to support more lights and/or different shading models.

Download: GLSL_Lighting.zip (Visual Studio 8 Project)
Download: GLSL_Lighting.tar.gz (Linux Makefile, thanks to Liran Nuna)
(If you create a project/makefile for a different platform/compiler, please send it to: christen(at)clockworkcoders.com and I will put it here.)

 

 

Previous: Texturing

Next: Discarding Fragments