CS 248 Marc Levoy Introduction to Computer Graphics Spring, 1995 Stanford University Shading computations for project #4 1. Local versus infinite light sources (a reiteration of the May 30 lecture) Composer offers two kinds of light sources: directional and point. A point light, sometimes called a local light, means that the light lies within (or nearby) the scene. A desk lamp is a local light. The location of a local light is specified by Composer as a 3D point S in world space. By contrast, a directional light, sometimes called an infinite light, means that the light is infinitely far away. The sun is an infinite light. The location of an infinite light is specified by Composer as a vector pointing from the light towards the scene. The advantage of local lights is that they give sharp highlights on flat surfaces, while infinite lights do not. The advantage of infinite lights is that they simplify shading calculations. In order to implement a local light, the lighting vector L used in the Phong illumination equation (16.20 in the textbook) must be recomputed at each polygon vertex from the light location S and the vertex location P. By contrast, in order to implement an infinite light, the L used in the Phong equation is simply set equal to the negative of the vector provided by Composer, and no per-vertex calculation of a lighting vector is necessary. 2. Local versus infinite viewers By analogy to local and infinite lights, there are also local and infinite viewers. A local viewer means that the viewer for the purposes of shading lies within (or nearby) the scene. The location of a local viewer is specified by a 3D point E in world space. The obvious place for a local viewer is at the same place as the viewer used in constructing the perspective matrix. In the formulation used in the textbook (equation 6.48), this is at the origin. By contrast, an infinite viewer means that the viewer for shading purposes is considered to be infinitely far away. In this case, it is specified by a direction V rather than by a location. The obvious direction for an infinite viewer in our perspective formulation is [0 0 1 0] transpose in eye space, i.e. pointing back from the scene towards the origin but parallel to the Z-axis. While Composer does not support selection of local versus infinite viewer (it implements only an infinite viewer), the underlying Graphics Library (GL) does. It is important not to confuse the viewer used in shading with the viewer used in constructing the perspective matrix. Given the definition above, it is possible to have an infinite viewer for the purposes of shading and a local viewer in the perspective matrix. Does this situation make physical sense? Of course not, but in the realm of graphics hacks and of practical workstations, there are reasons for it. The advantage of a local viewer for shading purposes is that it yields sharp highlights on flat surfaces. The advantage of an infinite viewer is that it simplifies shading calculations. As in the case of local lighting, in order to implement a local viewer, the viewer vector V used in the Phong illumination equation must be recomputed for each polygon vertex from the viewer location E and the vertex location P. By contrast, in order to implement an infinite viewer, its specification V is the same V that is used in the Phong equation; no per-vertex calculation of a viewer vector is necessary. To summarize, a local light gives better shading than an infinite light. The same applies for local viewers. In particular, flat surfaces (i.e. those that are not part of a mesh and/or do not have interpolated normals) exhibit a specular highlight if a local light or viewer are used, but not if an infinite light and viewer are used. A curved surface, including curved surfaces approximated by triangle meshes, will exhibit a specular highlight even if an infinite light source and viewer are used; they simply won't look as sharp. 3. Interpolated shading for a local light and local viewer To help you get started on project #4, I summarize in these next two sections the shading calculations you need to implement. In the interests of completeness and in order to serve as a reference for other renderers you may write in your long and illustrious careers, I first give the calculations needed to implement a local light source and viewer. I then describe how to simplify the calculations in order to implement an infinite light source and infinite viewer. Definitions and symbols: Rx(thetax) = X-rotation matrix Ry(thetay) = Y-rotation matrix Rz(thetaz) = Z-rotation matrix T(dx,dy,dz) = translation matrix persp = transpose of SGI persp matrix from Haeberli & Akeley M1 o M2 = composition of any matrices M1 and M2 (implemented by premultiplication, i.e. M2 M1) M^ = the matrix M-hat M(-1) = inverse of M for any matrix M Pv,Pv' = a vertex in world, screen space P = a sample point in world space Nv = a vertex normal in world space Np,N = unnormalized,normalized pixel normal in world space S = world space location of the local light source E = world space coordinates of the viewer eyepoint X.Y = dot product of any two vectors X and Y (X.Y)^n = dot product raised to the power n |X| = the L-2 norm (i.e. length) of a vector X [dx dy dz] = a 3-vector of increments in a scan-converter world space = the coordinate system given to you by Composer eye space = world space rotated and translated for viewing, but w/o any perspective mapping screen space = world space rotated, translated, and perspective mapped, a.k.a. clipping space In the pseudocode that follows, assume that all coordinates are 4-vectors and that all matrices are 4x4. In the section entitled "Reducing the dimensionality", I suggest how to improve on this. For each view of the scene: Compute the compound viewing transformation matrix with perspective: M <-- Rx(thetax) o Ry(thetay) o Rz(thetaz) o T(Tx,Ty,Tz-d1) o persp o S(1,1,-1/2) o T(0,0,-1/2) Compute another compound viewing matrix but without the perspective: M^ <-- Rx(thetax) o Ry(thetay) o Rz(thetaz) o T(Tx,Ty,Tz-d1) Compute the world space location of the local viewer: E <-- M^(-1) ([0 0 0 1] transpose) where [0 0 0 1] transpose is the eye space location of the viewer, at the origin. For each triangle in the scene: For each vertex in the triangle: Transform its coordinates from world space to screen space: Pv' <-- M Pv Clip to viewing frustum. If outside frustum, discard triangle and loop Divide by W as in assignment #3, then perform back-face culling. For each sample position in the triangle (using your scan converter): Interpolate Np from the Nv 's at the vertices (incrementally, as you now do for z') and normalize it: Np <-- Np + [dNx, dNy, dNz] transpose N <-- Np / |Np| Interpolate P similarly from the world space Pv 's at the vertices: P <-- P + [dPx, dPy, dPz] transpose Compute normalized vectors pointing toward the light and viewer: L <-- (S-P) / |S-P| V <-- (E-P) / |E-P| Compute the reflection vector (see p. 730 in the text): R = 2N(N.L)-L Compute shading (eqn. 16.20, page 734 in the text). I = Ia ka Od + fatt Ip [ kd Od (N.L) + ksOs (R.V)^n ] Note on reducing the dimensionality of the matrices and vectors: If you study where zeros fall in matrices M^, you will realize that M^(-1) can be reduced (after inversion) to a 3x4. In addition, vectors Nv, Np, N, L, V, and R can all be 3-vectors. Further optimizations: If you study the code above, you will realize that some steps (e.g. the increments) should be performed at every sample position in a triangle regardless of visibility, but that some steps (e.g. the normalizations and shading calculations) should only be performed at sample positions that pass the Z-buffer test, i.e. that are found to be in front of all other triangles encountered so far at this sample position. Construct your code carefully with respect to this issue. 4. Implementing an infinite light and infinite viewer So that's the story for a local light source and local viewer. To implement an infinite light and infinite viewer, a user-specified S and E are not used. Instead, L and V are treated as the same at all pixels. In your case, Composer provides you with -L (in world space) for each directional light. I strongly suggest that you set the eye space direction of the infinite viewer to [0 0 1 0] transpose. You will have to compute the world space direction of the infinite view yourself as M^(-1) [0 0 1 0] transpose. If you study the placement of zeros in this matrix multiplication, you will realize that you only need the upper-left 3x3 submatrix of M^, in which case M^(-1) is simply the inverse of the rotations. If you examine the above code, you will also realize that for this case you won't need to interpolate P and you won't need to recompute L or V at each pixel. Study the code and work out the details for yourself. To gain even more speed, you can replace (R.V)^n (and the calculation of R.V) with (N.H)^n where H = (L+V)/|L+V| assuming normalized L and V. H is the direction of maximum highlight, also called the halfway vector. This alternative method of computing highlights is described in your textbook on page 731. Note that this method is only advantageous if L and V are not recomputed per vertex, which is precisely the case with an infinite light and infinite viewer. P.S. User feedback for a local light If you implement a local light source, and if the light source is placed within the viewing frustum, it would be nice (and would help you debug your code) if you displayed a small icon at the light source location, i.e. at the point S, just as Composer does using a white sphere.