Published
Edited
Sep 26, 2021
3 forks
55 stars
Insert cell
Insert cell
Insert cell
viewof gl = {
const height = width;
const canvas = document.createElement("canvas");
canvas.width = width * devicePixelRatio;
canvas.height = height * devicePixelRatio;
canvas.style = `width: ${width}px; height: ${height}px`;
const gl = canvas.value = canvas.getContext("webgl", {antialias: false, depth: false, preserveDrawingBuffer: true});
gl.getExtension("OES_texture_float");
gl.blendFunc(gl.ONE, gl.ONE);
return canvas;
}
Insert cell
draw = {
gl.clear(gl.COLOR_BUFFER_BIT);
gl.enableVertexAttribArray(timestepPosition);
gl.enableVertexAttribArray(renderPosition);
for (let i = 0; i < 64; ++i) {
gl.useProgram(timestepProgram);
gl.viewport(0, 0, K, K);
gl.disable(gl.BLEND);
if (i & 1) {
gl.bindFramebuffer(gl.FRAMEBUFFER, fb1);
gl.bindTexture(gl.TEXTURE_2D, t2);
} else {
gl.bindFramebuffer(gl.FRAMEBUFFER, fb2);
gl.bindTexture(gl.TEXTURE_2D, t1);
}
gl.bindBuffer(gl.ARRAY_BUFFER, qb);
gl.vertexAttribPointer(timestepPosition, 2, gl.FLOAT, false, 0, 0);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
if (i < 8) continue;
gl.useProgram(renderProgram);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.enable(gl.BLEND);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.bindBuffer(gl.ARRAY_BUFFER, ib);
gl.vertexAttribPointer(renderPosition, 2, gl.UNSIGNED_SHORT, false, 0, 0);
gl.drawArrays(gl.POINTS, 0, K * K);
yield;
}
}
Insert cell
timestepFragmentShader = {
const shader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(shader, `
precision mediump float;
uniform sampler2D u_image;
uniform vec2 u_size;
uniform float u_a;
uniform float u_b;
uniform float u_c;
uniform float u_d;
void main() {
vec2 v = texture2D(u_image, gl_FragCoord.xy / u_size).xy;
gl_FragColor = vec4(
sin(u_a * v.y) + u_c * cos(u_a * v.x),
sin(u_b * v.x) + u_d * cos(u_b * v.y),
0.0,
0.0
);
}
`);
gl.compileShader(shader);
return shader;
}
Insert cell
timestepVertexShader = {
const shader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(shader, `
attribute vec2 a_position;
void main() {
gl_Position = vec4(a_position, 0.0, 1.0);
}
`);
gl.compileShader(shader);
return shader;
}
Insert cell
renderFragmentShader = {
const shader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(shader, `
void main() {
gl_FragColor = vec4(0.0, 0.0, 0.0, 0.01);
}
`);
gl.compileShader(shader);
return shader;
}
Insert cell
renderVertexShader = {
const shader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(shader, `
uniform sampler2D u_image;
uniform vec2 u_size;
attribute vec2 a_position;
void main() {
gl_Position = vec4(texture2D(u_image, a_position / u_size).xy, 0.0, ${Math.PI});
gl_PointSize = 1.0;
}
`);
gl.compileShader(shader);
return shader;
}
Insert cell
K = 512 * (devicePixelRatio > 1 ? 2 : 1)
Insert cell
qb = {
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]), gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
return buffer;
}
Insert cell
ib = {
const array = new Uint16Array(2 * K * K);
for (let i = -1, y = 0; y < K; ++y) {
for (let x = 0; x < K; ++x) {
array[++i] = x;
array[++i] = y;
}
}
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, array, gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
return buffer;
}
Insert cell
a1 = {
const array = new Float32Array(4 * K * K);
for (let y = 0, i = 0; y < K; ++y) {
for (let x = 0; x < K; ++x, i += 4) {
array[i + 0] = Math.random();
array[i + 1] = Math.random();
}
}
return array;
}
Insert cell
t1 = {
settings; // Reinitialize on interaction.
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, K, K, 0, gl.RGBA, gl.FLOAT, a1);
gl.bindTexture(gl.TEXTURE_2D, null);
return texture;
}
Insert cell
t2 = {
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, K, K, 0, gl.RGBA, gl.FLOAT, null);
gl.bindTexture(gl.TEXTURE_2D, null);
return texture;
}
Insert cell
fb1 = {
const framebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, t1, 0);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
return framebuffer;
}
Insert cell
fb2 = {
const framebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, t2, 0);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
return framebuffer;
}
Insert cell
renderProgram = {
const program = gl.createProgram();
gl.attachShader(program, renderVertexShader);
gl.attachShader(program, renderFragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
gl.uniform2f(gl.getUniformLocation(program, "u_size"), K, K);
gl.useProgram(null);
return program;
}
Insert cell
renderPosition = gl.getAttribLocation(renderProgram, "a_position")
Insert cell
timestepProgram = {
const program = gl.createProgram();
gl.attachShader(program, timestepVertexShader);
gl.attachShader(program, timestepFragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
gl.uniform2f(gl.getUniformLocation(program, "u_size"), K, K);
gl.uniform1f(gl.getUniformLocation(program, "u_a"), settings.a);
gl.uniform1f(gl.getUniformLocation(program, "u_b"), settings.b);
gl.uniform1f(gl.getUniformLocation(program, "u_c"), settings.c);
gl.uniform1f(gl.getUniformLocation(program, "u_d"), settings.d);
gl.useProgram(null);
return program;
}
Insert cell
timestepPosition = gl.getAttribLocation(timestepProgram, "a_position")
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