diff --git a/index.html b/index.html
index 1aa2134..da93878 100644
--- a/index.html
+++ b/index.html
@@ -70,17 +70,6 @@
makePalette(regl, data)
);
- // All this takes place in a full screen quad.
- const fullScreenQuad = makeFullScreenQuad(regl, uniforms);
- const renderer = makeMatrixRenderer(regl, config);
- const bloomPass = makeBloomPass(regl, config, renderer.fbo);
- const colorPass = makeColorPass(regl, config, bloomPass.fbo);
- const drawToScreen = regl({
- uniforms: {
- tex: colorPass.fbo
- }
- });
-
const resize = () => {
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
@@ -88,27 +77,29 @@
window.onresize = resize;
resize();
- fullScreenQuad(renderer.update);
-
document.body.onload = async () => {
- const resources = await loadImages(regl, {
+ const images = await loadImages(regl, {
msdfTex: config.glyphTexURL,
- backgroundTex:
- config.effect === "image" ? config.backgroundImage : null
+ bgTex: config.effect === "image" ? config.bgURL : null
});
- const loop = regl.frame(({ viewportWidth, viewportHeight }) => {
- // All the FBOs except the compute FBOs need to be sized to the window.
- renderer.resize(viewportWidth, viewportHeight);
- bloomPass.resize(viewportWidth, viewportHeight);
- colorPass.resize(viewportWidth, viewportHeight);
+ // All this takes place in a full screen quad.
+ const fullScreenQuad = makeFullScreenQuad(regl, uniforms);
+ const renderer = makeMatrixRenderer(regl, config, images);
+ const bloomPass = makeBloomPass(regl, config, renderer.output);
+ const colorPass = makeColorPass(regl, config, images, bloomPass.output);
+ const drawToScreen = regl({
+ uniforms: {
+ tex: colorPass.output
+ }
+ });
- // And here is the full draw sequence.
+ const passes = [renderer, bloomPass, colorPass];
+
+ const loop = regl.frame(({ viewportWidth, viewportHeight }) => {
+ passes.forEach(pass => pass.resize(viewportWidth, viewportHeight));
fullScreenQuad(() => {
- renderer.update();
- renderer.render(resources);
- bloomPass.render();
- colorPass.render(resources);
+ passes.forEach(pass => pass.render());
drawToScreen();
});
});
diff --git a/js/bloomPass.js b/js/bloomPass.js
index d35b528..f88a799 100644
--- a/js/bloomPass.js
+++ b/js/bloomPass.js
@@ -13,7 +13,7 @@ const levelStrengths = Array(pyramidHeight)
export default (regl, config, input) => {
if (config.effect === "none") {
return {
- fbo: input,
+ output: input,
resize: () => {},
render: () => {}
};
@@ -22,7 +22,7 @@ export default (regl, config, input) => {
const highPassPyramid = makePyramid(regl, pyramidHeight);
const horizontalBlurPyramid = makePyramid(regl, pyramidHeight);
const verticalBlurPyramid = makePyramid(regl, pyramidHeight);
- const fbo = makePassFBO(regl);
+ const output = makePassFBO(regl);
// The high pass restricts the blur to bright things in our input texture.
const highPass = regl({
@@ -103,11 +103,11 @@ export default (regl, config, input) => {
verticalBlurPyramid.map((fbo, index) => [`tex_${index}`, fbo])
)
),
- framebuffer: fbo
+ framebuffer: output
});
return {
- fbo,
+ output,
resize: (viewportWidth, viewportHeight) => {
// The blur pyramids can be lower resolution than the screen.
resizePyramid(
@@ -128,7 +128,7 @@ export default (regl, config, input) => {
viewportHeight,
config.bloomSize
);
- fbo.resize(viewportWidth, viewportHeight);
+ output.resize(viewportWidth, viewportHeight);
},
render: () => {
highPassPyramid.forEach(fbo => highPass({ fbo, tex: input }));
diff --git a/js/colorPass.js b/js/colorPass.js
index 684b5a9..c82213e 100644
--- a/js/colorPass.js
+++ b/js/colorPass.js
@@ -63,20 +63,20 @@ const colorizeByStripes = regl =>
}
});
-const colorizeByImage = regl =>
+const colorizeByImage = (regl, bgTex) =>
regl({
frag: `
precision mediump float;
uniform sampler2D tex;
- uniform sampler2D backgroundTex;
+ uniform sampler2D bgTex;
varying vec2 vUV;
void main() {
- gl_FragColor = vec4(texture2D(backgroundTex, vUV).rgb * (pow(texture2D(tex, vUV).r, 1.5) * 0.995 + 0.005), 1.0);
+ gl_FragColor = vec4(texture2D(bgTex, vUV).rgb * (pow(texture2D(tex, vUV).r, 1.5) * 0.995 + 0.005), 1.0);
}
`,
uniforms: {
- backgroundTex: regl.prop("backgroundTex")
+ bgTex
}
});
@@ -87,35 +87,35 @@ const colorizersByEffect = {
image: colorizeByImage
};
-export default (regl, config, inputFBO) => {
- const fbo = makePassFBO(regl);
-
+export default (regl, config, { bgTex }, input) => {
if (config.effect === "none") {
return {
- fbo: inputFBO,
+ output: input,
resize: () => {},
render: () => {}
};
}
+ const output = makePassFBO(regl);
+
const colorize = regl({
uniforms: {
tex: regl.prop("tex")
},
- framebuffer: fbo
+ framebuffer: output
});
const colorizer = (config.effect in colorizersByEffect
? colorizersByEffect[config.effect]
- : colorizeByPalette)(regl);
+ : colorizeByPalette)(regl, bgTex);
return {
- fbo,
- resize: fbo.resize,
+ output,
+ resize: output.resize,
render: resources => {
colorize(
{
- tex: inputFBO
+ tex: input
},
() => colorizer(resources)
);
diff --git a/js/config.js b/js/config.js
index f7fac14..6078ca6 100644
--- a/js/config.js
+++ b/js/config.js
@@ -204,7 +204,7 @@ export default (searchString, makePaletteTexture) => {
config.animationSpeed * config.fallSpeed == 0
? 1
: Math.min(1, Math.abs(config.animationSpeed * config.fallSpeed));
- config.backgroundImage = getParam(
+ config.bgURL = getParam(
"url",
"https://upload.wikimedia.org/wikipedia/commons/0/0a/Flammarion_Colored.jpg"
);
diff --git a/js/renderer.js b/js/renderer.js
index 04bc871..476bdb9 100644
--- a/js/renderer.js
+++ b/js/renderer.js
@@ -1,27 +1,20 @@
-import { makePassFBO } from "./utils.js";
+import { makePassFBO, makeDoubleBuffer } from "./utils.js";
-export default (regl, config) => {
+export default (regl, config, { msdfTex }) => {
// These two framebuffers are used to compute the raining code.
// they take turns being the source and destination of the "compute" shader.
// The half float data type is crucial! It lets us store almost any real number,
// whereas the default type limits us to integers between 0 and 255.
- // These FBOs are smaller than the screen, because their pixels correspond
+ // This double buffer is smaller than the screen, because its pixels correspond
// with glyphs in the final image, and the glyphs are much larger than a pixel.
- const state = Array(2)
- .fill()
- .map(() =>
- regl.framebuffer({
- color: regl.texture({
- radius: config.numColumns,
- wrapT: "clamp",
- type: "half float"
- }),
- depthStencil: false
- })
- );
+ const doubleBuffer = makeDoubleBuffer(regl, {
+ radius: config.numColumns,
+ wrapT: "clamp",
+ type: "half float"
+ });
- const fbo = makePassFBO(regl);
+ const output = makePassFBO(regl);
const update = regl({
frag: `
@@ -195,10 +188,10 @@ export default (regl, config) => {
`,
uniforms: {
- lastState: ({ tick }) => state[tick % 2]
+ lastState: doubleBuffer.back
},
- framebuffer: ({ tick }) => state[(tick + 1) % 2] // The crucial state FBO alternator
+ framebuffer: doubleBuffer.front
});
// We render the code into an FBO using MSDFs: https://github.com/Chlumsky/msdfgen
@@ -290,19 +283,21 @@ export default (regl, config) => {
`,
uniforms: {
- msdfTex: regl.prop("msdfTex"),
+ msdfTex,
height: regl.context("viewportWidth"),
width: regl.context("viewportHeight"),
- lastState: ({ tick }) => state[tick % 2]
+ lastState: doubleBuffer.front
},
- framebuffer: fbo
+ framebuffer: output
});
return {
- resize: fbo.resize,
- fbo,
- update,
- render
+ resize: output.resize,
+ output,
+ render: resources => {
+ update();
+ render(resources);
+ }
};
};
diff --git a/js/utils.js b/js/utils.js
index 976cd7a..7b60316 100644
--- a/js/utils.js
+++ b/js/utils.js
@@ -17,6 +17,21 @@ const makePyramid = (regl, height) =>
.fill()
.map(_ => makePassFBO(regl));
+const makeDoubleBuffer = (regl, props) => {
+ const state = Array(2)
+ .fill()
+ .map(() =>
+ regl.framebuffer({
+ color: regl.texture(props),
+ depthStencil: false
+ })
+ );
+ return {
+ front: ({ tick }) => state[tick % 2],
+ back: ({ tick }) => state[(tick + 1) % 2]
+ };
+};
+
const resizePyramid = (pyramid, vw, vh, scale) =>
pyramid.forEach((fbo, index) =>
fbo.resize(
@@ -48,7 +63,7 @@ const loadImage = async (regl, url) => {
});
};
-const makeFullScreenQuad = (regl, uniforms) =>
+const makeFullScreenQuad = (regl, uniforms = {}, context = {}) =>
regl({
vert: `
precision mediump float;
@@ -78,6 +93,8 @@ const makeFullScreenQuad = (regl, uniforms) =>
time: regl.context("time")
},
+ context,
+
depth: { enable: false },
count: 3
});
@@ -95,6 +112,7 @@ const makePalette = (regl, data) =>
export {
makePassTexture,
makePassFBO,
+ makeDoubleBuffer,
makePyramid,
resizePyramid,
loadImage,