mirror of
https://github.com/Rezmason/matrix.git
synced 2026-04-16 21:39:29 -07:00
Exploring ways to preserve the vanilla JS browser demo without compromising on the bundle. Experimenting with embedding images in the bundle as data URIs
This commit is contained in:
@@ -1,7 +1,4 @@
|
||||
import { makePassFBO, makePass } from "./utils";
|
||||
import highPassFrag from "../../shaders/glsl/bloomPass.highPass.frag.glsl";
|
||||
import blurFrag from "../../shaders/glsl/bloomPass.blur.frag.glsl";
|
||||
import combineFrag from "../../shaders/glsl/bloomPass.combine.frag.glsl";
|
||||
import { loadText, makePassFBO, makePass } from "./utils.js";
|
||||
|
||||
// The bloom pass is basically an added high-pass blur.
|
||||
// The blur approximation is the sum of a pyramid of downscaled, blurred textures.
|
||||
@@ -20,7 +17,7 @@ const resizePyramid = (pyramid, vw, vh, scale) =>
|
||||
fbo.resize(Math.floor((vw * scale) / 2 ** index), Math.floor((vh * scale) / 2 ** index)),
|
||||
);
|
||||
|
||||
export default ({ regl, config }, inputs) => {
|
||||
export default ({ regl, cache, config }, inputs) => {
|
||||
const { bloomStrength, bloomSize, highPassThreshold } = config;
|
||||
const enabled = bloomSize > 0 && bloomStrength > 0;
|
||||
|
||||
@@ -39,6 +36,7 @@ export default ({ regl, config }, inputs) => {
|
||||
const output = makePassFBO(regl, config.useHalfFloat);
|
||||
|
||||
// The high pass restricts the blur to bright things in our input texture.
|
||||
const highPassFrag = loadText(cache, "shaders/glsl/bloomPass.highPass.frag.glsl");
|
||||
const highPass = regl({
|
||||
frag: regl.prop("frag"),
|
||||
uniforms: {
|
||||
@@ -53,6 +51,7 @@ export default ({ regl, config }, inputs) => {
|
||||
// by blurring them all, this basic blur approximates a more complex gaussian:
|
||||
// https://web.archive.org/web/20191124072602/https://software.intel.com/en-us/articles/compute-shader-hdr-and-bloom
|
||||
|
||||
const blurFrag = loadText(cache, "shaders/glsl/bloomPass.blur.frag.glsl");
|
||||
const blur = regl({
|
||||
frag: regl.prop("frag"),
|
||||
uniforms: {
|
||||
@@ -65,6 +64,7 @@ export default ({ regl, config }, inputs) => {
|
||||
});
|
||||
|
||||
// The pyramid of textures gets flattened (summed) into a final blurry "bloom" texture
|
||||
const combineFrag = loadText(cache, "shaders/glsl/bloomPass.combine.frag.glsl");
|
||||
const combine = regl({
|
||||
frag: regl.prop("frag"),
|
||||
uniforms: {
|
||||
@@ -79,7 +79,7 @@ export default ({ regl, config }, inputs) => {
|
||||
primary: inputs.primary,
|
||||
bloom: output,
|
||||
},
|
||||
Promise.all([highPassFrag.loaded, blurFrag.loaded]),
|
||||
Promise.all([highPassFrag.loaded, blurFrag.loaded, combineFrag.loaded]),
|
||||
(w, h) => {
|
||||
// The blur pyramids can be lower resolution than the screen.
|
||||
resizePyramid(highPassPyramid, w, h, bloomSize);
|
||||
@@ -98,14 +98,14 @@ export default ({ regl, config }, inputs) => {
|
||||
const vBlurFBO = vBlurPyramid[i];
|
||||
highPass({
|
||||
fbo: highPassFBO,
|
||||
frag: highPassFrag,
|
||||
frag: highPassFrag.text(),
|
||||
tex: i === 0 ? inputs.primary : highPassPyramid[i - 1],
|
||||
});
|
||||
blur({ fbo: hBlurFBO, frag: blurFrag, tex: highPassFBO, direction: [1, 0] });
|
||||
blur({ fbo: vBlurFBO, frag: blurFrag, tex: hBlurFBO, direction: [0, 1] });
|
||||
blur({ fbo: hBlurFBO, frag: blurFrag.text(), tex: highPassFBO, direction: [1, 0] });
|
||||
blur({ fbo: vBlurFBO, frag: blurFrag.text(), tex: hBlurFBO, direction: [0, 1] });
|
||||
}
|
||||
|
||||
combine({ frag: combineFrag });
|
||||
combine({ frag: combineFrag.text() });
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { loadImage, loadText, makePassFBO, makePass } from "./utils.js";
|
||||
import imagePassFrag from "../../shaders/glsl/imagePass.frag.glsl";
|
||||
|
||||
// Multiplies the rendered rain and bloom by a loaded in image
|
||||
|
||||
@@ -10,6 +9,7 @@ export default ({ regl, cache, config }, inputs) => {
|
||||
const output = makePassFBO(regl, config.useHalfFloat);
|
||||
const bgURL = "bgURL" in config ? config.bgURL : defaultBGURL;
|
||||
const background = loadImage(cache, regl, bgURL);
|
||||
const imagePassFrag = loadText(cache, "shaders/glsl/imagePass.frag.glsl");
|
||||
const render = regl({
|
||||
frag: regl.prop("frag"),
|
||||
uniforms: {
|
||||
@@ -23,11 +23,11 @@ export default ({ regl, cache, config }, inputs) => {
|
||||
{
|
||||
primary: output,
|
||||
},
|
||||
Promise.all([background.loaded]),
|
||||
Promise.all([background.loaded, imagePassFrag.loaded]),
|
||||
(w, h) => output.resize(w, h),
|
||||
(shouldRender) => {
|
||||
if (shouldRender) {
|
||||
render({ frag: imagePassFrag });
|
||||
render({ frag: imagePassFrag.text() });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { makeFullScreenQuad, makePipeline } from "./utils.js";
|
||||
import createREGL from "regl";
|
||||
import fetchLibraries from "../fetchLibraries.js";
|
||||
import makeRain from "./rainPass.js";
|
||||
import makeBloomPass from "./bloomPass.js";
|
||||
import makePalettePass from "./palettePass.js";
|
||||
@@ -21,7 +21,13 @@ const effects = {
|
||||
mirror: makeMirrorPass,
|
||||
};
|
||||
|
||||
let createREGL, glMatrix;
|
||||
|
||||
export const init = async (canvas) => {
|
||||
const libraries = await fetchLibraries();
|
||||
createREGL = libraries.createREGL;
|
||||
glMatrix = libraries.glMatrix;
|
||||
|
||||
const resize = () => {
|
||||
const devicePixelRatio = window.devicePixelRatio ?? 1;
|
||||
canvas.width = Math.ceil(canvas.clientWidth * devicePixelRatio * rain.resolution);
|
||||
@@ -74,18 +80,12 @@ export const formulate = async (rain, config) => {
|
||||
}
|
||||
|
||||
const cameraTex = regl.texture(cameraCanvas);
|
||||
const lkg = await getLKG(config.useHoloplay, true);
|
||||
|
||||
// All this takes place in a full screen quad.
|
||||
const fullScreenQuad = makeFullScreenQuad(regl);
|
||||
const effectName = config.effect in effects ? config.effect : "palette";
|
||||
const context = { regl, cache, config, lkg, cameraTex, cameraAspectRatio };
|
||||
const pipeline = makePipeline(context, [
|
||||
makeRain,
|
||||
makeBloomPass,
|
||||
effects[effectName],
|
||||
makeQuiltPass,
|
||||
]);
|
||||
const context = { regl, cache, config, cameraTex, cameraAspectRatio, glMatrix };
|
||||
const pipeline = makePipeline(context, [makeRain, makeBloomPass, effects[effectName]]);
|
||||
|
||||
const screenUniforms = { tex: pipeline[pipeline.length - 1].outputs.primary };
|
||||
const drawToScreen = regl({ uniforms: screenUniforms });
|
||||
@@ -150,7 +150,7 @@ export const formulate = async (rain, config) => {
|
||||
rain.tick = tick;
|
||||
};
|
||||
|
||||
export const destroy = ({ regl, resize, doubleClick, cache, tick, canvas }) => {
|
||||
export const destroy = ({ regl, cache, resize, doubleClick, tick, canvas }) => {
|
||||
window.removeEventListener("resize", resize);
|
||||
window.removeEventListener("dblclick", doubleClick);
|
||||
cache.clear();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { loadText, makePassFBO, makePass } from "./utils.js";
|
||||
import mirrorPassFrag from "../../shaders/glsl/mirrorPass.frag.glsl";
|
||||
|
||||
let start;
|
||||
const numClicks = 5;
|
||||
@@ -14,8 +13,9 @@ window.onclick = (e) => {
|
||||
index = (index + 1) % numClicks;
|
||||
};
|
||||
|
||||
export default ({ regl, config, cameraTex, cameraAspectRatio }, inputs) => {
|
||||
export default ({ regl, cache, config, cameraTex, cameraAspectRatio }, inputs) => {
|
||||
const output = makePassFBO(regl, config.useHalfFloat);
|
||||
const mirrorPassFrag = loadText(cache, "shaders/glsl/mirrorPass.frag.glsl");
|
||||
const render = regl({
|
||||
frag: regl.prop("frag"),
|
||||
uniforms: {
|
||||
@@ -36,14 +36,14 @@ export default ({ regl, config, cameraTex, cameraAspectRatio }, inputs) => {
|
||||
{
|
||||
primary: output,
|
||||
},
|
||||
null, // No async loading, glsl bundled and loaded into memory at document load
|
||||
Promise.all([mirrorPassFrag.loaded]),
|
||||
(w, h) => {
|
||||
output.resize(w, h);
|
||||
aspectRatio = w / h;
|
||||
},
|
||||
(shouldRender) => {
|
||||
if (shouldRender) {
|
||||
render({ frag: mirrorPassFrag });
|
||||
render({ frag: mirrorPassFrag.text() });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import colorToRGB from "../utils/colorToRGB";
|
||||
import { make1DTexture, makePassFBO, makePass } from "./utils.js";
|
||||
import palettePassFrag from "../../shaders/glsl/palettePass.frag.glsl";
|
||||
import colorToRGB from "../utils/colorToRGB.js";
|
||||
import { loadText, make1DTexture, makePassFBO, makePass } from "./utils.js";
|
||||
|
||||
// Maps the brightness of the rendered rain and bloom to colors
|
||||
// in a 1D gradient palette texture generated from the passed-in color sequence
|
||||
@@ -55,7 +54,7 @@ const makePalette = (regl, entries) => {
|
||||
// won't persist across subsequent frames. This is a safe trick
|
||||
// in screen space.
|
||||
|
||||
export default ({ regl, config }, inputs) => {
|
||||
export default ({ regl, cache, config }, inputs) => {
|
||||
const output = makePassFBO(regl, config.useHalfFloat);
|
||||
const paletteTex = makePalette(regl, config.palette);
|
||||
const {
|
||||
@@ -67,6 +66,7 @@ export default ({ regl, config }, inputs) => {
|
||||
ditherMagnitude,
|
||||
} = config;
|
||||
|
||||
const palettePassFrag = loadText(cache, "shaders/glsl/palettePass.frag.glsl");
|
||||
const render = regl({
|
||||
frag: regl.prop("frag"),
|
||||
|
||||
@@ -92,7 +92,7 @@ export default ({ regl, config }, inputs) => {
|
||||
(w, h) => output.resize(w, h),
|
||||
(shouldRender) => {
|
||||
if (shouldRender) {
|
||||
render({ frag: palettePassFrag });
|
||||
render({ frag: palettePassFrag.text() });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
import { loadImage, makePassFBO, makeDoubleBuffer, makePass } from "./utils.js";
|
||||
import { mat4, vec3 } from "gl-matrix";
|
||||
import rainPassIntro from "../../shaders/glsl/rainPass.intro.frag.glsl";
|
||||
import rainPassRaindrop from "../../shaders/glsl/rainPass.raindrop.frag.glsl";
|
||||
import rainPassSymbol from "../../shaders/glsl/rainPass.symbol.frag.glsl";
|
||||
import rainPassEffect from "../../shaders/glsl/rainPass.effect.frag.glsl";
|
||||
import rainPassVert from "../../shaders/glsl/rainPass.vert.glsl";
|
||||
import rainPassFrag from "../../shaders/glsl/rainPass.frag.glsl";
|
||||
import { loadImage, loadText, makePassFBO, makeDoubleBuffer, makePass } from "./utils.js";
|
||||
|
||||
const extractEntries = (src, keys) =>
|
||||
Object.fromEntries(Array.from(Object.entries(src)).filter(([key]) => keys.includes(key)));
|
||||
@@ -37,7 +30,8 @@ const blVert = [1, 0];
|
||||
const brVert = [1, 1];
|
||||
const quadVertices = [tlVert, trVert, brVert, tlVert, brVert, blVert];
|
||||
|
||||
export default ({ regl, cache, config, lkg }) => {
|
||||
export default ({ regl, cache, config, glMatrix }) => {
|
||||
const { mat4, vec3 } = glMatrix;
|
||||
// The volumetric mode multiplies the number of columns
|
||||
// to reach the desired density, and then overlaps them
|
||||
const volumetric = config.volumetric;
|
||||
@@ -69,6 +63,7 @@ export default ({ regl, cache, config, lkg }) => {
|
||||
};
|
||||
|
||||
const introDoubleBuffer = makeComputeDoubleBuffer(regl, 1, numColumns);
|
||||
const rainPassIntro = loadText(cache, "shaders/glsl/rainPass.intro.frag.glsl");
|
||||
|
||||
const introUniforms = {
|
||||
...commonUniforms,
|
||||
@@ -85,6 +80,7 @@ export default ({ regl, cache, config, lkg }) => {
|
||||
});
|
||||
|
||||
const raindropDoubleBuffer = makeComputeDoubleBuffer(regl, numRows, numColumns);
|
||||
const rainPassRaindrop = loadText(cache, "shaders/glsl/rainPass.raindrop.frag.glsl");
|
||||
|
||||
const raindropUniforms = {
|
||||
...commonUniforms,
|
||||
@@ -108,6 +104,7 @@ export default ({ regl, cache, config, lkg }) => {
|
||||
});
|
||||
|
||||
const symbolDoubleBuffer = makeComputeDoubleBuffer(regl, numRows, numColumns);
|
||||
const rainPassSymbol = loadText(cache, "shaders/glsl/rainPass.symbol.frag.glsl");
|
||||
|
||||
const symbolUniforms = {
|
||||
...commonUniforms,
|
||||
@@ -125,6 +122,7 @@ export default ({ regl, cache, config, lkg }) => {
|
||||
});
|
||||
|
||||
const effectDoubleBuffer = makeComputeDoubleBuffer(regl, numRows, numColumns);
|
||||
const rainPassEffect = loadText(cache, "shaders/glsl/rainPass.effect.frag.glsl");
|
||||
|
||||
const effectUniforms = {
|
||||
...commonUniforms,
|
||||
@@ -224,8 +222,6 @@ export default ({ regl, cache, config, lkg }) => {
|
||||
screenSize: regl.prop("screenSize"),
|
||||
},
|
||||
|
||||
viewport: regl.prop("viewport"),
|
||||
|
||||
attributes: {
|
||||
aPosition: quadPositions,
|
||||
aCorner: Array(numQuads).fill(quadVertices),
|
||||
@@ -237,7 +233,6 @@ export default ({ regl, cache, config, lkg }) => {
|
||||
|
||||
// Camera and transform math for the volumetric mode
|
||||
const screenSize = [1, 1];
|
||||
//const { mat4, vec3 } = glMatrix;
|
||||
const transform = mat4.create();
|
||||
if (volumetric && config.isometric) {
|
||||
mat4.rotateX(transform, transform, (Math.PI * 1) / 8);
|
||||
@@ -253,7 +248,17 @@ export default ({ regl, cache, config, lkg }) => {
|
||||
{
|
||||
primary: output,
|
||||
},
|
||||
Promise.all([glyphMSDF.loaded, glintMSDF.loaded, baseTexture.loaded, glintTexture.loaded]),
|
||||
Promise.all([
|
||||
glyphMSDF.loaded,
|
||||
glintMSDF.loaded,
|
||||
baseTexture.loaded,
|
||||
glintTexture.loaded,
|
||||
rainPassIntro.loaded,
|
||||
rainPassRaindrop.loaded,
|
||||
rainPassSymbol.loaded,
|
||||
rainPassVert.loaded,
|
||||
rainPassFrag.loaded,
|
||||
]),
|
||||
(w, h) => {
|
||||
output.resize(w, h);
|
||||
const aspectRatio = w / h;
|
||||
@@ -276,10 +281,10 @@ export default ({ regl, cache, config, lkg }) => {
|
||||
[screenSize[0], screenSize[1]] = aspectRatio > 1 ? [1, aspectRatio] : [1 / aspectRatio, 1];
|
||||
},
|
||||
(shouldRender) => {
|
||||
intro({ frag: rainPassIntro });
|
||||
raindrop({ frag: rainPassRaindrop });
|
||||
symbol({ frag: rainPassSymbol });
|
||||
effect({ frag: rainPassEffect });
|
||||
intro({ frag: rainPassIntro.text() });
|
||||
raindrop({ frag: rainPassRaindrop.text() });
|
||||
symbol({ frag: rainPassSymbol.text() });
|
||||
effect({ frag: rainPassEffect.text() });
|
||||
|
||||
if (shouldRender) {
|
||||
regl.clear({
|
||||
@@ -292,8 +297,8 @@ export default ({ regl, cache, config, lkg }) => {
|
||||
transform,
|
||||
camera,
|
||||
screenSize,
|
||||
vert: rainPassVert,
|
||||
frag: rainPassFrag,
|
||||
vert: rainPassVert.text(),
|
||||
frag: rainPassFrag.text(),
|
||||
glyphTransform: [1, 0, 0, 1],
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import colorToRGB from "../utils/colorToRGB";
|
||||
import { make1DTexture, makePassFBO, makePass } from "./utils";
|
||||
import stripePassFrag from "../../shaders/glsl/stripePass.frag.glsl";
|
||||
import colorToRGB from "../utils/colorToRGB.js";
|
||||
import { loadText, make1DTexture, makePassFBO, makePass } from "./utils.js";
|
||||
|
||||
// Multiplies the rendered rain and bloom by a 1D gradient texture
|
||||
// generated from the passed-in color sequence
|
||||
@@ -28,7 +27,7 @@ const prideStripeColors = [
|
||||
.map((color) => Array(2).fill(color))
|
||||
.flat();
|
||||
|
||||
export default ({ regl, config }, inputs) => {
|
||||
export default ({ regl, cache, config }, inputs) => {
|
||||
const output = makePassFBO(regl, config.useHalfFloat);
|
||||
|
||||
const {
|
||||
@@ -52,6 +51,8 @@ export default ({ regl, config }, inputs) => {
|
||||
stripeColors.map((color) => [...colorToRGB(color), 1]),
|
||||
);
|
||||
|
||||
const stripePassFrag = loadText(cache, "shaders/glsl/stripePass.frag.glsl");
|
||||
|
||||
const render = regl({
|
||||
frag: regl.prop("frag"),
|
||||
|
||||
@@ -73,11 +74,11 @@ export default ({ regl, config }, inputs) => {
|
||||
{
|
||||
primary: output,
|
||||
},
|
||||
null,
|
||||
stripePassFrag.loaded,
|
||||
(w, h) => output.resize(w, h),
|
||||
(shouldRender) => {
|
||||
if (shouldRender) {
|
||||
render({ frag: stripePassFrag });
|
||||
render({ frag: stripePassFrag.text() });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
@@ -29,7 +29,6 @@ const makeDoubleBuffer = (regl, props) => {
|
||||
const isPowerOfTwo = (x) => Math.log2(x) % 1 == 0;
|
||||
|
||||
const loadImage = (cache, regl, url, mipmap) => {
|
||||
|
||||
const key = `${url}_${mipmap}`;
|
||||
if (cache.has(key)) {
|
||||
return cache.get(key);
|
||||
|
||||
Reference in New Issue
Block a user