Published
Edited
Sep 13, 2018
6 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function step (tex1, tex2, dest, u_Dt, dt) {
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, tex1);

gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, tex2);

gl.bindFramebuffer(gl.FRAMEBUFFER, dest);
gl.uniform1f(u_Dt, dt);
gl.drawArrays(gl.TRIANGLES, 0, 3);
}
Insert cell
{
const t0 = newTexture(seed);
const fb0 = newFramebuffer(t0);
const thalf = newTexture();
const fbhalf = newFramebuffer(thalf);
const t1 = newTexture();
const fb1 = newFramebuffer(t1);
for (let i = 0; true; ++i) {
gl.useProgram(timestepProgram);
const u_Dt = gl.getUniformLocation(timestepProgram, "u_Dt");
gl.viewport(0, 0, W, H);
for (let j = 0; j < 8; ++j) {
if (midpointIntegration) {
// RK-2 predictor and corrector steps:
step(t0, t0, fbhalf, u_Dt, timestep * 0.5);
step(t0, thalf, fb1, u_Dt, timestep);

// Same thing, framebuffers reversed:
step(t1, t1, fbhalf, u_Dt, timestep * 0.5);
step(t1, thalf, fb0, u_Dt, timestep);
} else {
// Euler step:
step(t0, t0, fb1, u_Dt, timestep);
// Same thing, framebuffers reversed:
step(t1, t1, fb0, u_Dt, timestep);
}
}
gl.useProgram(renderProgram);
gl.viewport(0, 0, canvas.width, canvas.height);
gl.bindTexture(gl.TEXTURE_2D, t0);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.drawArrays(gl.TRIANGLES, 0, 3);
yield i;
}
}
Insert cell
Insert cell
Insert cell
Insert cell
seed = {
restart;
const a = new Float32Array(4 * W * H);

for (let y = 0; y < H; ++y) {
for (let x = 0; x < W; ++x) {
const i = W * y + x << 2;
a[i + 0] = 0.9;
}
}

function dot(cx, cy, r = 5) {
const r2 = r ** 2;
for (let y = cy - r; y < cy + r; ++y) {
for (let x = cx - r; x < cx + r; ++x) {
if ((x - cx) ** 2 + (y - cy) ** 2 < r2) {
const i = W * y + x << 2;
a[i + 1] = 0.9;
}
}
}
}

for (let i = 0; i < 20; ++i) {
dot(W * Math.random(), H * Math.random());
}

return a;
}
Insert cell
function newTexture(source) {
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, W, H, 0, gl.RGBA, gl.FLOAT, source);
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.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
return texture;
}
Insert cell
function newFramebuffer(texture) {
const framebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
return framebuffer;
}
Insert cell
Insert cell
timestepShader = newShader(gl.FRAGMENT_SHADER, `
precision mediump float;
uniform sampler2D u_f, u_deriv;
uniform vec2 u_size;
uniform float u_F, u_K, u_Dt;
const float D_a = 0.2;
const float D_b = 0.1;

void main() {
vec2 position = gl_FragCoord.xy;

vec2 value = texture2D(u_deriv, position / u_size).xy;

vec2 laplacian = -3.0 * value + 0.5 * (
texture2D(u_deriv, (position + vec2(0.0, 1.0)) / u_size).xy +
texture2D(u_deriv, (position + vec2(1.0, 0.0)) / u_size).xy +
texture2D(u_deriv, (position + vec2(0.0, -1.0)) / u_size).xy +
texture2D(u_deriv, (position + vec2(-1.0, 0.0)) / u_size).xy
) + 0.25 * (
texture2D(u_deriv, (position + vec2(1.0, 1.0)) / u_size).xy +
texture2D(u_deriv, (position + vec2(-1.0, 1.0)) / u_size).xy +
texture2D(u_deriv, (position + vec2(-1.0, -1.0)) / u_size).xy +
texture2D(u_deriv, (position + vec2(1.0, -1.0)) / u_size).xy
);

vec2 delta = vec2(
D_a * laplacian.x - value.x * value.y * value.y + u_F * (1.0 - value.x),
D_b * laplacian.y + value.x * value.y * value.y - (u_K + u_F) * value.y
);

gl_FragColor = vec4(texture2D(u_f, position / u_size).xy + delta * u_Dt, 0.0, 1.0);
}`)
Insert cell
renderShader = newShader(gl.FRAGMENT_SHADER, `
precision mediump float;
uniform sampler2D u_f;
uniform vec2 u_size;

void main() {
vec2 x = texture2D(u_f, gl_FragCoord.xy / u_size).xy;
x.y = 1.0 - x.y;
gl_FragColor = vec4(x, 1.0, 1.0);
}`)
Insert cell
vertexShader = newShader(gl.VERTEX_SHADER, `
attribute vec2 a_position;

void main() {
gl_Position = vec4(a_position, 0.0, 1.0);
}`)
Insert cell
function newShader(type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) throw new Error("cannot compile shader");
return shader;
}
Insert cell
Insert cell
renderProgram = newProgram(vertexShader, renderShader)
Insert cell
timestepProgram = newProgram(vertexShader, timestepShader)
Insert cell
{
gl.useProgram(timestepProgram);
gl.uniform1f(gl.getUniformLocation(timestepProgram, "u_F"), FK.feed);
gl.uniform1f(gl.getUniformLocation(timestepProgram, "u_K"), FK.kill);
}
Insert cell
function newProgram(vertexShader, fragmentShader) {
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) throw new Error("cannot link program");
const a_position = gl.getAttribLocation(program, "a_position");
const u_size = gl.getUniformLocation(program, "u_size");
const u_Dt = gl.getUniformLocation(program, "u_Dt");
const u_deriv = gl.getUniformLocation(program, "u_deriv");
gl.useProgram(program);
gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-4, -4, 0, 4, 4, -4]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(a_position);
gl.vertexAttribPointer(a_position, 2, gl.FLOAT, false, 0, 0);
gl.uniform2f(u_size, W, H);
gl.uniform1f(u_Dt, 1.0);
gl.uniform1i(u_deriv, 1);
return program;
}
Insert cell
gl = {
const gl = canvas.getContext("webgl", {depth: false});
if (!gl) throw new Error("WebGL unsupported");
if (!gl.getExtension("OES_texture_float")) throw new Error("OES_texture_float unsupported");
if (gl.getParameter(gl.MAX_TEXTURE_SIZE) < W) throw new Error("big textures unsupported");
return gl;
}
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