Last active
June 27, 2017 18:08
-
-
Save rflow/7b6e060f6f26f3006299f73976b480e5 to your computer and use it in GitHub Desktop.
ReGL circle animation with blending
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<body> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/regl/1.3.0/regl.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/chroma-js/1.3.3/chroma.min.js"></script> | |
<script> | |
const regl = createREGL(); | |
const doc = document.body; | |
const docWidth = window.innerWidth; | |
const docHeight = window.innerHeight; | |
const aspectRatio = docHeight / docWidth; | |
const colCount = 50; | |
const colWidth = docWidth / colCount; | |
const rowCount = parseInt(colCount * aspectRatio); | |
const rowHeight = docHeight / rowCount; | |
const circleCount = rowCount * colCount; | |
const maxRadius = Math.min(colWidth, rowHeight) / 2; | |
const minScale = 1e-6; // use small number to avoid reaching zero | |
const maxScale = 2.00; | |
const remapSin = (i) => 0.5 + Math.sin(i) * 0.5; | |
const remapCos = (i) => 0.5 + Math.cos(i) * 0.5; | |
const palette = chroma.brewer.PuBu.slice(5, 10); | |
const paletteGL = palette.map(c => chroma(c).gl()); // gl mode uses [r,g,b] vector w normalised values | |
const randomScale = (i) => minScale + ((maxScale - minScale) * remapCos(i)); | |
const randomColor = (i) => paletteGL[Math.floor(palette.length * remapSin(i))]; | |
const getGridPoints = (cols, rows, colWidth, rowHeight, gridWidth, gridHeight) => { | |
let count = cols * rows; | |
let points = new Float32Array(count * 2); | |
let col, row; | |
let xPos, yPos, xIndex, yIndex; | |
for (let i = 0; i < count; i++) { | |
col = i % cols; | |
row = parseInt(i / cols); | |
xPos = (col * colWidth) + (colWidth / 2); | |
yPos = (row * rowHeight) + (rowHeight / 2); | |
xIndex = (2 * i); | |
yIndex = xIndex + 1; | |
points[xIndex] = (2 * xPos / gridWidth) - 1; // convert to (-1, 1) GL coord space | |
points[yIndex] = (2 * yPos / gridHeight) - 1; // convert to (-1, 1) GL coord space | |
} | |
return points; | |
} | |
const getAnimStates = (count, done) => { | |
const colors = new Float32Array(count * 3); | |
const scales = new Float32Array(count); | |
let r, g, b; | |
let rIndex, gIndex, bIndex; | |
for (let i = 0; i < count; i++) { | |
[r, g, b] = randomColor(i * done); | |
rIndex = (3 * i); | |
gIndex = rIndex + 1; | |
bIndex = rIndex + 2; | |
colors[rIndex] = r; | |
colors[gIndex] = g; | |
colors[bIndex] = b; | |
scales[i] = randomScale(i * done); | |
}; | |
return { colors, scales }; | |
} | |
// build inputs for our animation: | |
const centroids = getGridPoints(colCount, rowCount, colWidth, rowHeight, docWidth, docHeight); | |
// create a series of animation states for each circle | |
const numStates = 50; | |
const allStates = []; | |
while (allStates.length < numStates) { | |
allStates.push(getAnimStates(circleCount, allStates.length)); | |
} | |
// regl command featuring shaders that will draw supplied points | |
const drawPoints = regl({ | |
vert:` | |
precision highp float; | |
uniform float progress; // interpolation progress (from 0.0 to 1.0) | |
uniform float maxRadius; // max radius to draw (n.b. point is square of 2*r x 2*r) | |
attribute vec2 point; // position at which to draw | |
attribute float scaleA; // scale factor A | |
attribute float scaleB; // scale factor B | |
attribute vec3 colorA; // color A | |
attribute vec3 colorB; // color B | |
varying vec3 rgb; // interpolated color | |
varying float scale; // interpolated scale | |
void main () { | |
rgb = mix(colorA, colorB, progress); | |
scale = mix(scaleA, scaleB, progress); | |
gl_PointSize = maxRadius * 2. * scale; | |
gl_Position = vec4(point, 0, 1); | |
} | |
`, | |
frag:` | |
precision highp float; | |
varying vec3 rgb; | |
varying float scale; | |
void main () { | |
// determine normalized distance from center of point | |
float point_dist = length(gl_PointCoord * 2. - 1.); | |
// calc scale at which to start fading out the circle | |
float min_dist = 0.95; //scale * 0.90; | |
// calc scale at which we find the edge of the circle | |
float max_dist = 1.00;//scale; | |
// https://thebookofshaders.com/glossary/?search=smoothstep | |
float alpha = 1. - smoothstep(min_dist, max_dist, point_dist); | |
gl_FragColor = vec4(rgb, alpha * 0.85); | |
} | |
`, | |
// using textbook example from http://regl.party/api#blending | |
depth: { | |
enable: false | |
}, | |
blend: { | |
enable: true, | |
func: { | |
srcRGB: 'src alpha', | |
srcAlpha: 'src color', | |
dstRGB: 'one', | |
dstAlpha: 'one', | |
// src: 'one', | |
// dst: 'one' | |
}, | |
equation: 'add', | |
color: [0, 0, 0, 0] | |
}, | |
attributes: { | |
point: centroids, | |
colorA: regl.prop('currColors'), | |
colorB: regl.prop('nextColors'), | |
scaleA: regl.prop('currScales'), | |
scaleB: regl.prop('nextScales'), | |
}, | |
uniforms: { | |
maxRadius: maxRadius, | |
progress: regl.prop('progress') | |
}, | |
count: circleCount, | |
primitive: 'points' | |
}); | |
// render loop | |
const fps = 60; | |
const tweenTime = 3; | |
const tweenFrames = fps * tweenTime; | |
let state = 0; | |
regl.frame(({ tick }) => { | |
regl.clear({ | |
color: [0, 0, 0, 1], | |
depth: 1 | |
}) | |
// increment frame counter until we reach the desired loop point | |
let frame = tick % tweenFrames; | |
// increment state counter once we've looped back around | |
if (frame === 0) { | |
state = ++state % numStates; | |
}; | |
// track progress as proportion of frames completed | |
let progress = frame / tweenFrames; | |
// determine current and next state | |
let currState = allStates[state]; | |
let nextState = allStates[(state + 1) % numStates]; | |
drawPoints({ | |
currColors: currState.colors, | |
currScales: currState.scales, | |
nextColors: nextState.colors, | |
nextScales: nextState.scales, | |
progress: progress | |
}); | |
}) | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment