mirror of
https://github.com/Rezmason/matrix.git
synced 2026-04-16 21:39:29 -07:00
Also changed the way the glyphs cycle in the "operator" version, after noticing they only change once every four or five frames in the original effect.
196 lines
3.8 KiB
JavaScript
196 lines
3.8 KiB
JavaScript
const makePassTexture = (regl, halfFloat) =>
|
|
regl.texture({
|
|
width: 1,
|
|
height: 1,
|
|
type: halfFloat ? "half float" : "uint8",
|
|
wrap: "clamp",
|
|
min: "linear",
|
|
mag: "linear",
|
|
});
|
|
|
|
const makePassFBO = (regl, halfFloat) => regl.framebuffer({ color: makePassTexture(regl, halfFloat) });
|
|
|
|
// 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, halfFloat) =>
|
|
Array(height)
|
|
.fill()
|
|
.map((_) => makePassFBO(regl, halfFloat));
|
|
|
|
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 loadImage = (regl, url) => {
|
|
let texture = regl.texture([[0]]);
|
|
let loaded = false;
|
|
return {
|
|
texture: () => {
|
|
if (!loaded) {
|
|
console.warn(`texture still loading: ${url}`);
|
|
}
|
|
return texture;
|
|
},
|
|
loaded: (async () => {
|
|
if (url != null) {
|
|
const data = new Image();
|
|
data.crossOrigin = "anonymous";
|
|
data.src = url;
|
|
await data.decode();
|
|
loaded = true;
|
|
texture = regl.texture({
|
|
data,
|
|
mag: "linear",
|
|
min: "linear",
|
|
flipY: true,
|
|
});
|
|
}
|
|
})(),
|
|
};
|
|
};
|
|
|
|
const loadShader = (regl, url) => {
|
|
let texture = regl.texture([[0]]);
|
|
let loaded = false;
|
|
return {
|
|
texture: () => {
|
|
if (!loaded) {
|
|
console.warn(`texture still loading: ${url}`);
|
|
}
|
|
return texture;
|
|
},
|
|
loaded: (async () => {
|
|
if (url != null) {
|
|
const data = new Image();
|
|
data.crossOrigin = "anonymous";
|
|
data.src = url;
|
|
await data.decode();
|
|
loaded = true;
|
|
texture = regl.texture({
|
|
data,
|
|
mag: "linear",
|
|
min: "linear",
|
|
flipY: true,
|
|
});
|
|
}
|
|
})(),
|
|
};
|
|
};
|
|
|
|
const loadText = (url) => {
|
|
let text = "";
|
|
let loaded = false;
|
|
return {
|
|
text: () => {
|
|
if (!loaded) {
|
|
console.warn(`text still loading: ${url}`);
|
|
}
|
|
return text;
|
|
},
|
|
loaded: (async () => {
|
|
if (url != null) {
|
|
text = await (await fetch(url)).text();
|
|
loaded = 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],
|
|
},
|
|
count: 3,
|
|
|
|
uniforms: {
|
|
...uniforms,
|
|
time: regl.context("time"),
|
|
tick: regl.context("tick"),
|
|
},
|
|
|
|
context,
|
|
|
|
depth: { enable: false },
|
|
});
|
|
|
|
const make1DTexture = (regl, data) =>
|
|
regl.texture({
|
|
data,
|
|
width: data.length / 3,
|
|
height: 1,
|
|
format: "rgb",
|
|
mag: "linear",
|
|
min: "linear",
|
|
});
|
|
|
|
const makePass = (outputs, render, resize, ready) => {
|
|
if (render == null) {
|
|
render = () => {};
|
|
}
|
|
if (resize == null) {
|
|
resize = (w, h) => Object.values(outputs).forEach((output) => output.resize(w, h));
|
|
}
|
|
if (ready == null) {
|
|
ready = Promise.resolve();
|
|
} else if (ready instanceof Array) {
|
|
ready = Promise.all(ready);
|
|
}
|
|
return {
|
|
outputs,
|
|
render,
|
|
resize,
|
|
ready,
|
|
};
|
|
};
|
|
|
|
const makePipeline = (steps, getInputs, ...params) =>
|
|
steps.filter((f) => f != null).reduce((pipeline, f, i) => [...pipeline, f(...params, i == 0 ? null : getInputs(pipeline[i - 1]))], []);
|
|
|
|
export {
|
|
makePassTexture,
|
|
makePassFBO,
|
|
makeDoubleBuffer,
|
|
makePyramid,
|
|
resizePyramid,
|
|
loadImage,
|
|
loadText,
|
|
makeFullScreenQuad,
|
|
make1DTexture,
|
|
makePass,
|
|
makePipeline,
|
|
};
|