CS 294-13 Lab 2: Real-time Rendering with Environment and Shadow Mapping

Introduction

Recent developments in GPU technology have allowed rendering effects that were previously only tractable for offline rendering to be used for real time effects via programmable shading. This project demostrates two of the most popular effects: environment mapping and dynamic shadows via shadow mapping.

The demo was written in C++, OpenGL, and SDL. You can download the source code (for both the demo and the offline prefiltering) or a linux executable. All of the models and artwork are included in with both distributions, but you can get a source-only distribution here as well.

Commands

Left click drag
Move the camera
Mouse Scroll
Move the camera toward/away from the scene
'e'
Change the environment map
'r'
Toggle between scene view, environment irradiance map, and depth-map light views
'h'
Change the percentage total light contribution of the environment map. Cycles between 100%, 50%, 25%, and 0%.
'g'
Toggle the glossiness of the torus
'w'
Toggle between the sphere and Buddha meshes
'm'
Swap the mesh and ground materials
's'
Toggle between normal, PCF, and area-light shadow mapping
'p'
[Un]Pause the lights and the torus rotation

Implementation

Scene Details

The scene consists of three objects and the environment map sphere background. All three objects use the same fragment shader, rtr.frag, which contains the specular, glossy specular, and diffuse environment map lighting computation as well as shadow mapping. The shadows come from two lights rotating around the scene at different speeds.

The OpenGL model-view matrix was used only for the camera space transformation. A separate uniform shader variable was set for the object transformation on a per-vertex basis. This way, the fragment shader is completely independent of local object transformations.

Each object in the scene has its own material, consisting of the portion of light reflected through diffuse, specular, and glossy specular reflections.

Vertex Shader Effect

Part of the lab was to demonstrate a simple effect that is difficult or impossible to do without vertex or fragment shaders. In my demo, the torus is created from a 2D grid of xy points. The torus is animated by its object transformation as well as the inner and outer radii. The normals are generated on-the-fly in the vertex shader, using the two tangent vector Du(p) and Dv(p). Furthermore, the diffusely-reflected color of the torus is at a point is determined by the camera-space normal at each point.

The torus rotates on the y-axis. The inner radius change fluctuates periodically as well. The torus is completely generated from a normal grid in realtime via vertex shaders.

Environment Mapping

I used the light probe environment maps from Paul Debevec's website on image-based lighting. Per-pixel specular lighting was done in the typical fashion, by computing the texture coordinate based on the reflected vector in world space. The reflection vector was computed in camera space and transformed back into world space to reference the global environment map. Phong specular lighting was done in a similar fashion, using a heavily-blurred version of the original environment map. To implement the diffuse lighting, I used the spherical harmonics coefficients, as specified in [Ramamoorthi, 01]. The included program takes a light probe environment in .float format and outputs the quadratic-form irradiance matrix for each component. These matrices are stored in .irr files, which are then read by the demo program for each of the environment maps.
(Left) The sphere is lit largely by the environment. (Right) The irradiance map generated using the spherical harmonic coefficients.

Shadow Mapping

Note: In this section, all images are rendered without environment lighting to highlight the shadow mapping results.

I implemented percentage closer filtering using a brute-force 16-pixel method, as in [Bunnell]. Using a broader sampling neighborhood, area lights can be simulated as well.

The shadow maps are only 512x512 pixels, to exaggerate the artifacts. The shadow map is checked using one sample (left) and PCF via 16 samples (right).
The shadow maps are 2048x2048 pixels. (Left) PCF, with a normal-size neighborhood. (Right) Faux-area lights using PCF with a wide neighborhood. Wider neighborhoods are used to fake larger lights. Because of the large neighborhood, we have a light leak at the base of the torus.

With shadow mapping, the scene is rendered from the light's point of view. The z-buffer is then used as a projective onto the scene. At each point, the distance from the point to the light is compared to the stored depth, and points with further distances are not illuminated by the light. Though my implementation on uses point lights, one can use the technique with directional lights as well (by rendering with an orthogonal projection).

(Top) A shadow-mapped scene, with two lights. (Bottom) The depth map rendered from the point of view of each light.

To prevent z-fighting in light space, when rendering from the lights we only render the back faces. This way, the closest front-facing lights are always lit, and we can use a "greater than or equal to test" without noticeable artifacts.

Results

In addition to these, you can download a video demo of the program. (There are a couple of... imperfections, but nothing too distracting. The demo is different from the YouTube video below, but it demonstrates the same features.

In this scene, we see that the Buddha casts shadows on both itself and neighboring torus. The environment map is dimmed to 25% so the effect is more noticeable.
(Left) The scene contains a diffuse sphere, a mirror plane, and a glossy reflective torus. (Middle) The same scene, with the sphere and plane materials switched. (Right) The torus uses a specular material instead of a glossy one.

References

  • Bunnell, Michael and Pellacini, Fabio. 2004. "Shadow Map Antialiasing." GPU Gems Addison Wesley
  • Ramamoorthi, Ravi and Hanrahan, Pat. "An Efficient Representation for Irradiance Environment Maps." SIGGRAPH 2001