codepens/webgl-markers/dist/script.js

232 lines
4.9 KiB
JavaScript

// short writeup: https://bl.ocks.org/monfera/85aa9627de1ae521d3ac5b26c9cd1c49
// http://colorbrewer2.org/#type=qualitative&scheme=Paired&n=12
// except yellow
const palette = [
[166,206,227],
[ 31,120,180],
[178,223,138],
[ 51,160, 44],
[251,154,153],
[227, 26, 28],
[253,191,111],
[255,127, 0],
[202,178,214],
[106, 61,154],
[177, 89, 40]
]
const regl = window.createREGL()
const lineCount = 11
const pointCount = 5
const lineWidth = 2
// padding is interpreted as percentage of the screen width or height
const xPadding = 0.1
const yPadding = 0.25
const xScale = x => 2 * (1 - 2 * xPadding) * (x - 0.5)
const yScale = y => 2 * (1 - 2 * yPadding) * (y - 0.5)
// nd :: typedArrayClass -> array -> ndarray
const nd = (Array, dimensions) => ndarray(
new Array(dimensions.reduce((p, n) => p * n, 1)),
dimensions
)
const colorLineAesthetic = ({lineWidth}) => ({
vert: `
precision mediump float;
attribute vec4 position;
attribute vec4 color;
uniform float tween;
varying vec4 c;
float smootherstep(float edge0, float edge1, float x) {
x = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
return x*x*x*(x*(x*6.0 - 15.0) + 10.0);
}
void main() {
float tweenedX = mix(position[0], position[1], smootherstep(0.0, 1.0, tween));
float tweenedY = mix(position[2], position[3], smootherstep(0.0, 1.0, tween));
gl_Position = vec4(tweenedX, tweenedY, 0, 1);
c = color;
gl_PointSize = 40.0;
}`,
frag: `
precision mediump float;
uniform mat3 S1;
uniform mat3 S2;
uniform float tween;
varying vec4 c;
vec2 pos = vec2(0.5, 0.5);
float superFormula(mat3 S, float fi) {
float a = S[0][0];
float b = S[0][1];
float m1 = S[0][2];
float m2 = S[1][0];
float n1 = S[1][1];
float n2 = S[1][2];
float n3 = S[2][0];
float r = pow( pow(abs(cos(m1 * fi / 4.0) / a), n2)
+ pow(abs(sin(m2 * fi / 4.0) / b), n3),
-1.0 / n1
);
return r;
}
void main() {
float dist = distance(gl_PointCoord.xy, pos);
vec2 dist2 = gl_PointCoord - pos;
float fi = atan(dist2.y, dist2.x);
float r1 = superFormula(S1, fi);
float r2 = superFormula(S2, fi);
float r = mix(r1, r2, smoothstep(0.0, 1.0, tween));
if(dist > r / 2.5) discard;
gl_FragColor = c;
}`,
primitive: 'points',
lineWidth
})
/* For performance reasons, the model format is identical with the format
* that regl expects attribute arrays to be in.
*/
const model = (({lineCount, pointCount, xScale, yScale}) => {
const positionLength = 4
const colorLength = 4
const position = nd(Float32Array, [lineCount, pointCount, positionLength])
const color = nd(Float32Array, [lineCount, pointCount, colorLength])
let i, j, x1, x2, y1, y2
for(i = 0; i < lineCount; i++) {
for(j = 0; j < pointCount; j++) {
x1 = Math.random()
x2 = Math.random()
y1 = Math.random()
y2 = Math.random()
position.set(i, j, 0, xScale(x1))
position.set(i, j, 1, xScale(x2))
position.set(i, j, 2, yScale(y1))
position.set(i, j, 3, yScale(y2))
color.set(i, j, 0, palette[i][0] / 255)
color.set(i, j, 1, palette[i][1] / 255)
color.set(i, j, 2, palette[i][2] / 255)
color.set(i, j, 3, 1)
}
}
return {
lineCount,
pointCount,
attributes: {
position: position.data,
color: color.data
},
staticUniforms: {
// cross
S1 : [
/* a= */ 1,
/* b= */ 0.6875,
/* m1= */ 8,
/* m2= */ 8,
/* n1= */ 1.3,
/* n2= */ 0.01,
/* n3= */ 3.313,
/* */ 0,
/* */ 0,
],
// hexagon
S2 : [
/* a= */ 1,
/* b= */ 1,
/* m1= */ 6,
/* m2= */ 6,
/* n1= */ 100,
/* n2= */ 40,
/* n3= */ 40,
/* */ 0,
/* */ 0,
]
}
}
})({lineCount, pointCount, xScale, yScale})
const lineViewModelMaker = model => {
const elements = []
let i, j
let index = 0
const {lineCount, pointCount, attributes, staticUniforms} = model
const uniforms = Object.assign(
{},
staticUniforms,
{
tween: ({time}) => Math.cos(time / 2) + 1 / 2
})
for(i = 0; i < lineCount; i++) {
for(j = 0; j < pointCount; j++) {
elements.push(index)
index++
}
}
return {attributes, uniforms, elements}
}
const linesDrawerMaker = model => {
const aesthetic = colorLineAesthetic
const layer = Object.assign(
{},
lineViewModelMaker(model),
aesthetic({lineWidth: lineWidth})
)
return regl(layer)
}
const linesDrawer = linesDrawerMaker(model)
const render = () => {
regl.frame(() => {
regl.clear({
color: [1, 1, 1, 1]
})
linesDrawer()
})
}
render()