@voluma/vlam 0.2.2 → 0.2.4

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.
package/README.md CHANGED
@@ -16,9 +16,7 @@ Local site and generated API: `npm run dev` (http://localhost:5170, viewer at `/
16
16
  > Package `@voluma/vlam`, public **0.2.0**. APIs may still move before 1.0.
17
17
 
18
18
  ## Important notice!
19
- This is a pre-release. The API can still change in breaking ways before v1.0. Great for prototypes and experiments, not yet recommended for production.
20
-
21
- Phones and Intel/AMD integrated GPUs still need more device runs. A Galaxy S24 Ultra holds WebGPU; goose.sog is locked 60 rAF in SD and ~58 in HD, streamed million-splat views are fill-bound. On a MacBook Air M3 the demo keeps the fill-constrained (SD) preset; HD is a toggle, not the default.
19
+ This is a pre-release not yet recommended for production. The API can still change in breaking ways before v1.0.
22
20
 
23
21
 
24
22
  ## Install
package/dist/effects.d.ts CHANGED
@@ -15,6 +15,12 @@
15
15
  * tiles (or raw geometries) for PlayCanvas-style {@link SplatMesh.setRelighting}.
16
16
  * - {@link createRelightingShadowFactorMaterial}: writes a shadow **multiplier**
17
17
  * (1 = lit, <1 = umbra) so coverage does not statically tint splats.
18
+ * Accepts one light or {@link RelightingLightContribution}[] with intensity
19
+ * weights. Optional `color` / `diffuse` / `direction` add a Lambert boost on
20
+ * top of that identity (RGB may exceed 1 on a HalfFloat lighting RT).
21
+ * - {@link renderRelightingFactorMap}: host-safe lighting pass. Isolates
22
+ * `autoClear`, shadow maps, and swaps a passthrough `contextNode` so the
23
+ * example works on an existing `WebGPURenderer`, not only `createSplatRenderer`.
18
24
  * - {@link worldWarpPreset}: camera-centered sphere wrap (planet / bowl).
19
25
  * - {@link depthOfFieldPreset}: stylized modifier-based depth-of-field (M13).
20
26
  * For physically-modeled camera DoF prefer the core
@@ -262,6 +268,7 @@ export declare function worldWarpPreset(options?: {
262
268
  intensity?: number;
263
269
  radius?: number;
264
270
  }): WorldWarpPreset;
271
+ export { renderRelightingFactorMap, type RelightingFactorRenderer } from './relighting-pass';
265
272
  /** Handle returned by {@link createRelightingProxy}. */
266
273
  export interface RelightingProxy {
267
274
  /** World-space group of lit proxy meshes (hide from the main splat scene). */
@@ -274,14 +281,77 @@ export interface RelightingProxy {
274
281
  /** Disposes geometries and the default material owned by this helper. */
275
282
  dispose(): void;
276
283
  }
284
+ /** One independent shadow-casting light for {@link createRelightingShadowFactorMaterial}. */
285
+ export type RelightingLightContribution = {
286
+ light: THREE.Light;
287
+ /** Relative umbra weight. Default `1`. Values `<= 0` are skipped unless `fill` is set. */
288
+ intensity?: number;
289
+ /**
290
+ * Additive Lambert fill on top of the umbra factor (RGB may exceed 1).
291
+ * Gated by `shadow()`, facing, and for spot/point lights the Frostbite
292
+ * `distance` window (and the spot cone). `0` / omitted = umbra only.
293
+ */
294
+ fill?: number;
295
+ };
296
+ /** Options for {@link createRelightingShadowFactorMaterial}. */
297
+ export type RelightingShadowFactorOptions = {
298
+ umbra?: number;
299
+ receiveUpMin?: number;
300
+ color?: THREE.Color;
301
+ diffuse?: number;
302
+ direction?: THREE.Vector3;
303
+ /**
304
+ * How independent contributions combine into one multiplier.
305
+ * - `average` (default): intensity-weighted mean of shadows (fill lights).
306
+ * - `min`: union of umbras — each caster keeps its own silhouette. Contribution
307
+ * `intensity` scales umbra depth: `0` → none, `1` → default `umbra`,
308
+ * `>1` → darker than default (`umbra_i = clamp(1 - I*(1-umbra), 0, 1)`).
309
+ */
310
+ combine?: 'average' | 'min';
311
+ midLight?: THREE.Light;
312
+ midRadius?: number;
313
+ outerLight?: THREE.Light;
314
+ outerRadius?: number;
315
+ farLight?: THREE.Light;
316
+ nearRadius?: number;
317
+ };
318
+ /**
319
+ * Cap on independent shadow lights (cascades of the first directional do not
320
+ * count). Extra contributions are ignored. The compiled graph unrolls only
321
+ * the live contribution count, not a padded 32-wide loop.
322
+ */
323
+ export declare const MAX_RELIGHTING_SHADOW_LIGHTS = 32;
277
324
  /**
278
325
  * Node material for a **shadow-factor** lighting RT used with
279
326
  * {@link SplatMesh.setRelighting}.
280
327
  *
281
- * Fragment output is `vec4(vec3(mix(umbra, 1, shadow(light))), 1)`:
328
+ * Fragment output is `vec4(vec3(mix(umbra, 1, shadow)) + boost, 1)`:
282
329
  * - covered + lit → RGB ≈ 1 (identity modulate when brightness/background are 1)
283
330
  * - covered + umbra → RGB ≈ `umbra` (soft darken; never pure black)
284
331
  * - uncovered stays clear-alpha 0 from the RT clear
332
+ * - with `diffuse` &gt; 0, facing surfaces add `color * NdotL * diffuse`
333
+ * (RGB may exceed 1; use a HalfFloat lighting RT)
334
+ *
335
+ * Pass one {@link THREE.Light} (demo / single-sun) or an array of
336
+ * {@link RelightingLightContribution} so independent lights share one
337
+ * multiplier. Default combine is intensity-weighted average
338
+ * (`illum = sum(I_i * shadow_i) / sum(I_i)`); pass `combine: 'min'` for a
339
+ * union of umbras so each caster keeps its silhouette. Spot / point lights
340
+ * with `distance &gt; 0` fade their umbra to lit via a Frostbite cutoff window
341
+ * (steepened by `decay`) so the shadow map far plane is not a hard cliff.
342
+ * Contribution `fill` adds occluded Lambert (range + cone for punctual lights)
343
+ * on top of that identity so marker lamps can light splats without wrapping
344
+ * through collision. Cascades (`midLight` / `outerLight` / `farLight`) still
345
+ * attach to the **first** directional only. At most
346
+ * {@link MAX_RELIGHTING_SHADOW_LIGHTS} independent lights are used; extras
347
+ * are ignored. Graph size follows the live contribution count (not a padded
348
+ * loop of that width).
349
+ *
350
+ * By default, only **upward** proxy faces receive (`receiveUpMin`). Vertical
351
+ * and noisy foliage triangles still **cast**, but they do not self-shadow —
352
+ * that PCF acne reads as per-frame sparkle in trees. Pass `receiveUpMin: 0`
353
+ * to receive on every face. Contribution `fill` does **not** use that slope
354
+ * gate (stems would otherwise stay unlit in the factor map).
285
355
  *
286
356
  * Clear the lighting RT to **RGB 1, A 0** (not black). Softness / bilinear
287
357
  * samples at coverage edges otherwise pull in black and draw a dark outline
@@ -292,12 +362,36 @@ export interface RelightingProxy {
292
362
  * Assign to proxy meshes with `castShadow` and `receiveShadow` both true; the
293
363
  * umbra shape follows those triangles (splat foliage cannot cast).
294
364
  *
295
- * @param light - Shadow-casting light (typically a {@link THREE.DirectionalLight}).
365
+ * @param lights - Shadow-casting light, or intensity-weighted contributions.
366
+ * The first entry is the innermost cascade when `midLight` / `outerLight` /
367
+ * `farLight` are set.
296
368
  * @param options.umbra - Multiplier in full shadow, in `[0, 1]`. Default `0.45`.
369
+ * @param options.receiveUpMin - World-up `normal.y` below which the face does
370
+ * not receive. Default `0.25`. `0` disables the slope gate.
371
+ * @param options.color - Light tint for the optional Lambert boost. Mutate
372
+ * in place; the material holds this object. Default white.
373
+ * @param options.diffuse - Lambert boost on top of identity. Default `0`
374
+ * (shadow multiplier only). Facing, unshadowed coverage becomes
375
+ * `1 + color * NdotL * diffuse`.
376
+ * @param options.direction - World-space direction **toward** the light.
377
+ * Mutate in place each frame as the sun moves. Default `(0, 1, 0)`.
378
+ * @param options.midLight - Optional mid cascade (demo: ~50 m). Blend from
379
+ * the first light over `nearRadius`.
380
+ * @param options.midRadius - World metres where the mid cascade yields to
381
+ * `outerLight` (or `farLight`). Default `50`.
382
+ * @param options.outerLight - Optional outer cascade (demo: ~160 m) between
383
+ * mid and scene-sized far. Blend from mid over `midRadius`, then to
384
+ * `farLight` over `outerRadius`.
385
+ * @param options.outerRadius - World metres where the outer cascade yields to
386
+ * `farLight`. Default `160`. Ignored without `outerLight` and `farLight`.
387
+ * @param options.farLight - Optional scene-sized cascade. Out-of-frustum
388
+ * `shadow()` is 1 (lit), so inner maps can stay tight without clipping
389
+ * distant umbras. Blend by camera distance.
390
+ * @param options.nearRadius - World metres where the inner cascade yields to
391
+ * `midLight` (or to `farLight` if there is no mid). Default `20` with
392
+ * `midLight`, else `100`.
297
393
  */
298
- export declare function createRelightingShadowFactorMaterial(light: THREE.Light, options?: {
299
- umbra?: number;
300
- }): THREE.MeshStandardNodeMaterial;
394
+ export declare function createRelightingShadowFactorMaterial(lights: THREE.Light | RelightingLightContribution[], options?: RelightingShadowFactorOptions): THREE.MeshStandardNodeMaterial;
301
395
  /**
302
396
  * Builds a PlayCanvas-style relighting **proxy** from collision tiles or raw
303
397
  * geometries. The host places {@link RelightingProxy.group} on a dedicated
package/dist/effects.js CHANGED
@@ -1,87 +1,100 @@
1
- import * as g from "three/webgpu";
2
- import { shadow as te, mix as x, float as i, vec4 as O, vec3 as h, uniform as C, wgslFn as se, uniformArray as ne, smoothstep as X, int as oe, mat3 as re, modelViewMatrix as ae, vec2 as ce } from "three/tsl";
3
- import { w as ie } from "./logging-BfPdd7NJ.js";
4
- const Q = 5, le = { tint: 0, desaturate: 1, hide: 2, rim: 3 }, de = [0, 0, 0], ue = [1, 1, 1], fe = [0, 0, 0, 1];
5
- function ye(o = [], n = {}) {
6
- const r = Math.max(1, Math.floor(n.maxShapes ?? 32)), t = [];
7
- for (let e = 0; e < r * Q; e++) t.push(new g.Vector4());
8
- const d = ne(t, "vec4"), f = C(0, "int");
9
- let u = !1;
10
- const l = (e) => {
11
- const m = Math.min(e.length, r);
12
- e.length > r && !u && (u = !0, ie(
13
- `sdfEffects: ${e.length} shapes exceeds maxShapes=${r}; extra shapes are ignored. Raise maxShapes if you need more.`
1
+ import * as w from "three/webgpu";
2
+ import { context as mt, positionWorld as ot, cameraPosition as ft, smoothstep as F, float as a, normalWorld as Q, mix as p, min as ht, vec3 as v, uniform as R, vec4 as U, wgslFn as gt, uniformArray as xt, shadow as L, objectPosition as O, cos as nt, int as bt, mat3 as pt, modelViewMatrix as wt, reference as Mt, frameGroup as vt, vec2 as yt } from "three/tsl";
3
+ import { w as Ct } from "./logging-BfPdd7NJ.js";
4
+ const Z = new w.Vector2(), st = new w.Color(), Tt = mt({
5
+ getOutput: (t) => t
6
+ });
7
+ function Gt(t, e, c, o) {
8
+ t.getDrawingBufferSize(Z), o.setSize(Math.max(1, Z.x), Math.max(1, Z.y));
9
+ const m = t.getRenderTarget(), l = t.getActiveCubeFace(), d = t.getActiveMipmapLevel(), i = t.getMRT(), b = t.getClearAlpha(), n = t.autoClear, x = t.shadowMap.enabled, u = t.shadowMap.autoUpdate, s = t.contextNode;
10
+ t.getClearColor(st);
11
+ try {
12
+ t.contextNode = Tt, t.autoClear = !0, t.shadowMap.enabled = !0, u === !1 && (t.shadowMap.autoUpdate = !0), t.setMRT(null), t.setRenderTarget(o), t.setClearColor(16777215, 0), t.clear(), t.render(e, c);
13
+ } finally {
14
+ t.setRenderTarget(m, l, d), t.setMRT(i), t.setClearColor(st, b), t.autoClear = n, t.shadowMap.enabled = x, u !== void 0 && (t.shadowMap.autoUpdate = u), t.contextNode = s;
15
+ }
16
+ }
17
+ const tt = 5, Rt = { tint: 0, desaturate: 1, hide: 2, rim: 3 }, St = [0, 0, 0], kt = [1, 1, 1], Lt = [0, 0, 0, 1];
18
+ function Vt(t = [], e = {}) {
19
+ const c = Math.max(1, Math.floor(e.maxShapes ?? 32)), o = [];
20
+ for (let n = 0; n < c * tt; n++) o.push(new w.Vector4());
21
+ const m = xt(o, "vec4"), l = R(0, "int");
22
+ let d = !1;
23
+ const i = (n) => {
24
+ const x = Math.min(n.length, c);
25
+ n.length > c && !d && (d = !0, Ct(
26
+ `sdfEffects: ${n.length} shapes exceeds maxShapes=${c}; extra shapes are ignored. Raise maxShapes if you need more.`
14
27
  ));
15
- for (let c = 0; c < m; c++) {
16
- const s = e[c];
28
+ for (let u = 0; u < x; u++) {
29
+ const s = n[u];
17
30
  if (s.kind === "box") {
18
- const a = s.halfExtents;
19
- if (!a || !(a[0] > 0) || !(a[1] > 0) || !(a[2] > 0))
31
+ const f = s.halfExtents;
32
+ if (!f || !(f[0] > 0) || !(f[1] > 0) || !(f[2] > 0))
20
33
  throw new Error(
21
- `sdfEffects.setShapes: shape ${c} is a box and requires halfExtents with three positive components (got ${a ? `[${a.join(", ")}]` : "undefined"}).`
34
+ `sdfEffects.setShapes: shape ${u} is a box and requires halfExtents with three positive components (got ${f ? `[${f.join(", ")}]` : "undefined"}).`
22
35
  );
23
36
  } else if (typeof s.radius == "number" && s.radius > 0) {
24
37
  if (s.kind === "cylinder" && !(typeof s.height == "number" && s.height > 0))
25
38
  throw new Error(
26
- `sdfEffects.setShapes: shape ${c} is a cylinder and requires a positive height (got ${String(s.height)}).`
39
+ `sdfEffects.setShapes: shape ${u} is a cylinder and requires a positive height (got ${String(s.height)}).`
27
40
  );
28
41
  } else throw new Error(
29
- `sdfEffects.setShapes: shape ${c} is a ${s.kind} and requires a positive radius (got ${String(s.radius)}).`
42
+ `sdfEffects.setShapes: shape ${u} is a ${s.kind} and requires a positive radius (got ${String(s.radius)}).`
30
43
  );
31
44
  }
32
- for (let c = 0; c < m; c++) {
33
- const s = e[c], a = c * Q, w = s.center ?? de, p = s.color ?? ue, y = s.rotation ?? fe, E = s.kind === "box" ? 1 : s.kind === "cylinder" ? 2 : 0;
34
- if (t[a + 0].set(w[0], w[1], w[2], E), s.kind === "box") {
35
- const v = s.halfExtents;
36
- t[a + 1].set(v[0], v[1], v[2], s.falloff ?? 0);
37
- } else s.kind === "cylinder" ? t[a + 1].set(
45
+ for (let u = 0; u < x; u++) {
46
+ const s = n[u], f = u * tt, h = s.center ?? St, r = s.color ?? kt, g = s.rotation ?? Lt, y = s.kind === "box" ? 1 : s.kind === "cylinder" ? 2 : 0;
47
+ if (o[f + 0].set(h[0], h[1], h[2], y), s.kind === "box") {
48
+ const M = s.halfExtents;
49
+ o[f + 1].set(M[0], M[1], M[2], s.falloff ?? 0);
50
+ } else s.kind === "cylinder" ? o[f + 1].set(
38
51
  s.radius,
39
52
  s.height / 2,
40
53
  0,
41
54
  s.falloff ?? 0
42
- ) : t[a + 1].set(s.radius, 0, 0, s.falloff ?? 0);
43
- t[a + 2].set(p[0], p[1], p[2], le[s.mode]), t[a + 3].set(y[0], y[1], y[2], y[3]), t[a + 4].set(s.invert ? 1 : 0, s.strength ?? 1, 0, 0);
55
+ ) : o[f + 1].set(s.radius, 0, 0, s.falloff ?? 0);
56
+ o[f + 2].set(r[0], r[1], r[2], Rt[s.mode]), o[f + 3].set(g[0], g[1], g[2], g[3]), o[f + 4].set(s.invert ? 1 : 0, s.strength ?? 1, 0, 0);
44
57
  }
45
- f.value = m;
58
+ l.value = x;
46
59
  };
47
- return l(o), { modifier: (e) => {
48
- const m = e.localCenter;
49
- let c = e.color.rgb, s = i(1);
50
- for (let a = 0; a < r; a++) {
51
- const w = a * Q, p = (ee) => d.element(w + ee), y = p(0), E = p(1), v = p(2), R = p(3), D = p(4), U = y.xyz, P = y.w, T = E.xyz, $ = E.w.max(1e-4), B = v.xyz, M = v.w, L = D.x, N = D.y, V = oe(a).lessThan(f).select(i(1), i(0)), I = m.sub(U), z = xe(R, I), A = z.length().sub(T.x), k = me(z, T), q = he(z, T.x, T.y), F = P.greaterThan(0.5).and(P.lessThan(1.5)).select(i(1), i(0)), G = P.greaterThan(1.5).select(i(1), i(0)), W = x(x(A, k, F), q, G), _ = x(W, W.negate(), L), S = i(1).sub(X(0, $, _)).mul(N).mul(V), j = i(1).sub(X(0, $, _.abs())).mul(N).mul(V), H = M.lessThan(0.5).select(i(1), i(0)), J = M.greaterThan(0.5).and(M.lessThan(1.5)).select(i(1), i(0)), K = M.greaterThan(1.5).and(M.lessThan(2.5)).select(i(1), i(0)), Y = M.greaterThan(2.5).select(i(1), i(0)), Z = c.dot(h(0.2126, 0.7152, 0.0722));
52
- c = x(c, B, S.mul(H)), c = x(c, h(Z), S.mul(J)), c = x(c, B, j.mul(Y)), s = s.mul(i(1).sub(S.mul(K)));
60
+ return i(t), { modifier: (n) => {
61
+ const x = n.localCenter;
62
+ let u = n.color.rgb, s = a(1);
63
+ for (let f = 0; f < c; f++) {
64
+ const h = f * tt, r = (dt) => m.element(h + dt), g = r(0), y = r(1), M = r(2), C = r(3), T = r(4), J = g.xyz, N = g.w, z = y.xyz, _ = y.w.max(1e-4), G = M.xyz, S = M.w, K = T.x, V = T.y, B = bt(f).lessThan(l).select(a(1), a(0)), W = x.sub(J), E = At(C, W), P = E.length().sub(z.x), k = zt(E, z), $ = Et(E, z.x, z.y), D = N.greaterThan(0.5).and(N.lessThan(1.5)).select(a(1), a(0)), j = N.greaterThan(1.5).select(a(1), a(0)), q = p(p(P, k, D), $, j), H = p(q, q.negate(), K), A = a(1).sub(F(0, _, H)).mul(V).mul(B), X = a(1).sub(F(0, _, H.abs())).mul(V).mul(B), Y = S.lessThan(0.5).select(a(1), a(0)), ct = S.greaterThan(0.5).and(S.lessThan(1.5)).select(a(1), a(0)), rt = S.greaterThan(1.5).and(S.lessThan(2.5)).select(a(1), a(0)), lt = S.greaterThan(2.5).select(a(1), a(0)), ut = u.dot(v(0.2126, 0.7152, 0.0722));
65
+ u = p(u, G, A.mul(Y)), u = p(u, v(ut), A.mul(ct)), u = p(u, G, X.mul(lt)), s = s.mul(a(1).sub(A.mul(rt)));
53
66
  }
54
- return { color: O(c, e.color.a.mul(s)) };
55
- }, setShapes: l, maxShapes: r, _uniforms: { slots: t, count: f } };
67
+ return { color: U(u, n.color.a.mul(s)) };
68
+ }, setShapes: i, maxShapes: c, _uniforms: { slots: o, count: l } };
56
69
  }
57
- function me(o, n) {
58
- const r = o.abs().sub(n), t = r.max(h(0, 0, 0)).length(), d = r.x.max(r.y.max(r.z)).min(0);
59
- return t.add(d);
70
+ function zt(t, e) {
71
+ const c = t.abs().sub(e), o = c.max(v(0, 0, 0)).length(), m = c.x.max(c.y.max(c.z)).min(0);
72
+ return o.add(m);
60
73
  }
61
- function he(o, n, r) {
62
- const t = o.xz.length().sub(n), d = o.y.abs().sub(r), f = ce(t.max(0), d.max(0)).length(), u = t.max(d).min(0);
63
- return f.add(u);
74
+ function Et(t, e, c) {
75
+ const o = t.xz.length().sub(e), m = t.y.abs().sub(c), l = yt(o.max(0), m.max(0)).length(), d = o.max(m).min(0);
76
+ return l.add(d);
64
77
  }
65
- function xe(o, n) {
66
- const r = o.xyz, t = o.w, d = r.cross(n).mul(2);
67
- return n.sub(d.mul(t)).add(r.cross(d));
78
+ function At(t, e) {
79
+ const c = t.xyz, o = t.w, m = c.cross(e).mul(2);
80
+ return e.sub(m.mul(o)).add(c.cross(m));
68
81
  }
69
- function we(o = {}) {
70
- const n = new g.Vector3(...o.direction ?? [0.3, 1, 0.6]).normalize(), r = C(n), t = i(o.ambient ?? 0.35), d = i(o.diffuse ?? 0.75);
71
- return { modifier: (u) => {
72
- const l = u.normal.dot(r.normalize()).max(0), b = t.add(l.mul(d));
73
- return { color: O(u.color.rgb.mul(b), u.color.a) };
74
- }, direction: r };
82
+ function Bt(t = {}) {
83
+ const e = new w.Vector3(...t.direction ?? [0.3, 1, 0.6]).normalize(), c = R(e), o = a(t.ambient ?? 0.35), m = a(t.diffuse ?? 0.75);
84
+ return { modifier: (d) => {
85
+ const i = d.normal.dot(c.normalize()).max(0), b = o.add(i.mul(m));
86
+ return { color: U(d.color.rgb.mul(b), d.color.a) };
87
+ }, direction: c };
75
88
  }
76
- function ve(o = {}) {
77
- const n = C(Math.max(1e-4, o.focusDistance ?? 10)), r = C(Math.max(0, o.aperture ?? 0.5)), t = i(Math.max(0, o.maxBlur ?? 6));
78
- return { modifier: (f) => {
79
- const u = f.viewCenter.z.negate().max(1e-4), l = n.max(1e-4), b = u.sub(l).abs().div(l).mul(r).min(t), e = i(1).add(b), m = f.color.a.div(e.mul(e));
80
- return { scale: f.scale.mul(e), color: O(f.color.rgb, m) };
81
- }, focusDistance: n, aperture: r };
89
+ function Wt(t = {}) {
90
+ const e = R(Math.max(1e-4, t.focusDistance ?? 10)), c = R(Math.max(0, t.aperture ?? 0.5)), o = a(Math.max(0, t.maxBlur ?? 6));
91
+ return { modifier: (l) => {
92
+ const d = l.viewCenter.z.negate().max(1e-4), i = e.max(1e-4), b = d.sub(i).abs().div(i).mul(c).min(o), n = a(1).add(b), x = l.color.a.div(n.mul(n));
93
+ return { scale: l.scale.mul(n), color: U(l.color.rgb, x) };
94
+ }, focusDistance: e, aperture: c };
82
95
  }
83
- function Me(o = {}) {
84
- const n = C(0), r = o.frequency ?? 3, t = Math.min(Math.max(o.edge ?? 0.08, 0), 0.5), d = se(
96
+ function $t(t = {}) {
97
+ const e = R(0), c = t.frequency ?? 3, o = Math.min(Math.max(t.edge ?? 0.08, 0), 0.5), m = gt(
85
98
  /* wgsl */
86
99
  `
87
100
  fn vnoise( x: vec3<f32> ) -> f32 {
@@ -108,73 +121,163 @@ function Me(o = {}) {
108
121
  }
109
122
  `
110
123
  );
111
- return { modifier: (u) => {
112
- const l = d({ x: u.localCenter.mul(r) }), b = l.mul(1 - 2 * t).add(t), e = X(b.sub(t), b.add(t), n), m = n.greaterThan(b.sub(t));
113
- return { color: O(u.color.rgb, u.color.a.mul(e)), visible: m };
114
- }, progress: n };
115
- }
116
- function ke(o = {}) {
117
- const n = C(Math.min(1, Math.max(-1, o.intensity ?? 0))), r = C(Math.max(1e-4, o.radius ?? 1));
118
124
  return { modifier: (d) => {
119
- const f = n.clamp(-1, 1), u = r.max(1e-4), l = d.viewCenter, b = f.max(0), e = f.negate().max(0), m = l.z.negate().max(0), c = m.div(u).atan().mul(i(Math.PI * 0.5)), s = c.cos(), a = c.sin(), w = a.negate(), p = h(l.x, l.y.mul(s).sub(l.z.mul(a)), l.y.mul(a).add(l.z.mul(s))), y = h(l.x, l.y.mul(s).sub(l.z.mul(w)), l.y.mul(w).add(l.z.mul(s))), E = x(x(l, p, e), y, b), v = i(Math.PI * 0.5).mul(u), R = l.z.lessThan(0).select(v.negate().div(m.mul(m).add(u.mul(u))), i(0)), D = R.negate(), U = h(1, 0, 0), P = h(0, s, a), T = h(0, a.negate().sub(p.z.mul(R)), s.add(p.y.mul(R))), $ = h(1, 0, 0), B = h(0, s, w), M = h(
125
+ const i = m({ x: d.localCenter.mul(c) }), b = i.mul(1 - 2 * o).add(o), n = F(b.sub(o), b.add(o), e), x = e.greaterThan(b.sub(o));
126
+ return { color: U(d.color.rgb, d.color.a.mul(n)), visible: x };
127
+ }, progress: e };
128
+ }
129
+ function jt(t = {}) {
130
+ const e = R(Math.min(1, Math.max(-1, t.intensity ?? 0))), c = R(Math.max(1e-4, t.radius ?? 1));
131
+ return { modifier: (m) => {
132
+ const l = e.clamp(-1, 1), d = c.max(1e-4), i = m.viewCenter, b = l.max(0), n = l.negate().max(0), x = i.z.negate().max(0), u = x.div(d).atan().mul(a(Math.PI * 0.5)), s = u.cos(), f = u.sin(), h = f.negate(), r = v(i.x, i.y.mul(s).sub(i.z.mul(f)), i.y.mul(f).add(i.z.mul(s))), g = v(i.x, i.y.mul(s).sub(i.z.mul(h)), i.y.mul(h).add(i.z.mul(s))), y = p(p(i, r, n), g, b), M = a(Math.PI * 0.5).mul(d), C = i.z.lessThan(0).select(M.negate().div(x.mul(x).add(d.mul(d))), a(0)), T = C.negate(), J = v(1, 0, 0), N = v(0, s, f), z = v(0, f.negate().sub(r.z.mul(C)), s.add(r.y.mul(C))), _ = v(1, 0, 0), G = v(0, s, h), S = v(
120
133
  0,
121
- w.negate().sub(y.z.mul(D)),
122
- s.add(y.y.mul(D))
123
- ), L = h(1, 0, 0), N = h(0, 1, 0), V = h(0, 0, 1), I = x(x(L, U, e), $, b), z = x(x(N, P, e), B, b), A = x(x(V, T, e), M, b), k = I.div(I.length().max(1e-6)), q = z.sub(k.mul(k.dot(z))), F = q.div(q.length().max(1e-6)), G = A.sub(k.mul(k.dot(A))).sub(F.mul(F.dot(A))), W = G.div(G.length().max(1e-6)), _ = re(k, F, W), S = ae.toMat3(), j = S.transpose(), H = j.mul(_).mul(S);
134
+ h.negate().sub(g.z.mul(T)),
135
+ s.add(g.y.mul(T))
136
+ ), K = v(1, 0, 0), V = v(0, 1, 0), B = v(0, 0, 1), W = p(p(K, J, n), _, b), E = p(p(V, N, n), G, b), P = p(p(B, z, n), S, b), k = W.div(W.length().max(1e-6)), $ = E.sub(k.mul(k.dot(E))), D = $.div($.length().max(1e-6)), j = P.sub(k.mul(k.dot(P))).sub(D.mul(D.dot(P))), q = j.div(j.length().max(1e-6)), H = pt(k, D, q), A = wt.toMat3(), X = A.transpose(), Y = X.mul(H).mul(A);
124
137
  return {
125
- offset: d.offset.add(j.mul(E.sub(l))),
126
- rotation: H.mul(d.rotation)
138
+ offset: m.offset.add(X.mul(y.sub(i))),
139
+ rotation: Y.mul(m.rotation)
127
140
  };
128
- }, intensity: n, radius: r };
141
+ }, intensity: e, radius: c };
129
142
  }
130
- function Ce(o, n = {}) {
131
- const r = Number.isFinite(n.umbra) ? Math.min(1, Math.max(0, n.umbra)) : 0.45, t = new g.MeshStandardNodeMaterial();
132
- t.side = g.DoubleSide, t.color = new g.Color(1, 1, 1), t.roughness = 1, t.metalness = 0, t.transparent = !1, t.depthWrite = !0, t.polygonOffset = !0, t.polygonOffsetFactor = 1, t.polygonOffsetUnits = 1;
133
- const d = te(o), f = x(i(r), i(1), d);
134
- return t.outputNode = O(h(f), i(1)), t;
143
+ const Ft = 32, et = (t) => {
144
+ const e = t.intensity;
145
+ return Number.isFinite(e) ? Math.max(0, e) : 1;
146
+ }, it = (t) => {
147
+ const e = t.fill;
148
+ return Number.isFinite(e) ? Math.max(0, e) : 0;
149
+ }, I = (t, e, c) => {
150
+ const o = Mt(t, e, c);
151
+ return o.setGroup(vt), o;
152
+ }, at = (t) => {
153
+ const e = t;
154
+ if (!(e.isSpotLight === !0 || e.isPointLight === !0)) return a(1);
155
+ if (!(Number(e.distance) > 0)) return a(1);
156
+ const m = Math.max(Number(e.decay) || 1, 0.01), l = ot.distance(O(e)), d = I("distance", "float", e).max(1e-4), i = l.div(d).pow4().oneMinus().clamp().pow2();
157
+ return i.pow(a(m));
158
+ }, Nt = (t, e, c) => {
159
+ const o = t, m = o.isSpotLight === !0, l = o.isPointLight === !0;
160
+ if (!m && !l) return v(0, 0, 0);
161
+ const d = O(o), i = d.sub(ot), b = i.length().max(1e-4), n = i.div(b), x = Q.dot(n).max(0), u = at(t);
162
+ let s = u.mul(x).mul(c);
163
+ if (m) {
164
+ const h = t, r = O(h.target).sub(d).normalize(), g = I("angle", "float", h).max(1e-3), y = I("penumbra", "float", h).clamp(0, 0.95), M = g.mul(a(1).sub(y)).max(1e-3), C = F(nt(g), nt(M), n.negate().dot(r));
165
+ s = s.mul(C);
166
+ }
167
+ const f = I("color", "color", t);
168
+ return f.mul(a(e)).mul(s);
169
+ }, Pt = (t, e, c) => {
170
+ const o = t;
171
+ if (o.isDirectionalLight !== !0) return v(0, 0, 0);
172
+ const m = O(o.target).sub(O(o)).normalize(), l = Q.dot(m.negate()).max(0), d = I("color", "color", t);
173
+ return d.mul(a(e)).mul(l).mul(c);
174
+ }, Dt = (t) => {
175
+ const e = Array.isArray(t) ? t : [{ light: t, intensity: 1 }], c = [];
176
+ for (const o of e) {
177
+ if (c.length >= Ft) break;
178
+ et(o) <= 0 && it(o) <= 0 || c.push(o);
179
+ }
180
+ return c;
181
+ }, It = (t, e, c) => {
182
+ const o = Number.isFinite(e.nearRadius) ? Math.max(1, e.nearRadius) : e.midLight ? 20 : 100, m = Number.isFinite(e.midRadius) ? Math.max(o, e.midRadius) : 50, l = Number.isFinite(e.outerRadius) ? Math.max(m, e.outerRadius) : 160, d = (b, n, x) => p(b, n, F(a(x * 0.7), a(x * 0.95), c));
183
+ let i = L(t);
184
+ return e.midLight ? (i = d(i, L(e.midLight), o), e.outerLight ? (i = d(i, L(e.outerLight), m), e.farLight && (i = d(i, L(e.farLight), l))) : e.farLight && (i = d(i, L(e.farLight), m))) : e.farLight && (i = d(i, L(e.farLight), o)), i;
185
+ };
186
+ function qt(t, e = {}) {
187
+ const c = Number.isFinite(e.umbra) ? Math.min(1, Math.max(0, e.umbra)) : 0.45, o = Number.isFinite(e.receiveUpMin) ? Math.min(0.95, Math.max(0, e.receiveUpMin)) : 0.25, m = Number.isFinite(e.diffuse) ? Math.max(0, e.diffuse) : 0, l = new w.MeshStandardNodeMaterial();
188
+ l.side = w.DoubleSide, l.color = new w.Color(1, 1, 1), l.roughness = 1, l.metalness = 0, l.transparent = !1, l.depthWrite = !0, l.polygonOffset = !0, l.polygonOffsetFactor = 2, l.polygonOffsetUnits = 2;
189
+ const d = ot.distance(ft), i = Dt(t), b = o > 0 ? F(a(o), a(o + 0.3), Q.y) : a(1), n = (h, r) => {
190
+ const g = h === 0 ? It(r, e, d) : L(r);
191
+ return p(a(1), g, at(r));
192
+ }, x = i.map(
193
+ (h, r) => n(r, h.light)
194
+ );
195
+ let u, s = a(1);
196
+ if (e.combine === "min" && i.length > 0) {
197
+ let h = a(1);
198
+ for (let r = 0; r < i.length; r++) {
199
+ const g = i[r], y = et(g);
200
+ if (y <= 0) continue;
201
+ const M = Math.min(1, Math.max(0, 1 - y * (1 - c))), C = x[r], T = p(a(M), a(1), C);
202
+ h = ht(h, T);
203
+ }
204
+ s = h, u = p(a(1), h, b);
205
+ } else {
206
+ const h = i.map((r, g) => ({ index: g, intensity: et(r) })).filter(({ intensity: r }) => r > 0);
207
+ if (h.length === 1)
208
+ s = x[h[0].index];
209
+ else if (h.length > 1) {
210
+ let r = a(0), g = 0;
211
+ for (const { index: y, intensity: M } of h) {
212
+ g += M;
213
+ const C = x[y];
214
+ r = r.add(a(M).mul(C));
215
+ }
216
+ s = r.div(a(g));
217
+ }
218
+ if (h.length > 0) {
219
+ const r = p(a(1), s, b);
220
+ u = p(a(c), a(1), r);
221
+ } else
222
+ u = a(1);
223
+ }
224
+ let f = v(0, 0, 0);
225
+ if (m > 0) {
226
+ const h = R(e.color ?? new w.Color(1, 1, 1)), r = R(e.direction ?? new w.Vector3(0, 1, 0)), g = Q.dot(r.normalize()).max(0);
227
+ f = h.mul(g).mul(a(m)).mul(b).mul(s);
228
+ }
229
+ for (let h = 0; h < i.length; h++) {
230
+ const r = i[h], g = it(r);
231
+ if (g <= 0) continue;
232
+ const y = r.light, M = x[h], C = Nt(y, g, M), T = Pt(y, g, M);
233
+ f = f.add(C).add(T);
234
+ }
235
+ return l.outputNode = U(v(u).add(f), a(1)), l;
135
236
  }
136
- function ge(o) {
137
- const n = new g.BufferGeometry();
138
- return n.setAttribute("position", new g.BufferAttribute(o.positions.slice(), 3)), n.setIndex(new g.BufferAttribute(o.indices.slice(), 1)), n;
237
+ function Ot(t) {
238
+ const e = new w.BufferGeometry();
239
+ return e.setAttribute("position", new w.BufferAttribute(t.positions.slice(), 3)), e.setIndex(new w.BufferAttribute(t.indices.slice(), 1)), e;
139
240
  }
140
- function Ee(o = {}) {
141
- const n = o.albedo ?? 0.5, r = new g.Group(), t = o.matrixWorld ?? null, d = [], f = new g.MeshStandardMaterial({
142
- color: new g.Color(n, n, n),
241
+ function Ht(t = {}) {
242
+ const e = t.albedo ?? 0.5, c = new w.Group(), o = t.matrixWorld ?? null, m = [], l = new w.MeshStandardMaterial({
243
+ color: new w.Color(e, e, e),
143
244
  roughness: 1,
144
245
  metalness: 0,
145
- side: g.DoubleSide
146
- }), u = (e) => {
147
- t && e.applyMatrix4(t);
148
- }, l = (e, m) => {
149
- let c = e, s = m;
150
- t && !m && (c = e.clone(), s = !0), u(c), c.computeVertexNormals(), s && d.push(c);
151
- const a = new g.Mesh(c, f);
152
- a.castShadow = !0, a.receiveShadow = !0, a.raycast = () => {
153
- }, r.add(a);
246
+ side: w.DoubleSide
247
+ }), d = (n) => {
248
+ o && n.applyMatrix4(o);
249
+ }, i = (n, x) => {
250
+ let u = n, s = x;
251
+ o && !x && (u = n.clone(), s = !0), d(u), u.computeVertexNormals(), s && m.push(u);
252
+ const f = new w.Mesh(u, l);
253
+ f.castShadow = !0, f.receiveShadow = !0, f.raycast = () => {
254
+ }, c.add(f);
154
255
  };
155
- for (const e of o.tiles ?? [])
156
- l(ge(e.data), !0);
157
- for (const e of o.geometries ?? [])
158
- l(e, !1);
256
+ for (const n of t.tiles ?? [])
257
+ i(Ot(n.data), !0);
258
+ for (const n of t.geometries ?? [])
259
+ i(n, !1);
159
260
  return {
160
- group: r,
161
- configureMaterial: (e) => {
162
- e.side = g.DoubleSide, e.transparent = !1, e.opacity = 1, "color" in e && e.color instanceof g.Color && e.color.setRGB(n, n, n), "roughness" in e && typeof e.roughness == "number" && (e.roughness = 1), "metalness" in e && typeof e.metalness == "number" && (e.metalness = 0);
261
+ group: c,
262
+ configureMaterial: (n) => {
263
+ n.side = w.DoubleSide, n.transparent = !1, n.opacity = 1, "color" in n && n.color instanceof w.Color && n.color.setRGB(e, e, e), "roughness" in n && typeof n.roughness == "number" && (n.roughness = 1), "metalness" in n && typeof n.metalness == "number" && (n.metalness = 0);
163
264
  },
164
265
  dispose: () => {
165
- f.dispose();
166
- for (const e of d) e.dispose();
167
- r.clear();
266
+ l.dispose();
267
+ for (const n of m) n.dispose();
268
+ c.clear();
168
269
  }
169
270
  };
170
271
  }
171
272
  export {
172
- Ee as createRelightingProxy,
173
- Ce as createRelightingShadowFactorMaterial,
174
- ve as depthOfFieldPreset,
175
- we as lightingPreset,
176
- Me as revealPreset,
177
- ye as sdfEffects,
178
- ke as worldWarpPreset
273
+ Ft as MAX_RELIGHTING_SHADOW_LIGHTS,
274
+ Ht as createRelightingProxy,
275
+ qt as createRelightingShadowFactorMaterial,
276
+ Wt as depthOfFieldPreset,
277
+ Bt as lightingPreset,
278
+ Gt as renderRelightingFactorMap,
279
+ $t as revealPreset,
280
+ Vt as sdfEffects,
281
+ jt as worldWarpPreset
179
282
  };
180
283
  //# sourceMappingURL=effects.js.map