Ran all the JS through prettier.

This commit is contained in:
Rezmason
2021-10-20 03:25:04 -07:00
parent d8a1409907
commit 91deea34d6
10 changed files with 776 additions and 864 deletions

View File

@@ -2,10 +2,7 @@
<head> <head>
<title>Matrix digital rain</title> <title>Matrix digital rain</title>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0" />
name="viewport"
content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"
/>
<style> <style>
body { body {
background: black; background: black;

View File

@@ -1,39 +1,24 @@
import { import { loadText, extractEntries, makePassFBO, makePyramid, resizePyramid, makePass } from "./utils.js";
loadText,
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.
const pyramidHeight = 5; const pyramidHeight = 5;
const levelStrengths = Array(pyramidHeight) const levelStrengths = Array(pyramidHeight)
.fill() .fill()
.map((_, index) => .map((_, index) => Math.pow(index / (pyramidHeight * 2) + 0.5, 1 / 3).toPrecision(5))
Math.pow(index / (pyramidHeight * 2) + 0.5, 1 / 3).toPrecision(5)
)
.reverse(); .reverse();
export default (regl, config, inputs) => { export default (regl, config, inputs) => {
const enabled = config.bloomSize > 0 && config.bloomStrength > 0; const enabled = config.bloomSize > 0 && config.bloomStrength > 0;
if (!enabled) { if (!enabled) {
return makePass( return makePass({
{
primary: inputs.primary, primary: inputs.primary,
bloom: makePassFBO(regl) bloom: makePassFBO(regl),
} });
);
} }
const uniforms = extractEntries(config, [ const uniforms = extractEntries(config, ["bloomStrength", "highPassThreshold"]);
"bloomStrength",
"highPassThreshold"
]);
const highPassPyramid = makePyramid(regl, pyramidHeight, config.useHalfFloat); const highPassPyramid = makePyramid(regl, pyramidHeight, config.useHalfFloat);
const hBlurPyramid = makePyramid(regl, pyramidHeight, config.useHalfFloat); const hBlurPyramid = makePyramid(regl, pyramidHeight, config.useHalfFloat);
@@ -47,9 +32,9 @@ export default (regl, config, inputs) => {
frag: regl.prop("frag"), frag: regl.prop("frag"),
uniforms: { uniforms: {
...uniforms, ...uniforms,
tex: regl.prop("tex") tex: regl.prop("tex"),
}, },
framebuffer: regl.prop("fbo") framebuffer: regl.prop("fbo"),
}); });
// A 2D gaussian blur is just a 1D blur done horizontally, then done vertically. // A 2D gaussian blur is just a 1D blur done horizontally, then done vertically.
@@ -64,9 +49,9 @@ export default (regl, config, inputs) => {
tex: regl.prop("tex"), tex: regl.prop("tex"),
direction: regl.prop("direction"), direction: regl.prop("direction"),
height: regl.context("viewportWidth"), height: regl.context("viewportWidth"),
width: regl.context("viewportHeight") width: regl.context("viewportHeight"),
}, },
framebuffer: regl.prop("fbo") framebuffer: regl.prop("fbo"),
}); });
// The pyramid of textures gets flattened onto the source texture. // The pyramid of textures gets flattened onto the source texture.
@@ -74,34 +59,25 @@ export default (regl, config, inputs) => {
frag: ` frag: `
precision mediump float; precision mediump float;
varying vec2 vUV; varying vec2 vUV;
${vBlurPyramid ${vBlurPyramid.map((_, index) => `uniform sampler2D pyr_${index};`).join("\n")}
.map((_, index) => `uniform sampler2D pyr_${index};`)
.join("\n")}
uniform float bloomStrength; uniform float bloomStrength;
void main() { void main() {
vec4 total = vec4(0.); vec4 total = vec4(0.);
${vBlurPyramid ${vBlurPyramid.map((_, index) => `total += texture2D(pyr_${index}, vUV) * ${levelStrengths[index]};`).join("\n")}
.map(
(_, index) =>
`total += texture2D(pyr_${index}, vUV) * ${levelStrengths[index]};`
)
.join("\n")}
gl_FragColor = total * bloomStrength; gl_FragColor = total * bloomStrength;
} }
`, `,
uniforms: { uniforms: {
...uniforms, ...uniforms,
...Object.fromEntries( ...Object.fromEntries(vBlurPyramid.map((fbo, index) => [`pyr_${index}`, fbo])),
vBlurPyramid.map((fbo, index) => [`pyr_${index}`, fbo])
)
}, },
framebuffer: output framebuffer: output,
}); });
return makePass( return makePass(
{ {
primary: inputs.primary, primary: inputs.primary,
bloom: output bloom: output,
}, },
() => { () => {
for (let i = 0; i < pyramidHeight; i++) { for (let i = 0; i < pyramidHeight; i++) {

View File

@@ -2,18 +2,18 @@ const fonts = {
coptic: { coptic: {
glyphTexURL: "coptic_msdf.png", glyphTexURL: "coptic_msdf.png",
glyphSequenceLength: 32, glyphSequenceLength: 32,
glyphTextureColumns: 8 glyphTextureColumns: 8,
}, },
gothic: { gothic: {
glyphTexURL: "gothic_msdf.png", glyphTexURL: "gothic_msdf.png",
glyphSequenceLength: 27, glyphSequenceLength: 27,
glyphTextureColumns: 8 glyphTextureColumns: 8,
}, },
matrixcode: { matrixcode: {
glyphTexURL: "matrixcode_msdf.png", glyphTexURL: "matrixcode_msdf.png",
glyphSequenceLength: 57, glyphSequenceLength: 57,
glyphTextureColumns: 8 glyphTextureColumns: 8,
} },
}; };
const defaults = { const defaults = {
@@ -48,7 +48,7 @@ const defaults = {
{ hsl: [0.3, 0.9, 0.0], at: 0.0 }, { hsl: [0.3, 0.9, 0.0], at: 0.0 },
{ hsl: [0.3, 0.9, 0.2], at: 0.2 }, { hsl: [0.3, 0.9, 0.2], at: 0.2 },
{ hsl: [0.3, 0.9, 0.7], at: 0.7 }, { hsl: [0.3, 0.9, 0.7], at: 0.7 },
{ hsl: [0.3, 0.9, 0.8], at: 0.8 } { hsl: [0.3, 0.9, 0.8], at: 0.8 },
], ],
raindropLength: 1, raindropLength: 1,
slant: 0, slant: 0,
@@ -59,7 +59,7 @@ const defaults = {
const versions = { const versions = {
classic: { classic: {
...defaults, ...defaults,
...fonts.matrixcode ...fonts.matrixcode,
}, },
operator: { operator: {
...defaults, ...defaults,
@@ -80,9 +80,9 @@ const versions = {
paletteEntries: [ paletteEntries: [
{ hsl: [0.4, 0.8, 0.0], at: 0.0 }, { hsl: [0.4, 0.8, 0.0], at: 0.0 },
{ hsl: [0.4, 0.8, 0.5], at: 0.5 }, { hsl: [0.4, 0.8, 0.5], at: 0.5 },
{ hsl: [0.4, 0.8, 1.0], at: 1.0 } { hsl: [0.4, 0.8, 1.0], at: 1.0 },
], ],
raindropLength: 1.5 raindropLength: 1.5,
}, },
nightmare: { nightmare: {
...defaults, ...defaults,
@@ -97,10 +97,10 @@ const versions = {
{ hsl: [0.0, 1.0, 0.2], at: 0.2 }, { hsl: [0.0, 1.0, 0.2], at: 0.2 },
{ hsl: [0.0, 1.0, 0.4], at: 0.4 }, { hsl: [0.0, 1.0, 0.4], at: 0.4 },
{ hsl: [0.1, 1.0, 0.7], at: 0.7 }, { hsl: [0.1, 1.0, 0.7], at: 0.7 },
{ hsl: [0.2, 1.0, 1.0], at: 1.0 } { hsl: [0.2, 1.0, 1.0], at: 1.0 },
], ],
raindropLength: 0.6, raindropLength: 0.6,
slant: (22.5 * Math.PI) / 180 slant: (22.5 * Math.PI) / 180,
}, },
paradise: { paradise: {
...defaults, ...defaults,
@@ -120,9 +120,9 @@ const versions = {
{ hsl: [0.0, 0.8, 0.3], at: 0.3 }, { hsl: [0.0, 0.8, 0.3], at: 0.3 },
{ hsl: [0.1, 0.8, 0.5], at: 0.5 }, { hsl: [0.1, 0.8, 0.5], at: 0.5 },
{ hsl: [0.1, 1.0, 0.6], at: 0.6 }, { hsl: [0.1, 1.0, 0.6], at: 0.6 },
{ hsl: [0.1, 1.0, 0.9], at: 0.9 } { hsl: [0.1, 1.0, 0.9], at: 0.9 },
], ],
raindropLength: 0.4 raindropLength: 0.4,
}, },
resurrections: { resurrections: {
...defaults, ...defaults,
@@ -133,49 +133,48 @@ const versions = {
volumetric: true, volumetric: true,
density: 1.5, density: 1.5,
fallSpeed: 1.2, fallSpeed: 1.2,
raindropLength:1.25 raindropLength: 1.25,
} },
}; };
versions.throwback = versions.operator; versions.throwback = versions.operator;
versions["1999"] = versions.classic; versions["1999"] = versions.classic;
const range = (f, min = -Infinity, max = Infinity) => const range = (f, min = -Infinity, max = Infinity) => Math.max(min, Math.min(max, f));
Math.max(min, Math.min(max, f)); const nullNaN = (f) => (isNaN(f) ? null : f);
const nullNaN = f => (isNaN(f) ? null : f);
const paramMapping = { const paramMapping = {
version: { key: "version", parser: s => s }, version: { key: "version", parser: (s) => s },
effect: { key: "effect", parser: s => s }, effect: { key: "effect", parser: (s) => s },
width: { key: "numColumns", parser: s => nullNaN(parseInt(s)) }, width: { key: "numColumns", parser: (s) => nullNaN(parseInt(s)) },
numColumns: { key: "numColumns", parser: s => nullNaN(parseInt(s)) }, numColumns: { key: "numColumns", parser: (s) => nullNaN(parseInt(s)) },
density: { key: "density", parser: s => nullNaN(range(parseFloat(s), 0)) }, density: { key: "density", parser: (s) => nullNaN(range(parseFloat(s), 0)) },
resolution: { key: "resolution", parser: s => nullNaN(parseFloat(s)) }, resolution: { key: "resolution", parser: (s) => nullNaN(parseFloat(s)) },
animationSpeed: { animationSpeed: {
key: "animationSpeed", key: "animationSpeed",
parser: s => nullNaN(parseFloat(s)) parser: (s) => nullNaN(parseFloat(s)),
}, },
forwardSpeed: { forwardSpeed: {
key: "forwardSpeed", key: "forwardSpeed",
parser: s => nullNaN(parseFloat(s)) parser: (s) => nullNaN(parseFloat(s)),
}, },
cycleSpeed: { key: "cycleSpeed", parser: s => nullNaN(parseFloat(s)) }, cycleSpeed: { key: "cycleSpeed", parser: (s) => nullNaN(parseFloat(s)) },
fallSpeed: { key: "fallSpeed", parser: s => nullNaN(parseFloat(s)) }, fallSpeed: { key: "fallSpeed", parser: (s) => nullNaN(parseFloat(s)) },
raindropLength: { raindropLength: {
key: "raindropLength", key: "raindropLength",
parser: s => nullNaN(parseFloat(s)) parser: (s) => nullNaN(parseFloat(s)),
}, },
slant: { slant: {
key: "slant", key: "slant",
parser: s => nullNaN((parseFloat(s) * Math.PI) / 180) parser: (s) => nullNaN((parseFloat(s) * Math.PI) / 180),
}, },
bloomSize: { bloomSize: {
key: "bloomSize", key: "bloomSize",
parser: s => nullNaN(range(parseFloat(s), 0, 1)) parser: (s) => nullNaN(range(parseFloat(s), 0, 1)),
}, },
url: { key: "bgURL", parser: s => s }, url: { key: "bgURL", parser: (s) => s },
stripeColors: { key: "stripeColors", parser: s => s }, stripeColors: { key: "stripeColors", parser: (s) => s },
backgroundColor: { key: "backgroundColor", parser: s => s.split(",").map(parseFloat) }, backgroundColor: { key: "backgroundColor", parser: (s) => s.split(",").map(parseFloat) },
volumetric: { key: "volumetric", parser: s => s.toLowerCase().includes("true") } volumetric: { key: "volumetric", parser: (s) => s.toLowerCase().includes("true") },
}; };
paramMapping.dropLength = paramMapping.raindropLength; paramMapping.dropLength = paramMapping.raindropLength;
paramMapping.angle = paramMapping.slant; paramMapping.angle = paramMapping.slant;
@@ -185,20 +184,14 @@ export default (searchString, make1DTexture) => {
const urlParams = Object.fromEntries( const urlParams = Object.fromEntries(
Array.from(new URLSearchParams(searchString).entries()) Array.from(new URLSearchParams(searchString).entries())
.filter(([key]) => key in paramMapping) .filter(([key]) => key in paramMapping)
.map(([key, value]) => [ .map(([key, value]) => [paramMapping[key].key, paramMapping[key].parser(value)])
paramMapping[key].key,
paramMapping[key].parser(value)
])
.filter(([_, value]) => value != null) .filter(([_, value]) => value != null)
); );
const version = const version = urlParams.version in versions ? versions[urlParams.version] : versions.classic;
urlParams.version in versions
? versions[urlParams.version]
: versions.classic;
return { return {
...version, ...version,
...urlParams ...urlParams,
}; };
}; };

View File

@@ -1,7 +1,6 @@
import { loadImage, loadText, makePassFBO, makePass } from "./utils.js"; import { loadImage, loadText, makePassFBO, makePass } from "./utils.js";
const defaultBGURL = const defaultBGURL = "https://upload.wikimedia.org/wikipedia/commons/0/0a/Flammarion_Colored.jpg";
"https://upload.wikimedia.org/wikipedia/commons/0/0a/Flammarion_Colored.jpg";
export default (regl, config, inputs) => { export default (regl, config, inputs) => {
const output = makePassFBO(regl, config.useHalfFloat); const output = makePassFBO(regl, config.useHalfFloat);
@@ -13,13 +12,13 @@ export default (regl, config, inputs) => {
uniforms: { uniforms: {
backgroundTex: background.texture, backgroundTex: background.texture,
tex: inputs.primary, tex: inputs.primary,
bloomTex: inputs.bloom bloomTex: inputs.bloom,
}, },
framebuffer: output framebuffer: output,
}); });
return makePass( return makePass(
{ {
primary: output primary: output,
}, },
() => render({ frag: imagePassFrag.text() }), () => render({ frag: imagePassFrag.text() }),
null, null,

View File

@@ -9,19 +9,15 @@ import makeResurrectionPass from "./resurrectionPass.js";
const canvas = document.createElement("canvas"); const canvas = document.createElement("canvas");
document.body.appendChild(canvas); document.body.appendChild(canvas);
document.addEventListener("touchmove", e => e.preventDefault(), { document.addEventListener("touchmove", (e) => e.preventDefault(), {
passive: false passive: false,
}); });
const regl = createREGL({ const regl = createREGL({
canvas, canvas,
extensions: ["OES_texture_half_float", "OES_texture_half_float_linear"], extensions: ["OES_texture_half_float", "OES_texture_half_float_linear"],
// These extensions are also needed, but Safari misreports that they are missing // These extensions are also needed, but Safari misreports that they are missing
optionalExtensions: [ optionalExtensions: ["EXT_color_buffer_half_float", "WEBGL_color_buffer_float", "OES_standard_derivatives"],
"EXT_color_buffer_half_float",
"WEBGL_color_buffer_float",
"OES_standard_derivatives"
]
}); });
const effects = { const effects = {
@@ -32,7 +28,7 @@ const effects = {
pride: makeStripePass, pride: makeStripePass,
image: makeImagePass, image: makeImagePass,
resurrection: makeResurrectionPass, resurrection: makeResurrectionPass,
resurrections: makeResurrectionPass resurrections: makeResurrectionPass,
}; };
const config = makeConfig(window.location.search); const config = makeConfig(window.location.search);
@@ -51,28 +47,16 @@ const dimensions = { width: 1, height: 1 };
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); const fullScreenQuad = makeFullScreenQuad(regl);
const pipeline = makePipeline( const pipeline = makePipeline([makeMatrixRenderer, effect === "none" ? null : makeBloomPass, effects[effect]], (p) => p.outputs, regl, config);
[
makeMatrixRenderer,
effect === "none" ? null : makeBloomPass,
effects[effect]
],
p => p.outputs,
regl,
config
);
const drawToScreen = regl({ const drawToScreen = regl({
uniforms: { uniforms: {
tex: pipeline[pipeline.length - 1].outputs.primary tex: pipeline[pipeline.length - 1].outputs.primary,
} },
}); });
await Promise.all(pipeline.map(({ ready }) => ready)); await Promise.all(pipeline.map(({ ready }) => ready));
const tick = regl.frame(({ viewportWidth, viewportHeight }) => { const tick = regl.frame(({ viewportWidth, viewportHeight }) => {
// tick.cancel(); // tick.cancel();
if ( if (dimensions.width !== viewportWidth || dimensions.height !== viewportHeight) {
dimensions.width !== viewportWidth ||
dimensions.height !== viewportHeight
) {
dimensions.width = viewportWidth; dimensions.width = viewportWidth;
dimensions.height = viewportHeight; dimensions.height = viewportHeight;
for (const step of pipeline) { for (const step of pipeline) {

View File

@@ -15,16 +15,14 @@ const makePalette = (regl, entries) => {
const sortedEntries = entries const sortedEntries = entries
.slice() .slice()
.sort((e1, e2) => e1.at - e2.at) .sort((e1, e2) => e1.at - e2.at)
.map(entry => ({ .map((entry) => ({
rgb: colorToRGB(entry.hsl), rgb: colorToRGB(entry.hsl),
arrayIndex: Math.floor( arrayIndex: Math.floor(Math.max(Math.min(1, entry.at), 0) * (PALETTE_SIZE - 1)),
Math.max(Math.min(1, entry.at), 0) * (PALETTE_SIZE - 1)
)
})); }));
sortedEntries.unshift({ rgb: sortedEntries[0].rgb, arrayIndex: 0 }); sortedEntries.unshift({ rgb: sortedEntries[0].rgb, arrayIndex: 0 });
sortedEntries.push({ sortedEntries.push({
rgb: sortedEntries[sortedEntries.length - 1].rgb, rgb: sortedEntries[sortedEntries.length - 1].rgb,
arrayIndex: PALETTE_SIZE - 1 arrayIndex: PALETTE_SIZE - 1,
}); });
sortedEntries.forEach((entry, index) => { sortedEntries.forEach((entry, index) => {
paletteColors[entry.arrayIndex] = entry.rgb.slice(); paletteColors[entry.arrayIndex] = entry.rgb.slice();
@@ -36,7 +34,7 @@ const makePalette = (regl, entries) => {
paletteColors[entry.arrayIndex + i] = [ paletteColors[entry.arrayIndex + i] = [
entry.rgb[0] * (1 - ratio) + nextEntry.rgb[0] * ratio, entry.rgb[0] * (1 - ratio) + nextEntry.rgb[0] * ratio,
entry.rgb[1] * (1 - ratio) + nextEntry.rgb[1] * ratio, entry.rgb[1] * (1 - ratio) + nextEntry.rgb[1] * ratio,
entry.rgb[2] * (1 - ratio) + nextEntry.rgb[2] * ratio entry.rgb[2] * (1 - ratio) + nextEntry.rgb[2] * ratio,
]; ];
} }
} }
@@ -44,7 +42,7 @@ const makePalette = (regl, entries) => {
return make1DTexture( return make1DTexture(
regl, regl,
paletteColors.flat().map(i => i * 0xff) paletteColors.flat().map((i) => i * 0xff)
); );
}; };
@@ -64,20 +62,18 @@ export default (regl, config, inputs) => {
frag: regl.prop("frag"), frag: regl.prop("frag"),
uniforms: { uniforms: {
...extractEntries(config, [ ...extractEntries(config, ["backgroundColor"]),
"backgroundColor",
]),
tex: inputs.primary, tex: inputs.primary,
bloomTex: inputs.bloom, bloomTex: inputs.bloom,
palette, palette,
ditherMagnitude: 0.05 ditherMagnitude: 0.05,
}, },
framebuffer: output framebuffer: output,
}); });
return makePass( return makePass(
{ {
primary: output primary: output,
}, },
() => render({ frag: palettePassFrag.text() }), () => render({ frag: palettePassFrag.text() }),
null, null,

View File

@@ -1,26 +1,18 @@
import { import { extractEntries, loadImage, loadText, makePassFBO, makeDoubleBuffer, makePass } from "./utils.js";
extractEntries,
loadImage,
loadText,
makePassFBO,
makeDoubleBuffer,
makePass
} from "./utils.js";
const rippleTypes = { const rippleTypes = {
box: 0, box: 0,
circle: 1 circle: 1,
}; };
const cycleStyles = { const cycleStyles = {
cycleFasterWhenDimmed: 0, cycleFasterWhenDimmed: 0,
cycleRandomly: 1 cycleRandomly: 1,
}; };
const numVerticesPerQuad = 2 * 3; const numVerticesPerQuad = 2 * 3;
export default (regl, config) => { export default (regl, config) => {
const volumetric = config.volumetric; const volumetric = config.volumetric;
const density = volumetric && config.effect !== "none" ? config.density : 1; const density = volumetric && config.effect !== "none" ? config.density : 1;
const [numRows, numColumns] = [config.numColumns, config.numColumns * density]; const [numRows, numColumns] = [config.numColumns, config.numColumns * density];
@@ -39,10 +31,9 @@ export default (regl, config) => {
width: numColumns, width: numColumns,
height: numRows, height: numRows,
wrapT: "clamp", wrapT: "clamp",
type: "half float" type: "half float",
}); });
const output = makePassFBO(regl, config.useHalfFloat); const output = makePassFBO(regl, config.useHalfFloat);
const uniforms = { const uniforms = {
@@ -79,20 +70,13 @@ export default (regl, config) => {
numQuadRows, numQuadRows,
numQuadColumns, numQuadColumns,
quadSize, quadSize,
volumetric volumetric,
}; };
uniforms.rippleType = uniforms.rippleType = config.rippleTypeName in rippleTypes ? rippleTypes[config.rippleTypeName] : -1;
config.rippleTypeName in rippleTypes uniforms.cycleStyle = config.cycleStyleName in cycleStyles ? cycleStyles[config.cycleStyleName] : 0;
? 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.slantVec = [Math.cos(config.slant), Math.sin(config.slant)];
uniforms.slantScale = uniforms.slantScale = 1 / (Math.abs(Math.sin(2 * config.slant)) * (Math.sqrt(2) - 1) + 1);
1 / (Math.abs(Math.sin(2 * config.slant)) * (Math.sqrt(2) - 1) + 1);
uniforms.showComputationTexture = config.effect === "none"; uniforms.showComputationTexture = config.effect === "none";
const msdf = loadImage(regl, config.glyphTexURL); const msdf = loadImage(regl, config.glyphTexURL);
@@ -102,19 +86,28 @@ export default (regl, config) => {
frag: regl.prop("frag"), frag: regl.prop("frag"),
uniforms: { uniforms: {
...uniforms, ...uniforms,
lastState: doubleBuffer.back lastState: doubleBuffer.back,
}, },
framebuffer: doubleBuffer.front framebuffer: doubleBuffer.front,
}); });
const quadPositions = Array(numQuadRows).fill().map((_, y) => const quadPositions = Array(numQuadRows)
Array(numQuadColumns).fill().map((_, x) => .fill()
Array(numVerticesPerQuad).fill([x, y]) .map((_, y) =>
) Array(numQuadColumns)
.fill()
.map((_, x) => Array(numVerticesPerQuad).fill([x, y]))
); );
const quadCorners = Array(numQuads).fill([[0, 0], [0, 1], [1, 1], [0, 0], [1, 1], [1, 0]]); const quadCorners = Array(numQuads).fill([
[0, 0],
[0, 1],
[1, 1],
[0, 0],
[1, 1],
[1, 0],
]);
// 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 renderVert = loadText("../shaders/render.vert"); const renderVert = loadText("../shaders/render.vert");
@@ -126,8 +119,8 @@ export default (regl, config) => {
srcRGB: "src alpha", srcRGB: "src alpha",
srcAlpha: 1, srcAlpha: 1,
dstRGB: "dst alpha", dstRGB: "dst alpha",
dstAlpha: 1 dstAlpha: 1,
} },
}, },
vert: regl.prop("vert"), vert: regl.prop("vert"),
frag: regl.prop("frag"), frag: regl.prop("frag"),
@@ -140,16 +133,16 @@ export default (regl, config) => {
camera: regl.prop("camera"), camera: regl.prop("camera"),
transform: regl.prop("transform"), transform: regl.prop("transform"),
screenSize: regl.prop("screenSize") screenSize: regl.prop("screenSize"),
}, },
attributes: { attributes: {
aPosition: quadPositions, aPosition: quadPositions,
aCorner: quadCorners aCorner: quadCorners,
}, },
count: numQuads * numVerticesPerQuad, count: numQuads * numVerticesPerQuad,
framebuffer: output framebuffer: output,
}); });
const screenSize = [1, 1]; const screenSize = [1, 1];
@@ -163,7 +156,7 @@ export default (regl, config) => {
return makePass( return makePass(
{ {
primary: output primary: output,
}, },
() => { () => {
const time = Date.now(); const time = Date.now();
@@ -172,7 +165,7 @@ export default (regl, config) => {
regl.clear({ regl.clear({
depth: 1, depth: 1,
color: [0, 0, 0, 1], color: [0, 0, 0, 1],
framebuffer: output framebuffer: output,
}); });
render({ camera, transform, screenSize, vert: renderVert.text(), frag: renderFrag.text() }); render({ camera, transform, screenSize, vert: renderVert.text(), frag: renderFrag.text() });
}, },

View File

@@ -18,19 +18,17 @@ export default (regl, config, inputs) => {
frag: regl.prop("frag"), frag: regl.prop("frag"),
uniforms: { uniforms: {
...extractEntries(config, [ ...extractEntries(config, ["backgroundColor"]),
"backgroundColor",
]),
tex: inputs.primary, tex: inputs.primary,
bloomTex: inputs.bloom, bloomTex: inputs.bloom,
ditherMagnitude: 0.05 ditherMagnitude: 0.05,
}, },
framebuffer: output framebuffer: output,
}); });
return makePass( return makePass(
{ {
primary: output primary: output,
}, },
() => render({ frag: resurrectionPassFrag.text() }) () => render({ frag: resurrectionPassFrag.text() })
); );

View File

@@ -6,7 +6,7 @@ const neapolitanStripeColors = [
[0.8, 0.8, 0.6], [0.8, 0.8, 0.6],
[0.8, 0.8, 0.6], [0.8, 0.8, 0.6],
[1.0, 0.7, 0.8], [1.0, 0.7, 0.8],
[1.0, 0.7, 0.8] [1.0, 0.7, 0.8],
].flat(); ].flat();
const prideStripeColors = [ const prideStripeColors = [
@@ -15,22 +15,18 @@ const prideStripeColors = [
[1, 1, 0], [1, 1, 0],
[0, 1, 0], [0, 1, 0],
[0, 0, 1], [0, 0, 1],
[0.8, 0, 1] [0.8, 0, 1],
].flat(); ].flat();
export default (regl, config, inputs) => { export default (regl, config, inputs) => {
const output = makePassFBO(regl, config.useHalfFloat); const output = makePassFBO(regl, config.useHalfFloat);
const stripeColors = const stripeColors =
"stripeColors" in config "stripeColors" in config ? config.stripeColors.split(",").map(parseFloat) : config.effect === "pride" ? prideStripeColors : neapolitanStripeColors;
? config.stripeColors.split(",").map(parseFloat)
: config.effect === "pride"
? prideStripeColors
: neapolitanStripeColors;
const numStripeColors = Math.floor(stripeColors.length / 3); const numStripeColors = Math.floor(stripeColors.length / 3);
const stripes = make1DTexture( const stripes = make1DTexture(
regl, regl,
stripeColors.slice(0, numStripeColors * 3).map(f => Math.floor(f * 0xff)) stripeColors.slice(0, numStripeColors * 3).map((f) => Math.floor(f * 0xff))
); );
const stripePassFrag = loadText("../shaders/stripePass.frag"); const stripePassFrag = loadText("../shaders/stripePass.frag");
@@ -39,20 +35,18 @@ export default (regl, config, inputs) => {
frag: regl.prop("frag"), frag: regl.prop("frag"),
uniforms: { uniforms: {
...extractEntries(config, [ ...extractEntries(config, ["backgroundColor"]),
"backgroundColor",
]),
tex: inputs.primary, tex: inputs.primary,
bloomTex: inputs.bloom, bloomTex: inputs.bloom,
stripes, stripes,
ditherMagnitude: 0.05 ditherMagnitude: 0.05,
}, },
framebuffer: output framebuffer: output,
}); });
return makePass( return makePass(
{ {
primary: output primary: output,
}, },
() => render({ frag: stripePassFrag.text() }), () => render({ frag: stripePassFrag.text() }),
null, null,

View File

@@ -1,7 +1,4 @@
const extractEntries = (src, keys) => const extractEntries = (src, keys) => Object.fromEntries(Array.from(Object.entries(src)).filter(([key]) => keys.includes(key)));
Object.fromEntries(
Array.from(Object.entries(src)).filter(([key]) => keys.includes(key))
);
const makePassTexture = (regl, halfFloat) => const makePassTexture = (regl, halfFloat) =>
regl.texture({ regl.texture({
@@ -10,7 +7,7 @@ const makePassTexture = (regl, halfFloat) =>
type: halfFloat ? "half float" : "uint8", type: halfFloat ? "half float" : "uint8",
wrap: "clamp", wrap: "clamp",
min: "linear", min: "linear",
mag: "linear" mag: "linear",
}); });
const makePassFBO = (regl, halfFloat) => regl.framebuffer({ color: makePassTexture(regl, halfFloat) }); const makePassFBO = (regl, halfFloat) => regl.framebuffer({ color: makePassTexture(regl, halfFloat) });
@@ -20,7 +17,7 @@ const makePassFBO = (regl, halfFloat) => regl.framebuffer({ color: makePassTextu
const makePyramid = (regl, height, halfFloat) => const makePyramid = (regl, height, halfFloat) =>
Array(height) Array(height)
.fill() .fill()
.map(_ => makePassFBO(regl, halfFloat)); .map((_) => makePassFBO(regl, halfFloat));
const makeDoubleBuffer = (regl, props) => { const makeDoubleBuffer = (regl, props) => {
const state = Array(2) const state = Array(2)
@@ -28,22 +25,17 @@ const makeDoubleBuffer = (regl, props) => {
.map(() => .map(() =>
regl.framebuffer({ regl.framebuffer({
color: regl.texture(props), color: regl.texture(props),
depthStencil: false depthStencil: false,
}) })
); );
return { return {
front: ({ tick }) => state[tick % 2], front: ({ tick }) => state[tick % 2],
back: ({ tick }) => state[(tick + 1) % 2] back: ({ tick }) => state[(tick + 1) % 2],
}; };
}; };
const resizePyramid = (pyramid, vw, vh, scale) => const resizePyramid = (pyramid, vw, vh, scale) =>
pyramid.forEach((fbo, index) => pyramid.forEach((fbo, index) => fbo.resize(Math.floor((vw * scale) / 2 ** index), Math.floor((vh * scale) / 2 ** index)));
fbo.resize(
Math.floor((vw * scale) / 2 ** index),
Math.floor((vh * scale) / 2 ** index)
)
);
const loadImage = (regl, url) => { const loadImage = (regl, url) => {
let texture = regl.texture([[0]]); let texture = regl.texture([[0]]);
@@ -66,10 +58,10 @@ const loadImage = (regl, url) => {
data, data,
mag: "linear", mag: "linear",
min: "linear", min: "linear",
flipY: true flipY: true,
}); });
} }
})() })(),
}; };
}; };
@@ -94,10 +86,10 @@ const loadShader = (regl, url) => {
data, data,
mag: "linear", mag: "linear",
min: "linear", min: "linear",
flipY: true flipY: true,
}); });
} }
})() })(),
}; };
}; };
@@ -116,7 +108,7 @@ const loadText = (url) => {
text = await (await fetch(url)).text(); text = await (await fetch(url)).text();
loaded = true; loaded = true;
} }
})() })(),
}; };
}; };
@@ -142,19 +134,18 @@ const makeFullScreenQuad = (regl, uniforms = {}, context = {}) =>
`, `,
attributes: { attributes: {
aPosition: [-4, -4, 4, -4, 0, 4] aPosition: [-4, -4, 4, -4, 0, 4],
}, },
count: 3, count: 3,
uniforms: { uniforms: {
...uniforms, ...uniforms,
time: regl.context("time") time: regl.context("time"),
}, },
context, context,
depth: { enable: false }, depth: { enable: false },
}); });
const make1DTexture = (regl, data) => const make1DTexture = (regl, data) =>
@@ -164,7 +155,7 @@ const make1DTexture = (regl, data) =>
height: 1, height: 1,
format: "rgb", format: "rgb",
mag: "linear", mag: "linear",
min: "linear" min: "linear",
}); });
const makePass = (outputs, render, resize, ready) => { const makePass = (outputs, render, resize, ready) => {
@@ -172,8 +163,7 @@ const makePass = (outputs, render, resize, ready) => {
render = () => {}; render = () => {};
} }
if (resize == null) { if (resize == null) {
resize = (w, h) => resize = (w, h) => Object.values(outputs).forEach((output) => output.resize(w, h));
Object.values(outputs).forEach(output => output.resize(w, h));
} }
if (ready == null) { if (ready == null) {
ready = Promise.resolve(); ready = Promise.resolve();
@@ -184,20 +174,12 @@ const makePass = (outputs, render, resize, ready) => {
outputs, outputs,
render, render,
resize, resize,
ready ready,
}; };
}; };
const makePipeline = (steps, getInputs, ...params) => const makePipeline = (steps, getInputs, ...params) =>
steps steps.filter((f) => f != null).reduce((pipeline, f, i) => [...pipeline, f(...params, i == 0 ? null : getInputs(pipeline[i - 1]))], []);
.filter(f => f != null)
.reduce(
(pipeline, f, i) => [
...pipeline,
f(...params, i == 0 ? null : getInputs(pipeline[i - 1]))
],
[]
);
export { export {
extractEntries, extractEntries,
@@ -211,5 +193,5 @@ export {
makeFullScreenQuad, makeFullScreenQuad,
make1DTexture, make1DTexture,
makePass, makePass,
makePipeline makePipeline,
}; };