A technical overview of the algorithms, tools, and rendering techniques used in the Hyperspace Tunnel demo.
| Component | Technology |
|---|---|
| Language | Fortran 2018 (gfortran 13+, nvfortran compatible) |
| Graphics API | OpenGL 3.3+ Core Profile via iso_c_binding |
| Windowing | GLFW 3 (cross-platform window + input) |
| Shaders | GLSL 330 (loaded from files at runtime) |
| Build | Single build.sh script, no CMake/Make needed |
All OpenGL and GLFW calls go through hand-written iso_c_binding interfaces — roughly 140 function bindings covering shaders, buffers, textures, framebuffers, and input. No third-party Fortran-GL wrappers.
┌─────────────────────────────────────────────────────────────┐
│ CPU (Fortran) │
│ ┌──────────┐ ┌────────┐ ┌──────────┐ ┌──────────────┐ │
│ │ Timeline │→ │ Camera │→ │ Tunnel │→ │ Particles │ │
│ │ evaluate │ │ update │ │ rebuild │ │ spawn+update │ │
│ └──────────┘ └────────┘ └──────────┘ └──────────────┘ │
└─────────────────────────┬───────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────┐
│ GPU (OpenGL / GLSL) │
│ ┌──────────────┐ ┌────────────┐ │
│ │ Tunnel draw │ + │ Particles │ → HDR Scene FBO │
│ │ (triangles) │ │ (GL_LINES) │ (RGBA16F) │
│ └──────────────┘ └────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────┐ │
│ │ Post-Processing Chain (fullscreen passes) │ │
│ │ │ │
│ │ Bright Pass → Bloom Pyramid (3 levels) │ │
│ │ → Separable Gaussian Blur (H + V) │ │
│ │ → Final Composite: │ │
│ │ Chromatic Aberration │ │
│ │ Radial Motion Blur │ │
│ │ Bloom Combine │ │
│ │ Vignette │ │
│ │ ACES Filmic Tonemap │ │
│ │ Film Grain │ │
│ │ Gamma Correction │ │
│ └─────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ Backbuffer (screen) │
└─────────────────────────────────────────────────────────────┘
The tunnel centerline is a parametric 3D curve defined by 64 control points. Each point is computed from a sum of sines and cosines at different frequencies (Lissajous-style), creating a smoothly winding path through 3D space.
The standard Catmull-Rom formula with tension tau = 0.5:
P(t) = 0.5 * [(2*P1) + (-P0 + P2)*t + (2*P0 - 5*P1 + 4*P2 - P3)*t² + (-P0 + 3*P1 - 3*P2 + P3)*t³]
The camera follows this spline with a look-ahead offset for smooth orientation.
The tunnel is a swept tube: a circle extruded along the centerline. To avoid the twisting artifacts of Frenet frames (which become unstable on straight segments), we use parallel transport: at each step, the previous normal vector is projected onto the plane perpendicular to the new tangent, then renormalized.
n_new = normalize(n_prev - dot(n_prev, tangent_new) * tangent_new)
This produces a twist-free tube even through tight curves and near-straight segments.
The tunnel walls use 3D simplex noise (Ashima/McEwan public-domain GLSL implementation) layered with Fractional Brownian Motion (summing octaves at doubling frequency and halving amplitude). Two FBM layers at different scales create the plasma effect:
- Primary plasma: 3 octaves, scrolling along tunnel depth
- Turbulence overlay: 2 octaves, faster, finer detail
- Domain-warped arcs: Noise used to distort the input coordinates of another noise evaluation, creating flowing energy arcs
A hexagonal tiling pattern is computed analytically in the fragment shader:
float hexDist(vec2 p) {
p = abs(p);
return max(dot(p, vec2(0.866025, 0.5)), p.y);
}Overlaid at low opacity on the tunnel walls for the "Star Trek subspace conduit" look.
12,000 particles in a CPU-side ring buffer. Each particle is a position + velocity + color + lifetime. New particles spawn ahead of the camera along the tunnel, distributed at random angles and radii within the tube.
Rendered as GL_LINES with two vertices per particle: the head (bright) and a velocity-stretched tail (dim). Additive blending (GL_SRC_ALPHA, GL_ONE) makes them glow.
Multi-level bloom using a downsample pyramid:
- Bright pass: Extract pixels above a luminance threshold with a soft-knee curve
- Downsample: 3 levels, each half the previous resolution
- Separable Gaussian blur: Horizontal + vertical pass at each level (9-tap kernel)
- Combine: Additively blend all bloom levels back into the scene
The scene is rendered in HDR (RGBA16F framebuffers, values above 1.0). The final pass applies the Narkowicz 2015 ACES fitted curve:
vec3 aces(vec3 x) {
float a=2.51, b=0.03, c=2.43, d=0.59, e=0.14;
return clamp((x*(a*x+b)) / (x*(c*x+d)+e), 0.0, 1.0);
}This compresses HDR values into displayable range with a filmic shoulder and toe, avoiding harsh clipping.
RGB channels are offset radially from screen center. The offset increases quadratically with distance from center:
float aberr = strength * dist_from_center²;
color.r = texture(scene, uv + dir * aberr).r;
color.b = texture(scene, uv - dir * aberr).b;17 parameters (speed, FOV, bloom, aberration, palette, etc.) are driven by sorted keyframe arrays. Each parameter track is independently interpolated using smoothstep for organic transitions:
t = clamp((x - edge0) / (edge1 - edge0), 0, 1)
smoothstep = t * t * (3 - 2t)
Over 50 keyframes choreograph the 30-second demo across five acts.
Every 0.5 seconds, the engine checks shader file sizes on disk. If a change is detected, the shader is recompiled and linked. On failure, the old shader is kept. This enables live editing of any .frag or .vert file while the demo runs.
Four hand-crafted 256-entry color ramps:
- Cold Hyperspace — Deep blue → cyan → white (default)
- Hot Wormhole — Orange → magenta → violet → white
- Subspace — Dark green → teal → white
- Rainbow — Full HSV spectrum sweep
Palettes are sampled by depth + time for a scrolling effect, and the timeline blends between palettes during transitions.
iso_c_bindingfor all OpenGL/GLFW interop (c_ptr, c_loc, c_int, c_float, c_size_t)blockconstructs for scoped local variablesimplicit noneeverywherepurefunctions for all math operations- Allocatable arrays with
targetattribute for GPU data upload newunitspecifier for file I/Ocommand_argument_count/get_command_argumentfor CLI flags- Module-based architecture (one module per file)
The demo targets 1440p @ 120 FPS on an RTX 3070 (~8ms frame budget):
- Tunnel mesh: 200 segments x 48 radial = 9,600 quads (trivial)
- Particles: 12K line segments, CPU update + single
glBufferSubDataper frame - Post-FX: 3-level bloom pyramid + single composite pass
- Total VRAM: < 100 MB
On WSLg with Mesa software rendering, expect ~20 FPS at 1440p. On native GPU drivers, the budget is comfortable.