Skip to content

Commit ae7fdcc

Browse files
committed
docs: add an example of multi-pass post-processing effect (bloom)
1 parent 4c76307 commit ae7fdcc

File tree

8 files changed

+268
-0
lines changed

8 files changed

+268
-0
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
uniform sampler2D uImage;
2+
uniform vec2 uResolution;
3+
uniform vec2 uDirection;
4+
in vec2 vUv;
5+
out vec4 outColor;
6+
7+
const float weights[5] = float[](0.19638062, 0.29675293, 0.09442139, 0.01037598, 0.00025940);
8+
const float offsets[5] = float[](0.0, 1.41176471, 3.29411765, 5.17647059, 7.05882353);
9+
10+
void main() {
11+
vec3 color = texture(uImage, vUv).rgb * weights[0];
12+
float weightSum = weights[0];
13+
14+
if (vUv.x < 0.52) {
15+
for(int i = 1; i < 5; i++) {
16+
vec2 offset = (offsets[i] / uResolution) * 0.5 * uDirection;
17+
color += texture(uImage, vUv + offset).rgb * weights[i];
18+
color += texture(uImage, vUv - offset).rgb * weights[i];
19+
weightSum += 2.0 * weights[i];
20+
}
21+
color /= weightSum;
22+
}
23+
24+
outColor = vec4(color, 1.0);
25+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
uniform float uTime;
2+
in vec2 vUv;
3+
out vec4 outColor;
4+
5+
float sdCircle(vec2 p, float r) {
6+
return length(p) - r;
7+
}
8+
9+
vec3 drawCircle(vec2 pos, float radius, vec3 color) {
10+
return smoothstep(radius * 1.01, radius * .99, sdCircle(pos, radius)) * color;
11+
}
12+
13+
void main() {
14+
vec3 color = vec3(0, 0.07, 0.15);
15+
color += drawCircle(vUv - vec2(.4), .1 * (1. + sin(uTime/2.)/10.), vec3(vUv, 1.));
16+
color += drawCircle(vUv - vec2(.65, .65), .015 * (1. + sin(uTime/2.-1.5)/4.), vec3(vUv, 1.));
17+
color += drawCircle(vUv - vec2(.75, .4), .04 * (1. + sin(uTime/2.-1.)/4.), vec3(vUv, 1.));
18+
outColor = vec4(color, 1.);
19+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
uniform sampler2D uBaseImage;
2+
uniform sampler2D uBloomTexture;
3+
uniform vec2 uResolution;
4+
uniform float uMix;
5+
6+
in vec2 vUv;
7+
out vec4 outColor;
8+
9+
vec2 offset(float octave) {
10+
vec2 padding = 10.0 / uResolution;
11+
float octaveFloor = min(1.0, floor(octave / 3.0));
12+
vec2 offset = vec2(
13+
-octaveFloor * (0.25 + padding.x),
14+
-(1.0 - (1.0 / exp2(octave))) - padding.y * octave + octaveFloor * (0.35 + padding.y)
15+
);
16+
return offset + 0.5 / uResolution;
17+
}
18+
19+
vec3 blurredMipmapLevel(float octave) {
20+
vec2 offset = offset(octave - 1.0);
21+
return texture(uBloomTexture, vUv / exp2(octave) - offset).rgb;
22+
}
23+
24+
vec3 bloomColor() {
25+
return blurredMipmapLevel(1.0) * 0.8
26+
+ blurredMipmapLevel(3.0) * 0.5
27+
+ blurredMipmapLevel(4.0) * 1.2;
28+
}
29+
30+
void main() {
31+
vec4 baseColor = texture(uBaseImage, vUv);
32+
float baseColorGreyscale = dot(baseColor.rgb, vec3(0.299, 0.587, 0.114));
33+
float mixFactor = (1.0 - baseColorGreyscale * baseColor.a) * uMix;
34+
35+
vec4 combinedColor = baseColor;
36+
combinedColor.rgb += bloomColor() * mixFactor;
37+
38+
outColor = combinedColor;
39+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
title: "Multi pass (bloom)"
3+
---
4+
5+
::: example-editor {deps=tweakpane@^4.0.5}
6+
7+
<<< ./index.ts
8+
<<< ./circles.frag
9+
<<< ./mipmap.frag
10+
<<< ./blur.frag
11+
<<< ./combine.frag
12+
<<< ./styles.css
13+
<<< @/snippets/default/index.html
14+
15+
:::
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { useEffectPass, useWebGLCanvas, useCompositeEffectPass } from "usegl";
2+
import fragment from "./circles.frag?raw";
3+
import mipmapsShader from "./mipmap.frag?raw";
4+
import blurShader from "./blur.frag?raw";
5+
import combineShader from "./combine.frag?raw";
6+
import { Pane } from "tweakpane";
7+
import "./styles.css";
8+
9+
const mipmaps = useEffectPass({
10+
fragment: mipmapsShader,
11+
uniforms: {
12+
uThreshold: 0.2,
13+
},
14+
});
15+
16+
const horizontalBlur = useEffectPass({
17+
fragment: blurShader,
18+
uniforms: {
19+
uDirection: [1, 0],
20+
},
21+
});
22+
23+
const verticalBlur = useEffectPass({
24+
fragment: blurShader,
25+
uniforms: {
26+
uDirection: [0, 1],
27+
},
28+
});
29+
30+
const combine = useEffectPass({
31+
fragment: combineShader,
32+
uniforms: {
33+
uBaseImage: ({ inputPass }) => inputPass.target!.texture,
34+
uBloomTexture: () => verticalBlur.target!.texture,
35+
uMix: 1,
36+
},
37+
});
38+
39+
const bloomEffect = useCompositeEffectPass({
40+
mipmaps,
41+
horizontalBlur,
42+
verticalBlur,
43+
combine,
44+
});
45+
46+
const vignetteEffect = useEffectPass({
47+
fragment: /* glsl */ `
48+
uniform sampler2D uTexture;
49+
uniform float uSize; // (0.0 - 1.0)
50+
uniform float uRoundness; // (0.0 = rectangle, 1.0 = round)
51+
uniform float uStrength; // (0.0 - 1.0)
52+
varying vec2 vUv;
53+
54+
float vignette() {
55+
vec2 centered = vUv * 2.0 - 1.0;
56+
float circDist = length(centered);
57+
float rectDist = max(abs(centered.x), abs(centered.y));
58+
float dist = mix(rectDist, circDist, uRoundness);
59+
return 1. - smoothstep(uSize, uSize * 2., dist) * uStrength;
60+
}
61+
62+
void main() {
63+
vec4 color = texture(uTexture, vUv);
64+
color.rgb *= vignette();
65+
gl_FragColor = color;
66+
}
67+
`,
68+
uniforms: {
69+
uStrength: 0.5,
70+
uSize: 0.6,
71+
uRoundness: 0.7,
72+
},
73+
});
74+
75+
useWebGLCanvas({
76+
canvas: "#glCanvas",
77+
fragment: fragment,
78+
postEffects: [vignetteEffect, bloomEffect],
79+
});
80+
81+
const pane = new Pane({ title: "Uniforms" });
82+
83+
// You can update the uniforms of each individual pass, which will trigger a re-render
84+
const bloom = pane.addFolder({ title: "Bloom" });
85+
bloom.addBinding(bloomEffect.passes.mipmaps.uniforms, "uThreshold", { min: 0, max: 1 });
86+
bloom.addBinding(combine.uniforms, "uMix", { min: 0, max: 1 });
87+
88+
const vignette = pane.addFolder({ title: "Vignette" });
89+
vignette.addBinding(vignetteEffect.uniforms, "uStrength", { min: 0, max: 1 });
90+
vignette.addBinding(vignetteEffect.uniforms, "uSize", { min: 0, max: 1 });
91+
vignette.addBinding(vignetteEffect.uniforms, "uRoundness", { min: 0, max: 1 });
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
uniform sampler2D uImage;
2+
uniform vec2 uResolution;
3+
uniform float uThreshold;
4+
5+
in vec2 vUv;
6+
out vec4 outColor;
7+
8+
vec2 offset(float octave) {
9+
vec2 padding = 10.0 / uResolution;
10+
float octaveFloor = min(1.0, floor(octave / 3.0));
11+
return vec2(
12+
-octaveFloor * (0.25 + padding.x),
13+
-(1.0 - (1.0 / exp2(octave))) - padding.y * octave + octaveFloor * (0.35 + padding.y)
14+
);
15+
}
16+
17+
vec3 mipmapLevel(float octave) {
18+
float scale = exp2(octave);
19+
vec2 coord = (vUv + offset(octave - 1.0)) * scale;
20+
21+
if (any(lessThan(coord, vec2(0.0))) || any(greaterThan(coord, vec2(1.0)))) {
22+
return vec3(0.0);
23+
}
24+
25+
vec3 color = vec3(0.0);
26+
int spread = int(scale);
27+
28+
for (int i = 0; i < spread; i++) {
29+
for (int j = 0; j < spread; j++) {
30+
vec2 offset = (vec2(i, j) / uResolution) * scale / float(spread);
31+
vec3 imageColor = texture(uImage, coord + offset).rgb;
32+
color += max(vec3(0.0), imageColor - vec3(uThreshold));
33+
}
34+
}
35+
36+
return color / float(spread * spread);
37+
}
38+
39+
void main() {
40+
outColor = vec4(mipmapLevel(1.0) + mipmapLevel(3.0) + mipmapLevel(4.0), 1.0);
41+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
body {
2+
margin: 0;
3+
background: black;
4+
height: 100svh;
5+
display: grid;
6+
place-items: center;
7+
}
8+
9+
canvas {
10+
width: min(90svmin, 900px);
11+
aspect-ratio: 1;
12+
display: block;
13+
border-radius: 8px;
14+
border: 1px solid #555;
15+
}
16+
17+
.tp-dfwv {
18+
width: 260px !important;
19+
}

docs/types.d.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
declare module "*.glsl" {
2+
const content: string;
3+
export default content;
4+
}
5+
6+
declare module "*.vert" {
7+
const content: string;
8+
export default content;
9+
}
10+
11+
declare module "*.frag" {
12+
const content: string;
13+
export default content;
14+
}
15+
16+
declare module "*?raw" {
17+
const content: string;
18+
export default content;
19+
}

0 commit comments

Comments
 (0)