Ollin

ImageBasedLighting

environment(_:) lights the scene from an HDRI, so physically-based metals fill in with real reflections instead of reading near-black. The same environment also serves as the skybox backdrop.

Run
swift run --package-path Examples Example-3D-Environments-ImageBasedLighting
Source
Examples/3D/Environments/ImageBasedLighting/Sketch.swift
import Ollin

/// Image-based lighting: metals that reflect their surroundings.
///
/// `environment(_:)` lights the scene with an HDRI, so physically-based materials gather
/// their ambient and reflections from it. This is what makes a metal read as metal: the
/// near-black smooth metal from the direct-light-only chart fills in with a reflection of
/// the world around it.
///
/// The top row is metal sweeping roughness left to right (a mirror smearing into a soft
/// satin); the bottom row is a colored dielectric doing the same. The environment alone
/// lights them, no other lights set, and the camera orbits so the reflections move.
@main
final class ImageBasedLighting: Sketch {

    let columns = 6
    let spacing = 2.2

    override func draw() {
        background(Color(hex: 0x0B0C12))
        toneMap(.aces)   // a filmic highlight rolloff for the HDR environment

        cameraShowcase(.sway(amplitude: 0.5, period: .tau / 0.12), target: .zero, radius: 16,
                    elevation: 0.16, fieldOfView: .pi / 4.2)

        // The environment is the only light. Spin it slowly so the key light and the
        // reflections drift across the spheres.
        environment(.sunset.rotated(time * 0.15))

        for col in 0..<columns {
            let roughness = map(Double(col), 0, Double(columns - 1), 0.02, 1.0)
            // Top: metal (its reflection tinted by fill). Bottom: blue dielectric.
            withState {
                translate(xFor(col), 1.3, 0)
                fill(Color(hue: 0.09, saturation: 0.35, brightness: 0.95))
                material(.metal(roughness: roughness))
                drawSphere(radius: 0.85)
            }
            withState {
                translate(xFor(col), -1.3, 0)
                fill(Color(hue: 0.58, saturation: 0.65, brightness: 0.9))
                material(.dielectric(roughness: roughness))
                drawSphere(radius: 0.85)
            }
        }
    }

    private func xFor(_ col: Int) -> Double {
        (Double(col) - Double(columns - 1) / 2) * spacing
    }
}

↑↓ move ↩ open