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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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