Files
matrix/js/utils.js
2020-01-22 13:02:08 -08:00

142 lines
2.8 KiB
JavaScript

const makePassTexture = regl =>
regl.texture({
width: 1,
height: 1,
type: "half float",
wrap: "clamp",
min: "linear",
mag: "linear"
});
const makePassFBO = regl => regl.framebuffer({ color: makePassTexture(regl) });
// A pyramid is just an array of FBOs, where each FBO is half the width
// and half the height of the FBO below it.
const makePyramid = (regl, height) =>
Array(height)
.fill()
.map(_ => makePassFBO(regl));
const makeDoubleBuffer = (regl, props) => {
const state = Array(2)
.fill()
.map(() =>
regl.framebuffer({
color: regl.texture(props),
depthStencil: false
})
);
return {
front: ({ tick }) => state[tick % 2],
back: ({ tick }) => state[(tick + 1) % 2]
};
};
const resizePyramid = (pyramid, vw, vh, scale) =>
pyramid.forEach((fbo, index) =>
fbo.resize(
Math.floor((vw * scale) / 2 ** index),
Math.floor((vh * scale) / 2 ** index)
)
);
const loadImages = async (regl, manifest) => {
const keys = Object.keys(manifest);
const urls = Object.values(manifest);
const images = await Promise.all(urls.map(url => loadImage(regl, url)));
return Object.fromEntries(images.map((image, index) => [keys[index], image]));
};
const loadImage = async (regl, url) => {
if (url == null) {
return null;
}
const image = new Image();
image.crossOrigin = "anonymous";
image.src = url;
await image.decode();
return regl.texture({
data: image,
mag: "linear",
min: "linear",
flipY: true
});
};
const makeFullScreenQuad = (regl, uniforms = {}, context = {}) =>
regl({
vert: `
precision mediump float;
attribute vec2 aPosition;
varying vec2 vUV;
void main() {
vUV = 0.5 * (aPosition + 1.0);
gl_Position = vec4(aPosition, 0, 1);
}
`,
frag: `
precision mediump float;
varying vec2 vUV;
uniform sampler2D tex;
void main() {
gl_FragColor = texture2D(tex, vUV);
}
`,
attributes: {
aPosition: [-4, -4, 4, -4, 0, 4]
},
uniforms: {
...uniforms,
time: regl.context("time")
},
context,
depth: { enable: false },
count: 3
});
const make1DTexture = (regl, data) =>
regl.texture({
data,
width: data.length / 3,
height: 1,
format: "rgb",
mag: "linear",
min: "linear"
});
const makePass = (output, render, resize) => {
if (render == null) {
render = () => {};
}
if (resize === undefined) {
// "default" resize function is on the FBO
resize = (w, h) => output.resize(w, h);
}
if (resize == null) {
resize = () => {};
}
return {
output,
render,
resize
};
};
export {
makePassTexture,
makePassFBO,
makeDoubleBuffer,
makePyramid,
resizePyramid,
loadImage,
loadImages,
makeFullScreenQuad,
make1DTexture,
makePass
};