180 lines
6.4 KiB
GLSL
180 lines
6.4 KiB
GLSL
// Khaos im Kiez: CRT terminal on the stairs (palette from berlin.ccc.de/kik)
|
|
//
|
|
// Slow scenes crossfade into each other every ~26 s:
|
|
// terminal output, progress bar, radar sweep, rain, aurora.
|
|
// Fast things are rare events on top: glitch, fast scroll dump, red alert.
|
|
|
|
const vec3 BG = vec3(0.008, 0.040, 0.016); // #020a04
|
|
const vec3 FG = vec3(0.200, 1.000, 0.400); // #33ff66
|
|
const vec3 DIM = vec3(0.122, 0.604, 0.247); // #1f9a3f
|
|
const vec3 HOT = vec3(1.000, 0.165, 0.165); // #ff2a2a
|
|
|
|
const float LAST = 12.0; // last step trepped drives
|
|
const float SCENE_LEN = 26.0; // seconds per scene
|
|
const float SCENE_FADE = 2.5; // crossfade at the end of a scene
|
|
const float EVENT_SLOT = 45.0; // at most one fast event per slot
|
|
|
|
float hash(float n) {
|
|
return fract(sin(n * 12.9898) * 43758.5453);
|
|
}
|
|
|
|
// Line number n of terminal output
|
|
vec3 line(float n) {
|
|
float h = hash(n);
|
|
if (h < 0.15) return BG; // blank line
|
|
if (h > 0.92) return HOT * 0.9; // "error"
|
|
float len = 0.25 + 0.75 * hash(n + 7.0); // line length ~ brightness
|
|
return mix(DIM * 0.5, FG, len * len);
|
|
}
|
|
|
|
// --- Slow scenes -----------------------------------------------------------
|
|
|
|
// 0: terminal output, a new line every ~2 s, prompt and cursor at the end
|
|
vec3 sceneTerminal(float i, float t) {
|
|
if (i < LAST - 1.0) {
|
|
float s = t * 0.45;
|
|
float slide = smoothstep(0.5, 1.0, fract(s)); // 0..1 between two lines
|
|
float n = i + floor(s); // always an integer
|
|
vec3 a = line(n) + 0.10 * (line(n - 1.0) + line(n + 1.0));
|
|
vec3 b = line(n + 1.0) + 0.10 * (line(n) + line(n + 2.0));
|
|
return mix(a, b, slide);
|
|
}
|
|
if (i < LAST) return DIM * 0.9; // visitor@khaos-im-kiez:~$
|
|
return fract(t / 1.1) < 0.5 ? FG : BG; // blinking cursor, as on the site
|
|
}
|
|
|
|
// 1: progress bar filling up over ~14 s, then a slow double pulse
|
|
vec3 sceneProgress(float i, float t) {
|
|
float ph = fract(t / 18.0);
|
|
float fill = min(ph / 0.78, 1.0) * (LAST + 1.0);
|
|
float d = fill - (i + 0.5);
|
|
vec3 col = BG + DIM * 0.06; // empty track
|
|
float on = smoothstep(-0.25, 0.35, d);
|
|
col = mix(col, mix(FG, DIM * 0.7, smoothstep(0.0, 2.0, d)), on);
|
|
if (ph > 0.78) {
|
|
float p = (ph - 0.78) / 0.22;
|
|
col = mix(col, FG, 0.5 + 0.5 * sin(p * 12.566 - 1.57)) * (1.0 - 0.6 * p);
|
|
}
|
|
return col;
|
|
}
|
|
|
|
// 2: radar, a blip sweeps back and forth leaving a phosphor trail,
|
|
// now and then a red contact appears and fades
|
|
vec3 sceneRadar(float i, float t) {
|
|
float d0 = i - (6.0 + 6.0 * sin(t * 0.7));
|
|
float head = exp(-d0 * d0 * 1.2);
|
|
float trail = 0.0;
|
|
for (int j = 1; j <= 10; j++) {
|
|
float fj = float(j);
|
|
float d = i - (6.0 + 6.0 * sin((t - fj * 0.15) * 0.7));
|
|
trail = max(trail, exp(-fj * 0.3) * exp(-d * d * 1.2));
|
|
}
|
|
vec3 col = BG + DIM * 0.05 + DIM * 0.55 * trail + FG * head;
|
|
|
|
float k = floor(t / 7.0);
|
|
float cpos = floor(hash(k + 40.0) * (LAST + 1.0));
|
|
float life = sin(3.1416 * fract(t / 7.0));
|
|
col += HOT * 0.8 * life * life * step(abs(i - cpos), 0.5);
|
|
return col;
|
|
}
|
|
|
|
// 3: rain, three drops with fading trails at different speeds
|
|
vec3 sceneRain(float i, float t) {
|
|
vec3 col = BG + DIM * 0.04;
|
|
for (int k = 0; k < 3; k++) {
|
|
float fk = float(k);
|
|
float speed = 0.9 + 0.5 * fk;
|
|
float b = mod(t * speed + fk * 5.3, 20.0) - 3.0 - i; // steps behind the drop, enters from off the stairs
|
|
float trail = exp(-max(b, 0.0) * 0.6) * smoothstep(-0.9, 0.0, b);
|
|
float head = max(0.0, 1.0 - abs(b));
|
|
col += DIM * 0.6 * trail + FG * head;
|
|
}
|
|
return col;
|
|
}
|
|
|
|
// 4: aurora, slow drifting green waves with a faint red haze
|
|
vec3 sceneAurora(float i, float t) {
|
|
float w1 = 0.5 + 0.5 * sin(i * 0.5 - t * 0.35);
|
|
float w2 = 0.5 + 0.5 * sin(i * 0.31 + t * 0.23);
|
|
vec3 col = mix(DIM * 0.35, FG * 0.8, w1) * (0.55 + 0.45 * w2);
|
|
float hz = 6.0 + 7.0 * sin(t * 0.11);
|
|
col += HOT * 0.30 * exp(-(i - hz) * (i - hz) * 0.06);
|
|
return col;
|
|
}
|
|
|
|
vec3 scene(int id, float i, float t) {
|
|
if (id == 0) return sceneTerminal(i, t);
|
|
if (id == 1) return sceneProgress(i, t);
|
|
if (id == 2) return sceneRadar(i, t);
|
|
if (id == 3) return sceneRain(i, t);
|
|
return sceneAurora(i, t);
|
|
}
|
|
|
|
int sceneId(float k) {
|
|
// walks through all five scenes, shifted randomly every five slots
|
|
return int(mod(k * 3.0 + floor(hash(floor(k / 5.0)) * 5.0), 5.0));
|
|
}
|
|
|
|
// --- Rare fast events ------------------------------------------------------
|
|
|
|
vec3 events(vec3 col, float i, float t) {
|
|
float k = floor(t / EVENT_SLOT);
|
|
float r = hash(k + 17.0);
|
|
float local = t - (k * EVENT_SLOT + 5.0 + hash(k + 31.0) * 30.0);
|
|
if (local < 0.0) return col;
|
|
|
|
if (r < 0.22) {
|
|
// glitch, 0.7 s of flickering steps
|
|
if (local < 0.7) {
|
|
float s = floor(local * 20.0);
|
|
float h = hash(i * 7.1 + s * 13.3);
|
|
if (h > 0.93) return HOT;
|
|
if (h > 0.70) return FG;
|
|
if (h < 0.20) return BG * 0.5;
|
|
}
|
|
} else if (r < 0.60) {
|
|
// dump, 2.5 s of fast scrolling output
|
|
if (local < 2.5) {
|
|
float e = smoothstep(0.0, 0.2, local) * (1.0 - smoothstep(2.2, 2.5, local));
|
|
vec3 l = line(i + floor(local * 14.0) + 100.0);
|
|
return mix(col, l * 1.2, e);
|
|
}
|
|
} else if (r < 0.72) {
|
|
// red alert, a red bar sweeps over the stairs
|
|
if (local < 1.6) {
|
|
float e = sin(3.1416 * local / 1.6);
|
|
float d = i - (local / 1.6 * 16.0 - 1.0);
|
|
col = mix(col, col * 0.3 + HOT * 0.12, e);
|
|
col += HOT * exp(-d * d * 0.5);
|
|
}
|
|
}
|
|
return col;
|
|
}
|
|
|
|
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
float i = floor(fragCoord.x / iResolution.x * 16.0); // step, also in the big preview
|
|
float t = iTime;
|
|
|
|
float k = floor(t / SCENE_LEN);
|
|
float f = fract(t / SCENE_LEN);
|
|
float m = smoothstep(1.0 - SCENE_FADE / SCENE_LEN, 1.0, f);
|
|
|
|
vec3 col = scene(sceneId(k), i, t);
|
|
if (m > 0.0) {
|
|
col = mix(col, scene(sceneId(k + 1.0), i, t), m);
|
|
}
|
|
|
|
col = events(col, i, t);
|
|
|
|
// rolling scanline, slow
|
|
float band = fract(i / 16.0 * 2.0 - t * 0.15);
|
|
col *= 1.0 - 0.35 * smoothstep(0.0, 0.1, band) * (1.0 - smoothstep(0.1, 0.25, band));
|
|
|
|
// slight CRT flicker
|
|
col *= 0.96 + 0.04 * hash(floor(t * 30.0));
|
|
|
|
// steps trepped does not drive
|
|
if (i > LAST) col = vec3(0.0);
|
|
|
|
fragColor = vec4(col, 1.0);
|
|
}
|