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