Published
Edited
Oct 10, 2022
Insert cell
Insert cell
viewof gl = {
const canvas = DOM.canvas(ewidth * devicePixelRatio, height * devicePixelRatio);
canvas.style.width = `${ewidth}px`;
canvas.style.height = `${height}px`;
const gl = canvas.value = canvas.getContext("webgl2");
gl.viewport(0, 0, canvas.width, canvas.height);
return canvas;
}
Insert cell
Insert cell
Insert cell
geometry = new Float32Array([-.5, -.5, .5, -0.5, 0, .75])
Insert cell
Insert cell
vertexBuffer = {
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, geometry, gl.STATIC_DRAW
);

invalidation.then(() => gl.deleteBuffer(buffer));
return buffer;
}
Insert cell
Insert cell
vao = {
const vao = gl.createVertexArray();
invalidation.then(() => gl.deleteVertexArray(vao));
return vao;
}
Insert cell
Insert cell
indexdata = new Uint8Array([0, 1, 2])
Insert cell
Insert cell
indexBuffer = {
// Make sure the VAO is active
gl.bindVertexArray(vao);
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indexdata, gl.STATIC_DRAW
);

// Make sure VAO not active to prevent accidental modification
gl.bindVertexArray(null);

invalidation.then(() => gl.deleteBuffer(buffer));
return buffer;
}
Insert cell
Insert cell
{
const FLOAT_SIZE = Float32Array.BYTES_PER_ELEMENT;

// Make the appropriate vertex buffer and VBO active
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bindVertexArray(vao);

// Bind and activate the positions: 2 elements
const position = gl.getAttribLocation(program, "position");
gl.vertexAttribPointer(position, 2, gl.FLOAT, false, FLOAT_SIZE * 2, 0);
gl.enableVertexAttribArray(position);

// Make sure VAO not active to prevent accidental modification
gl.bindVertexArray(null);

return html`Attribute binding complete.`
}
Insert cell
Insert cell
Insert cell
vertexShaderCode = `#version 300 es
precision highp float;

// View and projection matrices and time for animation
uniform mat4 view;
uniform mat4 projection;
uniform float time;

const float PI_OVER_180 = 3.14159265359;

// Input attributes
in vec2 position;

void main(void)
{
// Rotate the triangle slowly (our model matrix)
float angle = 0.2 * PI_OVER_180 * time;
float c = cos(angle); float s = sin(angle);
mat4 rot = mat4(c, s, 0., 0., -s, c, 0., 0., 0., 0., 1., 0., 0., 0., 0., 1.);
gl_Position = projection * view * rot * vec4(position, 0.0, 1.0);
}
`
Insert cell
vertexShader = {
const shader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(shader, vertexShaderCode);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS))
throw gl.getShaderInfoLog(shader);

// Important: delete our resources when the cell is invalidated
invalidation.then(() => gl.deleteShader(shader));
return shader;
}
Insert cell
Insert cell
fragmentShaderCode = `#version 300 es
precision highp float;

out vec4 frag_color;

void main()
{
frag_color = vec4(0., 1., 0., 1.);
}
`
Insert cell
fragmentShader = {
const shader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(shader, fragmentShaderCode);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS))
throw gl.getShaderInfoLog(shader);

// Important: delete our resources when the cell is invalidated
invalidation.then(() => gl.deleteShader(shader));
return shader;
}
Insert cell
Insert cell
program = {
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS))
throw gl.getProgramInfoLog(program);
invalidation.then(() => gl.deleteProgram(program));
return program;
}
Insert cell
Insert cell
Insert cell
u_viewport = {
const u_viewport = gl.getUniformLocation(program, "viewport");

// We only need to update it when the canvas size changes; by setting the uniform here,
// Observable's dependecy graph will handle that for us because the Canvas is mutable
gl.uniform2uiv(u_viewport, new Uint8Array([devicePixelRatio * ewidth, devicePixelRatio * height]));

return u_viewport;
}
Insert cell
Insert cell
u_time = gl.getUniformLocation(program, "time")
Insert cell
Insert cell
glm = import("gl-matrix")
Insert cell
view = glm.mat4.lookAt(glm.mat4.create(), [0, 0, 0], [0, 0, -1], [0, 1, 0])
Insert cell
u_view = {
const u_view = gl.getUniformLocation(program, "view");

gl.uniformMatrix4fv(u_view, false, view);

return u_view;
}
Insert cell
Insert cell
projection = glm.mat4.ortho(glm.mat4.create(), -1, 1, -1, 1, -1, 1)
Insert cell
u_projection = {
const u_projection = gl.getUniformLocation(program, "projection");

gl.uniformMatrix4fv(u_projection, false, projection);

return u_projection;
}
Insert cell
Insert cell
setup = {
// One time setup
gl.useProgram(program);
gl.enable(gl.DEPTH_TEST);

return "Setup complete";
}
Insert cell
Insert cell
function draw() {
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

gl.bindVertexArray(vao);
gl.drawElements(gl.TRIANGLES, 3, gl.UNSIGNED_BYTE, 0);
gl.bindVertexArray(null);
}
Insert cell
Insert cell
renderLoop = {
setup;

while (true) {
const t = performance.now() / 1000;
gl.uniform1f(u_time, t);
draw();
yield t;
}
}
Insert cell
Insert cell
ewidth=Math.min(640, width)
Insert cell
height = ewidth
Insert cell
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