Published
Edited
Jun 25, 2020
1 fork
Insert cell
Insert cell
regl._gl.canvas
Insert cell
regl = {
var canvas = document.createElement('canvas');
var pixelRatio = Math.min(devicePixelRatio, 1);
var w = Math.min(640, width);
var h = w;
canvas.style.width = `${w}px`;
canvas.style.height = `${h}px`;
canvas.width = w * pixelRatio;
canvas.height = h * pixelRatio;
let regl = createREGL({
extensions: [],
optionalExtensions: ['OES_texture_float'],
canvas,
pixelRatio: pixelRatio,
attributes: {
antialias: false,
preserveDrawingBuffer: true
}
});
return regl;
}
Insert cell
updateSprites = regl({
// vert just draws a big triange, which covers all the texture
vert: `
precision mediump float;
attribute vec2 position;
void main () {
gl_Position = vec4(position, 0, 1);
}
`,

frag: `
precision highp float;
uniform sampler2D state;
uniform float shapeX, shapeY, deltaT, gravity;
void main () {
vec2 shape = vec2(shapeX, shapeY);
vec4 prevState = texture2D(state,
gl_FragCoord.xy / shape);
vec2 position = prevState.xy;
vec2 velocity = prevState.zw;
position += 0.5 * velocity * deltaT;
if (position.x < -1.0 || position.x > 1.0) {
velocity.x *= -1.0;
}
if (position.y < -1.0 || position.y > 1.0) {
velocity.y *= -1.0;
}
position += 0.5 * velocity * deltaT;
velocity.y = velocity.y + gravity * deltaT;
gl_FragColor = vec4(position, velocity);
}
`,

depth: {enable: false},

// buffer for next state rendering
// ?? can the frag shader have several buffers?
framebuffer: ({tick}) => vars.SPRITES[(tick + 1) % 2],

uniforms: {
// prev state
state: ({tick}) => vars.SPRITES[(tick) % 2],
// is it size of current buffer?
shapeX: regl.context('viewportWidth'),
shapeY: regl.context('viewportHeight'),
deltaT: 0.01,
gravity: -0.05
},

attributes: {
position: [
0, -4,
4, 4,
-4, 4
]
},
primitive: 'triangles',
elements: null,
offset: 0,
count: 3
})
Insert cell
drawSprites = regl({
vert: `
precision highp float;
attribute vec2 sprite;
uniform sampler2D state;
varying vec2 rg;
void main () {
vec2 position = texture2D(state, sprite).xy;
gl_PointSize = 4.0;
rg = sprite;
gl_Position = vec4(position, 0, 1);
}
`,

frag: `
precision highp float;
varying vec2 rg;
void main () {
gl_FragColor = vec4(1.);
}
`,

// array of coordinates of all pixels of FBO
attributes: {
sprite: Array(N * N).fill().map(function (_, i) {
const x = i % N
// same as Math.floor()
const y = (i / N) | 0
return [(x / N), (y / N)]
}).reverse()
},

uniforms: {
state: ({tick}) => vars.SPRITES[tick % 2]
},

primitive: 'points',
offset: (context, {count}) => N * N - count,
elements: null,
count: regl.prop('count')
})
Insert cell
vars = { return {
// total amount of particles to display.
count: 0,
// a couple of sprites, which swap every frame
SPRITES: Array(2).fill().map(() =>
regl.framebuffer({
radius: N,
colorType: 'float',
depthStencil: false
}))
}}
Insert cell
regl.frame(({tick, drawingBufferWidth, drawingBufferHeight, pixelRatio}) => {
// const mouseX = toScreen(mouse.x, drawingBufferWidth, pixelRatio)
// const mouseY = -toScreen(mouse.y, drawingBufferHeight, pixelRatio)
// if (mouse.buttons) {
// console.log('mouse')
// }
for (let i = 0; i < BLOCK_SIZE; ++i)
{
BLOCK.data[4 * i] = 0
BLOCK.data[4 * i + 1] = 0
BLOCK.data[4 * i + 2] = 0.25 * (Math.random() - 0.5)
BLOCK.data[4 * i + 3] = Math.random()
}
vars.SPRITES[(tick) % 2].color[0].subimage(BLOCK, vars.count % N, ((vars.count / N) | 0) % N)
// 😬 subimage? what is it for?
vars.count += BLOCK_SIZE

updateSprites()

regl.clear({
color: [0, 0, 0, 1],
depth: 1
})

drawSprites({
count: Math.min(vars.count, N * N)
})
})
Insert cell
// toScreen = (x, size, pixelRatio) => {
// return Math.min(Math.max(2.0 * pixelRatio * x / size - 1.0, -0.999), 0.999)
// }
Insert cell
// It seems to be a fragment of vars.SPRITES, which describes positions and velocities of block of particles
// (block contains BLOCK_SIZE of particles)
BLOCK = { return {
// these 4 elements will become x, y, z and w of every point
data: new Float32Array(4 * BLOCK_SIZE),
width: BLOCK_SIZE,
height: 1
}}
Insert cell
// N × N is maximum amount of particles
N = 128
Insert cell
BLOCK_SIZE = 1
Insert cell
createREGL = require('regl')
Insert cell
mat4 = require('https://bundle.run/gl-mat4@1.2.0')
Insert cell
mouse = require('https://bundle.run/mouse-change@1.4.0')
Insert cell
hsv2rgb = require('https://bundle.run/hsv2rgb@1.1.0')
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more