BrushCue Example: Desert Mirage Filter Animation¶

Open In Colab

You can use this tool online at https://www.brushcue.com/tools/desert-mirage-filter

In [ ]:
!pip install brushcue
In [1]:
import brushcue as bc

duration = 6.0


@bc.brushcue_fn
def frame(time):
    input_image = bc.Composition.monet_women_with_parasol()
    intensity = 0.25
    input_bounds = input_image.bounds()
    input_width = input_bounds.width()
    return (
        input_image.chroma_offset(
            bc.Vector2f.from_components((0.08 * intensity), (0.04 * intensity))
        )
        .brightness_adjust((0.15 * intensity))
        .saturation_adjust((1.0 + (0.6 * intensity)))
        .spacial_effect_shader(
            "let uv = (position - bounds_origin) / bounds_size;\n    // Normalize time over the 3-second duration (0 -> 1) then to radians.\n    // Integer multiples of 2 PI ensure every wave returns to zero at t=3.0.\n    let t = (time % 3.0) / 3.0;\n    let t1 = t * 6.283185 * 2.0;\n    let t2 = t * 6.283185 * 3.0;\n    let t3 = t * 6.283185 * 1.0;\n\n    // Mirage heat distortion: layered sine waves rising from the bottom.\n    let heat_influence = pow(1.0 - uv.y, 2.5);\n    let wave1 = sin(uv.x * 18.0 + uv.y * 6.0 + t1) * 0.007;\n    let wave2 = sin(uv.x * 31.0 - uv.y * 9.0 + 1.3 + t2) * 0.004;\n    let wave3 = sin(uv.x * 9.0 + uv.y * 22.0 + 2.7 + t3) * 0.005;\n    let distort = (wave1 + wave2 + wave3) * heat_influence * intensity;\n\n    // Chromatic aberration for a trippy fringe.\n    let offset = distort * 1.8;\n    let uv_r = clamp(uv + vec2f(distort + offset * 0.5, distort * 0.6), vec2f(0.0), vec2f(1.0));\n    let uv_g = clamp(uv + vec2f(distort, distort * 0.8), vec2f(0.0), vec2f(1.0));\n    let uv_b = clamp(uv + vec2f(distort - offset * 0.4, distort), vec2f(0.0), vec2f(1.0));\n    let r = sample(bounds_origin + uv_r * bounds_size).r;\n    let g = sample(bounds_origin + uv_g * bounds_size).g;\n    let b = sample(bounds_origin + uv_b * bounds_size).b;\n    let a = sample(bounds_origin + uv_g * bounds_size).a;\n    return vec4f(r, g, b, a);",
            "",
            (input_width * 0.031),
            bc.Dictionary.create()
            .add("intensity", intensity)
            .add("time", time)
            .add(
                "bounds_origin",
                bc.Vector2f.from_components(input_bounds.min_x(), input_bounds.min_y()),
            )
            .add(
                "bounds_size",
                bc.Vector2f.from_components(input_width, input_bounds.height()),
            ),
            bc.ColorRepresentation.srgb(),
        )
        .crop(input_bounds)
    )


graph = bc.Sequence.graph(duration, frame)

ctx = bc.Context()
sequence = graph.execute(ctx)
# Render `sequence` to a video file as needed.
[wgpu] using backend Metal — adapter 'Apple M5' (IntegratedGpu), driver ''