mirror of
https://github.com/Rezmason/matrix.git
synced 2026-04-18 14:19:30 -07:00
Passes now accept as input and provide as output Objects with FBOs as values. This allows passes to work with more than one input texture.
This commit is contained in:
6
TODO.txt
6
TODO.txt
@@ -1,7 +1,9 @@
|
|||||||
TODO:
|
TODO:
|
||||||
|
|
||||||
Maybe apply a further additive blend on top of the color-mapped bloom
|
Experiment with varying the colors in the palette pass
|
||||||
That could help add whatever's missing visually to the raindrops
|
Maybe a separate palette for the non-bloom
|
||||||
|
Maybe dim and widen the bloom
|
||||||
|
Just make sure the changes to non-default configurations are acceptable
|
||||||
|
|
||||||
Deja vu effect: flashing rows
|
Deja vu effect: flashing rows
|
||||||
Make them flash all the time
|
Make them flash all the time
|
||||||
|
|||||||
@@ -16,10 +16,8 @@ const levelStrengths = Array(pyramidHeight)
|
|||||||
)
|
)
|
||||||
.reverse();
|
.reverse();
|
||||||
|
|
||||||
export default (regl, config, input) => {
|
export default (regl, config, inputs) => {
|
||||||
const uniforms = extractEntries(config, [
|
const uniforms = extractEntries(config, [
|
||||||
"bloomRadius",
|
|
||||||
"bloomSize",
|
|
||||||
"bloomStrength",
|
"bloomStrength",
|
||||||
"highPassThreshold"
|
"highPassThreshold"
|
||||||
]);
|
]);
|
||||||
@@ -84,14 +82,13 @@ export default (regl, config, input) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// The pyramid of textures gets flattened onto the source texture.
|
// The pyramid of textures gets flattened onto the source texture.
|
||||||
const combineBloom = regl({
|
const flattenPyramid = regl({
|
||||||
frag: `
|
frag: `
|
||||||
precision mediump float;
|
precision mediump float;
|
||||||
varying vec2 vUV;
|
varying vec2 vUV;
|
||||||
${vBlurPyramid
|
${vBlurPyramid
|
||||||
.map((_, index) => `uniform sampler2D pyr_${index};`)
|
.map((_, index) => `uniform sampler2D pyr_${index};`)
|
||||||
.join("\n")}
|
.join("\n")}
|
||||||
uniform sampler2D tex;
|
|
||||||
uniform float bloomStrength;
|
uniform float bloomStrength;
|
||||||
void main() {
|
void main() {
|
||||||
vec4 total = vec4(0.);
|
vec4 total = vec4(0.);
|
||||||
@@ -101,12 +98,11 @@ export default (regl, config, input) => {
|
|||||||
`total += texture2D(pyr_${index}, vUV) * ${levelStrengths[index]};`
|
`total += texture2D(pyr_${index}, vUV) * ${levelStrengths[index]};`
|
||||||
)
|
)
|
||||||
.join("\n")}
|
.join("\n")}
|
||||||
gl_FragColor = total * bloomStrength + texture2D(tex, vUV);
|
gl_FragColor = total * bloomStrength;
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
uniforms: {
|
uniforms: {
|
||||||
...uniforms,
|
...uniforms,
|
||||||
tex: input,
|
|
||||||
...Object.fromEntries(
|
...Object.fromEntries(
|
||||||
vBlurPyramid.map((fbo, index) => [`pyr_${index}`, fbo])
|
vBlurPyramid.map((fbo, index) => [`pyr_${index}`, fbo])
|
||||||
)
|
)
|
||||||
@@ -115,16 +111,19 @@ export default (regl, config, input) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return makePass(
|
return makePass(
|
||||||
output,
|
{
|
||||||
|
primary: inputs.primary,
|
||||||
|
bloom: output
|
||||||
|
},
|
||||||
() => {
|
() => {
|
||||||
highPassPyramid.forEach(fbo => highPass({ fbo, tex: input }));
|
highPassPyramid.forEach(fbo => highPass({ fbo, tex: inputs.primary }));
|
||||||
hBlurPyramid.forEach((fbo, index) =>
|
hBlurPyramid.forEach((fbo, index) =>
|
||||||
blur({ fbo, tex: highPassPyramid[index], direction: [1, 0] })
|
blur({ fbo, tex: highPassPyramid[index], direction: [1, 0] })
|
||||||
);
|
);
|
||||||
vBlurPyramid.forEach((fbo, index) =>
|
vBlurPyramid.forEach((fbo, index) =>
|
||||||
blur({ fbo, tex: hBlurPyramid[index], direction: [0, 1] })
|
blur({ fbo, tex: hBlurPyramid[index], direction: [0, 1] })
|
||||||
);
|
);
|
||||||
combineBloom();
|
flattenPyramid();
|
||||||
},
|
},
|
||||||
(w, h) => {
|
(w, h) => {
|
||||||
// The blur pyramids can be lower resolution than the screen.
|
// The blur pyramids can be lower resolution than the screen.
|
||||||
|
|||||||
@@ -40,10 +40,10 @@ const defaults = {
|
|||||||
rippleSpeed: 0.2,
|
rippleSpeed: 0.2,
|
||||||
numColumns: 80,
|
numColumns: 80,
|
||||||
paletteEntries: [
|
paletteEntries: [
|
||||||
{ rgb: [0.00, 0.00, 0.00], at: 0.00 },
|
{ rgb: [0.0, 0.0, 0.0], at: 0.0 },
|
||||||
{ rgb: [0.09, 0.33, 0.04], at: 0.25 },
|
{ rgb: [0.09, 0.33, 0.04], at: 0.25 },
|
||||||
{ rgb: [0.39, 0.98, 0.38], at: 0.70 },
|
{ rgb: [0.39, 0.98, 0.38], at: 0.7 },
|
||||||
{ rgb: [0.57, 0.97, 0.61], at: 1.00 },
|
{ rgb: [0.57, 0.97, 0.61], at: 1.0 }
|
||||||
],
|
],
|
||||||
raindropLength: 1,
|
raindropLength: 1,
|
||||||
slant: 0
|
slant: 0
|
||||||
@@ -57,7 +57,6 @@ const versions = {
|
|||||||
operator: {
|
operator: {
|
||||||
...defaults,
|
...defaults,
|
||||||
...fonts.matrixcode,
|
...fonts.matrixcode,
|
||||||
bloomRadius: 0.3,
|
|
||||||
bloomStrength: 0.75,
|
bloomStrength: 0.75,
|
||||||
highPassThreshold: 0.0,
|
highPassThreshold: 0.0,
|
||||||
cycleSpeed: 0.05,
|
cycleSpeed: 0.05,
|
||||||
@@ -81,7 +80,6 @@ const versions = {
|
|||||||
nightmare: {
|
nightmare: {
|
||||||
...defaults,
|
...defaults,
|
||||||
...fonts.gothic,
|
...fonts.gothic,
|
||||||
bloomRadius: 0.8,
|
|
||||||
highPassThreshold: 0.7,
|
highPassThreshold: 0.7,
|
||||||
brightnessMix: 0.75,
|
brightnessMix: 0.75,
|
||||||
fallSpeed: 2.0,
|
fallSpeed: 2.0,
|
||||||
@@ -100,7 +98,6 @@ const versions = {
|
|||||||
paradise: {
|
paradise: {
|
||||||
...defaults,
|
...defaults,
|
||||||
...fonts.coptic,
|
...fonts.coptic,
|
||||||
bloomRadius: 1.15,
|
|
||||||
bloomStrength: 1.75,
|
bloomStrength: 1.75,
|
||||||
highPassThreshold: 0,
|
highPassThreshold: 0,
|
||||||
cycleSpeed: 0.1,
|
cycleSpeed: 0.1,
|
||||||
|
|||||||
@@ -3,26 +3,33 @@ import { loadImage, 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, input) => {
|
export default (regl, config, inputs) => {
|
||||||
const output = makePassFBO(regl);
|
const output = makePassFBO(regl);
|
||||||
const bgURL = "bgURL" in config ? config.bgURL : defaultBGURL;
|
const bgURL = "bgURL" in config ? config.bgURL : defaultBGURL;
|
||||||
const bgLoader = loadImage(regl, bgURL);
|
const bgLoader = loadImage(regl, bgURL);
|
||||||
return makePass(
|
return makePass(
|
||||||
output,
|
{
|
||||||
|
primary: output
|
||||||
|
},
|
||||||
regl({
|
regl({
|
||||||
frag: `
|
frag: `
|
||||||
precision mediump float;
|
precision mediump float;
|
||||||
uniform sampler2D tex;
|
uniform sampler2D tex;
|
||||||
|
uniform sampler2D bloomTex;
|
||||||
uniform sampler2D bgTex;
|
uniform sampler2D bgTex;
|
||||||
varying vec2 vUV;
|
varying vec2 vUV;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec3 bgColor = texture2D(bgTex, vUV).rgb;
|
vec3 bgColor = texture2D(bgTex, vUV).rgb;
|
||||||
float brightness = pow(texture2D(tex, vUV).r, 1.5);
|
float brightness = pow(min(1., texture2D(tex, vUV).r * 2.) + texture2D(bloomTex, vUV).r, 1.5);
|
||||||
gl_FragColor = vec4(bgColor * brightness, 1.0);
|
gl_FragColor = vec4(bgColor * brightness, 1.0);
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
uniforms: { bgTex: bgLoader.texture, tex: input },
|
uniforms: {
|
||||||
|
bgTex: bgLoader.texture,
|
||||||
|
tex: inputs.primary,
|
||||||
|
bloomTex: inputs.bloom
|
||||||
|
},
|
||||||
framebuffer: output
|
framebuffer: output
|
||||||
}),
|
}),
|
||||||
null,
|
null,
|
||||||
|
|||||||
@@ -51,13 +51,13 @@ document.body.onload = async () => {
|
|||||||
effect === "none" ? null : makeBloomPass,
|
effect === "none" ? null : makeBloomPass,
|
||||||
effects[effect]
|
effects[effect]
|
||||||
],
|
],
|
||||||
p => p.output,
|
p => p.outputs,
|
||||||
regl,
|
regl,
|
||||||
config
|
config
|
||||||
);
|
);
|
||||||
const drawToScreen = regl({
|
const drawToScreen = regl({
|
||||||
uniforms: {
|
uniforms: {
|
||||||
tex: pipeline[pipeline.length - 1].output
|
tex: pipeline[pipeline.length - 1].outputs.primary
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
await Promise.all(pipeline.map(({ ready }) => ready));
|
await Promise.all(pipeline.map(({ ready }) => ready));
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { make1DTexture, 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, config, input) => {
|
export default (regl, config, inputs) => {
|
||||||
const output = makePassFBO(regl);
|
const output = makePassFBO(regl);
|
||||||
|
|
||||||
const PALETTE_SIZE = 2048;
|
const PALETTE_SIZE = 2048;
|
||||||
@@ -41,16 +41,22 @@ export default (regl, config, input) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const palette = make1DTexture(regl, paletteColors.flat().map(i => i * 0xff));
|
const palette = make1DTexture(
|
||||||
|
regl,
|
||||||
|
paletteColors.flat().map(i => i * 0xff)
|
||||||
|
);
|
||||||
|
|
||||||
return makePass(
|
return makePass(
|
||||||
output,
|
{
|
||||||
|
primary: output
|
||||||
|
},
|
||||||
regl({
|
regl({
|
||||||
frag: `
|
frag: `
|
||||||
precision mediump float;
|
precision mediump float;
|
||||||
#define PI 3.14159265359
|
#define PI 3.14159265359
|
||||||
|
|
||||||
uniform sampler2D tex;
|
uniform sampler2D tex;
|
||||||
|
uniform sampler2D bloomTex;
|
||||||
uniform sampler2D palette;
|
uniform sampler2D palette;
|
||||||
uniform float ditherMagnitude;
|
uniform float ditherMagnitude;
|
||||||
uniform float time;
|
uniform float time;
|
||||||
@@ -63,13 +69,15 @@ export default (regl, config, input) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
float at = texture2D( tex, vUV ).r - rand( gl_FragCoord.xy, time ) * ditherMagnitude;
|
float brightness = texture2D( tex, vUV ).r + texture2D( bloomTex, vUV ).r;
|
||||||
|
float at = brightness - rand( gl_FragCoord.xy, time ) * ditherMagnitude;
|
||||||
gl_FragColor = texture2D( palette, vec2(at, 0.0));
|
gl_FragColor = texture2D( palette, vec2(at, 0.0));
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
|
||||||
uniforms: {
|
uniforms: {
|
||||||
tex: input,
|
tex: inputs.primary,
|
||||||
|
bloomTex: inputs.bloom,
|
||||||
palette,
|
palette,
|
||||||
ditherMagnitude: 0.05
|
ditherMagnitude: 0.05
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -356,7 +356,9 @@ export default (regl, config) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return makePass(
|
return makePass(
|
||||||
output,
|
{
|
||||||
|
primary: output
|
||||||
|
},
|
||||||
resources => {
|
resources => {
|
||||||
update();
|
update();
|
||||||
render(resources);
|
render(resources);
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const prideStripeColors = [
|
|||||||
[0.8, 0, 1]
|
[0.8, 0, 1]
|
||||||
].flat();
|
].flat();
|
||||||
|
|
||||||
export default (regl, config, input) => {
|
export default (regl, config, inputs) => {
|
||||||
const output = makePassFBO(regl);
|
const output = makePassFBO(regl);
|
||||||
|
|
||||||
const stripeColors =
|
const stripeColors =
|
||||||
@@ -34,13 +34,16 @@ export default (regl, config, input) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return makePass(
|
return makePass(
|
||||||
output,
|
{
|
||||||
|
primary: output
|
||||||
|
},
|
||||||
regl({
|
regl({
|
||||||
frag: `
|
frag: `
|
||||||
precision mediump float;
|
precision mediump float;
|
||||||
#define PI 3.14159265359
|
#define PI 3.14159265359
|
||||||
|
|
||||||
uniform sampler2D tex;
|
uniform sampler2D tex;
|
||||||
|
uniform sampler2D bloomTex;
|
||||||
uniform sampler2D stripes;
|
uniform sampler2D stripes;
|
||||||
uniform float ditherMagnitude;
|
uniform float ditherMagnitude;
|
||||||
uniform float time;
|
uniform float time;
|
||||||
@@ -54,13 +57,15 @@ export default (regl, config, input) => {
|
|||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec3 color = texture2D(stripes, vUV).rgb;
|
vec3 color = texture2D(stripes, vUV).rgb;
|
||||||
float brightness = texture2D(tex, vUV).r - rand( gl_FragCoord.xy, time ) * ditherMagnitude;
|
float brightness = min(1., texture2D(tex, vUV).r * 2.) + texture2D(bloomTex, vUV).r;
|
||||||
gl_FragColor = vec4(color * brightness, 1.0);
|
float at = brightness - rand( gl_FragCoord.xy, time ) * ditherMagnitude;
|
||||||
|
gl_FragColor = vec4(color * at, 1.0);
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
|
||||||
uniforms: {
|
uniforms: {
|
||||||
tex: input,
|
tex: inputs.primary,
|
||||||
|
bloomTex: inputs.bloom,
|
||||||
stripes,
|
stripes,
|
||||||
ditherMagnitude: 0.05
|
ditherMagnitude: 0.05
|
||||||
},
|
},
|
||||||
|
|||||||
11
js/utils.js
11
js/utils.js
@@ -142,31 +142,32 @@ const make1DTexture = (regl, data) =>
|
|||||||
min: "linear"
|
min: "linear"
|
||||||
});
|
});
|
||||||
|
|
||||||
const makePass = (output, render, resize, ready) => {
|
const makePass = (outputs, render, resize, ready) => {
|
||||||
if (render == null) {
|
if (render == null) {
|
||||||
render = () => {};
|
render = () => {};
|
||||||
}
|
}
|
||||||
if (resize == null) {
|
if (resize == null) {
|
||||||
resize = (w, h) => output.resize(w, h);
|
resize = (w, h) =>
|
||||||
|
Object.values(outputs).forEach(output => output.resize(w, h));
|
||||||
}
|
}
|
||||||
if (ready == null) {
|
if (ready == null) {
|
||||||
ready = Promise.resolve();
|
ready = Promise.resolve();
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
output,
|
outputs,
|
||||||
render,
|
render,
|
||||||
resize,
|
resize,
|
||||||
ready
|
ready
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const makePipeline = (steps, getInput, ...params) =>
|
const makePipeline = (steps, getInputs, ...params) =>
|
||||||
steps
|
steps
|
||||||
.filter(f => f != null)
|
.filter(f => f != null)
|
||||||
.reduce(
|
.reduce(
|
||||||
(pipeline, f, i) => [
|
(pipeline, f, i) => [
|
||||||
...pipeline,
|
...pipeline,
|
||||||
f(...params, i == 0 ? null : getInput(pipeline[i - 1]))
|
f(...params, i == 0 ? null : getInputs(pipeline[i - 1]))
|
||||||
],
|
],
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user