Published
Edited
Sep 11, 2020
2 stars
Insert cell
Insert cell
viewof PETALS = html`<input type="range" min="1" max="10" value="5">`
Insert cell
viewof NUM_QUADS = html`<input type="range" min="2" max="20" value="5">`
Insert cell
viewof width = html`<input type="range" min="0" max="1" value="0.5" step="0.01">`
Insert cell
viewof power = html`<input type="range" min="0.01" max="10" value="3" step="0.01">`
Insert cell
gl.canvas
Insert cell
cmd = gl.regl({
vert: `
// the vertex shader turns {radius, angle, side} into {x, y}
// the 'side' varies from -1 to +1, and is 0 along the center
// of the petal
precision mediump float;
attribute vec3 ras;
varying vec3 v_ras;
uniform float width, power, phase;
void main () {
float radius = ras.r;
// shape the petal! could be anything, sine waves, exponentials, but it's
// angular width as a function of the radius, ideally 0 at radius 0 and 0 at radius 1
float w = 1.0 - pow(radius, power);
// now that we know the width of the petal, we can use that to shift
// the angle left or right based on the 'side' and the width
float angle = ras.g + width * w * ras.b;
// wave the petal back and forth
angle += phase * 0.2 * radius;
// convert polar to cartesian
vec2 pos = vec2(radius * cos(angle), radius * sin(angle));
gl_Position = vec4(pos, ras.b, 1);
// pass this along for the fragment shader to use in the color
v_ras = ras;
}
`,
frag: `
precision mediump float;
varying vec3 v_ras;
void main () {
// color by how far we are from the center line of the petal
float s = abs(v_ras.b);
gl_FragColor = vec4(0.5 + 0.5*s, 0.3, 0.8 - 0.5*s, 1);
}
`,
attributes: {ras: flowerMesh},
uniforms: {
width: width,
power: power,
phase: gl.regl.prop('phase'),
},
count: flowerMesh.length,
})
Insert cell
function draw (t) {
gl.regl.clear({color: [0.6, 0.8, 0.5, 1]})
cmd({phase: Math.cos(t * 1e-3)})
}
Insert cell
draw(now)
Insert cell
flowerMesh = {
// construct a bunch of triangles that represent *square* petals
// and we'll let the vertex shader make them fancier
const dr = 1 / NUM_QUADS;
let mesh = [];
for (let petal = 0; petal < PETALS; petal++) {
let a = 2 * Math.PI / PETALS * petal;
for (let quad = 0; quad < NUM_QUADS; quad++) {
let r = quad / NUM_QUADS;
// we want four triangles, two on each side
// each list is the [r, a, s] values that go into the 'ras' vec3
// the shader wants arrays, not js objects
mesh.push([r, a, 0], [r+dr, a, 0], [r, a, -1]);
mesh.push([r, a, 0], [r+dr, a, 0], [r, a, +1]);
mesh.push([r+dr, a, 0], [r, a, -1], [r+dr, a, -1]);
mesh.push([r+dr, a, 0], [r, a, +1], [r+dr, a, +1]);
}
}
return mesh;
}
Insert cell
function createGL (opts) {
var canvas = html`<canvas width="400" height="400">`
var regl = createREGL(Object.assign({canvas: canvas}, opts || {}))
return {canvas, regl}
}
Insert cell
gl = createGL()
Insert cell
createREGL = require('https://unpkg.com/regl@1.3.1/dist/regl.min.js')
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