mirror of
https://github.com/Rezmason/matrix.git
synced 2026-04-18 14:19:30 -07:00
Cleaned up config by moving its responsibilities into the passes
This commit is contained in:
33
TODO.txt
33
TODO.txt
@@ -1,34 +1,11 @@
|
|||||||
TODO:
|
TODO:
|
||||||
|
|
||||||
Clean up config.js
|
Deja vu effect: flashing rows
|
||||||
Too many responsibilities
|
Make them flash all the time
|
||||||
Pass-specific properties should be made into uniforms in the passes
|
Then use a thunder-like pattern to show and hide the flash
|
||||||
|
|
||||||
Reach out to someone about producing sounds
|
Sounds
|
||||||
Raindrop sound
|
Raindrop sound
|
||||||
https://youtu.be/bPhu01wpf0k?t=34
|
|
||||||
https://youtu.be/Yt2-h13XK7Y?t=33
|
|
||||||
Deja vu sound
|
Deja vu sound
|
||||||
https://youtu.be/XfEuxRDYiyc?t=17
|
ripple sound
|
||||||
Square sound
|
|
||||||
https://youtu.be/ngnlBZNuVb8?t=204
|
|
||||||
https://youtu.be/-al0i3zSyj0?t=68
|
|
||||||
https://youtu.be/d8My5jIaXoY?t=191
|
|
||||||
Neo flying, apparently
|
|
||||||
https://youtu.be/gZzTVJ-NLYQ?t=230
|
|
||||||
And some kind of ambient sound that they play over
|
And some kind of ambient sound that they play over
|
||||||
|
|
||||||
Much later:
|
|
||||||
Deluxe compute variables
|
|
||||||
Flashing row effect
|
|
||||||
Neo flying
|
|
||||||
Staticky julia set looking silhouette
|
|
||||||
More patterns?
|
|
||||||
Symbol duplication is common
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Also interesting:
|
|
||||||
The Matrix code for the Zion Control construct is sparser, slower, bluer, and annotated
|
|
||||||
https://www.youtube.com/watch?v=Jt5z3OEjDzU
|
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
import { makePassFBO, makePyramid, resizePyramid, makePass } from "./utils.js";
|
import {
|
||||||
|
extractEntries,
|
||||||
|
makePassFBO,
|
||||||
|
makePyramid,
|
||||||
|
resizePyramid,
|
||||||
|
makePass
|
||||||
|
} from "./utils.js";
|
||||||
|
|
||||||
// The bloom pass is basically an added high-pass blur.
|
// The bloom pass is basically an added high-pass blur.
|
||||||
|
|
||||||
@@ -10,7 +16,14 @@ const levelStrengths = Array(pyramidHeight)
|
|||||||
)
|
)
|
||||||
.reverse();
|
.reverse();
|
||||||
|
|
||||||
export default (regl, { bloomSize }, input) => {
|
export default (regl, config, input) => {
|
||||||
|
const uniforms = extractEntries(config, [
|
||||||
|
"bloomRadius",
|
||||||
|
"bloomSize",
|
||||||
|
"bloomStrength",
|
||||||
|
"highPassThreshold"
|
||||||
|
]);
|
||||||
|
|
||||||
const highPassPyramid = makePyramid(regl, pyramidHeight);
|
const highPassPyramid = makePyramid(regl, pyramidHeight);
|
||||||
const hBlurPyramid = makePyramid(regl, pyramidHeight);
|
const hBlurPyramid = makePyramid(regl, pyramidHeight);
|
||||||
const vBlurPyramid = makePyramid(regl, pyramidHeight);
|
const vBlurPyramid = makePyramid(regl, pyramidHeight);
|
||||||
@@ -34,6 +47,7 @@ export default (regl, { bloomSize }, input) => {
|
|||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
uniforms: {
|
uniforms: {
|
||||||
|
...uniforms,
|
||||||
tex: regl.prop("tex")
|
tex: regl.prop("tex")
|
||||||
},
|
},
|
||||||
framebuffer: regl.prop("fbo")
|
framebuffer: regl.prop("fbo")
|
||||||
@@ -45,10 +59,10 @@ export default (regl, { bloomSize }, input) => {
|
|||||||
const blur = regl({
|
const blur = regl({
|
||||||
frag: `
|
frag: `
|
||||||
precision mediump float;
|
precision mediump float;
|
||||||
varying vec2 vUV;
|
uniform float width, height;
|
||||||
uniform sampler2D tex;
|
uniform sampler2D tex;
|
||||||
uniform vec2 direction;
|
uniform vec2 direction;
|
||||||
uniform float width, height;
|
varying vec2 vUV;
|
||||||
void main() {
|
void main() {
|
||||||
vec2 size = width > height ? vec2(width / height, 1.) : vec2(1., height / width);
|
vec2 size = width > height ? vec2(width / height, 1.) : vec2(1., height / width);
|
||||||
gl_FragColor =
|
gl_FragColor =
|
||||||
@@ -60,6 +74,7 @@ export default (regl, { bloomSize }, input) => {
|
|||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
uniforms: {
|
uniforms: {
|
||||||
|
...uniforms,
|
||||||
tex: regl.prop("tex"),
|
tex: regl.prop("tex"),
|
||||||
direction: regl.prop("direction"),
|
direction: regl.prop("direction"),
|
||||||
height: regl.context("viewportWidth"),
|
height: regl.context("viewportWidth"),
|
||||||
@@ -90,6 +105,7 @@ export default (regl, { bloomSize }, input) => {
|
|||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
uniforms: {
|
uniforms: {
|
||||||
|
...uniforms,
|
||||||
tex: input,
|
tex: input,
|
||||||
...Object.fromEntries(
|
...Object.fromEntries(
|
||||||
vBlurPyramid.map((fbo, index) => [`pyr_${index}`, fbo])
|
vBlurPyramid.map((fbo, index) => [`pyr_${index}`, fbo])
|
||||||
@@ -98,8 +114,6 @@ export default (regl, { bloomSize }, input) => {
|
|||||||
framebuffer: output
|
framebuffer: output
|
||||||
});
|
});
|
||||||
|
|
||||||
const scale = bloomSize;
|
|
||||||
|
|
||||||
return makePass(
|
return makePass(
|
||||||
output,
|
output,
|
||||||
() => {
|
() => {
|
||||||
@@ -114,9 +128,9 @@ export default (regl, { bloomSize }, input) => {
|
|||||||
},
|
},
|
||||||
(w, h) => {
|
(w, h) => {
|
||||||
// The blur pyramids can be lower resolution than the screen.
|
// The blur pyramids can be lower resolution than the screen.
|
||||||
resizePyramid(highPassPyramid, w, h, scale);
|
resizePyramid(highPassPyramid, w, h, config.bloomSize);
|
||||||
resizePyramid(hBlurPyramid, w, h, scale);
|
resizePyramid(hBlurPyramid, w, h, config.bloomSize);
|
||||||
resizePyramid(vBlurPyramid, w, h, scale);
|
resizePyramid(vBlurPyramid, w, h, config.bloomSize);
|
||||||
output.resize(w, h);
|
output.resize(w, h);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
196
js/config.js
196
js/config.js
@@ -17,8 +17,10 @@ const fonts = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const defaults = {
|
const defaults = {
|
||||||
|
animationSpeed: 1,
|
||||||
bloomRadius: 0.5,
|
bloomRadius: 0.5,
|
||||||
bloomStrength: 1,
|
bloomStrength: 1,
|
||||||
|
bloomSize: 0.5,
|
||||||
highPassThreshold: 0.3,
|
highPassThreshold: 0.3,
|
||||||
cycleSpeed: 1,
|
cycleSpeed: 1,
|
||||||
cycleStyleName: "cycleFasterWhenDimmed",
|
cycleStyleName: "cycleFasterWhenDimmed",
|
||||||
@@ -107,7 +109,7 @@ const versions = {
|
|||||||
{ rgb: [1.0, 1.0, 0.9], at: 1.0 }
|
{ rgb: [1.0, 1.0, 0.9], at: 1.0 }
|
||||||
],
|
],
|
||||||
raindropLength: 0.6,
|
raindropLength: 0.6,
|
||||||
slant: 360 / 16
|
slant: (22.5 * Math.PI) / 180
|
||||||
},
|
},
|
||||||
paradise: {
|
paradise: {
|
||||||
...defaults,
|
...defaults,
|
||||||
@@ -136,146 +138,56 @@ const versions = {
|
|||||||
versions.throwback = versions.operator;
|
versions.throwback = versions.operator;
|
||||||
versions["1999"] = versions.classic;
|
versions["1999"] = versions.classic;
|
||||||
|
|
||||||
export default (searchString, make1DTexture) => {
|
const range = (f, min = -Infinity, max = Infinity) =>
|
||||||
const urlParams = new URLSearchParams(searchString);
|
Math.max(min, Math.min(max, f));
|
||||||
const getParam = (keyOrKeys, defaultValue) => {
|
const nullNaN = f => (isNaN(f) ? null : f);
|
||||||
if (Array.isArray(keyOrKeys)) {
|
|
||||||
const keys = keyOrKeys;
|
|
||||||
const key = keys.find(key => urlParams.has(key));
|
|
||||||
return key != null ? urlParams.get(key) : defaultValue;
|
|
||||||
} else {
|
|
||||||
const key = keyOrKeys;
|
|
||||||
return urlParams.has(key) ? urlParams.get(key) : defaultValue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const versionName = getParam("version", "classic");
|
const paramMapping = {
|
||||||
const version =
|
version: { key: "version", parser: s => s },
|
||||||
versions[versionName] == null ? versions.classic : versions[versionName];
|
effect: { key: "effect", parser: s => s },
|
||||||
|
width: { key: "numColumns", parser: s => nullNaN(parseInt(s)) },
|
||||||
const config = { ...version };
|
animationSpeed: {
|
||||||
|
key: "animationSpeed",
|
||||||
config.animationSpeed = parseFloat(getParam("animationSpeed", 1));
|
parser: s => nullNaN(parseFloat(s))
|
||||||
config.fallSpeed *= parseFloat(getParam("fallSpeed", 1));
|
},
|
||||||
config.cycleSpeed *= parseFloat(getParam("cycleSpeed", 1));
|
cycleSpeed: { key: "cycleSpeed", parser: s => nullNaN(parseFloat(s)) },
|
||||||
config.numColumns = parseInt(getParam("width", config.numColumns));
|
fallSpeed: { key: "fallSpeed", parser: s => nullNaN(parseFloat(s)) },
|
||||||
config.raindropLength = parseFloat(
|
raindropLength: {
|
||||||
getParam(["raindropLength", "dropLength"], config.raindropLength)
|
key: "raindropLength",
|
||||||
);
|
parser: s => nullNaN(parseFloat(s))
|
||||||
config.glyphSequenceLength = config.glyphSequenceLength;
|
},
|
||||||
config.slant =
|
slant: {
|
||||||
(parseFloat(getParam(["slant", "angle"], config.slant)) * Math.PI) / 180;
|
key: "slant",
|
||||||
config.slantVec = [Math.cos(config.slant), Math.sin(config.slant)];
|
parser: s => nullNaN((parseFloat(s) * Math.PI) / 180)
|
||||||
config.slantScale =
|
},
|
||||||
1 / (Math.abs(Math.sin(2 * config.slant)) * (Math.sqrt(2) - 1) + 1);
|
bloomSize: {
|
||||||
config.glyphEdgeCrop = parseFloat(getParam("encroach", config.glyphEdgeCrop));
|
key: "bloomSize",
|
||||||
config.glyphHeightToWidth = parseFloat(
|
parser: s => nullNaN(range(parseFloat(s), 0.01, 1))
|
||||||
getParam("stretch", config.glyphHeightToWidth)
|
},
|
||||||
);
|
url: { key: "bgURL", parser: s => s },
|
||||||
config.cursorEffectThreshold = getParam(
|
colors: { key: "stripeColors", parser: s => s }
|
||||||
"cursorEffectThreshold",
|
};
|
||||||
config.cursorEffectThreshold
|
paramMapping.dropLength = paramMapping.raindropLength;
|
||||||
);
|
paramMapping.angle = paramMapping.slant;
|
||||||
config.bloomSize = Math.max(
|
|
||||||
0.01,
|
export default (searchString, make1DTexture) => {
|
||||||
Math.min(1, parseFloat(getParam("bloomSize", 0.5)))
|
const urlParams = Object.fromEntries(
|
||||||
);
|
Array.from(new URLSearchParams(searchString).entries())
|
||||||
config.effect = getParam("effect", "plain");
|
.filter(([key]) => key in paramMapping)
|
||||||
config.bgURL = getParam(
|
.map(([key, value]) => [
|
||||||
"url",
|
paramMapping[key].key,
|
||||||
"https://upload.wikimedia.org/wikipedia/commons/0/0a/Flammarion_Colored.jpg"
|
paramMapping[key].parser(value)
|
||||||
);
|
])
|
||||||
config.customStripes = getParam(
|
.filter(([_, value]) => value != null)
|
||||||
"colors",
|
);
|
||||||
"0.4,0.15,0.1,0.4,0.15,0.1,0.8,0.8,0.6,0.8,0.8,0.6,1.0,0.7,0.8,1.0,0.7,0.8,"
|
|
||||||
)
|
const version =
|
||||||
.split(",")
|
urlParams.version in versions
|
||||||
.map(parseFloat);
|
? versions[urlParams.version]
|
||||||
config.showComputationTexture = config.effect === "none";
|
: versions.classic;
|
||||||
|
|
||||||
switch (config.cycleStyleName) {
|
return {
|
||||||
case "cycleFasterWhenDimmed":
|
...version,
|
||||||
config.cycleStyle = 0;
|
...urlParams
|
||||||
break;
|
};
|
||||||
case "cycleRandomly":
|
|
||||||
default:
|
|
||||||
config.cycleStyle = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (config.rippleTypeName) {
|
|
||||||
case "box":
|
|
||||||
config.rippleType = 0;
|
|
||||||
break;
|
|
||||||
case "circle":
|
|
||||||
config.rippleType = 1;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
config.rippleType = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const PALETTE_SIZE = 2048;
|
|
||||||
const paletteColors = Array(PALETTE_SIZE);
|
|
||||||
const sortedEntries = version.paletteEntries
|
|
||||||
.slice()
|
|
||||||
.sort((e1, e2) => e1.at - e2.at)
|
|
||||||
.map(entry => ({
|
|
||||||
rgb: entry.rgb,
|
|
||||||
arrayIndex: Math.floor(
|
|
||||||
Math.max(Math.min(1, entry.at), 0) * (PALETTE_SIZE - 1)
|
|
||||||
)
|
|
||||||
}));
|
|
||||||
sortedEntries.unshift({ rgb: sortedEntries[0].rgb, arrayIndex: 0 });
|
|
||||||
sortedEntries.push({
|
|
||||||
rgb: sortedEntries[sortedEntries.length - 1].rgb,
|
|
||||||
arrayIndex: PALETTE_SIZE - 1
|
|
||||||
});
|
|
||||||
sortedEntries.forEach((entry, index) => {
|
|
||||||
paletteColors[entry.arrayIndex] = entry.rgb.slice();
|
|
||||||
if (index + 1 < sortedEntries.length) {
|
|
||||||
const nextEntry = sortedEntries[index + 1];
|
|
||||||
const diff = nextEntry.arrayIndex - entry.arrayIndex;
|
|
||||||
for (let i = 0; i < diff; i++) {
|
|
||||||
const ratio = i / diff;
|
|
||||||
paletteColors[entry.arrayIndex + i] = [
|
|
||||||
entry.rgb[0] * (1 - ratio) + nextEntry.rgb[0] * ratio,
|
|
||||||
entry.rgb[1] * (1 - ratio) + nextEntry.rgb[1] * ratio,
|
|
||||||
entry.rgb[2] * (1 - ratio) + nextEntry.rgb[2] * ratio
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
config.palette = make1DTexture(paletteColors.flat().map(i => i * 0xff));
|
|
||||||
|
|
||||||
let stripeColors = [0, 0, 0];
|
|
||||||
|
|
||||||
if (config.effect === "pride") {
|
|
||||||
config.effect = "stripes";
|
|
||||||
config.stripeColors = [
|
|
||||||
[1, 0, 0],
|
|
||||||
[1, 0.5, 0],
|
|
||||||
[1, 1, 0],
|
|
||||||
[0, 1, 0],
|
|
||||||
[0, 0, 1],
|
|
||||||
[0.8, 0, 1]
|
|
||||||
].flat();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.effect === "customStripes" || config.effect === "stripes") {
|
|
||||||
config.effect = "stripes";
|
|
||||||
const numStripeColors = Math.floor(config.stripeColors.length / 3);
|
|
||||||
stripeColors = config.stripeColors.slice(0, numStripeColors * 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
config.stripes = make1DTexture(stripeColors.map(f => Math.floor(f * 0xff)));
|
|
||||||
|
|
||||||
const uniforms = Object.fromEntries(
|
|
||||||
Object.entries(config).filter(([key, value]) => {
|
|
||||||
const type = typeof (Array.isArray(value) ? value[0] : value);
|
|
||||||
return type !== "string" && type !== "object";
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
return [config, uniforms];
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
import { loadImage, makePassFBO, makePass } from "./utils.js";
|
import { loadImage, makePassFBO, makePass } from "./utils.js";
|
||||||
|
|
||||||
export default (regl, { bgURL }, input) => {
|
const defaultBGURL =
|
||||||
|
"https://upload.wikimedia.org/wikipedia/commons/0/0a/Flammarion_Colored.jpg";
|
||||||
|
|
||||||
|
export default (regl, config, input) => {
|
||||||
const output = makePassFBO(regl);
|
const output = makePassFBO(regl);
|
||||||
|
const bgURL = "bgURL" in config ? config.bgURL : defaultBGURL;
|
||||||
const bgLoader = loadImage(regl, bgURL);
|
const bgLoader = loadImage(regl, bgURL);
|
||||||
return makePass(
|
return makePass(
|
||||||
output,
|
output,
|
||||||
|
|||||||
13
js/main.js
13
js/main.js
@@ -1,4 +1,4 @@
|
|||||||
import { makeFullScreenQuad, make1DTexture, makePipeline } from "./utils.js";
|
import { makeFullScreenQuad, makePipeline } from "./utils.js";
|
||||||
import makeConfig from "./config.js";
|
import makeConfig from "./config.js";
|
||||||
import makeMatrixRenderer from "./renderer.js";
|
import makeMatrixRenderer from "./renderer.js";
|
||||||
import makeBloomPass from "./bloomPass.js";
|
import makeBloomPass from "./bloomPass.js";
|
||||||
@@ -26,13 +26,13 @@ const regl = createREGL({
|
|||||||
const effects = {
|
const effects = {
|
||||||
none: null,
|
none: null,
|
||||||
plain: makePalettePass,
|
plain: makePalettePass,
|
||||||
|
customStripes: makeStripePass,
|
||||||
stripes: makeStripePass,
|
stripes: makeStripePass,
|
||||||
|
pride: makeStripePass,
|
||||||
image: makeImagePass
|
image: makeImagePass
|
||||||
};
|
};
|
||||||
|
|
||||||
const [config, uniforms] = makeConfig(window.location.search, data =>
|
const config = makeConfig(window.location.search);
|
||||||
make1DTexture(regl, data)
|
|
||||||
);
|
|
||||||
const effect = config.effect in effects ? config.effect : "plain";
|
const effect = config.effect in effects ? config.effect : "plain";
|
||||||
|
|
||||||
const resize = () => {
|
const resize = () => {
|
||||||
@@ -44,7 +44,7 @@ resize();
|
|||||||
|
|
||||||
document.body.onload = async () => {
|
document.body.onload = async () => {
|
||||||
// All this takes place in a full screen quad.
|
// All this takes place in a full screen quad.
|
||||||
const fullScreenQuad = makeFullScreenQuad(regl, uniforms);
|
const fullScreenQuad = makeFullScreenQuad(regl);
|
||||||
const pipeline = makePipeline(
|
const pipeline = makePipeline(
|
||||||
[
|
[
|
||||||
makeMatrixRenderer,
|
makeMatrixRenderer,
|
||||||
@@ -61,7 +61,8 @@ document.body.onload = async () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
await Promise.all(pipeline.map(({ ready }) => ready));
|
await Promise.all(pipeline.map(({ ready }) => ready));
|
||||||
regl.frame(({ viewportWidth, viewportHeight }) => {
|
const tick = regl.frame(({ viewportWidth, viewportHeight }) => {
|
||||||
|
// tick.cancel();
|
||||||
pipeline.forEach(({ resize }) => resize(viewportWidth, viewportHeight));
|
pipeline.forEach(({ resize }) => resize(viewportWidth, viewportHeight));
|
||||||
fullScreenQuad(() => {
|
fullScreenQuad(() => {
|
||||||
pipeline.forEach(({ render }) => render());
|
pipeline.forEach(({ render }) => render());
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { makePassFBO, makePass } from "./utils.js";
|
import { make1DTexture, makePassFBO, makePass } from "./utils.js";
|
||||||
|
|
||||||
// The rendered texture's values are mapped to colors in a palette texture.
|
// The rendered texture's values are mapped to colors in a palette texture.
|
||||||
// A little noise is introduced, to hide the banding that appears
|
// A little noise is introduced, to hide the banding that appears
|
||||||
@@ -6,8 +6,43 @@ import { makePassFBO, makePass } from "./utils.js";
|
|||||||
// won't persist across subsequent frames. This is a safe trick
|
// won't persist across subsequent frames. This is a safe trick
|
||||||
// in screen space.
|
// in screen space.
|
||||||
|
|
||||||
export default (regl, {}, input) => {
|
export default (regl, config, input) => {
|
||||||
const output = makePassFBO(regl);
|
const output = makePassFBO(regl);
|
||||||
|
|
||||||
|
const PALETTE_SIZE = 2048;
|
||||||
|
const paletteColors = Array(PALETTE_SIZE);
|
||||||
|
const sortedEntries = config.paletteEntries
|
||||||
|
.slice()
|
||||||
|
.sort((e1, e2) => e1.at - e2.at)
|
||||||
|
.map(entry => ({
|
||||||
|
rgb: entry.rgb,
|
||||||
|
arrayIndex: Math.floor(
|
||||||
|
Math.max(Math.min(1, entry.at), 0) * (PALETTE_SIZE - 1)
|
||||||
|
)
|
||||||
|
}));
|
||||||
|
sortedEntries.unshift({ rgb: sortedEntries[0].rgb, arrayIndex: 0 });
|
||||||
|
sortedEntries.push({
|
||||||
|
rgb: sortedEntries[sortedEntries.length - 1].rgb,
|
||||||
|
arrayIndex: PALETTE_SIZE - 1
|
||||||
|
});
|
||||||
|
sortedEntries.forEach((entry, index) => {
|
||||||
|
paletteColors[entry.arrayIndex] = entry.rgb.slice();
|
||||||
|
if (index + 1 < sortedEntries.length) {
|
||||||
|
const nextEntry = sortedEntries[index + 1];
|
||||||
|
const diff = nextEntry.arrayIndex - entry.arrayIndex;
|
||||||
|
for (let i = 0; i < diff; i++) {
|
||||||
|
const ratio = i / diff;
|
||||||
|
paletteColors[entry.arrayIndex + i] = [
|
||||||
|
entry.rgb[0] * (1 - ratio) + nextEntry.rgb[0] * ratio,
|
||||||
|
entry.rgb[1] * (1 - ratio) + nextEntry.rgb[1] * ratio,
|
||||||
|
entry.rgb[2] * (1 - ratio) + nextEntry.rgb[2] * ratio
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const palette = make1DTexture(regl, paletteColors.flat().map(i => i * 0xff));
|
||||||
|
|
||||||
return makePass(
|
return makePass(
|
||||||
output,
|
output,
|
||||||
regl({
|
regl({
|
||||||
@@ -35,6 +70,7 @@ export default (regl, {}, input) => {
|
|||||||
|
|
||||||
uniforms: {
|
uniforms: {
|
||||||
tex: input,
|
tex: input,
|
||||||
|
palette,
|
||||||
ditherMagnitude: 0.05
|
ditherMagnitude: 0.05
|
||||||
},
|
},
|
||||||
framebuffer: output
|
framebuffer: output
|
||||||
|
|||||||
105
js/renderer.js
105
js/renderer.js
@@ -1,4 +1,20 @@
|
|||||||
import { loadImage, makePassFBO, makeDoubleBuffer, makePass } from "./utils.js";
|
import {
|
||||||
|
extractEntries,
|
||||||
|
loadImage,
|
||||||
|
makePassFBO,
|
||||||
|
makeDoubleBuffer,
|
||||||
|
makePass
|
||||||
|
} from "./utils.js";
|
||||||
|
|
||||||
|
const rippleTypes = {
|
||||||
|
box: 0,
|
||||||
|
circle: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
const cycleStyles = {
|
||||||
|
cycleFasterWhenDimmed: 0,
|
||||||
|
cycleRandomly: 1
|
||||||
|
};
|
||||||
|
|
||||||
export default (regl, config) => {
|
export default (regl, config) => {
|
||||||
// These two framebuffers are used to compute the raining code.
|
// These two framebuffers are used to compute the raining code.
|
||||||
@@ -16,6 +32,47 @@ export default (regl, config) => {
|
|||||||
|
|
||||||
const output = makePassFBO(regl);
|
const output = makePassFBO(regl);
|
||||||
|
|
||||||
|
const uniforms = extractEntries(config, [
|
||||||
|
// rain general
|
||||||
|
"glyphHeightToWidth",
|
||||||
|
"glyphTextureColumns",
|
||||||
|
"numColumns",
|
||||||
|
// rain update
|
||||||
|
"animationSpeed",
|
||||||
|
"brightnessMinimum",
|
||||||
|
"brightnessMix",
|
||||||
|
"brightnessMultiplier",
|
||||||
|
"brightnessOffset",
|
||||||
|
"cursorEffectThreshold",
|
||||||
|
"cycleSpeed",
|
||||||
|
"fallSpeed",
|
||||||
|
"glyphSequenceLength",
|
||||||
|
"hasSun",
|
||||||
|
"hasThunder",
|
||||||
|
"raindropLength",
|
||||||
|
"rippleScale",
|
||||||
|
"rippleSpeed",
|
||||||
|
"rippleThickness",
|
||||||
|
// rain render
|
||||||
|
"glyphEdgeCrop",
|
||||||
|
"isPolar"
|
||||||
|
]);
|
||||||
|
|
||||||
|
uniforms.rippleType =
|
||||||
|
config.rippleTypeName in rippleTypes
|
||||||
|
? rippleTypes[config.rippleTypeName]
|
||||||
|
: -1;
|
||||||
|
uniforms.cycleStyle =
|
||||||
|
config.cycleStyleName in cycleStyles
|
||||||
|
? cycleStyles[config.cycleStyleName]
|
||||||
|
: 0;
|
||||||
|
uniforms.slantVec = [Math.cos(config.slant), Math.sin(config.slant)];
|
||||||
|
uniforms.slantScale =
|
||||||
|
1 / (Math.abs(Math.sin(2 * config.slant)) * (Math.sqrt(2) - 1) + 1);
|
||||||
|
uniforms.showComputationTexture = config.effect === "none";
|
||||||
|
|
||||||
|
const msdfLoader = loadImage(regl, config.glyphTexURL);
|
||||||
|
|
||||||
// This shader is the star of the show.
|
// This shader is the star of the show.
|
||||||
// In normal operation, each pixel represents a glyph's:
|
// In normal operation, each pixel represents a glyph's:
|
||||||
// R: brightness
|
// R: brightness
|
||||||
@@ -30,34 +87,19 @@ export default (regl, config) => {
|
|||||||
#define SQRT_2 1.4142135623730951
|
#define SQRT_2 1.4142135623730951
|
||||||
#define SQRT_5 2.23606797749979
|
#define SQRT_5 2.23606797749979
|
||||||
|
|
||||||
|
uniform float time;
|
||||||
uniform float numColumns;
|
uniform float numColumns;
|
||||||
uniform sampler2D lastState;
|
uniform sampler2D lastState;
|
||||||
|
|
||||||
uniform bool hasSun;
|
uniform bool hasSun;
|
||||||
uniform bool hasThunder;
|
uniform bool hasThunder;
|
||||||
uniform bool showComputationTexture;
|
uniform bool showComputationTexture;
|
||||||
|
uniform float brightnessMinimum, brightnessMultiplier, brightnessOffset, brightnessMix;
|
||||||
uniform float brightnessMinimum;
|
uniform float animationSpeed, fallSpeed, cycleSpeed;
|
||||||
uniform float brightnessMultiplier;
|
|
||||||
uniform float brightnessOffset;
|
|
||||||
uniform float brightnessMix;
|
|
||||||
|
|
||||||
uniform float time;
|
|
||||||
uniform float animationSpeed;
|
|
||||||
uniform float cycleSpeed;
|
|
||||||
uniform float fallSpeed;
|
|
||||||
uniform float raindropLength;
|
uniform float raindropLength;
|
||||||
|
uniform float glyphHeightToWidth, glyphSequenceLength, glyphTextureColumns;
|
||||||
uniform float glyphHeightToWidth;
|
|
||||||
uniform float glyphSequenceLength;
|
|
||||||
uniform float glyphTextureColumns;
|
|
||||||
uniform int cycleStyle;
|
uniform int cycleStyle;
|
||||||
|
uniform float rippleScale, rippleSpeed, rippleThickness;
|
||||||
uniform float rippleScale;
|
|
||||||
uniform float rippleSpeed;
|
|
||||||
uniform float rippleThickness;
|
|
||||||
uniform int rippleType;
|
uniform int rippleType;
|
||||||
|
|
||||||
uniform float cursorEffectThreshold;
|
uniform float cursorEffectThreshold;
|
||||||
|
|
||||||
float max2(vec2 v) {
|
float max2(vec2 v) {
|
||||||
@@ -88,10 +130,10 @@ export default (regl, config) => {
|
|||||||
|
|
||||||
float getGlyphCycleSpeed(float rainTime, float brightness) {
|
float getGlyphCycleSpeed(float rainTime, float brightness) {
|
||||||
float glyphCycleSpeed = 0.0;
|
float glyphCycleSpeed = 0.0;
|
||||||
if (cycleStyle == 1) {
|
if (cycleStyle == 0 && brightness > 0.0) {
|
||||||
glyphCycleSpeed = fract((rainTime + 0.7 * sin(SQRT_2 * rainTime) + 1.1 * sin(SQRT_5 * rainTime))) * 0.75;
|
|
||||||
} else if (cycleStyle == 0 && brightness > 0.0) {
|
|
||||||
glyphCycleSpeed = pow(1.0 - brightness, 4.0);
|
glyphCycleSpeed = pow(1.0 - brightness, 4.0);
|
||||||
|
} else if (cycleStyle == 1) {
|
||||||
|
glyphCycleSpeed = fract((rainTime + 0.7 * sin(SQRT_2 * rainTime) + 1.1 * sin(SQRT_5 * rainTime))) * 0.75;
|
||||||
}
|
}
|
||||||
return glyphCycleSpeed;
|
return glyphCycleSpeed;
|
||||||
}
|
}
|
||||||
@@ -207,20 +249,18 @@ export default (regl, config) => {
|
|||||||
`,
|
`,
|
||||||
|
|
||||||
uniforms: {
|
uniforms: {
|
||||||
|
...uniforms,
|
||||||
lastState: doubleBuffer.back
|
lastState: doubleBuffer.back
|
||||||
},
|
},
|
||||||
|
|
||||||
framebuffer: doubleBuffer.front
|
framebuffer: doubleBuffer.front
|
||||||
});
|
});
|
||||||
|
|
||||||
const msdfLoader = loadImage(regl, config.glyphTexURL);
|
|
||||||
|
|
||||||
// We render the code into an FBO using MSDFs: https://github.com/Chlumsky/msdfgen
|
// We render the code into an FBO using MSDFs: https://github.com/Chlumsky/msdfgen
|
||||||
const render = regl({
|
const render = regl({
|
||||||
vert: `
|
vert: `
|
||||||
attribute vec2 aPosition;
|
attribute vec2 aPosition;
|
||||||
uniform float width;
|
uniform float width, height;
|
||||||
uniform float height;
|
|
||||||
varying vec2 vUV;
|
varying vec2 vUV;
|
||||||
void main() {
|
void main() {
|
||||||
vUV = aPosition / 2.0 + 0.5;
|
vUV = aPosition / 2.0 + 0.5;
|
||||||
@@ -237,15 +277,11 @@ export default (regl, config) => {
|
|||||||
#endif
|
#endif
|
||||||
precision lowp float;
|
precision lowp float;
|
||||||
|
|
||||||
uniform sampler2D glyphTex;
|
|
||||||
uniform sampler2D lastState;
|
|
||||||
uniform float numColumns;
|
uniform float numColumns;
|
||||||
uniform float glyphTextureColumns;
|
uniform sampler2D glyphTex, lastState;
|
||||||
|
uniform float glyphHeightToWidth, glyphTextureColumns, glyphEdgeCrop;
|
||||||
uniform vec2 slantVec;
|
uniform vec2 slantVec;
|
||||||
uniform float slantScale;
|
uniform float slantScale;
|
||||||
uniform float glyphHeightToWidth;
|
|
||||||
uniform float glyphEdgeCrop;
|
|
||||||
|
|
||||||
uniform bool isPolar;
|
uniform bool isPolar;
|
||||||
uniform bool showComputationTexture;
|
uniform bool showComputationTexture;
|
||||||
|
|
||||||
@@ -309,6 +345,7 @@ export default (regl, config) => {
|
|||||||
`,
|
`,
|
||||||
|
|
||||||
uniforms: {
|
uniforms: {
|
||||||
|
...uniforms,
|
||||||
glyphTex: msdfLoader.texture,
|
glyphTex: msdfLoader.texture,
|
||||||
height: regl.context("viewportWidth"),
|
height: regl.context("viewportWidth"),
|
||||||
width: regl.context("viewportHeight"),
|
width: regl.context("viewportHeight"),
|
||||||
|
|||||||
@@ -1,7 +1,38 @@
|
|||||||
import { makePassFBO, makePass } from "./utils.js";
|
import { make1DTexture, makePassFBO, makePass } from "./utils.js";
|
||||||
|
|
||||||
export default (regl, {}, input) => {
|
const neapolitanStripeColors = [
|
||||||
|
[0.4, 0.15, 0.1],
|
||||||
|
[0.4, 0.15, 0.1],
|
||||||
|
[0.8, 0.8, 0.6],
|
||||||
|
[0.8, 0.8, 0.6],
|
||||||
|
[1.0, 0.7, 0.8],
|
||||||
|
[1.0, 0.7, 0.8]
|
||||||
|
].flat();
|
||||||
|
|
||||||
|
const prideStripeColors = [
|
||||||
|
[1, 0, 0],
|
||||||
|
[1, 0.5, 0],
|
||||||
|
[1, 1, 0],
|
||||||
|
[0, 1, 0],
|
||||||
|
[0, 0, 1],
|
||||||
|
[0.8, 0, 1]
|
||||||
|
].flat();
|
||||||
|
|
||||||
|
export default (regl, config, input) => {
|
||||||
const output = makePassFBO(regl);
|
const output = makePassFBO(regl);
|
||||||
|
|
||||||
|
const stripeColors =
|
||||||
|
"stripeColors" in config
|
||||||
|
? config.stripeColors.split(",").map(parseFloat)
|
||||||
|
: config.effect === "pride"
|
||||||
|
? prideStripeColors
|
||||||
|
: neapolitanStripeColors;
|
||||||
|
const numStripeColors = Math.floor(stripeColors.length / 3);
|
||||||
|
const stripes = make1DTexture(
|
||||||
|
regl,
|
||||||
|
stripeColors.slice(0, numStripeColors * 3).map(f => Math.floor(f * 0xff))
|
||||||
|
);
|
||||||
|
|
||||||
return makePass(
|
return makePass(
|
||||||
output,
|
output,
|
||||||
regl({
|
regl({
|
||||||
@@ -12,24 +43,26 @@ export default (regl, {}, input) => {
|
|||||||
uniform sampler2D tex;
|
uniform sampler2D tex;
|
||||||
uniform sampler2D stripes;
|
uniform sampler2D stripes;
|
||||||
uniform float ditherMagnitude;
|
uniform float ditherMagnitude;
|
||||||
|
uniform float time;
|
||||||
varying vec2 vUV;
|
varying vec2 vUV;
|
||||||
|
|
||||||
highp float rand( const in vec2 uv ) {
|
highp float rand( const in vec2 uv, const in float t ) {
|
||||||
const highp float a = 12.9898, b = 78.233, c = 43758.5453;
|
const highp float a = 12.9898, b = 78.233, c = 43758.5453;
|
||||||
highp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );
|
highp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );
|
||||||
return fract(sin(sn) * c);
|
return fract(sin(sn) * c + t);
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec3 color = texture2D(stripes, vUV).rgb - rand( gl_FragCoord.xy ) * ditherMagnitude;
|
vec3 color = texture2D(stripes, vUV).rgb;
|
||||||
float brightness = texture2D(tex, vUV).r;
|
float brightness = texture2D(tex, vUV).r - rand( gl_FragCoord.xy, time ) * ditherMagnitude;
|
||||||
gl_FragColor = vec4(color * brightness, 1.0);
|
gl_FragColor = vec4(color * brightness, 1.0);
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
|
||||||
uniforms: {
|
uniforms: {
|
||||||
tex: input,
|
tex: input,
|
||||||
ditherMagnitude: 0.1
|
stripes,
|
||||||
|
ditherMagnitude: 0.05
|
||||||
},
|
},
|
||||||
framebuffer: output
|
framebuffer: output
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
const extractEntries = (src, keys) =>
|
||||||
|
Object.fromEntries(
|
||||||
|
Array.from(Object.entries(src)).filter(([key]) => keys.includes(key))
|
||||||
|
);
|
||||||
|
|
||||||
const makePassTexture = regl =>
|
const makePassTexture = regl =>
|
||||||
regl.texture({
|
regl.texture({
|
||||||
width: 1,
|
width: 1,
|
||||||
@@ -167,6 +172,7 @@ const makePipeline = (steps, getInput, ...params) =>
|
|||||||
);
|
);
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
extractEntries,
|
||||||
makePassTexture,
|
makePassTexture,
|
||||||
makePassFBO,
|
makePassFBO,
|
||||||
makeDoubleBuffer,
|
makeDoubleBuffer,
|
||||||
|
|||||||
Reference in New Issue
Block a user