  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

1. Introduction

2. Shadows can be created by ray-tracing but too slow
• 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

• 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

• 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  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={0.0, 20.0, 0.0}; GLfloat m; //projection matrix int i,j; //set the projection matrix for(i=0;i<16;i++) m[i] = 0.0; m = m = m = 1.0; m = -1.0 / light; .... //draw normal object glTranslatef(light, light,light); glMultMatrixf(m); //multiplied by projection matrix //column-major glTranslatef(-light, -light,-light); .... //draw the object again ```

9. Stencil Buffer
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 #include #include 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); 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.
7. Restore old settings.

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  21. Resulted from extruding the silhouette edges from the point of view of light source

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

• Defines a surface structure:

 ```//a quad surface struct surface { float vertices; //contains 4 3D tuples representing vertices of a quadrilateral surface // relative to position of surface }; ```

• Defines a cube structure:

 ``` struct cube { struct surface *surfaces; //a cube has 6 surfaces float position; //position of the cube };```

• Rendering a surface's shadow volume:

 ```void render_surface_shadow_volume( struct surface *surf, //points to a surface float *surf_pos, //points to a 3D tuple representing the surface's position float *light_pos //points to a 3D vecotr representing position of light that's blocked by the surface ){ ```

Use a loop to

1. add the surface's position to each of the surface's vertices to obtain absolute vertice values ( at the beginning, a vertice is defined relative to the 'center' of the surface ( square ) )
2. subtract from vertices the light position so that v values are relative to the light position
3. store in the v array the surface's vertices extruded towards infinity in the direction from the light to each vertex

 ``` int i; float v; for(i = 0; i < 4; i++) { surf->vertices[i] += surf_pos; surf->vertices[i] += surf_pos; surf->vertices[i] += surf_pos; //move origin to light position, i.e. T(-xl, -yl, -zl) v[i] = (surf->vertices[i] - light_pos); //relative to light position v[i] = (surf->vertices[i] - light_pos); v[i] = (surf->vertices[i] - light_pos); normalize(v[i]); v[i] *= M_INFINITY; v[i] *= M_INFINITY; v[i] *= M_INFINITY; //move origin back to original place, i.e. T(xl, yl, zl) v[i] += light_pos; v[i] += light_pos; v[i] += light_pos; } ```

Next, we render the back and front caps of the shadow volume; the front cap is the same as the surface itself, and the back cap is the surface extruded towards infinity. This insures that we have a closed shadow volume, which is required for the stencil operations.

 ``` // back cap ( may extend to 'infinity' ) glBegin(GL_QUADS); glVertex3fv(v); glVertex3fv(v); glVertex3fv(v); glVertex3fv(v); glEnd(); // front cap glBegin(GL_QUADS); glVertex3fv(surf->vertices); glVertex3fv(surf->vertices); glVertex3fv(surf->vertices); glVertex3fv(surf->vertices); glEnd(); ```

The next loop renders a quadrilateral for each edge of the surface, resulting in four quads. Each quadrilateral starts at the edge of the surface itself, and ends at the corresponding edge of the extruded back cap of the surface.

 ``` glBegin(GL_QUAD_STRIP); glVertex3fv(surf->vertices); glVertex3fv(v); for(i = 1; i <= 4; i++) { glVertex3fv(surf->vertices[i % 4]); glVertex3fv(v[i % 4]); } glEnd(); ```

Finally, we restore the original surface vertices by subtracting the surface position from each of the vertices.

 ``` for(i = 0; i < 4; i++) { surf->vertices[i] -= surf_pos; surf->vertices[i] -= surf_pos; surf->vertices[i] -= surf_pos; } } ```

• Use depth-fail ( z-fail ) test to render shadow volume

Rendering a shadow volume for each cube surface with function render_cube_shadow(), starting with:

 ```//render a shadow for each surface of a cube void render_cube_shadow(struct cube *c) { int i; for(i = 0; i < 6; i++) { glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);//disable writing to color buffer glDepthMask(GL_FALSE); //disable writing to depth buffer (we only care stencil buffer here glEnable(GL_CULL_FACE); //will enable both front and back culling ( below ) glEnable(GL_STENCIL_TEST); //enable stencil testing glEnable(GL_POLYGON_OFFSET_FILL); //for working around for some depth buffer precision issues glPolygonOffset(0.0f, 100.0f); // some depth buffer precision issues ``` In order for the stencil operations to be carried out correctly, we'll render the shadow volume in two passes. In first pass, when the depth test fails (i.e., a pixel of the polygon is further away than the pixel that's already stored in its place on the screen), we increment the pixel's stencil value.

 ``` glCullFace(GL_FRONT); //cull front faces of shadow volume; only draws back faces glStencilFunc(GL_ALWAYS, 0x0, 0xff);//always passes, ref value = 0, mask = 0xFF //keep value when stencil test fails, increment value if depth test fails, keep value if both //stencil test and depth test pass glStencilOp(GL_KEEP, GL_INCR, GL_KEEP); render_surface_shadow_volume(c->surfaces[i], c->position, light_pos); ``` In the figure, the blue lines represent the camera's view frustum, so only pixels inside of that frustum have stencil values. The shaded portion is where the stencil value would be incremented. Because the stencil value is incremented when the depth-test fails, everything between the camera and the rendered back faces has its stencil value incremented.

As you can see, the entire "occluded sphere" is considered to be shadowed at this point, which is not correct. So we need the second pass:

 ``` glCullFace(GL_BACK); //cull back faces; only draw front faces of shadow volume glStencilFunc(GL_ALWAYS, 0x0, 0xff); glStencilOp(GL_KEEP, GL_DECR, GL_KEEP);//decrement stencil value when depth test fails render_surface_shadow_volume(c->surfaces[i], c->position, light_pos); ``` Finish the function with:

 ``` glDisable(GL_POLYGON_OFFSET_FILL); //disable polygon offset glDisable(GL_CULL_FACE); //disable front/back culling glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); //reanble writing to color buffer glDepthMask(GL_TRUE); //renable writing to depth buffer glStencilFunc(GL_NOTEQUAL, 0x0, 0xff); //reset each pixel's stencil value glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); // for anything we draw. draw_shadow(); //draw the shadow ( dark quad ) glDisable(GL_STENCIL_TEST); //disable stencil testing } } ```