Public
Edited
Sep 15, 2023
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
shaderSource = {
const vert = `
${glsl.precision}

attribute vec2 position;
attribute vec3 baryCentricCoord;

varying vec3 v_baryCentricCoord;

void main () {
v_baryCentricCoord = baryCentricCoord;
gl_Position = vec4(position, 0.0, 1.0);
}
`,
frag = `
${glsl.useStandardDerivatives}
${glsl.precision}
${glsl.dist}
${glsl.wireframe}

varying vec3 v_baryCentricCoord;

uniform float alpha, grids, gridWidth, gridFeather;

vec2 pow(vec2 src, float p) {
return vec2(pow(src.x, p), pow(src.y, p));
}

void main () {
float wf = 0.8 - wireframe(v_baryCentricCoord * grids, gridWidth, gridFeather);
gl_FragColor = mix(vec4(v_baryCentricCoord, alpha), vec4(1.0), wf);
}
`;

return { vert, frag };
}
Insert cell
/**
* Draw the triangle using regl
*/
{
now;

const { regl, blend } = reglDrawer;

function redraw() {
regl.clear({ color: backgroundColorForWebGL });

// Example of position parameter
// const position = [
// [-0.5, -0.5],
// [0.5, -0.5],
// [0, 0.5]
// ];

const position = triangles
.slice(0, numTriangles)
.map((triangle) => triangle.map((d) => [d.x, d.y])),
baryCentricCoord = triangles.map((triangle) => [
triangle.map((d) => d.baryCentricCoord)
]);

const drawTriangle = regl({
vert: shaderSource.vert,
frag: shaderSource.frag,
attributes: { position, baryCentricCoord },
uniforms,
count: triangles.length * 3,
blend,
depth: { enable: false }
});

drawTriangle();

statsMonitor.update();
}

if (animation) {
triangles.map((triangle) => {
triangle.update(movingSpeed / refreshRatio);
});
}

redraw();
}
Insert cell
triangles = [...new Array(20)].map(genTriangle)
Insert cell
genTriangle = () => {
const noiseGen = new simplexNoise(d3.randomUniform()()),
baryCentricCoord = [
[1, 0, 0],
[0, 1, 0],
[0, 0, 1]
],
t = 0;

const triangle = baryCentricCoord.map((bcc, i) =>
Object.assign(
{},
{
baryCentricCoord: bcc,
x: noiseGen.noise2D(i + t, 1),
y: noiseGen.noise2D(i + t, 10),
v: noiseGen.noise2D(i + t, 100)
}
)
);

function update(tStep = 0.0001) {
var { t, noiseGen } = triangle;
triangle.map((vertex, i) => {
t += tStep;
Object.assign(vertex, {
x: noiseGen.noise2D(i + t, 1),
y: noiseGen.noise2D(i + t, 10),
v: noiseGen.noise2D(i + t, 100)
});
});
Object.assign(triangle, { t: t + tStep });
}

Object.assign(triangle, { t, noiseGen, update });

return triangle;
}
Insert cell
reglDrawer = {
const canvas = DOM.canvas(width, height),
pixelRatio = 2.0,
extensions = ["oes_standard_derivatives"],
regl = wrapREGL({
canvas,
pixelRatio,
extensions
}),
blend = {
enable: true,
func: {
srcRGB: "src alpha",
srcAlpha: 1,
dstRGB: "one minus src alpha",
dstAlpha: 1
},
equation: {
rgb: "add",
alpha: "add"
}
};

Object.assign(canvas, { width, height });

{
const div = document.getElementById("canvasDivForWebGL");
div.innerHTML = "";
div.appendChild(canvas);
}

updateDivForREGL(shaderSource);

return { canvas, regl, blend };
}
Insert cell
updateDivForREGL = (shaderSource) => {
const { vert, frag } = shaderSource;

{
const div = document.getElementById("vertDivForREGL");
div.innerHTML = "";

d3.select(div)
.append("pre")
.attr("class", "prettyprint linenums lang-c")
.append("code")
.text(vert.trim());
}

{
const div = document.getElementById("fragDivForREGL");
div.innerHTML = "";

d3.select(div)
.append("pre")
.attr("class", "prettyprint linenums lang-c")
.append("code")
.text(frag.trim());
}

PR.prettyPrint();
}
Insert cell
Insert cell
width = 600
Insert cell
height = 600
Insert cell
backgroundColorForWebGL = [d3.color(backgroundColor)].map(
({ r, g, b, opacity }) => [r / 255, g / 255, b / 255, opacity]
)[0]
Insert cell
glsl = ({
useStandardDerivatives: `
#extension GL_OES_standard_derivatives : enable
`,
constants: `
#define PI 3.141592653589793238
#define HALF_PI 1.57079632679
#define HALF_PI_INV 0.15915494309
#define LOG_2 0.69314718056
#define C_ONE (vec2(1.0, 0.0))
#define C_I (vec2(0.0, 1.0))
#define TO_RADIANS 0.01745329251
`,
precision: `
precision highp float;
`,
dist: `
float dist(vec2 d) {
return sqrt(pow(d.x, 2.0) + pow(d.y, 2.0));
}

float dist(vec2 a, vec2 b) {
vec2 d = a - b;
return dist(d);
}
`,
hsv2rgb: `
vec3 hsv2rgb(vec3 c) {
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
`,
wireframe: `
float wireframe (vec3 parameter, float width, float feather) {
float w1 = width - feather * 0.5;
vec3 d = fwidth(parameter);
vec3 looped = 0.5 - abs(mod(parameter, 1.0) - 0.5);
vec3 a3 = smoothstep(d * w1, d * (w1 + feather), looped);
return min(min(a3.x, a3.y), a3.z);
}
`
})
Insert cell
Insert cell
Insert cell
statsMonitor = {
const stats = new Stats(),
{ dom } = stats;
dom.style.position = "relative";

return stats;
}
Insert cell
Stats = require("https://cdn.jsdelivr.net/npm/stats-js@1.0.1/build/stats.min.js")
Insert cell
PR = require("https://cdn.jsdelivr.net/gh/google/code-prettify@master/loader/run_prettify.js")
Insert cell
wrapREGL = require("regl")
Insert cell
Insert cell
simplexNoise = require("simplex-noise@2.4.0")
Insert cell
refreshRatio = 1000 / (await getRepaintInterval())
Insert cell
/**
* Get repaint interval, the value is used to compute the refreshRatio
* The result is of milliseconds
* Inspired by https://stackoverflow.com/questions/6131051/is-it-possible-to-find-out-what-is-the-monitor-frame-rate-in-javascript
*/
getRepaintInterval = () => {
return new Promise((resolve) => {
requestAnimationFrame((t1) => {
requestAnimationFrame((t2) => {
resolve(t2 - t1);
});
});
});
}
Insert cell
Insert cell
viewof backgroundColor = Inputs.color({
label: "Background Color",
value: "#455663"
})
Insert cell
viewof numTriangles = Inputs.select(
triangles.map((d, i) => i),
{ label: "Num triangles", value: 3 }
)
Insert cell
viewof movingSpeed = Inputs.range([0.01, 0.05], {
label: "Moving speed",
value: 0.02,
step: 0.001
})
Insert cell
viewof animation = Inputs.toggle({ label: "Animation", value: true })
Insert cell
viewof uniforms = Inputs.form({
alpha: Inputs.range([0, 1], { label: "Alpha", step: 0.01, value: 0.5 }),
grids: Inputs.range([2, 10], { label: "Grids", step: 1, value: 3 }),
gridWidth: Inputs.range([0.2, 2], {
label: "Grid width",
step: 0.1,
value: 0.5
}),
gridFeather: Inputs.range([0.5, 50.0], {
label: "Grid feather",
step: 0.1,
value: 1
})
})
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