Ollin

Glass

Physically-based transmission and refraction.

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

/// Glass: physically-based transmission and refraction.
///
/// `Material.glass()` lets light *through* a physically-based surface instead of
/// bouncing it all back: the diffuse body is replaced by whatever shows through,
/// tinted by the `fill`, bent by `ior`, and frosted by `roughness`. A `thickness`
/// makes the body solid, so a sphere magnifies and flips what's behind it the way a
/// lens does, and an `attenuationColor` deepens the tint the farther light travels
/// inside (thick bottle glass going green at the edges):
///
/// ```swift
/// environment(.studio)                     // something to transmit
/// material(.glass(thickness: 1.8))         // a solid clear body
/// drawSphere(radius: 0.9)
/// ```
///
/// Transmission needs an environment set; on its own it refracts *that*. With
/// `rayTracedReflections()` on a ray-tracing GPU, the view through the glass upgrades
/// to the actual scene: the striped wall behind this row of spheres appears inside
/// them, inverted by the solid ones. **Hold the space bar** to drop the ray-traced
/// upgrade and compare (the glass falls back to refracting the environment alone, and
/// the wall behind vanishes from the glass).
@main
final class Glass: Sketch {

    override func draw() {
        background(Color(hex: 0x14171d))
        cameraShowcase(.sway(amplitude: 0.22, period: .tau / 0.09),
                       target: Vector3(0, 1.35, 0), radius: 11, elevation: 0.18,
                       fieldOfView: .pi / 4, near: 1, far: 40)
        environment(.studio.intensified(to: 1.1).backgroundBlurred(0.5))
        directionalLight(.white, direction: Vector3(-0.4, -1, -0.25), intensity: 0.7)

        // Trace the view through the glass against the actual scene (hold space to compare).
        if !isKeyDown(" ") { rayTracedReflections() }

        // A matte floor and a wall of colored stripes behind the row: the content the
        // glass has to transmit, refract, and frost.
        withState {
            material(.dielectric(roughness: 0.8))
            fill(Color(hex: 0x3a3f4c))
            translate(0, -0.55, 0)
            drawBox(width: 30, height: 1.0, depth: 18)
        }
        let stripes = [Color(hex: 0xe6533c), Color(hex: 0xf2b134), Color(hex: 0x4fb477),
                       Color(hex: 0x3f7fd6), Color(hex: 0xb35fd1)]
        for (i, c) in stripes.enumerated() {
            withState {
                material(.dielectric(roughness: 0.6))
                fill(c)
                translate((Double(i) - 2) * 1.5, 1.7, -3.2)
                drawBox(width: 1.1, height: 4.4, depth: 0.5)
            }
        }

        // The glass row, left to right: a solid clear sphere (a lens: the stripes appear
        // inverted inside it), a solid absorbing sphere (bottle green deepening with
        // depth), a frosted solid, and a thin-walled bubble (tints without bending).
        let r = 0.95
        let bodies: [(fill: Color, mat: Material)] = [
            (.white, .glass(thickness: r * 2)),
            (.white, .glass(thickness: r * 2,
                            attenuationColor: Color(hex: 0x2e8f5b),
                            attenuationDistance: 1.4)),
            (.white, .glass(roughness: 0.45, thickness: r * 2)),
            (Color(hex: 0xcfe4ff), .glass()),
        ]
        for (i, b) in bodies.enumerated() {
            withState {
                material(b.mat)
                fill(b.fill)
                translate((Double(i) - 1.5) * 2.1, 0.95, 0.6)
                drawSphere(radius: r)
            }
        }

        drawCaption(isKeyDown(" ")
            ? "Refracting the environment only (release space for the traced scene)"
            : "Refracting the traced scene (hold space to compare)")
    }
}

↑↓ move ↩ open