@brochington/shader-backgrounds 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,261 @@
1
+ import { Color } from 'ogl';
2
+ import { ShaderPlugin } from '../core/types';
3
+
4
+ export type StainedGlassConfig = {
5
+ /** Background behind the glass (shows through a bit) */
6
+ backgroundColor: string;
7
+ /** Lead/edge color */
8
+ leadColor?: string; // default "#0b0b10"
9
+
10
+ /** Palette colors for glass cells */
11
+ colorA?: string; // default "#38bdf8"
12
+ colorB?: string; // default "#a78bfa"
13
+ colorC?: string; // default "#fb7185"
14
+ colorD?: string; // default "#fbbf24"
15
+
16
+ /** Cell scale */
17
+ scale?: number; // default 3.2
18
+ /**
19
+ * Pattern variant (changes the underlying coordinate transform).
20
+ * - 0: classic
21
+ * - 1: crystal (anisotropic)
22
+ * - 2: radial-ish twist
23
+ */
24
+ variant?: 0 | 1 | 2; // default 0
25
+ /** Random seed (any number). Change this to get a different layout. */
26
+ seed?: number; // default 0
27
+ /**
28
+ * Site jitter 0..1
29
+ * - 0 => very regular cells
30
+ * - 1 => fully random Voronoi sites
31
+ */
32
+ jitter?: number; // default 1.0
33
+ /** Rotate the pattern in radians */
34
+ rotation?: number; // default 0
35
+ /** Edge thickness */
36
+ edgeWidth?: number; // default 0.08
37
+ /** Edge sharpness (higher = crisper lines) */
38
+ edgeSharpness?: number; // default 1.25
39
+ /** Glow along edges */
40
+ edgeGlow?: number; // default 0.45
41
+ /** Warp amount (0..1.5) */
42
+ distortion?: number; // default 0.55
43
+ /** Motion speed */
44
+ speed?: number; // default 0.12
45
+ /** Grain */
46
+ grainAmount?: number; // default 0.02
47
+ };
48
+
49
+ export class StainedGlassPlugin implements ShaderPlugin {
50
+ name = 'stained-glass';
51
+
52
+ fragmentShader = /* glsl */ `
53
+ precision highp float;
54
+ #ifdef GL_OES_standard_derivatives
55
+ #extension GL_OES_standard_derivatives : enable
56
+ #endif
57
+
58
+ uniform float uTimeInternal;
59
+ uniform vec2 uResolution;
60
+ uniform vec3 uBg;
61
+ uniform vec3 uLead;
62
+ uniform vec3 uA;
63
+ uniform vec3 uB;
64
+ uniform vec3 uC;
65
+ uniform vec3 uD;
66
+ uniform float uScale;
67
+ uniform float uSeed;
68
+ uniform float uJitter;
69
+ uniform float uRotate;
70
+ uniform int uVariant;
71
+ uniform float uEdgeW;
72
+ uniform float uEdgeSharp;
73
+ uniform float uGlow;
74
+ uniform float uDist;
75
+ uniform float uGrain;
76
+
77
+ varying vec2 vUv;
78
+
79
+ float hash12(vec2 p) {
80
+ vec3 p3 = fract(vec3(p.xyx) * 0.1031);
81
+ p3 += dot(p3, p3.yzx + 33.33);
82
+ return fract((p3.x + p3.y) * p3.z);
83
+ }
84
+
85
+ vec2 hash22(vec2 p) {
86
+ float n = hash12(p);
87
+ return vec2(n, hash12(p + n + 19.19));
88
+ }
89
+
90
+ float noise(vec2 p) {
91
+ vec2 i = floor(p);
92
+ vec2 f = fract(p);
93
+ float a = hash12(i);
94
+ float b = hash12(i + vec2(1.0, 0.0));
95
+ float c = hash12(i + vec2(0.0, 1.0));
96
+ float d = hash12(i + vec2(1.0, 1.0));
97
+ vec2 u = f * f * (3.0 - 2.0 * f);
98
+ return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y;
99
+ }
100
+
101
+ vec3 palette4(float h) {
102
+ // Blend 4 colors in a loop-friendly way.
103
+ vec3 ab = mix(uA, uB, smoothstep(0.0, 0.50, h));
104
+ vec3 cd = mix(uC, uD, smoothstep(0.50, 1.0, h));
105
+ return mix(ab, cd, smoothstep(0.25, 0.85, h));
106
+ }
107
+
108
+ mat2 rot2(float a) {
109
+ float s = sin(a), c = cos(a);
110
+ return mat2(c, -s, s, c);
111
+ }
112
+
113
+ // Returns:
114
+ // x: distance to nearest site
115
+ // y: border metric (small near borders)
116
+ // z: random id
117
+ vec3 voronoi(vec2 x) {
118
+ vec2 n = floor(x);
119
+ vec2 f = fract(x);
120
+
121
+ float md = 8.0;
122
+ float md2 = 8.0;
123
+ vec2 mr = vec2(0.0);
124
+ vec2 seed2 = vec2(uSeed, uSeed * 1.618);
125
+
126
+ for (int j = -1; j <= 1; j++) {
127
+ for (int i = -1; i <= 1; i++) {
128
+ vec2 g = vec2(float(i), float(j));
129
+ vec2 o = hash22(n + g + seed2);
130
+ // Jitter controls how "random" the site is inside the cell.
131
+ o = mix(vec2(0.5), o, clamp(uJitter, 0.0, 1.0));
132
+ vec2 r = g + o - f;
133
+ float d = dot(r, r);
134
+ if (d < md) {
135
+ md2 = md;
136
+ md = d;
137
+ mr = n + g + o;
138
+ } else if (d < md2) {
139
+ md2 = d;
140
+ }
141
+ }
142
+ }
143
+
144
+ float d1 = sqrt(md);
145
+ float d2 = sqrt(md2);
146
+ float border = d2 - d1; // small at edges
147
+ return vec3(d1, border, hash12(mr + seed2));
148
+ }
149
+
150
+ void main() {
151
+ float aspect = uResolution.x / uResolution.y;
152
+ vec2 uv = vUv;
153
+ vec2 p = (uv - 0.5) * vec2(aspect, 1.0);
154
+ float t = uTimeInternal;
155
+
156
+ // Subtle low-frequency warp to avoid static mosaic vibe
157
+ vec2 warp = vec2(
158
+ noise(p * 1.5 + vec2(0.07 * t, -0.03 * t)) - 0.5,
159
+ noise(p * 1.5 + vec2(-0.05 * t, 0.06 * t) + 19.19) - 0.5
160
+ );
161
+ p += warp * (0.30 * uDist);
162
+
163
+ // Variant transforms (changes the cell character)
164
+ vec2 g = p;
165
+ g = rot2(uRotate) * g;
166
+ if (uVariant == 1) {
167
+ // Crystal: anisotropic scaling for more "shard-like" cells
168
+ g *= mat2(1.35, 0.35, -0.10, 0.85);
169
+ } else if (uVariant == 2) {
170
+ // Radial-ish twist: mild angular warp
171
+ float ang = atan(g.y, g.x);
172
+ float rad = length(g);
173
+ g += 0.15 * vec2(cos(ang * 3.0 + rad * 2.0), sin(ang * 2.0 - rad * 2.4));
174
+ }
175
+ g *= uScale;
176
+ vec3 v = voronoi(g);
177
+
178
+ float border = v.y;
179
+ float id = v.z;
180
+
181
+ // Lead line mask (1 at borders)
182
+ float w = max(0.0005, uEdgeW);
183
+ float aa = 0.0015;
184
+ #ifdef GL_OES_standard_derivatives
185
+ aa = fwidth(border) / max(0.0001, uEdgeSharp);
186
+ #endif
187
+ float lead = 1.0 - smoothstep(w - aa, w + aa, border);
188
+
189
+ // Cell color from palette + gentle variation
190
+ vec3 cell = palette4(id);
191
+ float tint = (noise(g * 0.85 + id * 11.7) - 0.5) * 0.18;
192
+ cell *= (1.0 + tint);
193
+
194
+ // Faux “glass thickness” / caustic-y highlight
195
+ float highlight = smoothstep(0.02, 0.30, noise(g * 2.2 + vec2(0.0, t * 0.6)));
196
+ highlight *= (1.0 - lead);
197
+
198
+ // Edge glow
199
+ float glow = exp(-border * border / max(0.00001, (w * w) * 0.35));
200
+ glow *= uGlow;
201
+
202
+ // Compose
203
+ vec3 col = mix(uBg, cell, 0.92);
204
+ col += cell * highlight * 0.10;
205
+
206
+ // Lead overrides + glow on top
207
+ col = mix(col, uLead, lead);
208
+ col += (cell * 0.55 + vec3(1.0) * 0.25) * glow;
209
+
210
+ // Subtle vignette
211
+ float vig = 1.0 - smoothstep(0.35, 1.15, length(p * vec2(1.0, 0.95)));
212
+ col *= 0.90 + 0.10 * vig;
213
+
214
+ // Grain
215
+ float gr = (hash12(uv * uResolution + t * 61.0) - 0.5) * 2.0;
216
+ col += gr * uGrain;
217
+
218
+ gl_FragColor = vec4(clamp(col, 0.0, 1.0), 1.0);
219
+ }
220
+ `;
221
+
222
+ uniforms: any;
223
+ private speed: number;
224
+
225
+ constructor(config: StainedGlassConfig) {
226
+ const bg = new Color(config.backgroundColor);
227
+ const lead = new Color(config.leadColor ?? '#0b0b10');
228
+ const a = new Color(config.colorA ?? '#38bdf8');
229
+ const b = new Color(config.colorB ?? '#a78bfa');
230
+ const c = new Color(config.colorC ?? '#fb7185');
231
+ const d = new Color(config.colorD ?? '#fbbf24');
232
+
233
+ this.speed = config.speed ?? 0.12;
234
+
235
+ this.uniforms = {
236
+ uBg: { value: [bg.r, bg.g, bg.b] },
237
+ uLead: { value: [lead.r, lead.g, lead.b] },
238
+ uA: { value: [a.r, a.g, a.b] },
239
+ uB: { value: [b.r, b.g, b.b] },
240
+ uC: { value: [c.r, c.g, c.b] },
241
+ uD: { value: [d.r, d.g, d.b] },
242
+ uScale: { value: config.scale ?? 3.2 },
243
+ uSeed: { value: config.seed ?? 0 },
244
+ uJitter: { value: config.jitter ?? 1.0 },
245
+ uRotate: { value: config.rotation ?? 0 },
246
+ uVariant: { value: config.variant ?? 0 },
247
+ uEdgeW: { value: config.edgeWidth ?? 0.08 },
248
+ uEdgeSharp: { value: config.edgeSharpness ?? 1.25 },
249
+ uGlow: { value: config.edgeGlow ?? 0.45 },
250
+ uDist: { value: config.distortion ?? 0.55 },
251
+ uGrain: { value: config.grainAmount ?? 0.02 },
252
+ uTimeInternal: { value: 0 },
253
+ };
254
+ }
255
+
256
+ onRender(dt: number) {
257
+ this.uniforms.uTimeInternal.value += dt * 0.001 * this.speed;
258
+ }
259
+ }
260
+
261
+
@@ -0,0 +1,11 @@
1
+ export * from './GradientPlugin';
2
+ export * from './GrainyFogPlugin';
3
+ export * from './RetroGridPlugin';
4
+ export * from './LiquidOrbPlugin';
5
+ export * from './CausticsPlugin';
6
+ export * from './AuroraWavesPlugin';
7
+ export * from './SoftStarfieldPlugin';
8
+ export * from './ContourLinesPlugin';
9
+ export * from './DreamyBokehPlugin';
10
+ export * from './InkWashPlugin';
11
+ export * from './StainedGlassPlugin';
package/tsconfig.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ESNext",
5
+ "lib": ["ES2020", "DOM", "WebWorker"],
6
+ "declaration": true,
7
+ "outDir": "./dist",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "moduleResolution": "bundler",
13
+ "allowImportingTsExtensions": false,
14
+ "resolveJsonModule": true,
15
+ "isolatedModules": true,
16
+ "noEmit": false,
17
+ "types": ["vite/client"]
18
+ },
19
+ "include": [
20
+ "src/**/*"
21
+ ],
22
+ "exclude": [
23
+ "node_modules",
24
+ "dist"
25
+ ]
26
+ }
package/vite.config.ts ADDED
@@ -0,0 +1,19 @@
1
+ import { defineConfig } from 'vite';
2
+ import dts from 'vite-plugin-dts';
3
+ import { resolve } from 'path';
4
+
5
+ export default defineConfig({
6
+ build: {
7
+ lib: {
8
+ entry: resolve(__dirname, 'src/index.ts'),
9
+ name: 'ShaderBackgrounds',
10
+ fileName: 'shader-backgrounds',
11
+ },
12
+ rollupOptions: {
13
+ // Ensure we don't bundle OGL if we want the user to bring their own,
14
+ // but for a standalone generic lib, bundling it is usually safer/easier for the user.
15
+ // We will bundle it here for "zero config" usage.
16
+ },
17
+ },
18
+ plugins: [dts()],
19
+ });
@@ -0,0 +1,13 @@
1
+ import { defineConfig } from 'vite';
2
+
3
+ // Build the repo-root `index.html` as a static demo site (for GitHub Pages).
4
+ // Set BASE_PATH in CI to `/<repo-name>/` so asset URLs work under Pages subpaths.
5
+ export default defineConfig({
6
+ base: process.env.BASE_PATH ?? '/',
7
+ build: {
8
+ outDir: 'dist-demo',
9
+ emptyOutDir: true,
10
+ },
11
+ });
12
+
13
+