// ============================================================ // RETROIZED — Storm Night Neige (ShaderGlass / RetroArch teaser) // Product: Retroized // Preset: Storm Night Neige // Producer: submergedsun // Home: https://www.retroized.com/shaderglass/storm-night-neige/ // Mac app: https://submergedsun.itch.io/retroized // YouTube: https://www.youtube.com/@Retroized // // This is a teaser slang for ShaderGlass (Windows) and RetroArch. // It is not the macOS overlay, not a ShaderGlass port, and not 1:1 // with the live Retroized engine (no persistence, faceplate, or // full noise/signal stack). Picture is contained in 4:3 (letterbox / // pillarbox) then barrelled in that tube, not stretched to the glass. // Import: Shader → Import custom. // Toggle "Show credit" off if you do not want the plate. // © 2026 submergedsun / Retroized // ============================================================ #version 450 #pragma parameter Boost "Boost" 1.3 0.5 2.5 0.05 #pragma parameter Curvature "Curvature" 0.33 0.0 0.5 0.01 #pragma parameter PhosphorStrength "Phosphor" 0.04 0.0 1.0 0.01 #pragma parameter PhosphorPitch "Pitch (px)" 3.0 2.0 8.0 0.1 #pragma parameter PhosphorSize "Phosphor fill" 0.90 0.2 1.0 0.01 #pragma parameter ScanlineAppearance "Scanline trough" 0.78 0.0 1.0 0.01 #pragma parameter BloomStrength "Bloom" 0.65 0.0 2.0 0.05 #pragma parameter BlackLevel "Black level" 0.07 -0.2 0.3 0.01 #pragma parameter Chromatic "Chroma spread" 0.6 0.0 1.0 0.01 #pragma parameter GlassTint "Glass tint" 0.15 0.0 1.0 0.01 #pragma parameter ShowCredit "Show credit" 1.0 0.0 1.0 1.0 layout(push_constant) uniform Push { vec4 SourceSize; vec4 OriginalSize; vec4 OutputSize; uint FrameCount; float Boost; float Curvature; float PhosphorStrength; float PhosphorPitch; float PhosphorSize; float ScanlineAppearance; float BloomStrength; float BlackLevel; float Chromatic; float GlassTint; float ShowCredit; } params; layout(std140, set = 0, binding = 0) uniform UBO { mat4 MVP; } global; #pragma stage vertex layout(location = 0) in vec4 Position; layout(location = 1) in vec2 TexCoord; layout(location = 0) out vec2 vTexCoord; void main() { gl_Position = global.MVP * Position; vTexCoord = TexCoord; } #pragma stage fragment layout(location = 0) in vec2 vTexCoord; layout(location = 0) out vec4 FragColor; layout(set = 0, binding = 2) uniform sampler2D Source; const float TUBE_AR = 4.0 / 3.0; const float SCAN_SCALE = 0.72; const float SCAN_THICK = 0.52; const float GLASS_SPEC = 0.3; const float FLICKER = 0.04; const float FLICKER_HZ = 55.0; const float TV_LINES = 360.0; const float SMEAR = 0.25; const float BLOCKY = 0.24; const float HEAT = 0.29; const float VIGNETTE = 0.15; const float GRAIN = 0.04; const float ENCODE = 0.05; const float SPARKLE = 0.07; const float CONTRAST = 1.2; const float SAT = 1.15; const float BRIGHT = 0.02; vec3 phosphor_slot_mask(float pitch, vec2 frag, float fill) { float col = pitch / 3.0; float ix = floor(frag.x / col); float lx = (fract(frag.x / col) - 0.5) * col; float n = floor(frag.y / pitch); float cy = (n + 0.5) * pitch; float ly = frag.y - cy; vec2 half_b = vec2(col * 0.50 * fill, pitch * mix(0.28, 0.42, fill)); float corner = min(half_b.x, half_b.y) * 0.72; vec2 q = abs(vec2(lx, ly)) - half_b + corner; float sd = min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - corner; float aa = max(fwidth(sd), 0.35); float blob = 1.0 - smoothstep(-aa, aa, sd); float id = mod(ix, 3.0); vec3 mask = vec3(0.0); if (id < 0.5) mask.r = 1.0; else if (id < 1.5) mask.g = 1.0; else mask.b = 1.0; return mask * blob; } vec3 phosphor_energy_mask(vec2 frag) { float pitch = max(2.0, params.PhosphorPitch); float fill = clamp(params.PhosphorSize, 0.20, 1.0); vec3 mask = clamp(phosphor_slot_mask(pitch, frag, fill), 0.0, 1.0); float avg = (mask.r + mask.g + mask.b) / 3.0; mask = mix(vec3(avg), mask, params.PhosphorStrength); float mask_mean = (mask.r + mask.g + mask.b) / 3.0; return mask / max(mask_mean, 0.18); } vec3 apply_scanline(vec3 rgb, vec2 curved_pixel, vec2 res) { float appear = params.ScanlineAppearance; if (appear <= 0.0) return rgb; float pitch = max(2.0, params.PhosphorPitch); float free_period = (1.0 + SCAN_SCALE * 5.0) * (res.y / 1080.0); float k = max(1.0, floor(free_period / max(pitch, 1.0e-4) + 0.5)); float period = k * pitch; float y_scan = curved_pixel.y - 0.5 * pitch; float d = abs(fract(y_scan / period + 0.5) - 0.5) * period; float sigma = period * mix(0.16, 0.40, SCAN_THICK); float beam_luma = dot(rgb, vec3(0.299, 0.587, 0.114)); float bright = smoothstep(0.18, 0.92, beam_luma); float core = exp(-0.5 * (d * d) / max(sigma * sigma, 1.0e-4)); float shoulder = exp(-0.5 * (d * d) / max((sigma * 2.0) * (sigma * 2.0), 1.0e-4)); float gauss = mix(core, mix(core, shoulder, 0.72), bright); return rgb * mix(1.0 - appear, 1.0, gauss); } float rand(vec2 co) { return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453); } vec3 noise_grain(vec2 pixel, float t) { vec2 seed = pixel + vec2(t * 97.1, t * 31.7); vec3 n = vec3( rand(seed), rand(seed + vec2(13.1, 17.7)), rand(seed + vec2(23.3, 41.1)) ); n = n * 2.0 - 1.0; n = sign(n) * pow(max(abs(n), vec3(0.001)), vec3(1.4)); return n; } vec3 apply_storm_noise(vec3 rgb, vec2 frag, float t) { rgb += GRAIN * noise_grain(frag, t); float eff_bits = floor(clamp(8.0 - ENCODE * 5.0, 3.0, 8.0)); float levels = exp2(eff_bits); float step_size = 1.0 / max(levels - 1.0, 1.0); vec3 dither = (vec3( rand(frag), rand(frag + vec2(13.1, 17.7)), rand(frag + vec2(23.3, 41.1)) ) - 0.5) * step_size * 0.5; rgb = floor((rgb + dither) / max(step_size, 1.0e-4) + 0.5) * step_size; vec2 seed_c = floor(frag * 0.5) + vec2(t * 97.1, t * 31.7); vec3 conv_n = vec3(rand(seed_c), rand(seed_c + 1.1), rand(seed_c + 2.2)) * 2.0 - 1.0; rgb += ENCODE * 0.12 * conv_n; vec2 sp_coord = floor(frag * 0.7) + vec2(t * 50.0, 0.0); float spark = step(1.0 - SPARKLE * 0.08, rand(sp_coord)); rgb += spark * SPARKLE * vec3(0.9, 0.9, 1.0); vec2 block = floor(frag / 8.0); float brk = (rand(block * 13.3 + t * 5.0) - 0.5) * 2.0; rgb += 0.12 * 0.15 * brk; return rgb; } // 5×7 caps. Packed 7 rows of 5 bits, y=0 in the low bits. int glyph_row(int ch, int y) { if (ch == 32) return 0; if (ch == 46) return (y >= 5) ? 4 : 0; if (ch == 67) { if (y == 0 || y == 6) return 14; return 16; } if (ch == 70) { if (y == 0) return 31; if (y == 3) return 30; return 16; } if (ch == 66) { if (y == 0) return 30; if (y == 3) return 30; if (y == 6) return 30; return 17; } if (ch == 68) { if (y == 0 || y == 6) return 30; return 17; } if (ch == 69) { if (y == 0 || y == 6) return 31; if (y == 3) return 30; return 16; } if (ch == 71) { if (y == 0 || y == 6) return 14; if (y == 1 || y == 5) return 17; if (y == 2) return 16; if (y == 3) return 19; return 17; } if (ch == 72) return 17 | ((y == 3) ? 14 : 0); if (ch == 73) { if (y == 0 || y == 6) return 31; return 4; } if (ch == 77) { if (y == 0) return 17; if (y == 1) return 27; if (y == 2) return 21; return 17; } if (ch == 78) { if (y == 1) return 25; if (y == 2) return 21; if (y == 3) return 19; return 17; } if (ch == 79) { if (y == 0 || y == 6) return 14; return 17; } if (ch == 82) { if (y == 0) return 30; if (y == 3) return 30; if (y == 4) return 20; if (y == 5) return 18; return 17; } if (ch == 83) { if (y == 0) return 15; if (y == 1) return 16; if (y == 2) return 16; if (y == 3) return 14; if (y == 4) return 1; if (y == 5) return 1; return 30; } if (ch == 84) return (y == 0) ? 31 : 4; if (ch == 85) { if (y == 6) return 14; return 17; } if (ch == 90) { if (y == 0 || y == 6) return 31; if (y == 1) return 1; if (y == 2) return 2; if (y == 3) return 4; if (y == 4) return 8; return 16; } return 0; } float stamp_char(vec2 px, int ch, float scale) { if (ch == 32 || px.x < 0.0 || px.y < 0.0) return 0.0; int gx = int(floor(px.x / scale)); int gy = int(floor(px.y / scale)); if (gx < 0 || gx > 4 || gy < 0 || gy > 6) return 0.0; int row = glyph_row(ch, gy); return float((row >> (4 - gx)) & 1); } float stamp_text18(vec2 px, float scale, int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m, int n, int o, int p, int q, int r) { float cell = scale * 6.0; float s = 0.0; s = max(s, stamp_char(px - vec2(cell * 0.0, 0.0), a, scale)); s = max(s, stamp_char(px - vec2(cell * 1.0, 0.0), b, scale)); s = max(s, stamp_char(px - vec2(cell * 2.0, 0.0), c, scale)); s = max(s, stamp_char(px - vec2(cell * 3.0, 0.0), d, scale)); s = max(s, stamp_char(px - vec2(cell * 4.0, 0.0), e, scale)); s = max(s, stamp_char(px - vec2(cell * 5.0, 0.0), f, scale)); s = max(s, stamp_char(px - vec2(cell * 6.0, 0.0), g, scale)); s = max(s, stamp_char(px - vec2(cell * 7.0, 0.0), h, scale)); s = max(s, stamp_char(px - vec2(cell * 8.0, 0.0), i, scale)); s = max(s, stamp_char(px - vec2(cell * 9.0, 0.0), j, scale)); s = max(s, stamp_char(px - vec2(cell * 10.0, 0.0), k, scale)); s = max(s, stamp_char(px - vec2(cell * 11.0, 0.0), l, scale)); s = max(s, stamp_char(px - vec2(cell * 12.0, 0.0), m, scale)); s = max(s, stamp_char(px - vec2(cell * 13.0, 0.0), n, scale)); s = max(s, stamp_char(px - vec2(cell * 14.0, 0.0), o, scale)); s = max(s, stamp_char(px - vec2(cell * 15.0, 0.0), p, scale)); s = max(s, stamp_char(px - vec2(cell * 16.0, 0.0), q, scale)); s = max(s, stamp_char(px - vec2(cell * 17.0, 0.0), r, scale)); return s; } float credit_mask(vec2 frag) { if (params.ShowCredit < 0.5) return 0.0; float scale = 1.0; vec2 res = max(params.OutputSize.xy, vec2(1.0)); float cell = scale * 6.0; float line = scale * 8.0; float plate_w = cell * 17.0; float plate_h = line + scale * 7.0; float screen_ar = res.x / max(res.y, 1.0); float box_w; float box_h; float box_x0; float box_y0; if (screen_ar > TUBE_AR) { box_h = res.y; box_w = box_h * TUBE_AR; box_x0 = 0.5 * (res.x - box_w); box_y0 = 0.0; } else { box_w = res.x; box_h = box_w / TUBE_AR; box_x0 = 0.0; box_y0 = 0.5 * (res.y - box_h); } vec2 origin = vec2(box_x0 + 8.0, box_y0 + box_h - plate_h - 6.0); vec2 px = frag; if (px.x < origin.x - 2.0 || px.y < origin.y - 2.0) return 0.0; if (px.x > origin.x + plate_w + 2.0 || px.y > origin.y + plate_h + 2.0) return 0.0; float m = stamp_text18(px - origin, scale, 82, 69, 84, 82, 79, 73, 90, 69, 68, 46, 67, 79, 77, 32, 32, 32, 32, 32); m = max(m, stamp_text18(px - origin - vec2(0.0, line), scale, 83, 84, 79, 82, 77, 32, 78, 73, 71, 72, 84, 32, 78, 69, 73, 71, 69, 32)); return m; } vec3 apply_credit(vec3 rgb, vec2 frag) { float m = credit_mask(frag); if (m <= 0.0) return rgb; vec3 ink = vec3(0.11, 0.08, 0.055); return mix(rgb, ink, m); } vec2 contain_tube(vec2 uv, vec2 outsz) { float screen_ar = outsz.x / max(outsz.y, 1.0); if (screen_ar > TUBE_AR) { float w = TUBE_AR / screen_ar; uv.x = (uv.x - 0.5) / max(w, 1.0e-6) + 0.5; } else if (screen_ar < TUBE_AR) { float h = screen_ar / TUBE_AR; uv.y = (uv.y - 0.5) / max(h, 1.0e-6) + 0.5; } return uv; } vec2 apply_tube_barrel(vec2 uv, float cx, float cy) { vec2 pos = 2.0 * uv - 1.0; float r2 = dot(pos, pos); pos.x *= 1.0 + cx * r2; pos.y *= 1.0 + cy * r2; return 0.5 * (pos + 1.0); } void main() { vec2 res = params.OutputSize.xy; float t = float(params.FrameCount) / 60.0; vec2 uv = contain_tube(vTexCoord, res); if (params.Curvature > 0.0 || HEAT > 0.0) { float c = max(params.Curvature, 0.0); uv = apply_tube_barrel(uv, c, c); vec2 pos = 2.0 * uv - 1.0; pos.x += HEAT * 0.045 * sin(pos.y * 3.2 + t * 0.35); pos.y += HEAT * 0.03 * sin(pos.x * 2.4); uv = 0.5 * (pos + 1.0); } if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) { FragColor = vec4(apply_credit(vec3(0.04, 0.04, 0.05), gl_FragCoord.xy), 1.0); return; } vec2 texel = 1.0 / max(res, vec2(1.0)); vec2 sample_uv = uv; if (BLOCKY > 0.0) { float q = mix(1.0, 3.2, BLOCKY); sample_uv = (floor(uv * res / q) + 0.5) * q / res; } float ca = params.Chromatic * 0.004; vec3 src; src.r = texture(Source, sample_uv + vec2(ca, 0.0)).r; src.g = texture(Source, sample_uv).g; src.b = texture(Source, sample_uv - vec2(ca, 0.0)).b; if (TV_LINES > 1.0) { float lines = TV_LINES; float y = (floor(sample_uv.y * lines) + 0.5) / lines; src.g = texture(Source, vec2(sample_uv.x, y)).g; src.r = texture(Source, vec2(sample_uv.x + ca, y)).r; src.b = texture(Source, vec2(sample_uv.x - ca, y)).b; } if (SMEAR > 0.0) { vec3 left = texture(Source, sample_uv - vec2(texel.x * SMEAR * 2.4, 0.0)).rgb; src = mix(src, left, SMEAR * 0.35); } src = (src - 0.5) * CONTRAST + 0.5 + BRIGHT; float luma = dot(src, vec3(0.299, 0.587, 0.114)); src = mix(vec3(luma), src, SAT); src += params.BlackLevel; src *= params.Boost; src *= 1.0 + FLICKER * sin(t * 6.28318 * FLICKER_HZ); vec2 curved_pixel = uv * res; src = apply_scanline(src, curved_pixel, res); if (params.PhosphorStrength > 0.0) { vec3 linear = pow(max(src, 0.0), vec3(2.2)); linear *= phosphor_energy_mask(gl_FragCoord.xy); src = pow(clamp(linear, 0.0, 1.0), vec3(1.0 / 2.2)); } if (params.BloomStrength > 0.0) { float d = 0.004; vec3 bloom = src; bloom += texture(Source, uv + vec2(d, 0.0)).rgb; bloom += texture(Source, uv + vec2(-d, 0.0)).rgb; bloom += texture(Source, uv + vec2(0.0, d)).rgb; bloom += texture(Source, uv + vec2(0.0, -d)).rgb; bloom *= 0.2; float lum = dot(bloom, vec3(0.299, 0.587, 0.114)); src += params.BloomStrength * max(0.0, lum - 0.35) * bloom; } if (params.GlassTint > 0.0) src = mix(src, src * vec3(0.78, 0.76, 0.72), params.GlassTint); vec2 to_spot = uv - vec2(0.72, 0.28); src += GLASS_SPEC * vec3(1.0, 0.98, 0.95) * exp(-28.0 * dot(to_spot, to_spot)); if (VIGNETTE > 0.0) { vec2 v = 2.0 * uv - 1.0; src *= max(1.0 - VIGNETTE * dot(v, v), 0.0); } if (GRAIN > 0.0 || ENCODE > 0.0 || SPARKLE > 0.0) src = apply_storm_noise(src, gl_FragCoord.xy, t); FragColor = vec4(clamp(src, 0.0, 1.0), 1.0); }