draw3 = regl3({
vert: `
precision highp float;
attribute vec2 xy;
void main () {
gl_Position = vec4(xy, 0, 1);
}`,
frag: `
#extension GL_OES_standard_derivatives : enable
precision highp float;
uniform vec2 iResolution;
uniform float iTime, fadeRange, tileSize, N, pointSize, fillDensity, bleed;
${libGLSL}
${quasirandomGLSL}
${ditherGLSL}
void main () {
vec2 p = (gl_FragCoord.xy - iResolution * 0.5) * 0.5 * exp(-fract(iTime) * log(2.0));
float pixelSize = 1.0 / dFdx(p.x);
float localOctave = log2(pixelSize / tileSize);
float baseOctave = floor(localOctave);
float octProgress = localOctave - baseOctave;
const int NOCT = ${octaves3};
int selector = int(floor((gl_FragCoord.x / iResolution.x) * float(NOCT + 1) - octProgress));
bool showBlended = gl_FragCoord.y / iResolution.y < 0.75;
float shade = showBlended ? fillDensity * (1.0 - gl_FragCoord.x / iResolution.x) : 1.0;
// Compensate slightly for overlapping dots by overshooting the target density
float b = 0.7;
float targetDensity = shade * 1.4 / (1.0 - (1.0 - b) * pow(shade, 4.0));
float useDensity, fill;
float texScale = pow(2.0, baseOctave - float(NOCT - 1));
float scale = bleed * pow(2.0, float(1 - NOCT) - octProgress);
float sdf = 100.0;
float accumDensity = 0.0;
for (int i = NOCT - 1; i >= 0; i--) {
float fade = linearstep(0.0, fadeRange, octProgress + float(i)) *
linearstep(fadeRange, 0.0, octProgress + float(i - NOCT) + fadeRange);
float density = bleed * bleed / pow(2.0, 2.0 * (float(i) + octProgress));
if (showBlended) {
float useDensity = clamp(targetDensity - accumDensity, 0.0, fade * density);
accumDensity += useDensity;
fill = useDensity / density;
} else {
fill = fade;
}
if (i == selector || showBlended) {
sdf = min(sdf, ditherPattern(p * texScale, scale, int(N), fill));
}
texScale *= 2.0;
scale *= 2.0;
}
sdf = linearstep(-0.5, 0.5, sdf);
gl_FragColor = vec4(vec3(pow(sdf, 0.454)), 1.0);
if (gl_FragCoord.y / iResolution.y < 0.2) {
gl_FragColor = vec4(vec3(1.0 - shade), 1);
}
}`,
attributes: { xy: [-4, -4, 4, -4, 0, 4] },
uniforms: {
iResolution: ({ framebufferWidth: w, framebufferHeight: h }) => [w, h],
iTime: ({ time }, { opts3, iTime3 }) =>
opts3.includes("animate") ? time * 0.5 : iTime3,
fadeRange: regl3.prop("fadeRange3"),
tileSize: regl3.prop("tileSize3"),
N: regl3.prop("N3"),
fillDensity: regl3.prop("fill3"),
pointSize: regl3.prop("pointSize3"),
bleed: regl3.prop("bleed3")
},
count: 3,
depth: { enable: false }
})