forked from Rezmason/matrix
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbloomPass.js
More file actions
106 lines (94 loc) · 3.46 KB
/
bloomPass.js
File metadata and controls
106 lines (94 loc) · 3.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import { fullscreenQuadReglBase, loadText, makePassFBO, makePass, requireShaderString } from "./utils.js";
// The bloom pass is basically an added high-pass blur.
// The blur approximation is the sum of a pyramid of downscaled, blurred textures.
const pyramidHeight = 5;
// A pyramid is just an array of FBOs, where each FBO is half the width
// and half the height of the FBO below it.
const makePyramid = (regl, height, halfFloat) =>
Array(height)
.fill()
.map((_) => makePassFBO(regl, halfFloat));
const resizePyramid = (pyramid, vw, vh, scale) =>
pyramid.forEach((fbo, index) => fbo.resize(Math.floor((vw * scale) / 2 ** index), Math.floor((vh * scale) / 2 ** index)));
export default ({ regl, config }, inputs) => {
const { bloomStrength, bloomSize, highPassThreshold } = config;
const enabled = bloomSize > 0 && bloomStrength > 0;
// If there's no bloom to apply, return a no-op pass with an empty bloom texture
if (!enabled) {
return makePass({
primary: inputs.primary,
bloom: makePassFBO(regl),
});
}
// Build three pyramids of FBOs, one for each step in the process
const highPassPyramid = makePyramid(regl, pyramidHeight, config.useHalfFloat);
const hBlurPyramid = makePyramid(regl, pyramidHeight, config.useHalfFloat);
const vBlurPyramid = makePyramid(regl, pyramidHeight, config.useHalfFloat);
const output = makePassFBO(regl, config.useHalfFloat);
// The high pass restricts the blur to bright things in our input texture.
const highPassFrag = loadText("shaders/glsl/bloomPass.highPass.frag.glsl");
const blurFrag = loadText("shaders/glsl/bloomPass.blur.frag.glsl");
const combineFrag = loadText("shaders/glsl/bloomPass.combine.frag.glsl");
let highPass;
let blur;
let combine;
const programsReady = Promise.all([highPassFrag.loaded, blurFrag.loaded, combineFrag.loaded]).then(() => {
highPass = regl({
...fullscreenQuadReglBase,
frag: requireShaderString("bloomPass.highPass.frag", () => highPassFrag.text()),
uniforms: {
highPassThreshold,
tex: regl.prop("tex"),
},
framebuffer: regl.prop("fbo"),
});
blur = regl({
...fullscreenQuadReglBase,
frag: requireShaderString("bloomPass.blur.frag", () => blurFrag.text()),
uniforms: {
tex: regl.prop("tex"),
direction: regl.prop("direction"),
height: regl.context("viewportWidth"),
width: regl.context("viewportHeight"),
},
framebuffer: regl.prop("fbo"),
});
combine = regl({
...fullscreenQuadReglBase,
frag: requireShaderString("bloomPass.combine.frag", () => combineFrag.text()),
uniforms: {
bloomStrength,
...Object.fromEntries(vBlurPyramid.map((fbo, index) => [`pyr_${index}`, fbo])),
},
framebuffer: output,
});
});
return makePass(
{
primary: inputs.primary,
bloom: output,
},
programsReady,
(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);
},
(shouldRender) => {
if (!shouldRender) {
return;
}
for (let i = 0; i < pyramidHeight; i++) {
const highPassFBO = highPassPyramid[i];
const hBlurFBO = hBlurPyramid[i];
const vBlurFBO = vBlurPyramid[i];
highPass({ fbo: highPassFBO, tex: i === 0 ? inputs.primary : highPassPyramid[i - 1] });
blur({ fbo: hBlurFBO, tex: highPassFBO, direction: [1, 0] });
blur({ fbo: vBlurFBO, tex: hBlurFBO, direction: [0, 1] });
}
combine({});
},
);
};