3D Design Mar – Apr 2026

Passenger

A cinematic short — a "passenger" walks through a canyon, finds an abandoned train swallowed by a decade of growth, and picks up something that briefly opens the world backward. Made in Maya and Unreal Engine 5 with custom post-process shaders, Lumen global illumination, and real-time ray tracing.

Role

Solo — Environment,
Lighting, Animation, Render

Timeline

Mar – Apr 2026
Personal Project

Tools

Unreal Engine 5 Maya HLSL / GLSL Megascans Movie Render Pipeline

Output

Cinematic film
2560 × 1080 rendered

A Passenger moves. The world does not wait.

Passenger crosses a canyon — sandstone walls scorched by a sun that has done this ten million times before, indifferent to whoever passes beneath it. At the canyon's edge, he finds a train. It stopped running long enough ago that birch trees have come up through the floor, vines have claimed the grab rails, and wildflowers have covered what was once an aisle. Inside, an apple — still there, still quiet. He picks it up, and the world opens somewhere else.

When it closes again, the canyon is the same. The train is not there.

Built entirely in Unreal Engine 5 with character rigging and animation from Maya. The two environments — a sun-scorched Utah canyon and an overgrown subway car — share a single lighting philosophy: how golden hour wraps around sandstone, and how diffused sky light filters through a canopy of leaves growing inside a metal tube.

Building a Utah canyon in Unreal Engine 5

The canyon environment uses Megascans photogrammetry assets — high-resolution rock formations, cliff faces, and desert ground scatter — assembled into a believable canyon valley. The goal was for the geometry to feel like it had always been there: no obvious tiling, no floating rocks, a sense of geological weight.

Lumen global illumination handles the bounce light that wraps into the canyon walls from the sun angle. As the virtual sun nears the horizon during golden hour, Lumen re-solves the GI in real time, pulling the canyon into deep amber shadow on one side while the lit face burns orange. No baking, no lightmaps — the lighting reacts as the camera moves through the environment.

Canyon golden hour — sun over the rim

Golden hour — Lumen GI pulling warm light deep into the canyon walls

Canyon at blue hour

Blue hour — sun below the horizon, cool skylight fills the scene

Three passes, one environment

Before locking the canyon lighting for the final animation, three distinct lighting conditions were explored to find the most cinematic read. Each uses the same geometry, the same camera framing — only the sun angle, sky color, and post-process tone changes.

  • Golden hour (photoreal) — warm directional light at 8° elevation, orange-tinted sky atmosphere, soft fill from the Lumen skylight. The sun peaks just over the canyon rim, casting a razor line of light across the rock face.
  • Stylized / cell shader — same geometry with a post-process material that posterizes the shading into bands, flattens specular, and boosts saturation. Used for a brief stylized section of the animation as a visual break.
  • Blue hour — sun dropped below the horizon, sky fills with a deep teal-blue. Stars begin to resolve in the sky. The canyon drops into shadow but retains form from the cool skylight ambient.

A subway car swallowed by a decade of growth

The subway environment is built on a modular NYC-style train kit — individual car shells, door panels, ceiling marquees, overhead grab rails, and seating units assembled into a coherent vehicle. The departure from the standard kit begins here: all signs of function are replaced by signs of abandonment.

Birch trees grow through the floor panels where the roots have found gaps. European hornbeam foliage fills the gaps between the door frames. Meadow grass and wildflowers cover what used to be the aisle. Vines loop the ceiling grab rails. The LED marquee still works — "DELAYS AHEAD" — but there are no delays because there is no train anymore.

The lighting inside the train comes entirely from the sky through the canopy above. Lumen computes the soft, diffuse fill of overcast-sky light filtering through a tree canopy growing through a broken roof panel. Thin god rays catch the mist particles scattered through the interior volume.

Subway interior — DELAYS AHEAD marquee

LED marquee still cycles "DELAYS AHEAD" — the train beneath it doesn't move

Train exterior — foliage overtaking the car

Exterior — wildflowers and foliage colonising the gap between car and platform

Subway interior — trees and vines

Interior — birch trees through the floor panels, vines looping the grab rails, grass covering the aisle

Low angle — Lumen light shafts through canopy

Low angle — Lumen god rays filtering through the canopy, volumetric mist catching the light

Custom post-process shaders for a painterly look

Two post-process materials were written for the project and applied as blendable volumes in Unreal's material system. The primary stylisation pass active in the final film is the Line Shader.

  • Line shader — anisotropic Kuwahara filter — a Sobel operator runs on scene luminance to detect the local edge gradient and compute its direction angle. That angle rotates four sampling quadrants around each pixel, aligning the Kuwahara regions with underlying surface features. For each quadrant, mean colour and variance are computed; the mean from the lowest-variance quadrant is returned. The result is a painterly, oil-painting look — flat colour areas resolve into coherent tonal patches while edges stay sharp and defined. Parameterised via Radius X/Y (brush size), KuwPow (painterliness), LineDistance, and CloseLineThickness controls. Two instances — Line_Shader_Inst and Line_Shader_Inst_Two — are applied at different blend weights.
  • Cell shader — toon shading — samples the scene BaseColor from the GBuffer and uses If-node comparisons against shadow depth to quantize the scene into discrete tonal bands. Separate tint colours apply to the shadow and highlight regions, controlled by Shadow Bias, Shadow Contrast, and Shadow Intensity parameters. The effect can be faded out by distance using the Disable Distance parameter.

Both materials read GBuffer channels — BaseColor, WorldNormal, SceneDepth, and PostProcessInput0 — through Unreal's SceneTexture nodes, so the stylisation responds to actual surface geometry rather than just the final composited pixel.

Canyon 1 — off (photoreal Lumen GI)

Canyon 1 — off (photoreal Lumen GI)

Canyon 2 — off (different angle)

Canyon 2 — On (anisotropic Kuwahara filter)

Canyon 2 — blue hour variant

Canyon 2 — off (blue hour variant)

Canyon 1 — on (Kuwahara painterly shader)

Canyon 1 — On (anisotropic Kuwahara filter)

Train exterior — off

Train exterior — off

Train exterior — on

Train exterior — on (cell shader)

Train interior — off

Train interior — off

Train interior — on

Train interior — on (cell shader)

GBuffer passes — what the renderer sees

Unreal's high-resolution screenshot tool can output individual GBuffer passes alongside the final composite. These passes reveal how the deferred renderer decomposes the scene before lighting, and they were used to validate material authoring and debug the post-process shaders.

BaseColor pass

BaseColor — flat albedo, no lighting

WorldNormal pass

WorldNormal — encoded surface normals

Roughness pass

Roughness — PBR roughness channel

Metallic pass

Metallic — PBR metallic channel

SceneDepth pass

SceneDepth — linear depth buffer

Pre-tonemap HDR colour

Pre-tonemap HDR — scene before exposure

Character rigging, texturing, and animation in Maya

The character — a stylised low-poly cube passenger — was modelled, rigged, textured, and animated in Maya before export to Unreal. The pipeline follows a standard FBX workflow with skeletal mesh and animation sequence import.

  • Rigging — joint chain with IK handles for the limbs, a simple spline IK for the spine, and orient constraints on the feet. Controls are built with nurbs curves parented to the joints for a clean, animator-friendly rig.
  • Texturing — hand-painted diffuse maps in a flat, graphic style to complement the low-poly geometry. UV unwrapping done in Maya with minimal distortion across the major surfaces.
  • Animation — a set of core cycles and actions authored in Maya: idle, walk, jump, sit, situp, push-up, and object pickup. Each animation exported as a separate FBX clip and imported into Unreal's Animation Blueprint for state-machine blending.
  • Unreal integration — Animation Blueprint handles transitions between clips with blend spaces for locomotion. Post-process animation layers apply procedural secondary motion on top of the keyed animation.
Character rendered in canyon environment

Character in-engine — rendered in the canyon environment with Lumen GI and Unreal's deferred pipeline

Maya rig — perspective viewport

Maya perspective — full rig visible with joint chain, IK handles, and NURBS control curves

Maya rig — front view

Front — joint hierarchy and control curves

Maya rig — side view

Side — IK leg chain and foot orient constraints

Maya rig — top view

Top — shoulder and arm control layout

Movie Render Pipeline — cinematic output from a real-time engine

The final animation is rendered through Unreal's Movie Render Pipeline (MRP) rather than a viewport capture. MRP gives access to temporal anti-aliasing accumulation over multiple samples per frame, high-resolution output beyond the viewport size, and per-pass EXR export for compositing.

  • Anti-aliasing — Temporal Super Resolution accumulated over 64 samples per frame, eliminating the subpixel shimmer that real-time TAA leaves in motion.
  • Resolution — output at 2560 × 1080 (cinematic 2.4:1 aspect) to enforce letterboxing and push the eye wide across the canyon landscape.
  • GBuffer pass capture — the breakdown images in this case study were captured using MRP's multi-pass output, writing each GBuffer channel to a separate PNG alongside the final composite.
  • Ray tracing — hardware ray tracing active for reflections and shadows; Lumen handles global illumination and sky occlusion at the scene level.

What a real-time engine demands from a filmmaker's eye

Working in Unreal for animation rather than games changes your relationship with the tools. In a game context you're optimising — fewer draw calls, smaller textures, shorter shadow distances. In a cinematic context you're chasing the image, and the real-time engine is a tool for iteration speed: change the light angle, press play, see the result in milliseconds instead of hours.

The hardest part was the subway environment — getting foliage assets to feel integrated rather than placed. Plants in the real world grow toward gaps in shade and follow the path of least resistance. Recreating that logic manually, one asset at a time, required looking at a lot of reference and killing a lot of placements that were technically correct but compositionally dead.

The post-process shader work was the most technically interesting part. Writing HLSL inside Unreal's material graph — using custom expression nodes for the Sobel filter, quantisation math, and GBuffer sampling — made the connection between shader theory and visual output concrete in a way that coursework alone hadn't.