#version 300 es #ifdef GL_FRAGMENT_PRECISION_HIGH precision highp float; #else precision mediump float; #endif out vec4 fragColor; uniform float time; uniform vec2 resolution; const float sep = .3; const float tau = 2. * 3.1415926535897932384626433832795; const float rpm = .5; const float pan_amp = .2; const float pan_speed = .25; const float bg_spoke_length = .1; const float dither_pan_speed = .6; const float dither_rpm = .4; const int spokes = 5; // helps with precedence float addinv(float n) { return 1.-n; } // [n_min, n_max] -> [0, 1] float squash(float n, float n_min, float n_max) { n = max(n, n_min); n = min(n, n_max); return (n - n_min) / (n_max - n_min); } vec2 rot(vec2 uv, float a) { float r = distance(uv, vec2(0)); a += atan(uv.y, uv.x); a = mod(a, tau); float u = r * cos(a); float v = r * sin(a); return vec2(u,v); } vec2 spin_wiggle(vec2 uv, vec2 center, float amp, float freq, float speed) { float r = distance(uv, center); float a = atan(uv.y - center.y, uv.x - center.x); float max_r = sqrt(2.); float twirl_factor = squash(r, 0., max_r); //twirl_factor = smoothstep(0.,1., twirl_factor); a += amp * sin(twirl_factor*freq - time*speed); //sin(pow(addinv(twirl_factor), 2.)) * twirl_factor; a = mod(a, tau); float u = r * cos(a) + center.x; float v = r * sin(a) + center.y; return vec2(u,v); } vec2 hwiggle(vec2 uv, float amp, float freq, float speed) { uv.x += amp * sin(tau * (time * speed + uv.y * freq)); return uv; } float dither(vec2 uv, float size, float fill_amount, float darkness) { vec2 cur = mod(uv, size); vec2 cmp = vec2(size) / 2.; float n = distance(cur, cmp) < size*fill_amount ? darkness : 1.; return n; } vec3 shader(vec2 uv) { vec2 uv_dither = uv; uv_dither += sin(dither_pan_speed * time/60.); uv_dither = rot(uv, dither_rpm * time * tau/60.); // low frequency component to avoid looking too radial uv = hwiggle(uv, .2, .9, .05); vec2 pan = pan_amp * vec2(sin(time*pan_speed), cos(time*pan_speed)); uv = spin_wiggle(uv, pan, 3., 3., .25); // low frequency component to avoid looking too radial uv = hwiggle(uv, .5, 2., .05); float r = distance(uv, vec2(0)); float a = atan(uv.y, uv.x); // rotate whole screen a += rpm * tau * time/60.; a = mod(a, tau); float section = a / tau * float(spokes); // [0, spokes) float center_threshold_wiggle = 0.; float center_threshold_wiggle_amp = 1.; center_threshold_wiggle += sin(400.*a/tau)/50.; center_threshold_wiggle_amp *= addinv(abs(tan(a/4. + 2. * tau * time/60.))); float center_threshold = sep + center_threshold_wiggle_amp * center_threshold_wiggle; float center = r / center_threshold; // [0, 1) -> "center" float bg_lightness = ( // center is dark center < 1. ? center // middle parts of spokes are dark : fract(section) < bg_spoke_length ? addinv( // beginning of spokes are light squash(r, sep, 6.*sep/5.) // rest of spoke is dark * addinv( 2. * abs(fract(section) - bg_spoke_length / 2.) / bg_spoke_length ) ) // rest is light : 1. ); vec3 bg = .6 * bg_lightness * vec3(0,0,1) * dither(uv_dither, 0.025, .4, 0.); vec3 yellow = vec3(1,0,0) + vec3(0,1,0) * dither(uv_dither, 0.008, .3, .8); vec3 cyan = vec3(0,0,1) + vec3(0,1,0) * dither(uv_dither, 0.008, .3, .8)*.9; vec3 magenta = vec3(0,0,1) + vec3(1,0,0) * dither(uv_dither, 0.008, .3, .8); return ( center < 1. ? bg : fract(section) < 0.1 ? bg : section < 1. ? yellow : mod(floor(section), 2.) == 0. ? magenta : cyan ); } void main(void) { vec2 uv = gl_FragCoord.xy - resolution.xy / 2.; float mx = max(resolution.x, resolution.y); uv /= mx / 2.; // (-1,1) // chromatic aberration vec3 color = vec3( shader(uv+0.004).r, shader(uv).g, shader(uv-0.004).b ); fragColor = vec4(color, 1.0); } // vi: set ts=2 sts=2 sw=2 et: