Syllabus   Blank Homework   Quizzes  
Notes   Labs   Scores   Blank

Lecture Notes
Dr. Tong Lai Yu, 2010
    1. Introduction
    2. OpenGL Shading Language ( GLSL ) I
    3. GLSL II
    4. Curve and Surface Design
    5. Modeling Shapes with Polygonal Meshes
    6. Texture Mapping
    7. Casting Shadows
    8. Tools for Raster Display
    9. Parsing External Objects

    Casting Shadows

    1. Introduction

    2. Shadows can be created by ray-tracing but too slow
    3. Why shadows?
      • Shadows provide visual cues about the spatial relationships between the different components in a scene.
      • Additional information and views of objects
      • Improved "realism".
      • Lighting environment cues
      • Anchors: Without shadows objects seem to float/hover.

        Without shadows, objects look hovered.

    4. Simple Projection

    5. Shadow mapping ( projective shadowing ):
      • A popular real-time shadowing technique ( e.g. Toy Story )
      • Render the scene from the view point of the light source. Store the z-buffer of every pixel into a "shadow map".
      • Render a scene from the camera view. Let T be the transform re-aligning the camera coordinate system to the coordinate system of the light source.
      • Let P=(x,y,z) be the 3D coordinates of a visible scene point in camera coordinates. Transform the camera coordinates (x,y,z) into light source coordinates (u,v,w): i.e.(u,v,w)=T(x,y,z)
      • Let q be the "z" value at the (u,v) pixel in the shadow map.
      • If(q < w) then P is in shadow not lit by this source else P is shaded and lit by this source
      • Do this for each source if multiple sources are present.


        Scene without shadow

        Scene with shadow mapping

    6. Shadow volume:
      • A popular technique to add shadows to a rendered scene
      • A volume enclosing every point that may be shadowed by an object
      • Popularized by the computer game Doom 3
      • Practical implementation of shadow volumes -- use stencil buffer
      • Accurate to the pixel level
    7. Simple Shadow

    8. Shadow polygon is created by projecting the original polygon onto a flat surface.
      • Suppose the shadow falls on the surface y = 0 and the light source is at (xl,yl,zl).
      • We translate everything by T(-xl,-yl,-zl) so that the light source coincides with the origin.
      • The projection matrix becomes
      • We then translate everything back with T(xl,yl,zl). This translates ( x, y, z ) to ( xp, yp, zp ):

          xp = xl - ( x - xl ) * yl / ( y - yl )

          yp = 0

          zp = zl - ( z - zl ) * yl / ( y - yl )

        GLfloat light[3]={0.0, 20.0, 0.0};
        GLfloat m[16];			//projection matrix
        
        int i,j;
        //set the projection matrix
        for(i=0;i<16;i++) 
          m[i] = 0.0;
          
          m[0] = m[5] = m[10] = 1.0;
          m[7] = -1.0 / light[1];
        
          ....				//draw normal object
        
          glTranslatef(light[0], light[1],light[2]);
          glMultMatrixf(m);		//multiplied by projection matrix
        				//column-major
          glTranslatef(-light[0], -light[1],-light[2]);
        
          ....				//draw the object again
        

    9. Stencil Buffer

      See http://www.opengl.org/archives/resources/features/StencilTalk/

    10. Stencil buffer is not the same as z-buffer and depth-buffer
    11. Used to restrict drawing to a certain portion of the screen
    12. Can be turned on or off
    13. Constructive Solid Geometry
    14. The pixel value is rendered only if the stencil test passes.
    15. Stencil Test:
      • Takes place only if there is a stencil buffer.
      • Compares a reference value with the value stored at a pixel position of the stencil buffer
      • The value of the stencil buffer may be modified, depending on the result of the test.

        void glStencilFunc ( GLenum func, GLint ref, GLuint mask );
        Sets the comparison function ( func ), the reference value ( ref ), and a mask ( mask ) for use with the stencil test. The reference value is compared with the value in the stencil buffer using the comparison function, but the comparison applies only to those bits for which the corresponding bits of the mask are 1.
         
        void glStencilOp ( GLenum fail, GLenum zfail, GLenum zpass );
        Specifies how the data in the stencil buffer is modified when a fragment passes or fails the stencil test:
          fail -- Specifies the action to take when the stencil test fails.
          zfail -- Specifies the stencil action when the stencil test passes, but the depth test fails.
          zpass -- Specifies the stencil action when both the stencil test and the depth test pass,
          or when the stencil test passes and either there is no depth buffer or depth testing is not enabled.

    16. Example
      /*
       *  stencil.cpp
       *  This program demonstrates use of the stencil buffer for
       *  masking rectangular regions.  
       *  Whenever the window is redrawn, a value of 1 is drawn 
       *  into a diamond-shaped region in the stencil buffer.  
       *  Elsewhere in the stencil buffer, the value is 0.
       */
      #include <GL/glut.h>
      #include <stdlib.h>
      #include <stdio.h>
      
      void init (void) 
      {
         glEnable(GL_DEPTH_TEST);
         glClearStencil(0x0);
         glEnable(GL_STENCIL_TEST);
         glClear ( GL_STENCIL_BUFFER_BIT );           //fill stencil buffer with 0
      }
      
      //green diamond inside red square
      void display(void)
      {
         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
      
         glStencilFunc (GL_NOTEQUAL, 0x1, 0x1);	//fail in diamond-region, pass outside
         glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP);	//no change in stencil buffer values
        
        glColor3f ( 1, 0, 0 );			//red
        glBegin ( GL_POLYGON );
            glVertex3f (-1.0, 1.0, -1);		//z = -1, so red polygon behind green ( below )
            glVertex3f (1.0, 1.0, -1);
            glVertex3f (1.0, -1.0, -1);
            glVertex3f (-1.0, -1.0, -1);
         glEnd();
         glStencilFunc (GL_EQUAL, 0x1, 0x1);		//pass in diamond-region, fail outside
        glColor3f ( 0, 1, 0 );			//green
        glBegin ( GL_POLYGON );
            glVertex3f (-1.0, 1.0, 0);		//z = 0, in front of red polygon
            glVertex3f (1.0, 1.0, 0);
            glVertex3f (1.0, -1.0, 0);
            glVertex3f (-1.0, -1.0, 0);
         glEnd();
      }
      
      /*  Whenever the window is reshaped, redefine the 
       *  coordinate system and redraw the stencil area.
       */
      void reshape(int w, int h)
      {
         glViewport(0, 0, (GLsizei) w, (GLsizei) h);
         glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
      
         glMatrixMode(GL_PROJECTION);
         glLoadIdentity();
         if (w <= h)
            gluOrtho2D(-3.0, 3.0, -3.0*(GLfloat)h/(GLfloat)w,
                       3.0*(GLfloat)h/(GLfloat)w);
         else
            gluOrtho2D(-3.0*(GLfloat)w/(GLfloat)h,
                       3.0*(GLfloat)w/(GLfloat)h, -3.0, 3.0);
         glMatrixMode(GL_MODELVIEW);
         glLoadIdentity();
         
         //create a diamond shaped stencil area 
         glClear(GL_STENCIL_BUFFER_BIT);	//fill stencil buffer with 0s
         glStencilFunc (GL_ALWAYS, 0x1, 0x1);
         glStencilOp (GL_REPLACE, GL_REPLACE, GL_REPLACE);
         glBegin(GL_QUADS);		//stencil buffer will have diamond-shaped region filled with 1s
            glVertex2f (-1.0, 0.0);
            glVertex2f (0.0, 1.0);
            glVertex2f (1.0, 0.0);
            glVertex2f (0.0, -1.0);
         glEnd();
      
         glMatrixMode(GL_PROJECTION);
         glLoadIdentity();
         gluPerspective(45.0, (GLfloat) w/(GLfloat) h, 3.0, 7.0);
         glMatrixMode(GL_MODELVIEW);
         glLoadIdentity();
         glTranslatef(0.0, 0.0, -5.0);
      }
      
      void keyboard(unsigned char key, int x, int y)
      {
         switch (key) {
            case 27:
               exit(0);
               break;
         }
      }
      
      /* Main Loop
       * Be certain to request stencil bits.
       */
      int main(int argc, char** argv)
      {
         glutInit(&argc, argv);
         glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB 
                              | GLUT_DEPTH | GLUT_STENCIL);
         glutInitWindowSize (400, 400);
         glutInitWindowPosition (100, 100);
         glutCreateWindow (argv[0]);
         init ();
         glutReshapeFunc(reshape);
         glutDisplayFunc(display);
         glutKeyboardFunc(keyboard);
         glutMainLoop();
         return 0;
      }
        
    17. Casting shadow using stencil buffer:
      1. Limit the rendering of the shadows of the desired surfaces using the stencil buffer. ( Draw the surface in the stencil buffer. )
      2. Project the shadow on the surface.
      3. Turn off lights and texture mapping.
      4. Turn off depth test.
      5. Enable blending.
      6. Render shadows of objects.
      7. Restore old settings.
    18. Shadow Volume

      Some of the materials are adopted from http://www.gamedev.net/reference/articles/article1873.asp

    19. In 1977, Franklin Crow proposed an algorithm to generate polygons that represent the shadow volume of objects in the scene, then placing them into the rendering data structure as invisible objects

    20. Stencil shadow volume


        The shaded region represents the shadow volume.

    21. Resulted from extruding the silhouette edges from the point of view of light source

      How to find Shadow Volume?

      • First, need to get the silhouette edges of the occluder with respect to the light source.
      • A silhouette edge is an edge that separates a front-facing polygon from a back-facing polygon (with respect to the light source).
      • For triangles, the silhouette edges of a triangle are equal to the edges of the triangle.
      • Next, we extrude the silhouette edges by projecting the vertices away from the light source (assuming point light source).
      • The resulting quadrilaterals define the shadow volume.

    22. A user may have many viewing directions

        Depth-Pass stencil operation

    23. Stencil buffer values generated from depth-pass ( z-pass ) stencil operations:
      1. Render objects of scene so that z-buffer contains the 'nearest' z value of objects for each pixel.
      2. Render front face of shadow volume: If depth test passes, increment stencil value, otherwise does nothing. (Note that in the figure above, only the shaded part is the shadow volume.) Disable draw to frame and depth buffer.
      3. Render back face of shadow volume: If depth test passes, decrement stencil value, otherwise does nothing. Disable draw to frame and depth buffer.

    24. Algorithm works for multiple shadow volumes.

    25. Finite volume may fail to shadow other objects

    26. Depth-pass stencil operation fails when the eye coordinate is inside the shadow volume.

    27. Carmack's depth-fail ( z-fail )algorithm ( reverse of above ):
      1. Render back face: If depth test fails, increment stencil value, otherwise does nothing. Disable draw to frame and depth buffer.
      2. Render front face: If depth test fails, decrement stencil value, otherwise does nothing. Disable draw to frame and depth buffer.

    28. Capping For Depth-Fail Test
      1. Need capping to produce correct results for various eye positions
      2. Front and back caps can be created by extruding
      3. Need to ensure that the primitives that define the entire shadow volume are outward facing.

    29. Summary
      1. Render all the objects using only ambient lighting and any other surface-shading attribute.
      2. For each light source, clear the stencil buffer and calculate the silhouette of all occluders with respect to the light source.
      3. Extrude the silhouette to form the shadow volume; generates caps if necessary
      4. Render the shadow volumes using depth-pass or depth-fail test.
      5. Use the updated stencil buffer to the fragments corresponding to nonzero stencil values.
    30. An Example: Stenciled Shadow Volume in OpenGL

      Adopted from http://www.3ddrome.com/