Shadows
Directional cast shadows. Solids drop shadows onto a floor and onto one another through castShadows().
- Run
swift run --package-path Examples Example-3D-Lighting-Shadows- Source
- Examples/3D/Lighting/Shadows/Sketch.swift
import Ollin
/// Shadows — solids grounded by cast shadows from a directional key light.
///
/// `castShadows()` turns on shadow mapping for the frame: the renderer renders a
/// depth pass from the scene's primary directional light, then dims that light on
/// every surface it can't reach. It's opt-in and needs a camera plus a directional
/// light — here a warm key from the upper-left that swings a little, so the shadows
/// sweep. The solids drop shadows onto the floor and across one another, and the
/// camera orbits so you see them from every side.
///
/// This scene casts from a directional light (an orthographic shadow map, auto-fit to
/// the scene around the camera target). A spot light can cast too; see the
/// `3D/SpotShadow` example. Point (omnidirectional) casters are a later step.
@main
final class Shadows3D: Sketch {
override func draw() {
background(Color(hex: 0x12141A))
// Orbit the scene. The shadow frustum auto-fits to the eye→target distance,
// so a radius that frames the scene also sizes the shadow map's coverage.
cameraShowcase(.turntable(period: .tau / 0.2), target: Vector3(0, 1.2, 0), radius: 13,
elevation: 0.5, fieldOfView: .pi / 4.2)
// A warm key from the upper-left (the caster, since it's the first
// directional), gently swinging so the shadows move; a soft cool fill and a
// little ambient keep the shaded sides from going black.
ambientLight(Color(white: 0.16))
directionalLight(Color(hue: 0.09, saturation: 0.25, brightness: 1.0),
direction: Vector3(-0.55 + sin(time * 0.25) * 0.3, -0.85, -0.4),
intensity: 1.0)
// A dim fill for the shaded sides. It throws nothing: one key shadow reads
// cleaner here, and every caster costs its own pass over the scene.
directionalLight(Color(white: 0.5), direction: Vector3(0.5, 0.4, 0.5), intensity: 0.3,
castsShadow: false)
castShadows()
// The ground that catches the shadows (nearly matte, so they read clearly).
withState {
fill(Color(white: 0.82))
specular(0.05)
drawPlane(width: 22, depth: 22)
}
// A few solids above the floor — two resting on it, one floating and bobbing
// — all turning, so the cast shadows shift and overlap.
specular(0.3)
specularSharpness(40)
withState {
translate(-3.2, 1.4, -1.0)
rotateY(time * 0.6)
rotateX(time * 0.3)
fill(Color(hue: 0.02, saturation: 0.65, brightness: 0.95))
drawBox(width: 2.4, height: 2.4, depth: 2.4)
}
withState {
translate(2.7, 1.7, 1.2)
rotateY(-time * 0.5)
fill(Color(hue: 0.55, saturation: 0.55, brightness: 0.95))
drawSphere(radius: 1.7)
}
withState {
translate(0.2, 3.4 + sin(time) * 0.6, -2.6) // floating, bobbing on time
rotateX(time * 0.7)
rotateZ(time * 0.4)
fill(Color(hue: 0.13, saturation: 0.7, brightness: 1.0))
drawTorus(radius: 1.3, tube: 0.45)
}
withState {
translate(3.1, 1.1, -3.0)
rotateY(time * 0.9)
fill(Color(hue: 0.75, saturation: 0.5, brightness: 0.95))
drawCone(radius: 1.1, height: 2.2)
}
drawCaption("Cast shadows — castShadows() from a directional key light")
}
}