drawBunny = regl({
frag: `
precision mediump float;
void main () {
gl_FragColor = vec4(1.0, 1.0, 0.5, 1.) * .1;
}`,
vert: `
precision mediump float;
// input an index for each item.
attribute float ix;
attribute float a_x;
attribute float a_y;
attribute float a_radius;
uniform float u_time;
uniform vec2 u_aspect;
uniform float u_size_factor;
// General purpose random number.
const float tau = 3.14159265358 * 2.;
highp float ix_to_random(in float ix, in float seed) {
// For high numbers, taking the log avoids coincidence.
highp float seed2 = log(ix + .1) + 1.;
vec2 co = vec2(seed2, seed);
highp float a = 12.9898;
highp float b = 78.233;
highp float c = 43758.5453;
highp float dt= dot(co.xy ,vec2(a,b));
highp float sn= mod(dt,3.14);
return fract(sin(sn) * c);
}
highp vec2 box_muller(in float ix, in float seed) {
// Box-Muller transform gives you two gaussian randoms for two uniforms.
// It's the best deal on the boardwalk.
highp float U = ix_to_random(ix, seed);
highp float V = ix_to_random(ix, seed + 17.123123);
return vec2(
sqrt(-2.*log(U))*cos(tau*V),
sqrt(-2.*log(U))*sin(tau*V)
);
}
vec3 spherical_jitter(in float ix, in float angle) {
// ix: a random seed that is stable for the point.
// angle: a number in radians expressing how many turns around the sphere to take.
// if linked to time, points will animate.
// Start by randomly distributing a point on the unit sphere from
// three gaussian coordinates.
vec2 g1 = box_muller(ix, 10.);
vec2 g2 = box_muller(ix, 13.);
vec3 pos = vec3(g1.xy, g2.x);
pos = pos / sqrt(dot(pos, pos));
// We're going to rotate it around three angles.
// First, an arbitrary alpha.
float alpha = 3.;
float gamma = -.1;
// Finally, the angle of rotation. This is how far along the particular circular path we've gone.
float beta = ix_to_random(ix, 2.) * tau - angle;
vec3 pos_3d = pos *
mat3(1., 0., 0.,
0., cos(alpha), -sin(alpha),
0., sin(alpha), cos(alpha)) *
mat3(cos(beta), 0., sin(beta),
0., 1., 0.,
-sin(beta), 0., cos(beta)) *
mat3(cos(gamma), -sin(gamma), 0.,
sin(gamma), cos(gamma), 0.,
0., 0., 1.);
return pos_3d;
}
void main () {
if (a_x * a_y == 0.) {
gl_Position = vec4(100., 100., 100., 100.);
return;
}
gl_PointSize = 1.;
vec3 pos_3d = spherical_jitter(ix, u_time);
if (pos_3d.z < 0.) {
// throw away points on the back side of the sphere.
gl_Position = vec4(100., 100., 100., 100.);
return;
}
float radius = pow(a_radius * u_size_factor, 1./3.);
vec2 jitter = pos_3d.xy * radius / u_aspect;
vec2 xy = vec2(a_x, a_y * -1.) / u_aspect + jitter;
gl_Position = vec4(xy.x, xy.y, 0., 1.);
}`,
primitive: "points",
count: N,
attributes: {
ix: ixes,
a_x: my_buffers.x,
a_y: my_buffers.y,
a_radius: my_buffers.pop
},
blend: {
enable: true,
func: {
srcRGB: 'src alpha',
srcAlpha: 'src alpha',
dstRGB: 'one minus src alpha',
dstAlpha: 'one minus src alpha',
},
},
depth: {enable: false},
uniforms: {
u_aspect: ctx => {
const ar = ctx.viewportWidth / ctx.viewportHeight;
console.log(ar > 1 ? [ar, 1] : [1, 1 / ar]);
return ar > 1 ? [ar, 1] : [1, 1 / ar];
},
u_size_factor: () => hidden.size,
u_time: ({time}) => time
}
})