Lecture Notes
Dr. Tong Yu, April, 2010
    1. Introduction
    2. Color and Lighting
    3. Ray Tracing
    4. Intersection Testing
    5. Radiance
    6. Animation
     
    7. Sky and Terrain Rendering
    8. 3D Movies I
    9. 3D Movies II
    10. Particle Systems
    11. Radiosity

    Color and Lighting

    1. Color Perception

    2. The Trichromatic Theory of Vision

      G. Palmer, 1797     T. Young, 1801

      • Humans perceive color in 3 components: Red, Green, Blue,

      • The retina of the eye contains several kinds of light-sensitive receptors called cones and rods.

      • Human eyes contain three kinds of cones, sensitive to red, green and blue light respectively.

      • Rods, the fourth kind of light-sensitive cell, are mostly used for vision in very light levels and peripherals vision; they do not have the ability to distinguish different colors.
        Additive colors
          Subtractive colors

    3. Computer graphics -- based on the trichromatic theory

      • Red, green, blue -- primary colors

      • For a given color with intensity C, one tries to mix intensities of red, green, blue lights so as to create a color that is visually indistinguishable from color C
          C = rR + gG + bB
        It has been experimentally verified that all colors can be expressed as a linear combination of red, green and blue in this way.

        A pixel is formed from subregions or subpixels, each of which displays one of three colors

      • When colors are combined, they act as a vector space,
        i.e.
          C1 + C2 = (r1 + r2)R + (g1 + g2)G + (b1 + b2)B

      • A computer-graphics monitor emulates visible colors by lighting pixels with a combination of red, green and blue
    4. Computer Color

    5. Representation of RGB Colors

      • 24-bit color → 16,777,216 colors ( true color )
        32-bit color: 8-bits for each of R, G, B, the remaining 8 bits may be used for an alpha ( α ) value

      • RGBA mode : Red, Green, Blue, Alpha

      • the memory for all the pixels is called the memory buffer

      • Color cube

          The Color Cube in Black and White

          glColor3f( 1.0, 0.0, 0.0 ); //full red, no green, no blue

          glBegin( GL_POINT );
              glVertex3fv( point_array );
          glEnd();

    6. Importance of Lighting

    7. lighting and shading are important tools for making graphics images appear more realistic and more understandable

    8. provides crucial visual cues about the curvature and orientation of surfaces

    9. makes three-dimensionality apparent in a graphics image

      Figure A lit and an unlit sphere

    10. teapot example

      (a) Wireframe teapot
      (b) Teapot drawn with solid color but no lighting or shading

      (c) flat shading with only ambient and diffuse lighting
      (d) Gouraud interpolation with only ambient and diffuse reflection

      (e) flat shading with ambient, diffuse, and specular lighting
      (f) Gouraud shading with ambient, diffuse, and specular lighting

    11. The Phong Lighting Model

      Simplest and by far the most popular, lighting and shading model

    12. All light sources are modeled as point light sources

    13. Light is modeled as consisting of Red, Green, Blue

    14. Apply superposition principle to calculate light reflection intensities independently for each light source and for each of the 3 color components ( R, G, B ), e.g. R = R1 + R2

    15. Treats light or illumination as being of three distinct kinds:

      • Ambient Light -- light that arrives equally from all directions, i.e. light that has been scattered so much by its environment that it is impossible to determine its direction

      • Diffuse Light -- light from a point source that will be reflected diffusely ( i.e. refelected evenly in all directions away from the surface )

      • Specular Light -- light from a point source that will be reflected specularly ( i.e. reflected in a mirror-like fashion, as from a shinny surface )

    16. Gives material properties to each surface:

      • Ambient Reflection Properties

      • Diffuse Reflection Properties

      • Specular Reflection Properties

      • Emissive Properties -- The emissivity of a surface controls how much light the surface emits in the absence of any incident light. Light emitted from a surface does not act as light source that illuminates other surfaces; instead, it only affects the color seen by the observer

      • Specular light interacts with specular material property; diffuse light interacts with diffuse material property.

    17. Ambient Reflection and Emissivity

        Iain = total intensity of incoming ambient light

        ca = reflectivity coefficient

        The intensity of outgoing light is ( see next section )

          Ia = ca Iain

        In addition, a surface can also be given an emissive intensity constant Ie which is equal to the light emitted by the surface


          Sphere with only ambient light

    18. Diffuse Reflection

        The intensity of outgoing light is

          Id = cd Idin cos θ

        where cos θ = |L.N|, cd = 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.

    19. Specular Reflection

        The intensity of outgoing light is

          Is = cs Isin (cos α) f

        where α = angle between perfect reflection and the observing reflection
                  f = experimentally adjusted positive constant
                  cs = specular reflectivity coefficient

           
          Specular light distribution


          Sphere with only specular light

          Specular highlights on a pair of spheres.

    20. Multiple Lights and Colors

        I = Ia + Id + Is + Ie


          Phong Distribution


            Sphere with Phong lighting
    21. Angle of Reflection

      Fundamental vectors of the Phong lighting model

      Specular Reflection

      Phong model

    22. The Cook-Torrance Lighting Model
    23. Better capture reflectance properties of a wide range of surface materials
    24. Use a microfacet ( consisting of small flat pieces ) model for rough surfaces
    25. Compute light reflectance using a single reflectivity function BRIDF ( bidirectional reflected intensity distribution function )

      BRIDF ( l, v, λ ) = Iλ,out / Iλ,in

        l = incoming direction ( unit vector )
        v = outgoing direction ( unit vector )
        λ = wavelength of incoming light

      Treat light as being composed of ambient, diffuse and specular components.

        I = Ia + Id + Is
          = ρaIain + ρdIdin(ln) + Is

      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⋅l) Isin measures the amount of light hitting a unit area

        (n⋅v) Is 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 + l|   
        	
      For perfect microfacet reflection,
        n = h
      Let ψ = angle between n and h = cos-1 ( h⋅n )
      D(ψ) = the fraction of microfacets that are aligned for perfect refection

      Some suggested forms for D:

      where c and m are constants.
      The Beckmann distribution is more accurate but evaluation of the Gaussian
      distribution is much easier than Beckmann’s thus making the approximation a
      compelling option for real-time use. The only potential problem of the Gaussian
      distribution is that it relies on an arbitrary constant that needs to be empirically
      determined for best results.

      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 V-shaped
      grooves with the tops of the grooves all at the same height:

      Specularly reflected light occlusion may occur:

      Self-shadowing:

      Masking:

      Shadowing without masking does not reduce the intensity of the reflected light:

      Given that these microfacets are supposed to be extremely small it might seem
      irrelevant that they block light energy, but it is a very important factor in the
      final image
      Effects of shadowing and masking increase with the depth of the microfacets leading
      to very rough surfaces being quite dull in appearance.

      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.

      Then
        	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 Cook-Torrance model is that the reflected specular
      light is not a constant colour; rather it is view-dependent, which can be modeled by
      the Fresnel term.
      Full Fresnel equation is very complicated.
      For real-time computer graphics it is generally more suitable to use an
      approximation presented by Christophe Schlick:

        F = F0 + ( 1 - hv ) 5 x ( 1 - F0 )
      where F0 is the Fresnel reflectance as measured at normal incidence.
      Available Fresnel information is typically expressed in this form which makes it
      a convenient parameter to work with.
      Schlick's approximation is simply a nonlinear interpolation between the refraction
      at normal incidence and total reflection at grazing angles.

      Implementation
      The following code, adopted from "gamedev.net", shows an implementation of the Cook-torrance 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.0-1.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 );
      }
      	

      Results

      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.

    26. Summary of Lighting in OpenGL

    27. To enable Phong lighting calculation:
        glEnable ( GL_LIGHTING );

    28. OpenGL includes eight point sources, they could be "turned on" by
        glEnable ( GL_LIGHTi );     //i = 0, 1, 2, .. 7

    29. Gourand shading can be turned on ( default ) or off by
        glShadeModel ( GL_FLAT );     //off

        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. )

    30. Can rednder both faces ( front and back ) or one face ( default ) of polygons.
        glLightModeli( GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE );

    31. Specifies normal at a vertex and material properties by
        	glNormal3f( x, y, z );	//(x, y, z) is the normal
        	glMaterial*(...);	//multiple glMaterial command O.K.
        	glVertex*(...);		//vertex position
        	

    32. Setting light properties by, for example,
        	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.

    33. Setting light position by
        	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

    34. Setting light intensity values ( colors ) by, for example,
        	float color[4] = { r, g, b, a };
        	glLightfv( GL_LIGHTi, GL_AMBIENT, color );
        	

    35. Setting material properties by, for example,
        	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* ( .... );	
        	
    36. Simple Example: Rendering a Lit Sphere

      1. Define normal vectors for each vertex of all the objects. These normals determine the orientation of the object relative to the light sources. ( In our example, the normals are defined as part of the glutSolidSphere() )

      2. Create, select, and position one or more light sources.

      3. Create and select a lighting model, which defines the level of global ambient light and the effective location of the viewpoint (for the purposes of lighting calculations).

      4. Define material properties for the objects in the scene.

      //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 single-valued 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

      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

      For real-world lights, the intensity of light decreases as distance from the light increases. Since a directional light is infinitely far away, it doesn't make sense to attenuate its intensity over distance, so attenuation is disabled for a directional light. However, you might want to attenuate the light from a positional light. OpenGL attenuates a light source by multiplying the contribution of that source by an attenuation factor:

        attenuation factor =           1          
          kc + kld + kqd2
      where
      d = distance between the light's position and the vertex
      
      kc = GL_CONSTANT_ATTENUATION
      
      kl = GL_LINEAR_ATTENUATION
      
      kq = GL_QUADRATIC_ATTENUATION
      

      By default, kc = 1.0, kl = kq = 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);