| 1 | const shader = ` |
| 2 | // Heavily modified off of https://www.shadertoy.com/view/3sKGWw |
| 3 | // big thanks to these resources as well: |
| 4 | // https://youtu.be/3CycKKJiwis |
| 5 | // https://www.shadertoy.com/view/4djSRW |
| 6 | // https://www.shadertoy.com/view/3s3GDn |
| 7 | |
| 8 | precision mediump float; |
| 9 | uniform vec2 u_resolution; |
| 10 | uniform float u_time; |
| 11 | uniform bool u_mask; |
| 12 | |
| 13 | float random(vec2 p){ |
| 14 | vec3 p3 = fract(vec3(p.xyx) * .1031); |
| 15 | p3 += dot(p3, p3.yzx + 33.33); |
| 16 | return fract((p3.x + p3.y) * p3.z); |
| 17 | } |
| 18 | |
| 19 | vec2 random2(vec2 p){ |
| 20 | vec3 p3 = fract(vec3(p.xyx) * vec3(.1031, .1030, .0973)); |
| 21 | p3 += dot(p3, p3.yzx+33.33); |
| 22 | return fract((p3.xx+p3.yz)*p3.zy); |
| 23 | } |
| 24 | |
| 25 | void main() { |
| 26 | float glow; |
| 27 | vec2 uv; |
| 28 | vec2 fl; |
| 29 | vec2 local_uv; |
| 30 | vec2 seed; |
| 31 | vec2 centre; |
| 32 | vec2 cell; |
| 33 | float t = u_time * 0.00015; |
| 34 | float blinkTime = u_time * 0.0068; |
| 35 | float rotationAngle = -t * 21.0; |
| 36 | mat2 rotation = mat2(cos(rotationAngle), -sin(rotationAngle), |
| 37 | sin(rotationAngle), cos(rotationAngle)); |
| 38 | vec3 col = vec3(0); |
| 39 | |
| 40 | centre = vec2(cos(t), sin(t)) * 5.0 + 0.5; |
| 41 | |
| 42 | // Get uv from the fragment coordinates, rotation and depth |
| 43 | uv = (centre - (gl_FragCoord.xy - u_resolution / 2.0) / (1600.0 / 25.0)) * rotation; |
| 44 | fl = floor(uv); |
| 45 | // The local cell coordinates. uv-fl == frac(uv) |
| 46 | local_uv = uv - fl - 0.5; |
| 47 | |
| 48 | // For a 3x3 group of cells around the fragment, find the distance from the points |
| 49 | // of each to the current fragment and draw an accumulative glow accordingly |
| 50 | // The local cell is (0, 0) |
| 51 | for(float j = -1.0; j <= 1.0; j++){ |
| 52 | for(float k = -1.0; k <= 1.0; k++){ |
| 53 | cell = vec2(j, k); |
| 54 | seed = 128.0 + (fl + cell); |
| 55 | glow = |
| 56 | ((0.6 + 0.5 * sin((128.0 * random(seed)) * blinkTime))) * 25.0 |
| 57 | * pow(0.005 / length(local_uv - (cell + 0.9 * (random2(seed) - 0.5))), 2.0); |
| 58 | col += vec3(0.2 * glow) + 0.1 * glow; |
| 59 | } |
| 60 | } |
| 61 | |
| 62 | col = 1.0 - exp(-col); |
| 63 | col = pow(col, vec3(0.45)); |
| 64 | |
| 65 | if (u_mask) { |
| 66 | float mask = smoothstep(100.0, 300.0, length(vec2(gl_FragCoord.x - u_resolution.x/2.0, gl_FragCoord.y - 200.0))); |
| 67 | col *= mask; |
| 68 | } |
| 69 | |
| 70 | gl_FragColor = vec4(col * (u_mask ? 0.6 : 0.25), 0); |
| 71 | } |
| 72 | `; |
| 73 | |
| 74 | function createShader(gl, type, source) { |
| 75 | const shader = gl.createShader(type); |
| 76 | gl.shaderSource(shader, source); |
| 77 | gl.compileShader(shader); |
| 78 | const success = gl.getShaderParameter(shader, gl.COMPILE_STATUS); |
| 79 | if (success) { |
| 80 | return shader; |
| 81 | } |
| 82 | |
| 83 | // eslint-disable-next-line no-console |
| 84 | console.error(gl.getShaderInfoLog(shader)); |
| 85 | gl.deleteShader(shader); |
| 86 | throw new Error("Failed to compile shader"); |
| 87 | } |
| 88 | |
| 89 | function createProgram(gl, vertexShader, fragmentShader) { |
| 90 | const program = gl.createProgram(); |
| 91 | |
| 92 | gl.attachShader(program, vertexShader); |
| 93 | gl.attachShader(program, fragmentShader); |
| 94 | gl.linkProgram(program); |
| 95 | |
| 96 | const success = gl.getProgramParameter(program, gl.LINK_STATUS); |
| 97 | if (success) { |
| 98 | return program; |
| 99 | } |
| 100 | |
| 101 | // eslint-disable-next-line no-console |
| 102 | console.error(gl.getProgramInfoLog(program)); |
| 103 | gl.deleteProgram(program); |
| 104 | throw new Error("Failed to link program"); |
| 105 | } |