Skip to content

Commit 47ade94

Browse files
committed
feat: add bloom effect
1 parent 086f899 commit 47ade94

File tree

8 files changed

+239
-260
lines changed

8 files changed

+239
-260
lines changed

lib/playground/src/pages/post-processing/bloom.astro

Lines changed: 20 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -3,78 +3,25 @@ import Layout from "../../layouts/Layout.astro";
33
---
44

55
<script>
6-
import { useEffectPass, useWebGLCanvas, useCompositeEffectPass } from "usegl";
7-
import { fragment, mipmapsShader, blurShader, combineShader } from "../../shaders/bloom";
6+
import { useWebGLCanvas, loadTexture, bloom } from "usegl";
7+
import { fragment } from "../../shaders/bloom";
88
import { incrementRenderCount } from "../../components/renderCount";
99

10-
const mipmaps = useEffectPass({
11-
fragment: mipmapsShader,
12-
uniforms: {
13-
uThreshold: 0.2,
14-
},
15-
});
16-
17-
const horizontalBlur = useEffectPass({
18-
fragment: blurShader,
19-
uniforms: {
20-
uDirection: [1, 0],
21-
},
22-
});
23-
24-
const verticalBlur = useEffectPass({
25-
fragment: blurShader,
26-
uniforms: {
27-
uDirection: [0, 1],
28-
},
29-
});
30-
31-
const combine = useEffectPass({
32-
fragment: combineShader,
33-
uniforms: {
34-
uImage: ({ inputPass }) => inputPass.target!.texture,
35-
uBloomTexture: () => verticalBlur.target!.texture,
36-
uMix: 0,
37-
},
38-
});
39-
40-
const bloomEffect = useCompositeEffectPass({
41-
mipmaps,
42-
horizontalBlur,
43-
verticalBlur,
44-
combine,
45-
});
46-
47-
bloomEffect.passes.combine.uniforms.uMix = 1;
48-
49-
const vignetteEffect = useEffectPass({
50-
fragment: /* glsl */ `
51-
uniform sampler2D uTexture;
52-
varying vec2 vUv;
53-
54-
#define SIZE .6 // (0.0 - 1.0)
55-
#define ROUNDNESS .7 // (0.0 = rectangle, 1.0 = round)
56-
#define STRENGTH .6 // (0.0 - 1.0)
57-
58-
float vignette() {
59-
vec2 centered = vUv * 2.0 - 1.0;
60-
float circDist = length(centered);
61-
float rectDist = max(abs(centered.x), abs(centered.y));
62-
float dist = mix(rectDist, circDist, ROUNDNESS);
63-
return 1. - smoothstep(SIZE, SIZE * 2., dist) * STRENGTH;
64-
}
65-
66-
void main() {
67-
vec4 color = texture(uTexture, vUv);
68-
color.rgb *= vignette();
69-
gl_FragColor = color;
70-
}
71-
`,
72-
});
10+
const radius = 0.65;
11+
const levels = 8;
12+
const mix = 0.5;
7313

7414
const { onAfterRender } = useWebGLCanvas({
7515
canvas: "#glCanvas",
7616
fragment: fragment,
77-
postEffects: [vignetteEffect, bloomEffect],
17+
uniforms: {
18+
uTexture: loadTexture(
19+
"https://st.depositphotos.com/2300153/2648/i/950/depositphotos_26489675-stock-photo-dark-room-with-window.jpg",
20+
// "https://st.depositphotos.com/8521256/56706/v/600/depositphotos_567060820-stock-video-white-picture-of-rectangle-on.jpg",
21+
{ colorSpace: "srgb" }
22+
),
23+
},
24+
postEffects: [bloom({ levels, radius, mix })],
7825
});
7926

8027
onAfterRender(incrementRenderCount);
@@ -83,3 +30,10 @@ import Layout from "../../layouts/Layout.astro";
8330
<Layout title="Bloom">
8431
<canvas id="glCanvas"></canvas>
8532
</Layout>
33+
34+
<style>
35+
canvas {
36+
width: 600px;
37+
height: 600px;
38+
}
39+
</style>
Lines changed: 28 additions & 194 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,6 @@
1-
export const vertex = /*glsl*/ `
2-
in vec2 a_position;
3-
in float a_size;
4-
out vec2 vUv;
5-
uniform vec2 uResolution;
6-
7-
void main() {
8-
vUv = (a_position + 1.0) / 2.0;
9-
gl_Position = vec4(a_position, 0.0, 1.0);
10-
gl_PointSize = a_size;
11-
}
12-
`;
13-
141
export const fragment = /*glsl*/ `
152
in vec2 vUv;
3+
uniform sampler2D uTexture;
164
175
float sdCircle(vec2 p, float r) {
186
return length(p) - r;
@@ -23,187 +11,33 @@ export const fragment = /*glsl*/ `
2311
}
2412
2513
void main() {
26-
vec3 color = vec3(0, 0.07, 0.15);
14+
// circles
15+
vec3 color = vec3(0, 0.02, 0.06);
2716
color += drawCircle(vUv - vec2(.4), .1, vec3(vUv, 1.));
28-
color += drawCircle(vUv - vec2(.65, .65), .02, vec3(vUv, 1.));
29-
color += drawCircle(vUv - vec2(.75, .4), .04, vec3(vUv, 1.));
30-
gl_FragColor.rgba = vec4(color, 1.);
17+
color += drawCircle(vUv - vec2(.5, .68), .003, vec3(vUv, 1.));
18+
color += drawCircle(vUv - vec2(.66, .6), .015, vec3(vUv, 1.));
19+
color += drawCircle(vUv - vec2(.75, .4), .03, vec3(vUv, 1.));
20+
gl_FragColor.rgba = vec4(color * 1.5, 1.);
21+
gl_FragColor.rgb = pow(gl_FragColor.rgb, vec3(2.2));
22+
23+
// grid of dots
24+
// vec2 center = vec2(0.5, 0.5);
25+
// vec2 size = vec2(0.05); // Half the size of each small square
26+
// vec2 gridSize = vec2(10.0); // Number of squares in each dimension
27+
28+
// vec2 gridPos = floor(vUv * gridSize);
29+
// vec2 gridUv = fract(vUv * gridSize);
30+
31+
// vec2 dist = abs(gridUv - center);
32+
33+
// if (dist.x < size.x && dist.y < size.y) {
34+
// gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); // White square
35+
// } else {
36+
// gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); // Black background
37+
// }
38+
// gl_FragColor = vec4(vec3(1. - step(size.x, length(gridUv - center))), 1.);
39+
40+
// image texture
41+
// gl_FragColor.rgba = texture(uTexture, vUv);
3142
}
3243
`;
33-
34-
export const mipmapsShader = /*glsl*/ `
35-
uniform sampler2D uImage;
36-
uniform vec2 uResolution;
37-
uniform float uThreshold;
38-
39-
in vec2 vUv;
40-
out vec4 outColor;
41-
42-
vec2 CalcOffset(float octave) {
43-
vec2 offset = vec2(0.0);
44-
vec2 padding = vec2(10.0) / uResolution.xy;
45-
46-
offset.x = -min(1.0, floor(octave / 3.0)) * (0.25 + padding.x);
47-
48-
offset.y = -(1.0 - (1.0 / exp2(octave))) - padding.y * octave;
49-
offset.y += min(1.0, floor(octave / 3.0)) * (0.35 + padding.y);
50-
51-
return offset;
52-
}
53-
54-
55-
vec3 mipmapLevel(float octave) {
56-
float scale = exp2(octave);
57-
vec2 offset = CalcOffset(octave - 1.);
58-
vec2 coord = (vUv + offset) * scale;
59-
60-
if (coord.x < 0.0 || coord.x > 1.0 || coord.y < 0.0 || coord.y > 1.0) {
61-
return vec3(0.0);
62-
}
63-
64-
vec3 color = vec3(0.0);
65-
float weights = 0.0;
66-
67-
int spread = int(scale);
68-
69-
for (int i = 0; i < spread; i++) {
70-
for (int j = 0; j < spread; j++) {
71-
vec2 off = (vec2(i, j) / uResolution.xy + vec2(0.0) / uResolution.xy) * scale / float(spread);
72-
vec3 imageColor = texture(uImage, coord + off).rgb;
73-
color += max(vec3(0.0), imageColor.rgb - vec3(uThreshold));
74-
75-
weights += 1.0;
76-
}
77-
}
78-
79-
color /= weights;
80-
81-
return color;
82-
}
83-
84-
void main() {
85-
vec3 color = mipmapLevel(1.0);
86-
color += mipmapLevel(3.0);
87-
color += mipmapLevel(5.0);
88-
89-
outColor = vec4(color, 1.0);
90-
}
91-
`;
92-
93-
export const blurShader = /*glsl*/ `
94-
uniform sampler2D uImage;
95-
uniform vec2 uResolution;
96-
uniform vec2 uDirection;
97-
in vec2 vUv;
98-
out vec4 outColor;
99-
100-
vec3 ColorFetch(vec2 coord) {
101-
return texture(uImage, coord).rgb;
102-
}
103-
104-
float weights[5];
105-
float offsets[5];
106-
107-
108-
void main() {
109-
weights[0] = 0.19638062;
110-
weights[1] = 0.29675293;
111-
weights[2] = 0.09442139;
112-
weights[3] = 0.01037598;
113-
weights[4] = 0.00025940;
114-
115-
offsets[0] = 0.00000000;
116-
offsets[1] = 1.41176471;
117-
offsets[2] = 3.29411765;
118-
offsets[3] = 5.17647059;
119-
offsets[4] = 7.05882353;
120-
121-
vec2 uv = vUv;
122-
123-
vec3 color = vec3(0.0);
124-
float weightSum = 0.0;
125-
126-
if (uv.x < 0.52) {
127-
color += ColorFetch(uv) * weights[0];
128-
weightSum += weights[0];
129-
130-
for(int i = 1; i < 5; i++)
131-
{
132-
vec2 offset = vec2(offsets[i]) / uResolution.xy;
133-
color += ColorFetch(uv + offset * .5 * uDirection) * weights[i];
134-
color += ColorFetch(uv - offset * .5 * uDirection) * weights[i];
135-
weightSum += weights[i] * 2.0;
136-
}
137-
138-
color /= weightSum;
139-
}
140-
141-
outColor = vec4(color,1.0);
142-
}
143-
`;
144-
145-
export const combineShader = /* glsl */ `
146-
uniform sampler2D uImage;
147-
uniform sampler2D uBloomTexture;
148-
uniform float uMix;
149-
uniform vec2 uResolution;
150-
in vec2 vUv;
151-
out vec4 outColor;
152-
153-
154-
155-
vec3 ColorFetch(vec2 coord) {
156-
return texture(uImage, coord).rgb;
157-
}
158-
159-
vec3 BloomFetch(vec2 coord) {
160-
return texture(uBloomTexture, coord).rgb;
161-
}
162-
163-
164-
vec3 Grab(vec2 coord, const float octave, const vec2 offset)
165-
{
166-
float scale = exp2(octave);
167-
168-
coord /= scale;
169-
coord -= offset;
170-
171-
return BloomFetch(coord);
172-
}
173-
174-
vec2 CalcOffset(float octave) {
175-
vec2 offset = vec2(0.0);
176-
177-
vec2 padding = vec2(10.0) / uResolution.xy;
178-
179-
offset.x = -min(1.0, floor(octave / 3.0)) * (0.25 + padding.x);
180-
181-
offset.y = -(1.0 - (1.0 / exp2(octave))) - padding.y * octave;
182-
offset.y += min(1.0, floor(octave / 3.0)) * (0.35 + padding.y);
183-
184-
return offset + .5 / uResolution.xy;
185-
}
186-
187-
vec3 GetBloom(vec2 coord) {
188-
vec3 bloom = vec3(0.0);
189-
190-
//Reconstruct bloom from multiple blurred images
191-
bloom += Grab(coord, 1.0, vec2(CalcOffset(0.0))) * .2;
192-
bloom += Grab(coord, 3.0, vec2(CalcOffset(2.0))) * .2;
193-
bloom += Grab(coord, 5.0, vec2(CalcOffset(4.0))) * .6;
194-
195-
return bloom;
196-
}
197-
198-
void main() {
199-
vec4 baseColor = texture(uImage, vUv);
200-
vec4 bloomColor = vec4(GetBloom(vUv), 1);
201-
202-
outColor = baseColor;
203-
204-
float baseColorGreyscale = dot(baseColor.rgb, vec3(0.299, 0.587, 0.114));
205-
float mixFactor = (bloomColor.a - baseColorGreyscale * baseColor.a) * uMix;
206-
207-
outColor = mix(baseColor, baseColor + bloomColor, mixFactor);
208-
}
209-
`;

0 commit comments

Comments
 (0)