﻿ Section 12.4.  The ÜberLight Shader Free JavaScript Editor     Ajax Editor ﻿

Main Page

### 12.4. The ÜberLight Shader

So far in this chapter we've discussed lighting algorithms that simulate the effect of global illumination for more realistic lighting effects. Traditional point, directional, and spotlights can be used in conjunction with these global illumination effects. However, the traditional light sources leave a lot to be desired in terms of their flexibility and ease of use.

#### 12.4.1. ÜberLight Controls

In movies, lighting helps to tell the story in several different ways. Sharon Calahan gives a good overview of this process in the book Advanced RenderMan: Creating CGI for Motion Pictures. This description includes five important fundamentals of good lighting design that were derived from the book Matters of Light & Depth by Ross Lowell:

• Directing the viewer's eye

• Creating depth

• Conveying time of day and season

• Enhancing mood, atmosphere, and drama

• Revealing character personality and situation

Because of the importance of lighting to the final product, movies have dedicated lighting designers. To light computer graphics scenes, lighting designers must have an intuitive and versatile lighting model to use.

For the best results in lighting a scene, it is crucial to make proper decisions about the shape and placement of the lights. For the überlight lighting model, lights are assigned a position in world coordinates. The überlight shader uses a pair of superellipses to determine the shape of the light. A superellipse is a function that varies its shape from an ellipse to a rectangle, based on the value of a roundness parameter. By varying the roundness parameter, we can shape the beam of illumination in a variety of ways (see Figure 12.3 for some examples). The superellipse function is defined as ##### Figure 12.3. A variety of light beam shapes produced with the überlight shader. We enabled barn shaping and varied the roundness and edge width parameters of the superellipse shaping function. The top row uses edge widths of 0 and the bottom row uses 0.3. From left to right, the roundness parameter is set to 0.2, 0.5, 1.0, 2.0, and 4.0. As the value for d nears 0, this function becomes the equation for a rectangle, and when d is equal to 1, the function becomes the equation for an ellipse. Values in between create shapes in between a rectangle and an ellipse, and these shapes are also useful for lighting. This is referred to in the shader as barn shaping since devices used in the theater for shaping light beams are referred to as barn doors.

It is also desirable to have a soft edge to the light, in other words, a gradual drop-off from full intensity to zero intensity. We accomplish this by defining a pair of nested superellipses. Inside the innermost superellipse, the light has full intensity. Outside the outermost superellipse, the light has zero intensity. In between, we can apply a gradual transition by using the smoothstep function. See Figure 12.3 for examples of lights with and without such soft edges.

Two more controls that add to the versatility of this lighting model are the near and far distance parameters, also known as the cuton and cutoff values. These define the region of the beam that actually provides illumination (see Figure 12.4). Again, smooth transition zones are desired so that the lighting designer can control the transition. Of course, this particular control has no real-world analogy, but it has proved to be useful for softening the lighting in a scene and preventing the light from reaching areas where no light is desired. See Figure 12.5 for an example of the effect of modifying these parameters.

##### Figure 12.4. Effects of the near and far distance parameters for the überlight shader ##### Figure 12.5. Dramatic lighting effects achieved by alteration of the depth cutoff parameters of the überlight shader. In the first frame, the light barely reaches the elephant model. By simply adjusting the far depth edge value, we can gradually bathe our model in light. #### 12.4.2. Vertex Shader

Listing 12.6 shows the code for the vertex shader for the überlight model. The main purpose of the vertex shader is to transform vertex positions, surface normals, and the viewing (camera) position into the lighting coordinate system. In this coordinate system the light is at the origin and the z axis is pointed toward the origin of the world coordinate system. This allows us to more easily perform the lighting computations in the fragment shader. The computed values are passed to the fragment shader in the form of the varying variables LCpos, LCnorm, and LCcamera.

To perform these calculations, the application must provide ViewPosition, the position of the camera in world space, and WCLightPos, the position of the light source in world coordinates.

To do the necessary transformations, we need matrices that transform points from modeling coordinates to world coordinates (MCtoWC) and from world coordinates to the light coordinate system (WCtoLC). The corresponding matrices for transforming normals between the same coordinate systems are the inverse transpose matrices (MCtoWCit and WCtoLCit).

##### Listing 12.6. Überlight vertex shader

 ```uniform vec3 WCLightPos; // Position of light in world coordinates uniform vec4 ViewPosition; // Position of camera in world space uniform mat4 WCtoLC; // World to light coordinate transform uniform mat4 WCtoLCit; // World to light inverse transpose uniform mat4 MCtoWC; // Model to world coordinate transform uniform mat4 MCtoWCit; // Model to world inverse transpose varying vec3 LCpos; // Vertex position in light coordinates varying vec3 LCnorm; // Normal in light coordinates varying vec3 LCcamera; // Camera position in light coordinates void main() { gl_Position = ftransform(); // Compute world space position and normal vec4 wcPos = MCtoWC * gl_Vertex; vec3 wcNorm = (MCtoWCit * vec4(gl_Normal, 0.0)).xyz; // Compute light coordinate system camera position, // vertex position and normal LCcamera = (WCtoLC * ViewPosition).xyz; LCpos = (WCtoLC * wcPos).xyz; LCnorm = (WCtoLCit * vec4(wcNorm, 0.0)).xyz; }```

#### 12.4.3. Fragment Shader

With the key values transformed into the lighting coordinate system for the specified light source, the fragment shader (Listing 12.7) can perform the necessary lighting computations. One subroutine in this shader (superEllipseShape) computes the attenuation factor of the light across the cross section of the beam. This value is 1.0 for fragments within the inner superellipse, 0 for fragments outside the outer superellipse, and a value between 0 and 1.0 for fragments between the two superellipses. Another subroutine (distanceShape) computes a similar attenuation factor along the direction of the light beam. These two values are multiplied together to give us the illumination factor for the fragment.

The computation of the light reflection is done in a manner similar to shaders we've examined in previous chapters. Because the computed normals may become denormalized by linear interpolation, we must renormalize them in the fragment shader to obtain more accurate results. After the attenuation factors are computed, we perform a simple reflection computation that gives a plastic appearance. You could certainly modify these computations to simulate the reflection from some other type of material.

##### Listing 12.7. Überlight fragment shader

 ```uniform vec3 SurfaceColor; // Light parameters uniform vec3 LightColor; uniform vec3 LightWeights; // Surface parameters uniform vec3 SurfaceWeights; uniform float SurfaceRoughness; uniform bool AmbientClamping; // Super ellipse shaping parameters uniform bool BarnShaping; uniform float SeWidth; uniform float SeHeight; uniform float SeWidthEdge; uniform float SeHeightEdge; uniform float SeRoundness; // Distance shaping parameters uniform float DsNear; uniform float DsFar; uniform float DsNearEdge; uniform float DsFarEdge; varying vec3 LCpos; // Vertex position in light coordinates varying vec3 LCnorm; // Normal in light coordinates varying vec3 LCcamera; // Camera position in light coordinates float superEllipseShape(vec3 pos) { if (!BarnShaping) return 1.0; else { // Project the point onto the z = 1.0 plane vec2 ppos = pos.xy / pos.z; vec2 abspos = abs(ppos); float w = SeWidth; float W = SeWidth + SeWidthEdge; float h = SeHeight; float H = SeHeight + SeHeightEdge; float exp1 = 2.0 / SeRoundness; float exp2 = -SeRoundness / 2.0; float inner = w * h * pow(pow(h * abspos.x, exp1) + pow(w * abspos.y, exp1), exp2); float outer = W * H * pow(pow(H * abspos.x, exp1) + pow(W * abspos.y, exp1), exp2); return 1.0 - smoothstep(inner, outer, 1.0); } } float distanceShape(vec3 pos) { float depth; depth = abs(pos.z); float dist = smoothstep(DsNear - DsNearEdge, DsNear, depth) * (1.0 - smoothstep(DsFar, DsFar + DsFarEdge, depth)); return dist; } void main() { vec3 tmpLightColor = LightColor; vec3 N = normalize(LCnorm); vec3 L = -normalize(LCpos); vec3 V = normalize(LCcamera-LCpos); vec3 H = normalize(L + V); vec3 tmpColor = tmpLightColor; float attenuation = 1.0; attenuation *= superEllipseShape(LCpos); attenuation *= distanceShape(LCpos); float ndotl = dot(N, L); float ndoth = dot(N, H); vec3 litResult; litResult = AmbientClamping ? max(ndotl, 0.0) : 1.0; litResult = max(ndotl, 0.0); litResult = litResult * max(ndoth, 0.0) * SurfaceRoughness; litResult *= SurfaceWeights * LightWeights; vec3 ambient = tmpLightColor * SurfaceColor * litResult; vec3 diffuse = tmpColor * SurfaceColor * litResult; vec3 specular = tmpColor * litResult; gl_FragColor = vec4(attenuation * (ambient + diffuse + specular), 1.0); }```

An example of using this shader is shown in Color Plate 20, along with a screen shot of a user interface designed by Philip Rideout for manipulating its controls. The überlight shader as described by Barzel and Gritz actually has several additional features. It can support multiple lights, but our example shader showed just one for simplicity. The key parameters can be defined as arrays, and a loop can be executed to perform the necessary computations for each light source. In the following chapter, we show how to add shadows to this shader.

﻿ Ajax Editor     JavaScript Editor