diff --git a/js/regl/bloomPass.js b/js/regl/bloomPass.js index e874b69..8ac28c8 100644 --- a/js/regl/bloomPass.js +++ b/js/regl/bloomPass.js @@ -9,7 +9,7 @@ const levelStrengths = Array(pyramidHeight) .map((_, index) => Math.pow(index / (pyramidHeight * 2) + 0.5, 1 / 3).toPrecision(5)) .reverse(); -export default (regl, config, inputs) => { +export default ({ regl, config }, inputs) => { const { bloomStrength, bloomSize, highPassThreshold } = config; const enabled = bloomSize > 0 && bloomStrength > 0; @@ -80,6 +80,14 @@ export default (regl, config, inputs) => { primary: inputs.primary, bloom: output, }, + Promise.all([highPassFrag.loaded, blurFrag.loaded]), + (w, h) => { + // The blur pyramids can be lower resolution than the screen. + resizePyramid(highPassPyramid, w, h, bloomSize); + resizePyramid(hBlurPyramid, w, h, bloomSize); + resizePyramid(vBlurPyramid, w, h, bloomSize); + output.resize(w, h); + }, () => { for (let i = 0; i < pyramidHeight; i++) { const highPassFBO = highPassPyramid[i]; @@ -91,14 +99,6 @@ export default (regl, config, inputs) => { } sumPyramid(); - }, - (w, h) => { - // The blur pyramids can be lower resolution than the screen. - resizePyramid(highPassPyramid, w, h, bloomSize); - resizePyramid(hBlurPyramid, w, h, bloomSize); - resizePyramid(vBlurPyramid, w, h, bloomSize); - output.resize(w, h); - }, - [highPassFrag.loaded, blurFrag.loaded] + } ); }; diff --git a/js/regl/imagePass.js b/js/regl/imagePass.js index 381ffa6..7428026 100644 --- a/js/regl/imagePass.js +++ b/js/regl/imagePass.js @@ -4,7 +4,7 @@ import { loadImage, loadText, makePassFBO, makePass } from "./utils.js"; const defaultBGURL = "https://upload.wikimedia.org/wikipedia/commons/thumb/0/0a/Flammarion_Colored.jpg/917px-Flammarion_Colored.jpg"; -export default (regl, config, inputs) => { +export default ({ regl, config }, inputs) => { const output = makePassFBO(regl, config.useHalfFloat); const bgURL = "bgURL" in config ? config.bgURL : defaultBGURL; const background = loadImage(regl, bgURL); @@ -22,8 +22,8 @@ export default (regl, config, inputs) => { { primary: output, }, - () => render({ frag: imagePassFrag.text() }), - null, - [background.loaded, imagePassFrag.loaded] + Promise.all([background.loaded, imagePassFrag.loaded]), + (w, h) => output.resize(w, h), + () => render({ frag: imagePassFrag.text() }) ); }; diff --git a/js/regl/main.js b/js/regl/main.js index 2646ae4..3026ec3 100644 --- a/js/regl/main.js +++ b/js/regl/main.js @@ -40,7 +40,7 @@ export default async (canvas, config) => { // All this takes place in a full screen quad. const fullScreenQuad = makeFullScreenQuad(regl); const effectName = config.effect in effects ? config.effect : "plain"; - const pipeline = makePipeline([makeRain, makeBloomPass, effects[effectName]], (p) => p.outputs, regl, config); + const pipeline = makePipeline({ regl, config }, [makeRain, makeBloomPass, effects[effectName]]); const screenUniforms = { tex: pipeline[pipeline.length - 1].outputs.primary }; const drawToScreen = regl({ uniforms: screenUniforms }); await Promise.all(pipeline.map((step) => step.ready)); @@ -50,12 +50,12 @@ export default async (canvas, config) => { dimensions.width = viewportWidth; dimensions.height = viewportHeight; for (const step of pipeline) { - step.resize(viewportWidth, viewportHeight); + step.setSize(viewportWidth, viewportHeight); } } fullScreenQuad(() => { for (const step of pipeline) { - step.render(); + step.execute(); } drawToScreen(); }); diff --git a/js/regl/palettePass.js b/js/regl/palettePass.js index 64d0a78..e653ab4 100644 --- a/js/regl/palettePass.js +++ b/js/regl/palettePass.js @@ -62,7 +62,7 @@ const makePalette = (regl, entries) => { // won't persist across subsequent frames. This is a safe trick // in screen space. -export default (regl, config, inputs) => { +export default ({ regl, config }, inputs) => { const output = makePassFBO(regl, config.useHalfFloat); const palette = makePalette(regl, config.paletteEntries); const { backgroundColor } = config; @@ -86,8 +86,8 @@ export default (regl, config, inputs) => { { primary: output, }, - () => render({ frag: palettePassFrag.text() }), - null, - palettePassFrag.loaded + palettePassFrag.loaded, + (w, h) => output.resize(w, h), + () => render({ frag: palettePassFrag.text() }) ); }; diff --git a/js/regl/rainPass.js b/js/regl/rainPass.js index 7a7c0fd..e8ec541 100644 --- a/js/regl/rainPass.js +++ b/js/regl/rainPass.js @@ -19,7 +19,7 @@ const blVert = [1, 0]; const brVert = [1, 1]; const quadVertices = [tlVert, trVert, brVert, tlVert, brVert, blVert]; -export default (regl, config) => { +export default ({ regl, config }) => { // The volumetric mode multiplies the number of columns // to reach the desired density, and then overlaps them const volumetric = config.volumetric; @@ -170,15 +170,7 @@ export default (regl, config) => { { primary: output, }, - () => { - compute({ frag: rainPassCompute.text() }); - regl.clear({ - depth: 1, - color: [0, 0, 0, 1], - framebuffer: output, - }); - render({ camera, transform, screenSize, vert: rainPassVert.text(), frag: rainPassFrag.text() }); - }, + Promise.all([msdf.loaded, rainPassCompute.loaded, rainPassVert.loaded, rainPassFrag.loaded]), (w, h) => { output.resize(w, h); const aspectRatio = w / h; @@ -193,6 +185,14 @@ export default (regl, config) => { } [screenSize[0], screenSize[1]] = aspectRatio > 1 ? [1, aspectRatio] : [1 / aspectRatio, 1]; }, - [msdf.loaded, rainPassCompute.loaded, rainPassVert.loaded, rainPassFrag.loaded] + () => { + compute({ frag: rainPassCompute.text() }); + regl.clear({ + depth: 1, + color: [0, 0, 0, 1], + framebuffer: output, + }); + render({ camera, transform, screenSize, vert: rainPassVert.text(), frag: rainPassFrag.text() }); + } ); }; diff --git a/js/regl/resurrectionPass.js b/js/regl/resurrectionPass.js index cb5d9ee..25e4e6f 100644 --- a/js/regl/resurrectionPass.js +++ b/js/regl/resurrectionPass.js @@ -8,7 +8,7 @@ import { loadText, make1DTexture, makePassFBO, makePass } from "./utils.js"; // Downward-flowing glyphs should be tinted slightly blue on top and golden on the bottom // Cheat a lens blur, interpolating between the texture and bloom at the edges -export default (regl, config, inputs) => { +export default ({ regl, config }, inputs) => { const output = makePassFBO(regl, config.useHalfFloat); const { backgroundColor } = config; const resurrectionPassFrag = loadText("shaders/glsl/resurrectionPass.frag.glsl"); @@ -29,8 +29,8 @@ export default (regl, config, inputs) => { { primary: output, }, - () => render({ frag: resurrectionPassFrag.text() }), - null, - resurrectionPassFrag.loaded + resurrectionPassFrag.loaded, + (w, h) => output.resize(w, h), + () => render({ frag: resurrectionPassFrag.text() }) ); }; diff --git a/js/regl/stripePass.js b/js/regl/stripePass.js index da10505..78af3e2 100644 --- a/js/regl/stripePass.js +++ b/js/regl/stripePass.js @@ -28,7 +28,7 @@ const prideStripeColors = [ [0.8, 0, 1], ].flat(); -export default (regl, config, inputs) => { +export default ({ regl, config }, inputs) => { const output = makePassFBO(regl, config.useHalfFloat); const { backgroundColor } = config; @@ -61,8 +61,8 @@ export default (regl, config, inputs) => { { primary: output, }, - () => render({ frag: stripePassFrag.text() }), - null, - stripePassFrag.loaded + stripePassFrag.loaded, + (w, h) => output.resize(w, h), + () => render({ frag: stripePassFrag.text() }) ); }; diff --git a/js/regl/utils.js b/js/regl/utils.js index 545097e..10c46a2 100644 --- a/js/regl/utils.js +++ b/js/regl/utils.js @@ -129,28 +129,15 @@ const make1DTexture = (regl, data) => min: "linear", }); -const makePass = (outputs, render, resize, ready) => { - if (render == null) { - render = () => {}; - } - if (resize == null) { - resize = (w, h) => Object.values(outputs).forEach((output) => output.resize(w, h)); - } - if (ready == null) { - ready = Promise.resolve(); - } else if (ready instanceof Array) { - ready = Promise.all(ready); - } - return { - outputs, - render, - resize, - ready, - }; -}; +const makePass = (outputs, ready, setSize, execute) => ({ + outputs: outputs ?? {}, + ready: ready ?? Promise.resolve(), + setSize: setSize ?? (() => {}), + execute: execute ?? (() => {}), +}); -const makePipeline = (steps, getInputs, ...params) => - steps.filter((f) => f != null).reduce((pipeline, f, i) => [...pipeline, f(...params, i == 0 ? null : getInputs(pipeline[i - 1]))], []); +const makePipeline = (context, steps) => + steps.filter((f) => f != null).reduce((pipeline, f, i) => [...pipeline, f(context, i == 0 ? null : pipeline[i - 1].outputs)], []); export { makePassTexture, diff --git a/js/webgpu/imagePass.js b/js/webgpu/imagePass.js index a754899..b10c1b2 100644 --- a/js/webgpu/imagePass.js +++ b/js/webgpu/imagePass.js @@ -76,5 +76,5 @@ export default (context, getInputs) => { renderPass.endPass(); }; - return makePass(ready, setSize, getOutputs, execute); + return makePass(getOutputs, ready, setSize, execute); }; diff --git a/js/webgpu/palettePass.js b/js/webgpu/palettePass.js index c9c2b33..c3c1535 100644 --- a/js/webgpu/palettePass.js +++ b/js/webgpu/palettePass.js @@ -161,5 +161,5 @@ export default (context, getInputs) => { renderPass.endPass(); }; - return makePass(ready, setSize, getOutputs, execute); + return makePass(getOutputs, ready, setSize, execute); }; diff --git a/js/webgpu/rainPass.js b/js/webgpu/rainPass.js index dd221a3..892d202 100644 --- a/js/webgpu/rainPass.js +++ b/js/webgpu/rainPass.js @@ -193,5 +193,5 @@ export default (context, getInputs) => { renderPass.endPass(); }; - return makePass(ready, setSize, getOutputs, execute); + return makePass(getOutputs, ready, setSize, execute); }; diff --git a/js/webgpu/resurrectionPass.js b/js/webgpu/resurrectionPass.js index 27b2c8f..5b7b562 100644 --- a/js/webgpu/resurrectionPass.js +++ b/js/webgpu/resurrectionPass.js @@ -83,5 +83,5 @@ export default (context, getInputs) => { renderPass.endPass(); }; - return makePass(ready, setSize, getOutputs, execute); + return makePass(getOutputs, ready, setSize, execute); }; diff --git a/js/webgpu/stripePass.js b/js/webgpu/stripePass.js index a8d282f..93a0b59 100644 --- a/js/webgpu/stripePass.js +++ b/js/webgpu/stripePass.js @@ -125,5 +125,5 @@ export default (context, getInputs) => { renderPass.endPass(); }; - return makePass(ready, setSize, getOutputs, execute); + return makePass(getOutputs, ready, setSize, execute); }; diff --git a/js/webgpu/utils.js b/js/webgpu/utils.js index e3df22c..e961200 100644 --- a/js/webgpu/utils.js +++ b/js/webgpu/utils.js @@ -80,10 +80,10 @@ const makeBindGroup = (device, pipeline, index, entries) => })), }); -const makePass = (ready, setSize, getOutputs, execute) => ({ +const makePass = (getOutputs, ready, setSize, execute) => ({ + getOutputs: getOutputs ?? (() => ({})), ready: ready ?? Promise.resolve(), setSize: setSize ?? (() => {}), - getOutputs: getOutputs ?? (() => ({})), execute: execute ?? (() => {}), });