mirror of
https://github.com/mr0xb/dotfiles_chezmoi.git
synced 2026-08-27 19:34:57 -04:00
202 lines
6.7 KiB
GLSL
202 lines
6.7 KiB
GLSL
// geocities.glsl — a tribute to the golden age of blinking, tiled,
|
|
// under-construction personal homepages. Caution-stripe wallpaper,
|
|
// a rainbow sparkle field with hard blink (not a gentle twinkle),
|
|
// a scrolling pixel-font "UNDER CONSTRUCTION" marquee, and a bouncing
|
|
// rotating star badge, DVD-logo style.
|
|
|
|
// transparent background
|
|
const bool transparent = true;
|
|
|
|
// terminal contents luminance threshold to be considered background (0.0 to 1.0)
|
|
const float threshold = 0.02;
|
|
|
|
// show/hide individual gimmicks
|
|
const bool showStripes = true;
|
|
const bool showSparkles = true;
|
|
const bool showMarquee = true;
|
|
const bool showBadge = true;
|
|
|
|
// caution-stripe wallpaper opacity (0.0 to 1.0)
|
|
const float stripeOpacity = 0.02;
|
|
|
|
// sparkle field brightness
|
|
const float sparkleIntensity = 0.1;
|
|
|
|
// marquee scroll speed, in pixels per second
|
|
const float scrollSpeed = 90.0;
|
|
|
|
// ---------------------------------------------------------------
|
|
// helpers
|
|
// ---------------------------------------------------------------
|
|
|
|
float luminance(vec3 color) {
|
|
return dot(color, vec3(0.2126, 0.7152, 0.0722));
|
|
}
|
|
|
|
vec3 hsv2rgb(vec3 c) {
|
|
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
|
|
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
|
|
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
|
|
}
|
|
|
|
float hash21(vec2 p) {
|
|
p = fract(p * vec2(233.34, 851.73));
|
|
p += dot(p, p + 23.45);
|
|
return fract(p.x * p.y);
|
|
}
|
|
|
|
// 0 -> 1 -> 0 triangle wave, used to ping-pong the bouncing badge
|
|
float triangleWave(float x, float period) {
|
|
float t = fract(x / period);
|
|
return 1.0 - abs(2.0 * t - 1.0);
|
|
}
|
|
|
|
// ---------------------------------------------------------------
|
|
// tiny 5x7 pixel font, just enough glyphs to spell "UNDER CONSTRUCTION"
|
|
// each row is packed as a 5-bit value, bit weight 16 = leftmost column
|
|
// ---------------------------------------------------------------
|
|
|
|
const int FONT_ROWS = 7;
|
|
const int FONT[77] = int[77](
|
|
0, 0, 0, 0, 0, 0, 0, // 0: space
|
|
17, 17, 17, 17, 17, 17, 14, // 1: U
|
|
17, 25, 21, 21, 19, 17, 17, // 2: N
|
|
30, 17, 17, 17, 17, 17, 30, // 3: D
|
|
31, 16, 16, 30, 16, 16, 31, // 4: E
|
|
30, 17, 17, 30, 20, 18, 17, // 5: R
|
|
15, 16, 16, 16, 16, 16, 15, // 6: C
|
|
14, 17, 17, 17, 17, 17, 14, // 7: O
|
|
15, 16, 16, 14, 1, 1, 30, // 8: S
|
|
31, 4, 4, 4, 4, 4, 4, // 9: T
|
|
31, 4, 4, 4, 4, 4, 31 // 10: I
|
|
);
|
|
|
|
// "UNDER CONSTRUCTION" as indices into the glyph table above
|
|
const int MSG_LEN = 18;
|
|
const int MSG[18] = int[18](1, 2, 3, 4, 5, 0, 6, 7, 2, 8, 9, 5, 1, 6, 9, 10, 7, 2);
|
|
|
|
float fontBit(int code, int row, int col) {
|
|
int rowVal = FONT[code * FONT_ROWS + row];
|
|
float shifted = floor(float(rowVal) / pow(2.0, float(4 - col)));
|
|
return mod(shifted, 2.0);
|
|
}
|
|
|
|
// ---------------------------------------------------------------
|
|
// background gimmicks
|
|
// ---------------------------------------------------------------
|
|
|
|
vec3 hazardStripes(vec2 fragCoord) {
|
|
float stripeWidth = 18.0;
|
|
float diag = (fragCoord.x + fragCoord.y) + iTime * 40.0;
|
|
float m = mod(diag, stripeWidth * 2.0);
|
|
vec3 yellow = vec3(1.0, 0.82, 0.0);
|
|
vec3 blk = vec3(0.05);
|
|
return m < stripeWidth ? yellow : blk;
|
|
}
|
|
|
|
vec3 sparkleStars(vec2 uv, vec2 resolution) {
|
|
vec2 gridUV = uv;
|
|
gridUV.x *= resolution.x / resolution.y;
|
|
gridUV *= 26.0;
|
|
|
|
vec2 ipos = floor(gridUV);
|
|
vec2 fpos = fract(gridUV);
|
|
|
|
// only a fraction of cells host a star
|
|
float rnd = hash21(ipos);
|
|
if (rnd > 0.12) return vec3(0.0);
|
|
|
|
vec2 center = vec2(hash21(ipos + 3.1), hash21(ipos + 7.7));
|
|
float d = length(fpos - center);
|
|
|
|
// hard on/off blink rather than a smooth twinkle, tacky on purpose
|
|
float phase = hash21(ipos + 11.0) * 10.0;
|
|
float blink = step(0.5, fract(iTime * (1.5 + rnd * 4.0) + phase));
|
|
|
|
float glow = smoothstep(0.12, 0.0, d);
|
|
vec3 hue = hsv2rgb(vec3(fract(rnd * 4.0 + iTime * 0.1), 1.0, 1.0));
|
|
return hue * glow * blink;
|
|
}
|
|
|
|
vec3 starBadge(vec2 fragCoord, vec2 resolution) {
|
|
float radius = 26.0;
|
|
float px = mix(radius, resolution.x - radius, triangleWave(iTime * 0.09, 1.0));
|
|
float py = mix(radius, resolution.y - radius, triangleWave(iTime * 0.13 + 0.37, 1.0));
|
|
vec2 p = fragCoord - vec2(px, py);
|
|
|
|
float d = length(p);
|
|
float a = atan(p.y, p.x) - iTime * 2.0;
|
|
float starR = radius * (0.55 + 0.45 * cos(a * 5.0));
|
|
float shape = smoothstep(1.5, -1.5, d - starR);
|
|
|
|
vec3 hue = hsv2rgb(vec3(fract(iTime * 0.3), 1.0, 1.0));
|
|
return hue * shape;
|
|
}
|
|
|
|
// returns rgb + coverage alpha for the scrolling marquee ticker
|
|
vec4 marquee(vec2 fragCoord, vec2 resolution) {
|
|
float scale = clamp(resolution.y / 220.0, 2.0, 6.0);
|
|
float charW = 6.0 * scale; // 5 columns + 1 spacing column
|
|
float charH = 8.0 * scale; // 7 rows + 1 spacing row
|
|
|
|
int gap = 6; // blank characters between loops of the message
|
|
int totalChars = MSG_LEN + gap;
|
|
|
|
// band hugs the bottom edge of the terminal
|
|
float bandY0 = resolution.y - charH - scale * 2.0;
|
|
float localY = fragCoord.y - bandY0;
|
|
if (localY < 0.0 || localY >= charH) return vec4(0.0);
|
|
int row = int(floor(localY / scale));
|
|
if (row >= FONT_ROWS) return vec4(0.0);
|
|
|
|
float scrollX = fragCoord.x + iTime * scrollSpeed;
|
|
float totalWidth = float(totalChars) * charW;
|
|
float wrapped = mod(scrollX, totalWidth);
|
|
int charIndex = int(floor(wrapped / charW));
|
|
if (charIndex >= MSG_LEN) return vec4(0.0); // in the gap, blank
|
|
|
|
float localX = mod(wrapped, charW);
|
|
int col = int(floor(localX / scale));
|
|
if (col >= 5) return vec4(0.0);
|
|
|
|
int code = MSG[charIndex];
|
|
if (fontBit(code, row, col) < 0.5) return vec4(0.0);
|
|
|
|
vec3 color = hsv2rgb(vec3(fract(float(charIndex) / float(MSG_LEN) + iTime * 0.15), 1.0, 1.0));
|
|
return vec4(color, 1.0);
|
|
}
|
|
|
|
// ---------------------------------------------------------------
|
|
// main
|
|
// ---------------------------------------------------------------
|
|
|
|
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
vec2 uv = fragCoord / iResolution.xy;
|
|
vec4 terminalColor = texture(iChannel0, uv);
|
|
|
|
vec3 deco = vec3(0.0);
|
|
|
|
if (showStripes) {
|
|
deco += hazardStripes(fragCoord) * stripeOpacity;
|
|
}
|
|
if (showSparkles) {
|
|
deco += sparkleStars(uv, iResolution.xy) * sparkleIntensity;
|
|
}
|
|
if (showBadge) {
|
|
deco = max(deco, starBadge(fragCoord, iResolution.xy));
|
|
}
|
|
if (showMarquee) {
|
|
vec4 marq = marquee(fragCoord, iResolution.xy);
|
|
deco = mix(deco, marq.rgb, marq.a);
|
|
}
|
|
|
|
if (transparent) {
|
|
deco += terminalColor.rgb;
|
|
}
|
|
|
|
// only show the effect where the terminal is background (not text)
|
|
float mask = 1.0 - step(threshold, luminance(terminalColor.rgb));
|
|
vec3 blended = mix(terminalColor.rgb, deco, mask);
|
|
|
|
fragColor = vec4(blended, terminalColor.a);
|
|
}
|