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:
Rezmason
2020-06-25 02:23:59 -07:00
parent e100117d8b
commit 021ade45e1
9 changed files with 61 additions and 40 deletions

View File

@@ -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

View File

@@ -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.

View File

@@ -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,

View File

@@ -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,

View File

@@ -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));

View File

@@ -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
}, },

View File

@@ -356,7 +356,9 @@ export default (regl, config) => {
}); });
return makePass( return makePass(
output, {
primary: output
},
resources => { resources => {
update(); update();
render(resources); render(resources);

View File

@@ -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
}, },

View File

@@ -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]))
], ],
[] []
); );