Last active
December 8, 2025 07:46
-
-
Save AKST/2b6a0bfda0917846955f9cf1c26f8f18 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| const float DITHER_SCALE = 4.0; | |
| const float TARGET_FPS = 30.0; | |
| const float SCANLINE_FPS = 60.0; | |
| const vec3 LUM_VECTOR = vec3(0.299, 0.587, 0.114); | |
| const mat4 BAYER_MATRIX = mat4( | |
| 0.0/16.0, 8.0/16.0, 2.0/16.0, 10.0/16.0, | |
| 12.0/16.0, 4.0/16.0, 14.0/16.0, 6.0/16.0, | |
| 3.0/16.0, 11.0/16.0, 1.0/16.0, 9.0/16.0, | |
| 15.0/16.0, 7.0/16.0, 13.0/16.0, 5.0/16.0 | |
| ); | |
| vec2 square_coord(vec2 frag_coord) { | |
| return floor(frag_coord / DITHER_SCALE) * DITHER_SCALE + DITHER_SCALE * 0.5; | |
| } | |
| /** | |
| * This is based on this guys tweet, with some changes. | |
| * | |
| * https://x.com/XorDev/status/1995634971030196401 | |
| */ | |
| vec3 colour_fn(vec2 coord, float time) { | |
| vec2 p = coord * 6.0 / iResolution.y; | |
| for (float i = 1.0; i <= 32.0; i++) { | |
| p += sin(p.yx * i + i * i + time * i + iResolution.xy) / i; | |
| } | |
| vec4 fragColor = tanh(0.2 / tan(p.y + vec4(0.0, 0.1, 0.3, 0.0))); | |
| return (fragColor * fragColor).xyz; | |
| } | |
| const vec3 DARK_BLUE = vec3(0.0, 0.0, 0.2); | |
| const vec3 CYAN = vec3(0.0, 0.2, 0.8); | |
| const vec3 YELLOW = vec3(1.0, 0.9, 0.0); | |
| const vec3 WHITE = vec3(1.0, 1.0, 1.0); | |
| vec3 gradient_map(float t) { | |
| if (t < 0.33) { | |
| return mix(DARK_BLUE, CYAN, t / 0.33); | |
| } else if (t < 0.66) { | |
| return mix(CYAN, YELLOW, (t - 0.33) / 0.33); | |
| } else { | |
| return mix(YELLOW, WHITE, (t - 0.66) / 0.34); | |
| } | |
| } | |
| vec3 render_cell(vec2 coord, float time) { | |
| float wave = sin(coord.y * 0.02 + time / 16.0) * 0.5 + 0.5; | |
| vec2 coord_dist = vec2(coord.x + 200.0 * wave, coord.y); | |
| vec3 col_base = colour_fn(square_coord(coord_dist), 2.0 * time); | |
| ivec2 pixelPos = ivec2(coord / DITHER_SCALE) % 4; | |
| float threshold = BAYER_MATRIX[pixelPos.x][pixelPos.y]; | |
| float lum = dot(col_base, LUM_VECTOR); | |
| vec3 mapped_col = gradient_map(lum); | |
| return step(threshold, mapped_col); | |
| } | |
| void mainImage(out vec4 fragColor, in vec2 fragCoord) { | |
| float quantized_time = floor(iTime * TARGET_FPS) / TARGET_FPS; | |
| float scanline_time = floor(iTime * SCANLINE_FPS) / SCANLINE_FPS; | |
| float is_even_frame = mod(scanline_time, 2.0); | |
| float a = mod(fragCoord.y, DITHER_SCALE * 2.0); | |
| float b = DITHER_SCALE; | |
| float refresh = mix(step(a, b), step(b, a), is_even_frame); | |
| if (refresh == 1.0) { | |
| float time_in_frame = fract(iTime * SCANLINE_FPS); | |
| float decay = mix(0.5, 1.0, time_in_frame); | |
| vec3 cell_base = render_cell(fragCoord, quantized_time); | |
| fragColor = vec4(cell_base * time_in_frame, 1.0); | |
| } else { | |
| float time_in_frame = fract(iTime * SCANLINE_FPS); | |
| float last_frame = (floor(iTime * TARGET_FPS) - 1.0) / TARGET_FPS; | |
| vec3 cell_base = render_cell(fragCoord, last_frame); | |
| vec2 coord_u = fragCoord - vec2(0.0, DITHER_SCALE); | |
| vec2 coord_b = fragCoord + vec2(0.0, DITHER_SCALE); | |
| vec3 col_u = render_cell(coord_u, quantized_time); | |
| vec3 col_b = render_cell(coord_b, quantized_time); | |
| vec3 col_bleed = mix(col_u, col_b, 0.5); | |
| fragColor = vec4(mix(col_bleed * 0.9, cell_base * 0.75, 0.6 * (1.0 - time_in_frame)), 1.0); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment