Documentation

Shader Example: Sci-fi Tunnel

Your content here

Header

Your content here

Issues

  • Left side UV.

Snippets

ShaderToy

// Shadertoy - Flying Through a Neon Tunnel
// Paste into Shadertoy as-is.

#define PI 3.14159265359
#define TAU 6.28318530718

mat2 rot(float a)
{
    float c = cos(a), s = sin(a);
    return mat2(c, -s, s, c);
}

float hash11(float p)
{
    p = fract(p * 0.1031);
    p *= p + 33.33;
    p *= p + p;
    return fract(p);
}

float hash21(vec2 p)
{
    p = fract(p * vec2(234.34, 435.345));
    p += dot(p, p + 34.23);
    return fract(p.x * p.y);
}

float noise(vec2 p)
{
    vec2 i = floor(p);
    vec2 f = fract(p);

    float a = hash21(i);
    float b = hash21(i + vec2(1.0, 0.0));
    float c = hash21(i + vec2(0.0, 1.0));
    float d = hash21(i + vec2(1.0, 1.0));

    vec2 u = f * f * (3.0 - 2.0 * f);

    return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
}

float fbm(vec2 p)
{
    float v = 0.0;
    float a = 0.5;

    for (int i = 0; i < 5; i++)
    {
        v += noise(p) * a;
        p = rot(0.5) * p * 2.0 + 1.37;
        a *= 0.5;
    }

    return v;
}

vec3 palette(float t)
{
    vec3 a = vec3(0.5, 0.5, 0.5);
    vec3 b = vec3(0.5, 0.5, 0.5);
    vec3 c = vec3(1.0, 1.0, 1.0);
    vec3 d = vec3(0.02, 0.18, 0.32);
    return a + b * cos(TAU * (c * t + d));
}

float lanePulse(float x, float count, float sharpness)
{
    float v = cos(x * count);
    v = 0.5 + 0.5 * v;
    return pow(v, sharpness);
}

void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
    vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
    float t = iTime;

    // Slight camera wobble for screensaver feel
    vec2 cam = vec2(
        0.10 * sin(t * 0.70),
        0.10 * cos(t * 0.53)
    );

    vec2 p = uv + cam;

    // Polar mapping
    float r = length(p);
    float ang = atan(p.y, p.x);

    // Tunnel coordinates:
    // z grows rapidly toward center, giving "forward motion"
    float z = 1.0 / max(r, 0.08);
    float forward = t * 6.0;
    float tz = z + forward;

    // Twist tunnel as it moves
    ang += 0.7 * sin(tz * 0.12 + t * 0.8) + 0.15 * t;
    ang += 0.25 * fbm(vec2(tz * 0.05, ang * 2.0));

    // Tunnel surface coordinates
    vec2 tuv = vec2(ang * 2.5, tz * 0.22);

    // Surface detail
    float n1 = fbm(tuv * 2.0);
    float n2 = fbm(vec2(tuv.x * 6.0, tuv.y * 1.2 + t * 0.3));
    float n3 = noise(vec2(ang * 12.0, tz * 0.5));

    // Neon ring structure along depth
    float rings = sin(tz * 1.8 - n1 * 4.0);
    rings = pow(0.5 + 0.5 * rings, 10.0);

    // Angular lane lights
    float lanes = lanePulse(ang + n2 * 0.3, 10.0, 14.0);
    float lanes2 = lanePulse(ang * 2.0 - tz * 0.05, 6.0, 18.0);

    // Grid-ish energy pattern
    float grid = rings * lanes;
    grid += 0.6 * rings * lanes2;

    // Extra streaking
    float streaks = pow(1.0 - abs(sin(ang * 20.0 + tz * 0.35)), 18.0);
    streaks *= 0.4 + 0.6 * n3;

    // Central glow / vanishing point
    float core = 0.015 / (r * r + 0.002);

    // Tunnel mask: brightest around a cylindrical wall feel
    float wall = smoothstep(0.02, 0.18, r) * (1.0 - smoothstep(0.85, 1.4, r));

    // Color phase shifts over depth
    float hue = tz * 0.025 + n1 * 0.6 + lanes * 0.15;
    vec3 base = palette(hue);

    vec3 col = vec3(0.0);

    // Base tunnel glow
    col += base * (0.25 + 0.75 * n1) * wall;

    // Neon structures
    col += palette(hue + 0.10) * grid * 1.6 * wall;
    col += vec3(0.2, 0.8, 1.6) * streaks * wall * 1.2;
    col += vec3(0.8, 0.2, 1.4) * rings * 0.5 * wall;

    // Bright moving flashes down the tunnel
    float flash = sin(tz * 0.9 - t * 5.0 + ang * 3.0);
    flash = pow(0.5 + 0.5 * flash, 24.0);
    col += palette(hue + 0.35) * flash * 2.0 * wall;

    // Vanishing point energy
    col += vec3(0.7, 0.9, 1.4) * core;

    // Edge vignette
    float vignette = smoothstep(1.5, 0.25, length(uv));
    col *= vignette;

    // Fake bloom / tonemap
    col = 1.0 - exp(-col * 1.1);

    // Slight gamma
    col = pow(col, vec3(0.92));

    fragColor = vec4(col, 1.0);
}