\( \newcommand{\vecIII}[3]{\left[\begin{array}{c} #1\\#2\\#3 \end{array}\right]} \newcommand{\vecIV}[4]{\left[\begin{array}{c} #1\\#2\\#3\\#4 \end{array}\right]} \newcommand{\Choose}[2]{ { { #1 }\choose{ #2 } } } \newcommand{\vecII}[2]{\left[\begin{array}{c} #1\\#2 \end{array}\right]} \newcommand{\vecIII}[3]{\left[\begin{array}{c} #1\\#2\\#3 \end{array}\right]} \newcommand{\vecIV}[4]{\left[\begin{array}{c} #1\\#2\\#3\\#4 \end{array}\right]} \newcommand{\matIIxII}[4]{\left[ \begin{array}{cc} #1 & #2 \\ #3 & #4 \end{array}\right]} \newcommand{\matIIIxIII}[9]{\left[ \begin{array}{ccc} #1 & #2 & #3 \\ #4 & #5 & #6 \\ #7 & #8 & #9 \end{array}\right]} \)

Reading: More Material and Lighting

We now understand the basics of the Lambertian and Phong models, and the essential properties of

  • ambient
  • diffuse
  • specular

Let's turn to some additional material and lighting information.

Directional Lights

A directional light casts parallel light rays throughout the scene. You can think of it as a point source infinitely far away in a particular direction, and indeed, the mathematical model of it is like this.

We create a directional light in Three.js as follows:

var dirlight = new THREE.DirectionalLight( TW.WHITE, 0.3 );
dirlight.position.set( 0, 1, 0 ); // the direction the light comes from
scene.add(dirlight);

The arguments to the THREE.DirectionalLight() constructor are the color and intensity of the light. The direction of the light is also key, and it is determined by the light's position. But this is confusing, because a directional light has no position, or, its position is at infinity.

In the example above, (0, 1, 0), would be a light that is shining from directly above the scene, using the usual coordinate system where the +Y axis points up through the top of the monitor. That is, you can think of the position as a vector pointing towards the light. (This vector can then be directly used in the Lambertian and Phong models as the LV vector.) Another way to think of it is that the position of the light is at (0, 10000000, 0), which of course is straight overhead at infinity.

Flat versus Smooth Shading

To begin, look at the following picture: Flat versus Phong shading

The graphics card renders triangles (fragments) based on the properties of its vertices. In particular, if the surface normals at the three vertices are different, the lighting calculation will differ and the computed color at the three vertices will be different.

You can request that all the pixels of the triangle be given the same color as the first vertex, or the color can be interpolated across the triangle. Giving all the pixels the same color is called flat shading and interpolating the color is called smooth shading. That's because flat shading is appropriate for a polyhedron that is modeling a polyhedral (flat-sided) object, while smooth shading is appropriate for a polyhedron that is an approximation of a smooth surface.

Consider the following picture:

flat versus smooth shading
The magenta and teal objects have the same three vertices and the same shape, but the teal object is flat-sided, like a faceted jewel, while the magenta object is smooth-sided, like a ball. The surface normals (brown arrows) would be different for the jewel and the ball. More importantly, because the color between the pairs of vertices on the magenta object are different (different normals), the color will be interpolated between them. In the jewel, the normals are the same (the middle point has two vertices: one belonging to the upper face and one belonging to the lower face, each with a surface normal that is correct for that face).

The key code to achieve these different effects is shown in the two material examples below, which are identical except for their flatShading property:

var ballMaterial = new THREE.MeshPhongMaterial(
    {color: THREE.ColorKeywords.cornflowerblue,
     specular: 0xCCCCCC,
     flatShading: THREE.SmoothShading
    });

var jewelMaterial = new THREE.MeshPhongMaterial(
    {color: THREE.ColorKeywords.cornflowerblue,
     specular: 0xCCCCCC,
     flatShading: THREE.FlatShading
    });

Here is a demo using Three.js that contrasts smooth shading and flat shading: Jewel and Ball.

Note that flat versus smooth shading is a property of the material not the light.

Spotlights

A spotlight is just a point source limited by a cone of angle θ (think of those Luxo lamps made famous by Pixar, or a flashlight). Intensity is greatest in the direction that the spotlight is pointing and trails off towards the sides, dropping to zero when you reach the cutoff angle θ. Something like the following figure:

A spotlight has a cone and illuminates an area where it is facing. The light is brighter towards the center.

The following is one way to implement the effect of a spotlight. Let α be the angle between the spotlight direction and the direction that we're interested in. When α=0, the intensity is at its maximum (I=I0, where I0 is the intensity of the light source). The cosine function has this property, so we use it as a building block. To give us additional control of the speed at which the intensity drops off (therefore, how concentrated the spotlight is), we allow the user to choose an exponent, e, for the cosine function. The resulting function is:

\[ I = I_0 \cos^e(\alpha) \] if $\alpha\le\theta$ otherwise zero

The newest version of Three.js replaces the exponent with a penumbra percentage, but doesn't adequately define it. It has a similar (though not identical) effect, but ranges from 0 to 1 instead.

To do this in Three.js, we create a THREE.SpotLight:

var spotlight = new THREE.SpotLight( color,   // rgb values
                                     intensity,  // a multiplication factor
                                     distance,   // where the light ends
                                     cutoffAngle, // in radians < π/2
                                     penumbra,    // 0-1, default is 0
                                     decay       // how the light decays with distance
                                    );
spotlight.position.set( 1, 2, 3);
scene.add(dirlight);

The last parameter, decay, is also not well explained. Most examples ignore it and use the default value. We will do so as well.

Spotlight Direction

Aiming the spotlight is important. It could be done with a vector, but in Three.js, they use the position of a target. The target is a property of the spotlight, and is also an object in the scene. (It doesn't have to be a visible object; it could just be an empty location.) Here's the code to aim the spotlight at the origin:

var spotlight = new THREE.SpotLight( color,   // rgb values
                                     intensity,  // a multiplication factor
                                     distance,   // where the light ends
                                     cutoffAngle, // in radians < π/2
                                     penumbra,    // 0-1, default is 0
                                     decay        // a scalar
                                    );
spotlight.position.set( 1, 2, 3);
spotlight.target.position.set( 0, 0, 0);
scene.add(spotlight);
scene.add(spotlight.target);

Spotlight Demo

Here is a demo that shows how to control a spotlight. We'll explore it in class, but take a minute to play with it now.

Summary

  • There are three kinds of light: ambient, diffuse, and specular
  • Ambient is constant from all directions
  • Diffuse depends on the angle between the light and the surface
  • Specular depends on the angle between the reflected incoming light and the eye, and also depends on a scalar quantity called shininess
  • In Three.js, the color attribute of a material captures how it interacts with diffuse and ambient light, and the specular and shininess properties capture the specular reflection of the surface material
  • In Three.js, you can have:
    • Point lights
    • Ambient light
    • Directional lights
    • Spotlights
  • Materials can use either smooth or flat shading; this controls the interpolation of pixel colors across triangular facets
  • Spotlights have
    • a cutoff angle
    • a decrease in intensity as the direction to the pixel approaches the cutoff angle (exponent or penumbra)
    • a direction, defined by a target's position.