gpuAccumulate = regl({
vert: `
precision highp float;
attribute float n;
attribute float presets;
uniform mat3 view3;
uniform float colorSpeed, colorPhase, pointSize, sqrtBatchSize, colorSign;
uniform vec2 offset;
varying vec3 color;
#define PI ${Math.PI}
struct Mobius {
vec2 a, b, c, d;
};
struct IFS {
Mobius a, b, c; // can we not make arrays?
};
IFS skull = IFS( Mobius( vec2(0.4866733034189119,-0.3457824044206985), vec2(1.313262575615668,-1.397026140339055), vec2(0.6516350951229584,1.638719823441038), vec2(-1.8569883564943233,0.08818231772017304)), Mobius( vec2(0.5795578225793658,1.3549839078686818), vec2(-1.8961028244298554,1.6087998022720393), vec2(-1.6882600255129114,0.3069160294563704), vec2(0.0036256316221829365,-2.0331095041409233) ), Mobius( vec2(0.31508558391625563,1.0498951917336012), vec2(-0.7344985142475648,-0.9506556944928284), vec2(-1.288507550763319,-0.5351060846302649), vec2(-1.5200243034970713,0.21087848965428035) ) );
IFS stars = IFS( Mobius( vec2(1.496509486320491,-0.5973510559562649), vec2(1.363031770057287,0.5030253265047588), vec2(-0.4006162540899725,-0.31598846100639305), vec2(0.3877012648959322,-1.1534511520185347)), Mobius( vec2(0.37948263847029284,1.0729183770897013), vec2(-1.6818327845132162,-1.5667178744100405), vec2(-0.6804687668035543,-1.8026611211435384), vec2(0.18355448066088614,1.1028015889392742)), Mobius( vec2(-0.5482198806114792,1.0682003658572363), vec2(0.1303466381970876,-0.924249479448636), vec2(0.06475305759483793,-0.6112945687883007), vec2(1.130285084727879,-1.554137395983805)));
IFS dragon = IFS( Mobius( vec2(-1.5049655316774313,-1.0046110176648915), vec2(0.6950357700194231,-0.432217929539951), vec2(1.0743308469633628,0.6074866445391349), vec2(-1.4504110751965176,1.084841058616748)), Mobius( vec2(0.8879006445000018,0.1631992819881769), vec2(1.8187741523098753,1.4747701825935757), vec2(0.9488978374634134,0.7531535508928877), vec2(0.2046657793807769,-2.1349204141857965)), Mobius( vec2(0.6810830544856391,0.41883146175194397), vec2(-1.247827026913857,1.3800558371943485), vec2(1.2236590009001986,0.5638551016962444), vec2(-1.9784761192044398,-0.8425079666566594)));
IFS gasket = IFS( Mobius( vec2(-0.1018252798481565,-1.6993659680125772), vec2(1.379590288668221,1.1523609868431468), vec2(0.4689151107386992,0.4121648021857115), vec2(1.7244514564858764,-1.4367706357328038)), Mobius( vec2(-0.7373058434565494,-0.08334095212148668), vec2(-1.6491235833287208,0.9879685699677004), vec2(-0.09822345862515205,-0.7683141638693157), vec2(-0.9538636622332818,-0.4259876509291862)), Mobius( vec2(-1.022065959402516,0.2559407944576475), vec2(1.6979795385246752,-0.501974961351221), vec2(1.410013313430138,-1.2657227272429628), vec2(1.0331107145329936,-0.25404098701767536)));
IFS horsehead = IFS( Mobius( vec2(-0.620175038921428,1.3486543214753421), vec2(-0.1484539805952454,-0.44787716345703377), vec2(0.16257288894496202,1.8122404157983816), vec2(-0.3222155161945405,0.9625246056492645) ), Mobius( vec2(-0.7583961504085504,-1.3767091668206852), vec2(-0.9072365341139186,-0.9145698171667607), vec2(1.0177899842219502,1.347423640986904), vec2(0.4915774405750364,-1.7247189839909915) ), Mobius( vec2(-1.962175731310627,-0.7706747774053593), vec2(-1.3333358846582626,-1.5350176212096804), vec2(1.5549725520690356,0.32891254004323117), vec2(-1.3882946360470316,-1.8181413285838781) ) );
IFS ifs = stars; // choose your preset here
vec2 mul_complex( vec2 a, vec2 b ) {
return vec2( a.x * b.x - a.y * b.y, a.y * b.x + a.x * b.y );
}
vec2 div_complex( vec2 a, vec2 b ) {
float len2_b = b.x * b.x + b.y * b.y;
return vec2( ( a.x * b.x + a.y * b.y ) / len2_b, ( a.y * b.x - a.x * b.y ) / len2_b );
}
vec2 add( vec2 a, vec2 b ) {
return vec2( a.x + b.x, a.y + b.y );
}
vec2 mobius_on_point( vec2 a, vec2 b, vec2 c, vec2 d, vec2 xy ) {
return div_complex( add( mul_complex( a, xy ), b ), add( mul_complex( c, xy ), d ) );
}
float rand(vec2 co) {
return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453);
}
vec2 iterate (vec2 xy, out float branch_value) {
branch_value = rand(xy + branch_value); // is there a better way to make randomness?
Mobius mobius;
if( branch_value < 0.333 ) { mobius = ifs.a; }
else if( branch_value < 0.666 ) { mobius = ifs.b; }
else { mobius = ifs.c; }
return mobius_on_point( mobius.a, mobius.b, mobius.c, mobius.d, xy );
}
vec3 colorscale (float t) {
return 0.5 + 0.5 * vec3(
cos((2.0 * PI) * t - colorPhase),
cos((2.0 * PI) * (t - 1.0 / 3.0) - colorPhase),
cos((2.0 * PI) * (t - 2.0 / 3.0) - colorPhase)
);
}
void main () {
vec2 p = 4.0 * vec2(mod(n, sqrtBatchSize), floor(n / sqrtBatchSize)) / sqrtBatchSize + offset;
float smoothed_branch_value = 0.5;
float branch_value;
for (int i = 0; i < ${iterations}; i++) {
p = iterate(p, branch_value);
smoothed_branch_value = smoothed_branch_value * 0.9 + branch_value * 0.1;
}
// smoothed_branch_value tells us the last transforms used, and allows color speed and phase to be applied
color = colorscale(smoothed_branch_value * colorSpeed * colorSign);
vec2 xy = (view3 * vec3(p, 1)).xy;
gl_Position = vec4(xy, 0, 1);
gl_PointSize = pointSize;
}`,
frag: `
precision lowp float;
varying vec3 color;
uniform highp float pointSize;
void main () {
gl_FragColor = vec4(1, color) / (pointSize * pointSize);
}`,
attributes: {
n: randobuffer
},
uniforms: {
offset: () => [Math.random() * 2 - 1, Math.random() * 2 - 1],
colorSpeed: regl.prop("colorSpeed"),
colorPhase: regl.prop("colorPhase"),
pointSize: regl.prop("pointSize"),
colorSign: regl.prop("colorSign"),
sqrtBatchSize: Math.floor(Math.sqrt(batchSize))
},
blend: {
enable: true,
func: {
srcRGB: 1,
srcAlpha: 1,
dstRGB: 1,
dstAlpha: 1
},
equation: {
rgb: "add",
alpha: "add"
}
},
framebuffer: regl.prop("dst"),
primitive: "points",
depth: { enable: false },
count: batchSize
})