7: Shadows
- Download code
- 07-shadows.zip
- Live example
- examples/07-shadows/index.html
At the end of the last section, our scene looked like this:
We’re going to add support for shadows, so that shapes will cast shadows, and areas that are in shadow won’t be illuminated by the associated light source.
All this happens inside the Shape
class - in fact, we only need to modify the getColorAt
method.
Here’s shape.js
with the modified method in place:
// modules/shape.js
import { THRESHOLD } from './settings.js';
import { Vector } from './vector.js';
import { Color } from './color.js';
import { Ray } from './ray.js';
export class Shape {
constructor(appearance) {
this.appearance = appearance;
}
intersect = () => { throw ("Classes which extend Shape must implement intersect"); };
getNormalAt = () => { throw ("Classes which extend Shape must implement getNormalAt"); }
closestDistanceAlongRay = (ray) => {
let distances = this.intersect(ray).filter(d => d > THRESHOLD);
let shortestDistance = Math.min.apply(Math, distances);
return shortestDistance;
}
/** return true if the specified light casts a shadow of this shape at the specified point */
castsShadowFor = (point, vector) => {
let distanceToLight = vector.length;
let ray = new Ray(point, vector);
return (this.closestDistanceAlongRay(ray) <= distanceToLight);
}
getColorAt = (point, scene) => {
let normal = this.getNormalAt(point);
let color = Color.Black;
scene.lights.forEach(light => {
let v = Vector.from(point).to(light.position);
// If this point is in shadow, do not add any illumination for this light source
if (scene.shapes.some(shape => shape.castsShadowFor(point, v))) return;
let brightness = normal.dot(v.unit());
if (brightness <= 0) return;
let illumination = light.illuminate(this.appearance, point, brightness);
color = color.add(illumination);
});
return color;
}
}
What we do here is, for each point on the shape’s surface, we trace a ray from that point to the light source, and check whether that ray intersects any other shapes along the way. If it does, then we’re in that shape’s shadow, and so we should skip that light source when calculating how much light falls on this shape.
Compare the result below with the original image above – see how the shadows add depth and definition to the scene?
- Download code
- 07-shadows.zip
- Live example
- examples/07-shadows/index.html