@3driseai/3drise-viewer 0.1.35 → 0.1.37
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/dist/animationsV2/ClipsRuntime.d.ts.map +1 -1
- package/dist/animationsV2/ClipsRuntime.js +3 -1
- package/dist/components/effects/HarmonicShellEffect.d.ts.map +1 -1
- package/dist/components/effects/HarmonicShellEffect.js +165 -173
- package/dist/components/effects/harmonicShellField.d.ts +77 -0
- package/dist/components/effects/harmonicShellField.d.ts.map +1 -0
- package/dist/components/effects/harmonicShellField.js +752 -0
- package/dist/operations/catalogue/inShell.d.ts +22 -0
- package/dist/operations/catalogue/inShell.d.ts.map +1 -0
- package/dist/operations/catalogue/inShell.js +245 -0
- package/dist/operations/catalogue/shaderLayers.d.ts.map +1 -1
- package/dist/operations/catalogue/shaderLayers.js +10 -7
- package/dist/operations/registry.d.ts.map +1 -1
- package/dist/operations/registry.js +2 -1
- package/package.json +3 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ClipsRuntime.d.ts","sourceRoot":"","sources":["../../src/animationsV2/ClipsRuntime.ts"],"names":[],"mappings":"AA2CA,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACjF,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1E,OAAO,EAA8E,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAE3H,OAAO,EAEL,KAAK,UAAU,EAAE,KAAK,aAAa,EAEpC,MAAM,iBAAiB,CAAC;AAkDzB,oDAAoD;AACpD,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IACpC,QAAQ,EAAE,iBAAiB,CAAC,gBAAgB,CAAC,CAAC;IAC9C,iEAAiE;IACjE,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,CAAC;AACxD,MAAM,WAAW,YAAY;IAAG,IAAI,EAAE,gBAAgB,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE;AAC/F,iDAAiD;AACjD,MAAM,MAAM,SAAS,GAAG,YAAY,CAAC;AAwBrC;;;;;GAKG;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,EAAE,IAAI,EAAE;IACvF,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,cAAc,GAAG,SAAS,CAAC;CACzH,KAAK,OAAO,CAAC;AAEd,cAAM,YAAY;IAChB,OAAO,CAAC,SAAS,CAA0B;IAC3C,qDAAqD;IACrD,YAAY,CAAC,EAAE,EAAE,SAAS,GAAG,IAAI,GAAG,IAAI;IAExC,OAAO,CAAC,KAAK,CAAmC;IAChD,OAAO,CAAC,SAAS,CAAuC;IACxD,OAAO,CAAC,GAAG,CAA6B;IACxC,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,SAAS,CAAyB;IAC1C,wFAAwF;IACxF,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,cAAc,CAAyB;IAC/C,OAAO,CAAC,cAAc,CAAwC;IAC9D,wEAAwE;IACxE,OAAO,CAAC,cAAc,CAAe;IACrC,0DAA0D;IAC1D,OAAO,CAAC,OAAO,CAA6B;IAC5C,0FAA0F;IAC1F,OAAO,CAAC,IAAI,CAA6B;IACzC,4FAA4F;IAC5F,OAAO,CAAC,OAAO,CAA6B;IAC5C,8EAA8E;IAC9E,OAAO,CAAC,IAAI,CAA+B;IAO3C,KAAK,UAAS;IACd,OAAO,CAAC,KAAK,CAA6B;IAC1C,IAAI,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAG9B,qCAAqC;IACrC,OAAO,CAAC,SAAS;IAOjB,OAAO,CAAC,MAAM;IAMd,UAAU,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,GAAG,IAAI;IAU1C;;;;;OAKG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO;
|
|
1
|
+
{"version":3,"file":"ClipsRuntime.d.ts","sourceRoot":"","sources":["../../src/animationsV2/ClipsRuntime.ts"],"names":[],"mappings":"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"}
|
|
@@ -136,7 +136,9 @@ class ClipsRuntime {
|
|
|
136
136
|
return false;
|
|
137
137
|
const key = targetKey(state, node, path);
|
|
138
138
|
this.live.set(key, value);
|
|
139
|
-
|
|
139
|
+
// The live write wins over a clip that merely HOLDS this property: the clip writes again only
|
|
140
|
+
// when its winning value moves (a UI slider beside a scrubbed story must stick while the story
|
|
141
|
+
// stands still). Previously the mark was cleared here, so the held value came straight back.
|
|
140
142
|
return true;
|
|
141
143
|
}
|
|
142
144
|
/** What was last written by `setLiveValue`, else the authored value (action patches included). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HarmonicShellEffect.d.ts","sourceRoot":"","sources":["../../../src/components/effects/HarmonicShellEffect.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGpG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"HarmonicShellEffect.d.ts","sourceRoot":"","sources":["../../../src/components/effects/HarmonicShellEffect.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGpG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAQhE,MAAM,WAAW,wBAAyB,SAAQ,gBAAgB;IAC9D,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,mBAAmB,CAAC;IAC5B,mFAAmF;IACnF,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,8EAA8E;IAC9E,SAAS,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC/B,yEAAyE;IACzE,SAAS,CAAC,EAAE,eAAe,CAAC;CAC/B;AAwCD,QAAA,MAAM,mBAAmB,EAAE,KAAK,CAAC,EAAE,CAAC,wBAAwB,CA8I3D,CAAC;AACF,eAAe,mBAAmB,CAAC"}
|
|
@@ -6,166 +6,23 @@ import { withConfigDefaults } from '../../utils/configAnimations.js';
|
|
|
6
6
|
import { DEFAULT_GENERATIVE_EFFECTS, EFFECTS_ANIMATABLE } from "@3driseai/3drise-core";
|
|
7
7
|
import { useObjectReady } from '../../hooks/useObjectReady.js';
|
|
8
8
|
import { useConfigDriver } from '../../hooks/useConfigDriver.js';
|
|
9
|
+
import { findById } from '../../utils/sceneQuery.js';
|
|
10
|
+
import { shellAttach, shellAttached, shellRelease } from '../../operations/catalogue/inShell.js';
|
|
11
|
+
import { isOperationOn } from '../../operations/runtime.js';
|
|
12
|
+
import { SHELL_FRAG, SHELL_VERT, bakeObjectSdf, bakeShapeSdf, createHarmonicState, createShellUniforms, registerShellField, unregisterShellField, updateHarmonicWeights, } from './harmonicShellField.js';
|
|
9
13
|
/**
|
|
10
14
|
* An iridescent shell around the host, displaced by real spherical harmonics
|
|
11
15
|
* up to l = 4 whose weights drift slowly. The colour is thin-film
|
|
12
16
|
* interference, as on a soap bubble, and the lines where the harmonic field
|
|
13
17
|
* crosses zero glow.
|
|
18
|
+
*
|
|
19
|
+
* With a target (text, a shape, or another scene object) the shell becomes it:
|
|
20
|
+
* `morph` 0 → 1 traces every vertex of the sphere onto the target's distance
|
|
21
|
+
* field (see harmonicShellField.ts), the harmonics keep breathing on it, and
|
|
22
|
+
* the triangles stretched across the gaps between letters fade out.
|
|
14
23
|
*/
|
|
15
|
-
/**
|
|
16
|
-
const
|
|
17
|
-
function mulberry32(seed) {
|
|
18
|
-
return () => {
|
|
19
|
-
seed |= 0;
|
|
20
|
-
seed = (seed + 0x6d2b79f5) | 0;
|
|
21
|
-
let r = Math.imul(seed ^ (seed >>> 15), 1 | seed);
|
|
22
|
-
r = (r + Math.imul(r ^ (r >>> 7), 61 | r)) ^ r;
|
|
23
|
-
return ((r ^ (r >>> 14)) >>> 0) / 4294967296;
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
/** Fixed per-term frequencies and phases, so the same config always moves the same way. */
|
|
27
|
-
function createHarmonicState() {
|
|
28
|
-
const rand = mulberry32(7);
|
|
29
|
-
const f1 = new Float32Array(24), f2 = new Float32Array(24), p1 = new Float32Array(24), p2 = new Float32Array(24);
|
|
30
|
-
for (let i = 0; i < 24; i++) {
|
|
31
|
-
f1[i] = 0.13 + 0.3 * rand();
|
|
32
|
-
f2[i] = 0.07 + 0.2 * rand();
|
|
33
|
-
p1[i] = rand() * Math.PI * 2;
|
|
34
|
-
p2[i] = rand() * Math.PI * 2;
|
|
35
|
-
}
|
|
36
|
-
// per-band gate, eased so changing `degree` blends bands in and out
|
|
37
|
-
return { f1, f2, p1, p2, gate: new Float32Array([0, 0, 0, 0, 0]) };
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Writes the 24 weights for time T into `out`. Bands above `degree` fade
|
|
41
|
-
* out; the top band leads and lower bands sit at about half strength.
|
|
42
|
-
*/
|
|
43
|
-
function updateHarmonicWeights(state, out, T, degree, dt) {
|
|
44
|
-
const deg = Math.round(Math.min(Math.max(degree, 1), 4));
|
|
45
|
-
const ease = Math.min(1, dt * 2.5);
|
|
46
|
-
let energy = 0;
|
|
47
|
-
for (let l = 1; l <= 4; l++) {
|
|
48
|
-
// l = 1 only shifts the shell sideways, so it stays quiet unless it is the only band
|
|
49
|
-
const target = l > deg ? 0 : l === deg ? 1 : l === 1 ? 0.2 : 0.55;
|
|
50
|
-
state.gate[l] += (target - state.gate[l]) * ease;
|
|
51
|
-
energy += state.gate[l] * state.gate[l] * (2 * l + 1);
|
|
52
|
-
}
|
|
53
|
-
const norm = 1 / Math.sqrt(Math.max(energy / 5, 1e-3));
|
|
54
|
-
for (let i = 0; i < 24; i++) {
|
|
55
|
-
const g = state.gate[SH_BANDS[i]];
|
|
56
|
-
out[i] = g * norm * (0.7 * Math.sin(T * state.f1[i] + state.p1[i]) + 0.3 * Math.sin(T * state.f2[i] + state.p2[i]));
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
const vert = /* glsl */ `
|
|
60
|
-
uniform float uW[24];
|
|
61
|
-
uniform float uAmplitude;
|
|
62
|
-
uniform float uBound;
|
|
63
|
-
uniform float uSizeMultiplier;
|
|
64
|
-
varying vec3 vNormal;
|
|
65
|
-
varying vec3 vViewDir;
|
|
66
|
-
varying float vField;
|
|
67
|
-
|
|
68
|
-
// Real spherical harmonics for l = 1..4, as polynomials in the unit vector,
|
|
69
|
-
// each scaled so its peak is about 1. The CPU animates the 24 weights.
|
|
70
|
-
float sh(vec3 n){
|
|
71
|
-
float x = n.x, y = n.y, z = n.z;
|
|
72
|
-
float x2 = x * x, y2 = y * y, z2 = z * z;
|
|
73
|
-
float s = 0.0;
|
|
74
|
-
s += uW[0] * y + uW[1] * z + uW[2] * x;
|
|
75
|
-
s += uW[3] * 2.0 * x * y + uW[4] * 2.0 * y * z + uW[5] * (1.5 * z2 - 0.5)
|
|
76
|
-
+ uW[6] * 2.0 * x * z + uW[7] * (x2 - y2);
|
|
77
|
-
s += uW[8] * y * (3.0 * x2 - y2)
|
|
78
|
-
+ uW[9] * 5.2 * x * y * z
|
|
79
|
-
+ uW[10] * 0.8 * y * (5.0 * z2 - 1.0)
|
|
80
|
-
+ uW[11] * 0.5 * z * (5.0 * z2 - 3.0)
|
|
81
|
-
+ uW[12] * 0.8 * x * (5.0 * z2 - 1.0)
|
|
82
|
-
+ uW[13] * 2.6 * z * (x2 - y2)
|
|
83
|
-
+ uW[14] * x * (x2 - 3.0 * y2);
|
|
84
|
-
s += uW[15] * 4.0 * x * y * (x2 - y2)
|
|
85
|
-
+ uW[16] * 3.0 * y * z * (3.0 * x2 - y2)
|
|
86
|
-
+ uW[17] * 1.5 * x * y * (7.0 * z2 - 1.0)
|
|
87
|
-
+ uW[18] * 0.95 * y * z * (7.0 * z2 - 3.0)
|
|
88
|
-
+ uW[19] * 0.125 * (35.0 * z2 * z2 - 30.0 * z2 + 3.0)
|
|
89
|
-
+ uW[20] * 0.95 * x * z * (7.0 * z2 - 3.0)
|
|
90
|
-
+ uW[21] * 0.78 * (x2 - y2) * (7.0 * z2 - 1.0)
|
|
91
|
-
+ uW[22] * 3.0 * x * z * (x2 - 3.0 * y2)
|
|
92
|
-
+ uW[23] * (x2 * (x2 - 3.0 * y2) - y2 * (3.0 * x2 - y2));
|
|
93
|
-
return s;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
vec3 shellPoint(vec3 n, out float f){
|
|
97
|
-
f = sh(n);
|
|
98
|
-
return n * uBound * uSizeMultiplier * (1.0 + uAmplitude * f);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
void main(){
|
|
102
|
-
vec3 n = normalize(position);
|
|
103
|
-
float f, f1, f2;
|
|
104
|
-
vec3 p0 = shellPoint(n, f);
|
|
105
|
-
|
|
106
|
-
// normal from two nearby points on the displaced surface
|
|
107
|
-
vec3 up = abs(n.y) > 0.99 ? vec3(1.0, 0.0, 0.0) : vec3(0.0, 1.0, 0.0);
|
|
108
|
-
vec3 t1 = normalize(cross(n, up));
|
|
109
|
-
vec3 t2 = cross(n, t1);
|
|
110
|
-
vec3 p1 = shellPoint(normalize(n + t1 * 0.01), f1);
|
|
111
|
-
vec3 p2 = shellPoint(normalize(n + t2 * 0.01), f2);
|
|
112
|
-
vec3 nrm = normalize(cross(p1 - p0, p2 - p0));
|
|
113
|
-
if (dot(nrm, n) < 0.0) nrm = -nrm;
|
|
114
|
-
|
|
115
|
-
vField = f;
|
|
116
|
-
vec4 mv = modelViewMatrix * vec4(p0, 1.0);
|
|
117
|
-
vNormal = normalize(normalMatrix * nrm);
|
|
118
|
-
vViewDir = normalize(-mv.xyz);
|
|
119
|
-
gl_Position = projectionMatrix * mv;
|
|
120
|
-
}
|
|
121
|
-
`;
|
|
122
|
-
const frag = /* glsl */ `
|
|
123
|
-
uniform vec3 uColor;
|
|
124
|
-
uniform vec3 uRimColor;
|
|
125
|
-
uniform float uOpacity;
|
|
126
|
-
uniform float uIntensity;
|
|
127
|
-
uniform float uFilmThickness;
|
|
128
|
-
uniform float uNodalLines;
|
|
129
|
-
uniform float uTint;
|
|
130
|
-
varying vec3 vNormal;
|
|
131
|
-
varying vec3 vViewDir;
|
|
132
|
-
varying float vField;
|
|
133
|
-
|
|
134
|
-
const float TAU = 6.28318530718;
|
|
135
|
-
|
|
136
|
-
void main(){
|
|
137
|
-
vec3 N = normalize(vNormal);
|
|
138
|
-
vec3 V = normalize(vViewDir);
|
|
139
|
-
float ndv = abs(dot(N, V));
|
|
140
|
-
|
|
141
|
-
// Thin-film interference, as on a soap bubble: a film of index 1.33 and
|
|
142
|
-
// thickness d delays the inner reflection by 2*n*d*cos(theta_t). Each
|
|
143
|
-
// wavelength interferes on its own, which splits white light into bands.
|
|
144
|
-
// The film is thicker where the harmonic pushes the shell out, so the
|
|
145
|
-
// colour bands follow the shape.
|
|
146
|
-
float cosT = sqrt(max(1.0 - (1.0 - ndv * ndv) / (1.33 * 1.33), 0.0));
|
|
147
|
-
float opd = 2.0 * 1.33 * uFilmThickness * (1.0 + 0.35 * vField) * cosT;
|
|
148
|
-
vec3 film = 0.5 + 0.5 * cos(TAU * opd / vec3(650.0, 532.0, 450.0));
|
|
149
|
-
// tint: keep the film's light and dark bands, carry them in the base colour
|
|
150
|
-
float lum = dot(film, vec3(0.299, 0.587, 0.114));
|
|
151
|
-
film = mix(film, lum * uColor * 1.7 + film * 0.12, uTint);
|
|
152
|
-
|
|
153
|
-
float fres = pow(1.0 - ndv, 3.0);
|
|
154
|
-
vec3 col = uColor * 0.25 + film * (0.2 + 0.8 * fres);
|
|
155
|
-
col = mix(col, uRimColor, fres * 0.45);
|
|
156
|
-
|
|
157
|
-
// nodal lines: where the harmonic field crosses zero
|
|
158
|
-
float nd = abs(vField) / max(fwidth(vField), 1e-4);
|
|
159
|
-
float nodal = (1.0 - smoothstep(0.5, 1.5, nd)) * uNodalLines;
|
|
160
|
-
col += uRimColor * nodal * 1.5;
|
|
161
|
-
|
|
162
|
-
float a = (0.06 + 0.9 * fres + nodal * 0.8) * uOpacity;
|
|
163
|
-
if (a < 0.003) discard;
|
|
164
|
-
gl_FragColor = vec4(col * uIntensity, a);
|
|
165
|
-
}
|
|
166
|
-
`;
|
|
167
|
-
/** Nothing rebuilds the shell: the vertex shader displaces a unit sphere. */
|
|
168
|
-
const STRUCTURAL = [];
|
|
24
|
+
/** Knobs that rebuild the target field (never driven per frame). The sphere itself never rebuilds. */
|
|
25
|
+
const STRUCTURAL = ['target', 'text', 'font', 'shape', 'targetObject'];
|
|
169
26
|
/** Knobs a keyframe or pointer binding may drive per frame — exactly what applyConfig pushes. */
|
|
170
27
|
const ANIMATABLE = new Set(EFFECTS_ANIMATABLE.harmonicShell.map((p) => p.value));
|
|
171
28
|
/** The single push point: every config knob reaches its uniform here. */
|
|
@@ -173,15 +30,19 @@ function applyConfig(u, c, bound) {
|
|
|
173
30
|
u.uSpeed.value = c.speed;
|
|
174
31
|
u.uIntensity.value = c.intensity;
|
|
175
32
|
u.uOpacity.value = c.opacity;
|
|
176
|
-
u.
|
|
177
|
-
u.
|
|
178
|
-
u.
|
|
179
|
-
u.
|
|
180
|
-
u.
|
|
181
|
-
u.
|
|
33
|
+
u.uHshColor.value.set(c.color);
|
|
34
|
+
u.uHshRim.value.set(c.rimColor);
|
|
35
|
+
u.uHshAmplitude.value = c.amplitude;
|
|
36
|
+
u.uHshBound.value = bound;
|
|
37
|
+
u.uHshSize.value = c.sizeMultiplier;
|
|
38
|
+
u.uHshFilm.value = c.filmThickness;
|
|
182
39
|
u.uNodalLines.value = c.nodalLines;
|
|
183
40
|
u.uDegree.value = c.degree;
|
|
184
|
-
u.
|
|
41
|
+
u.uHshTint.value = c.tint ?? 0;
|
|
42
|
+
u.uHshMorph.value = c.morph ?? 0;
|
|
43
|
+
u.uHshDepth.value = (c.depth ?? 0.25) * bound;
|
|
44
|
+
u.uBreath.value = c.breath ?? 0.12;
|
|
45
|
+
u.uWeb.value = (c.webCut ?? 0.02) * bound;
|
|
185
46
|
}
|
|
186
47
|
const HarmonicShellEffect = ({ boundingRadius, config, id, keyframes, mouseMove, onObjectReady, onObjectDispose }) => {
|
|
187
48
|
const cfg = useMemo(() => withConfigDefaults(DEFAULT_GENERATIVE_EFFECTS.harmonicShell, config), [config]);
|
|
@@ -189,34 +50,165 @@ const HarmonicShellEffect = ({ boundingRadius, config, id, keyframes, mouseMove,
|
|
|
189
50
|
useObjectReady(rootRef, config, onObjectReady, onObjectDispose);
|
|
190
51
|
const state = useMemo(createHarmonicState, []);
|
|
191
52
|
const uniforms = useMemo(() => ({
|
|
192
|
-
|
|
53
|
+
...createShellUniforms(),
|
|
193
54
|
uSpeed: { value: 1 },
|
|
194
55
|
uIntensity: { value: 1 },
|
|
195
56
|
uOpacity: { value: 1 },
|
|
196
|
-
uColor: { value: new THREE.Color() },
|
|
197
|
-
uRimColor: { value: new THREE.Color() },
|
|
198
|
-
uAmplitude: { value: 0.22 },
|
|
199
|
-
uBound: { value: 1 },
|
|
200
|
-
uSizeMultiplier: { value: 1.35 },
|
|
201
|
-
uFilmThickness: { value: 380 },
|
|
202
57
|
uNodalLines: { value: 0.6 },
|
|
203
58
|
uDegree: { value: 3 },
|
|
204
|
-
|
|
205
|
-
|
|
59
|
+
uBreath: { value: 0.12 },
|
|
60
|
+
uWeb: { value: 0.02 },
|
|
206
61
|
}), []);
|
|
207
|
-
const material = useMemo(() => new THREE.ShaderMaterial({ vertexShader:
|
|
62
|
+
const material = useMemo(() => new THREE.ShaderMaterial({ vertexShader: SHELL_VERT, fragmentShader: SHELL_FRAG, uniforms, transparent: true, depthWrite: false, blending: THREE.AdditiveBlending, side: THREE.DoubleSide }), [uniforms]);
|
|
208
63
|
useEffect(() => () => material.dispose(), [material]);
|
|
209
64
|
// the vertex shader displaces a unit sphere, so the mesh never needs rebuilding
|
|
210
65
|
const geometry = useMemo(() => new THREE.SphereGeometry(1, 192, 128), []);
|
|
211
66
|
useEffect(() => () => geometry.dispose(), [geometry]);
|
|
67
|
+
// the field other objects read (the inShell effect): registered while mounted
|
|
68
|
+
const field = useMemo(() => ({ root: null, uniforms: uniforms, inv: new THREE.Matrix4(), target: null }), [uniforms]);
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
if (!rootRef.current)
|
|
71
|
+
return;
|
|
72
|
+
field.root = rootRef.current;
|
|
73
|
+
registerShellField(field);
|
|
74
|
+
return () => unregisterShellField(field);
|
|
75
|
+
}, [field]);
|
|
212
76
|
const push = useCallback((merged) => {
|
|
213
77
|
applyConfig(uniforms, merged, boundingRadius);
|
|
214
78
|
}, [uniforms, boundingRadius]);
|
|
215
79
|
useLayoutEffect(() => { push(cfg); }, [push, cfg]);
|
|
216
80
|
useConfigDriver({ rootRef, id, family: 'effects', base: cfg, structural: STRUCTURAL, allowed: ANIMATABLE, keyframes, mouseMove, push });
|
|
81
|
+
// ── the target field: rebuilt when a structural knob changes ──
|
|
82
|
+
const targetKind = cfg.target ?? 'none';
|
|
83
|
+
const targetObjectId = cfg.targetObject ?? '';
|
|
84
|
+
const targetRef = useRef(null);
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
let cancelled = false;
|
|
87
|
+
const u = uniforms;
|
|
88
|
+
targetRef.current = null;
|
|
89
|
+
field.target = null;
|
|
90
|
+
if (targetKind === 'text' || targetKind === 'shape') {
|
|
91
|
+
const kind = targetKind === 'text' ? 'text' : (cfg.shape ?? 'star');
|
|
92
|
+
// the field spans a little more than the resting shell, so letters can reach past the sphere
|
|
93
|
+
bakeShapeSdf(kind, String(cfg.text ?? ''), String(cfg.font ?? 'sans'), boundingRadius * cfg.sizeMultiplier * 1.15).then((sdf) => {
|
|
94
|
+
if (cancelled || !sdf)
|
|
95
|
+
return;
|
|
96
|
+
const old = u.uHshSdf.value;
|
|
97
|
+
u.uHshSdf.value = sdf.tex;
|
|
98
|
+
u.uHshSdfHalf.value.set(sdf.half, sdf.half);
|
|
99
|
+
u.uHshMode.value = 1;
|
|
100
|
+
if (old && old.image?.width > 1)
|
|
101
|
+
old.dispose();
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
else if (targetKind === 'object' && targetObjectId) {
|
|
105
|
+
// the object may land after us (a model loading): look again until it is there
|
|
106
|
+
let tries = 0;
|
|
107
|
+
const find = () => {
|
|
108
|
+
if (cancelled)
|
|
109
|
+
return;
|
|
110
|
+
const obj = findById(targetObjectId);
|
|
111
|
+
if (!obj) {
|
|
112
|
+
if (tries++ < 60)
|
|
113
|
+
setTimeout(find, 500);
|
|
114
|
+
else
|
|
115
|
+
console.warn(`[harmonicShell] target object "${targetObjectId}" not found in the scene`);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
bakeObjectSdf(obj).then((sdf) => {
|
|
119
|
+
if (cancelled || !sdf)
|
|
120
|
+
return;
|
|
121
|
+
targetRef.current = obj;
|
|
122
|
+
field.target = obj;
|
|
123
|
+
u.uHshSdf3.value = sdf.tex;
|
|
124
|
+
u.uHshSdf3Min.value.copy(sdf.min);
|
|
125
|
+
u.uHshSdf3Size.value.copy(sdf.size);
|
|
126
|
+
u.uHshMode.value = 2;
|
|
127
|
+
}).catch((err) => console.warn('[harmonicShell] object field bake failed', err));
|
|
128
|
+
};
|
|
129
|
+
find();
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
u.uHshMode.value = 0;
|
|
133
|
+
}
|
|
134
|
+
return () => { cancelled = true; };
|
|
135
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
136
|
+
}, [targetKind, targetObjectId, cfg.text, cfg.font, cfg.shape, boundingRadius, uniforms, field]);
|
|
137
|
+
// ── objects crossing the shell show it: the inShell layer, attached and released at runtime ──
|
|
138
|
+
const inside = useMemo(() => ({ frame: 0, on: new Set(), box: new THREE.Box3(), sphere: new THREE.Sphere(), centre: new THREE.Vector3(), scale: new THREE.Vector3() }), []);
|
|
139
|
+
// the objects the shell affects (ids on the shell's config; nothing on them). Looked up each scan: they may land later or be replaced.
|
|
140
|
+
const insideIds = useMemo(() => (Array.isArray(cfg.insideObjects) ? cfg.insideObjects.filter((v) => typeof v === 'string' && v) : []), [cfg.insideObjects]);
|
|
141
|
+
const insideIdsRef = useRef(insideIds);
|
|
142
|
+
insideIdsRef.current = insideIds;
|
|
143
|
+
const look = useMemo(() => ({ look: cfg.insideLook === 'ghost' ? 'ghost' : 'film', bands: cfg.insideBands ?? 1, seam: cfg.insideSeam ?? 1, warp: cfg.insideWarp ?? 0.6, core: cfg.insideCore ?? 0.5 }), [cfg.insideLook, cfg.insideBands, cfg.insideSeam, cfg.insideWarp, cfg.insideCore]);
|
|
144
|
+
const lookRef = useRef(look);
|
|
145
|
+
lookRef.current = look;
|
|
146
|
+
// the look changed: push it to everything the shell is on
|
|
147
|
+
useEffect(() => { inside.on.forEach((o) => shellAttach(o, look)); }, [look, inside]);
|
|
148
|
+
useEffect(() => () => { inside.on.forEach((o) => shellRelease(o)); inside.on.clear(); }, [inside]);
|
|
149
|
+
// Objects not on the list are never touched. No separate pre-warm: the runtime's warm pass applies and quietly reverts the operation, and
|
|
150
|
+
// that revert would strip a layer the shell has just attached. The attach compiles its program
|
|
151
|
+
// in the background before anything shows (runProgress), and fires when the bounds touch — before
|
|
152
|
+
// any pixel is inside — so the compile is hidden on the first crossing.
|
|
153
|
+
const scanInside = () => {
|
|
154
|
+
const root = rootRef.current;
|
|
155
|
+
if (!root)
|
|
156
|
+
return;
|
|
157
|
+
const ids = insideIdsRef.current;
|
|
158
|
+
if (!ids.length) {
|
|
159
|
+
if (inside.on.size) {
|
|
160
|
+
inside.on.forEach((o) => shellRelease(o));
|
|
161
|
+
inside.on.clear();
|
|
162
|
+
}
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
inside.centre.setFromMatrixPosition(root.matrixWorld);
|
|
166
|
+
root.matrixWorld.decompose(new THREE.Vector3(), new THREE.Quaternion(), inside.scale);
|
|
167
|
+
const R = uniforms.uHshBound.value * uniforms.uHshSize.value * (1 + uniforms.uHshAmplitude.value) * Math.max(inside.scale.x, inside.scale.y, inside.scale.z);
|
|
168
|
+
const seen = new Set();
|
|
169
|
+
for (const id of ids) {
|
|
170
|
+
const o = findById(id);
|
|
171
|
+
// objects with real meshes (mesh / model); the shell's own wrap target is the shell, not something inside it
|
|
172
|
+
if (!o || o.userData?.visualFx !== true || o.userData.objectType === 'group' || o === targetRef.current || !o.visible)
|
|
173
|
+
continue;
|
|
174
|
+
inside.box.setFromObject(o);
|
|
175
|
+
if (inside.box.isEmpty())
|
|
176
|
+
continue;
|
|
177
|
+
inside.box.getBoundingSphere(inside.sphere);
|
|
178
|
+
const d = inside.sphere.center.distanceTo(inside.centre);
|
|
179
|
+
const was = inside.on.has(o);
|
|
180
|
+
// a little hysteresis, so an object resting on the boundary doesn't flicker on and off
|
|
181
|
+
if (d < R + inside.sphere.radius * (was ? 1.15 : 1.0))
|
|
182
|
+
seen.add(o);
|
|
183
|
+
}
|
|
184
|
+
for (const o of seen)
|
|
185
|
+
if (!inside.on.has(o) && shellAttach(o, lookRef.current, isOperationOn('inShell', o)))
|
|
186
|
+
inside.on.add(o);
|
|
187
|
+
for (const o of inside.on)
|
|
188
|
+
if (!seen.has(o)) {
|
|
189
|
+
inside.on.delete(o);
|
|
190
|
+
shellRelease(o);
|
|
191
|
+
}
|
|
192
|
+
for (const o of inside.on)
|
|
193
|
+
if (!shellAttached(o))
|
|
194
|
+
inside.on.delete(o); // taken away underneath (an own effect reverted) → re-attached next scan
|
|
195
|
+
};
|
|
196
|
+
const _m = useMemo(() => new THREE.Matrix4(), []);
|
|
217
197
|
useFrame((_, delta) => {
|
|
218
|
-
|
|
219
|
-
|
|
198
|
+
if (++inside.frame % 6 === 0)
|
|
199
|
+
scanInside();
|
|
200
|
+
uniforms.uHshTime.value += delta * uniforms.uSpeed.value;
|
|
201
|
+
updateHarmonicWeights(state, uniforms.uHshW.value, uniforms.uHshTime.value, uniforms.uDegree.value, delta);
|
|
202
|
+
const root = rootRef.current;
|
|
203
|
+
if (root) {
|
|
204
|
+
field.inv.copy(root.matrixWorld).invert();
|
|
205
|
+
const t = targetRef.current;
|
|
206
|
+
// shell-local → the target object's local space (where its field was baked), live: both may move
|
|
207
|
+
if (t && uniforms.uHshMode.value === 2) {
|
|
208
|
+
uniforms.uHshSdf3Inv.value.copy(_m.copy(t.matrixWorld).invert()).multiply(root.matrixWorld);
|
|
209
|
+
uniforms.uHshSdf3Mat.value.copy(uniforms.uHshSdf3Inv.value).invert();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
220
212
|
});
|
|
221
213
|
return (_jsx("group", { ref: rootRef, children: _jsx("mesh", { geometry: geometry, material: material }) }));
|
|
222
214
|
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import type { Object3D } from 'three';
|
|
3
|
+
export interface HarmonicState {
|
|
4
|
+
f1: Float32Array;
|
|
5
|
+
f2: Float32Array;
|
|
6
|
+
p1: Float32Array;
|
|
7
|
+
p2: Float32Array;
|
|
8
|
+
gate: Float32Array;
|
|
9
|
+
}
|
|
10
|
+
/** Fixed per-term frequencies and phases, so the same config always moves the same way. */
|
|
11
|
+
export declare function createHarmonicState(): HarmonicState;
|
|
12
|
+
/**
|
|
13
|
+
* Writes the 24 weights for time T into `out`. Bands above `degree` fade
|
|
14
|
+
* out; the top band leads and lower bands sit at about half strength.
|
|
15
|
+
*/
|
|
16
|
+
export declare function updateHarmonicWeights(state: HarmonicState, out: Float32Array, T: number, degree: number, dt: number): void;
|
|
17
|
+
/** The shared uniforms, declared (the shell's own ShaderMaterial; a layered material gets them from the kit). */
|
|
18
|
+
export declare const HSH_FIELD_DECL = "\n uniform float uHshW[24];\n uniform float uHshAmplitude; uniform float uHshBound; uniform float uHshSize;\n uniform vec3 uHshColor; uniform vec3 uHshRim; uniform float uHshFilm; uniform float uHshTint; uniform float uHshTime;\n uniform float uHshMorph; uniform float uHshMode;\n uniform sampler2D uHshSdf; uniform vec2 uHshSdfHalf; uniform float uHshDepth;\n uniform sampler3D uHshSdf3; uniform vec3 uHshSdf3Min; uniform vec3 uHshSdf3Size; uniform mat4 uHshSdf3Inv; uniform mat4 uHshSdf3Mat;\n";
|
|
19
|
+
/**
|
|
20
|
+
* The field, for any shader (after the declarations): the harmonic polynomial, the
|
|
21
|
+
* thin film, and the target distance functions. Include once per stage.
|
|
22
|
+
*/
|
|
23
|
+
export declare const HSH_FIELD_FUNCS = "\n\n // Real spherical harmonics for l = 1..4, as polynomials in the unit vector,\n // each scaled so its peak is about 1. The CPU animates the 24 weights.\n float hshSh(vec3 n){\n float x = n.x, y = n.y, z = n.z;\n float x2 = x * x, y2 = y * y, z2 = z * z;\n float s = 0.0;\n s += uHshW[0] * y + uHshW[1] * z + uHshW[2] * x;\n s += uHshW[3] * 2.0 * x * y + uHshW[4] * 2.0 * y * z + uHshW[5] * (1.5 * z2 - 0.5)\n + uHshW[6] * 2.0 * x * z + uHshW[7] * (x2 - y2);\n s += uHshW[8] * y * (3.0 * x2 - y2)\n + uHshW[9] * 5.2 * x * y * z\n + uHshW[10] * 0.8 * y * (5.0 * z2 - 1.0)\n + uHshW[11] * 0.5 * z * (5.0 * z2 - 3.0)\n + uHshW[12] * 0.8 * x * (5.0 * z2 - 1.0)\n + uHshW[13] * 2.6 * z * (x2 - y2)\n + uHshW[14] * x * (x2 - 3.0 * y2);\n s += uHshW[15] * 4.0 * x * y * (x2 - y2)\n + uHshW[16] * 3.0 * y * z * (3.0 * x2 - y2)\n + uHshW[17] * 1.5 * x * y * (7.0 * z2 - 1.0)\n + uHshW[18] * 0.95 * y * z * (7.0 * z2 - 3.0)\n + uHshW[19] * 0.125 * (35.0 * z2 * z2 - 30.0 * z2 + 3.0)\n + uHshW[20] * 0.95 * x * z * (7.0 * z2 - 3.0)\n + uHshW[21] * 0.78 * (x2 - y2) * (7.0 * z2 - 1.0)\n + uHshW[22] * 3.0 * x * z * (x2 - 3.0 * y2)\n + uHshW[23] * (x2 * (x2 - 3.0 * y2) - y2 * (3.0 * x2 - y2));\n return s;\n }\n\n // The resting shell: radius along direction n, in shell-local units.\n float hshRadius(vec3 n, out float field){\n field = hshSh(n);\n return uHshBound * uHshSize * (1.0 + uHshAmplitude * field);\n }\n\n // Thin-film interference, as on a soap bubble: a film of index 1.33 and\n // thickness d delays the inner reflection by 2*n*d*cos(theta_t). Each\n // wavelength interferes on its own, which splits white light into bands.\n vec3 hshFilm(float ndv, float field){\n float cosT = sqrt(max(1.0 - (1.0 - ndv * ndv) / (1.33 * 1.33), 0.0));\n float opd = 2.0 * 1.33 * uHshFilm * (1.0 + 0.35 * field) * cosT;\n vec3 film = 0.5 + 0.5 * cos(6.28318530718 * opd / vec3(650.0, 532.0, 450.0));\n float lum = dot(film, vec3(0.299, 0.587, 0.114));\n return mix(film, lum * uHshColor * 1.7 + film * 0.12, uHshTint);\n }\n\n // \u2500\u2500 the target: signed distance in shell-local units (negative inside) \u2500\u2500\n float hshSdf2(vec2 p){\n vec2 uv = p / (2.0 * uHshSdfHalf) + 0.5;\n vec2 c = clamp(uv, 0.0, 1.0);\n return texture2D(uHshSdf, c).r + length((uv - c) * 2.0 * uHshSdfHalf);\n }\n float hshSdf(vec3 p){\n if (uHshMode > 1.5) {\n // an object's own field, baked in its local space\n vec3 q = (uHshSdf3Inv * vec4(p, 1.0)).xyz;\n vec3 uvw = (q - uHshSdf3Min) / uHshSdf3Size; vec3 c = clamp(uvw, 0.0, 1.0);\n return texture(uHshSdf3, c).r + length((uvw - c) * uHshSdf3Size);\n }\n // text / shape: the 2D field extruded to \u00B1depth\n float d2 = hshSdf2(p.xy); float dz = abs(p.z) - uHshDepth;\n vec2 w = max(vec2(d2, dz), 0.0);\n return min(max(d2, dz), 0.0) + length(w);\n }\n vec3 hshSdfGrad(vec3 p){\n float e = 0.012;\n vec3 g = vec3(hshSdf(p + vec3(e, 0, 0)) - hshSdf(p - vec3(e, 0, 0)),\n hshSdf(p + vec3(0, e, 0)) - hshSdf(p - vec3(0, e, 0)),\n hshSdf(p + vec3(0, 0, e)) - hshSdf(p - vec3(0, 0, e)));\n float l = length(g); return l > 1e-6 ? g / l : vec3(0.0, 0.0, 1.0);\n }\n\n // Signed distance from a shell-local point to the shell's CURRENT surface\n // (the resting shell blended with the target by uHshMorph). 'field' is the\n // harmonic value in that direction, for the film.\n // Where a sphere vertex starts its trace: the resting shell point stretched to the\n // target's extent, so a wide word or a long object gets vertices along its whole length\n // instead of everything converging on the part nearest the centre.\n vec3 hshTraceStart(vec3 p0){\n float R0 = uHshBound * uHshSize;\n if (uHshMode > 1.5) {\n vec3 c = uHshSdf3Min + 0.5 * uHshSdf3Size; // the target's centre, in its own space\n vec3 h = uHshSdf3Size * 0.44; // half extents (the baked box is padded 12%)\n vec3 ct = (uHshSdf3Inv * vec4(0.0, 0.0, 0.0, 1.0)).xyz;\n vec3 qt = (uHshSdf3Inv * vec4(p0, 1.0)).xyz;\n qt = c + (qt - ct) * (h / R0);\n return (uHshSdf3Mat * vec4(qt, 1.0)).xyz;\n }\n return p0 * vec3(uHshSdfHalf / R0, max(uHshDepth, 0.05) / R0);\n }\n\n float hshDist(vec3 p, out float field){\n float r = length(p); vec3 n = r > 1e-5 ? p / r : vec3(0.0, 1.0, 0.0);\n float dS = r - hshRadius(n, field);\n return uHshMode > 0.5 && uHshMorph > 0.0 ? mix(dS, hshSdf(p), uHshMorph) : dS;\n }\n";
|
|
24
|
+
/** Declarations + functions: the whole field for a shader that declares nothing itself. */
|
|
25
|
+
export declare const HSH_FIELD_GLSL: string;
|
|
26
|
+
/** The shell's own material: the sphere displaced by the field, traced onto the target when it has one. */
|
|
27
|
+
export declare const SHELL_VERT = "\n uniform float uBreath;\n varying vec3 vNormal;\n varying vec3 vViewDir;\n varying float vField;\n varying vec3 vLocal;\n \n uniform float uHshW[24];\n uniform float uHshAmplitude; uniform float uHshBound; uniform float uHshSize;\n uniform vec3 uHshColor; uniform vec3 uHshRim; uniform float uHshFilm; uniform float uHshTint; uniform float uHshTime;\n uniform float uHshMorph; uniform float uHshMode;\n uniform sampler2D uHshSdf; uniform vec2 uHshSdfHalf; uniform float uHshDepth;\n uniform sampler3D uHshSdf3; uniform vec3 uHshSdf3Min; uniform vec3 uHshSdf3Size; uniform mat4 uHshSdf3Inv; uniform mat4 uHshSdf3Mat;\n\n\n // Real spherical harmonics for l = 1..4, as polynomials in the unit vector,\n // each scaled so its peak is about 1. The CPU animates the 24 weights.\n float hshSh(vec3 n){\n float x = n.x, y = n.y, z = n.z;\n float x2 = x * x, y2 = y * y, z2 = z * z;\n float s = 0.0;\n s += uHshW[0] * y + uHshW[1] * z + uHshW[2] * x;\n s += uHshW[3] * 2.0 * x * y + uHshW[4] * 2.0 * y * z + uHshW[5] * (1.5 * z2 - 0.5)\n + uHshW[6] * 2.0 * x * z + uHshW[7] * (x2 - y2);\n s += uHshW[8] * y * (3.0 * x2 - y2)\n + uHshW[9] * 5.2 * x * y * z\n + uHshW[10] * 0.8 * y * (5.0 * z2 - 1.0)\n + uHshW[11] * 0.5 * z * (5.0 * z2 - 3.0)\n + uHshW[12] * 0.8 * x * (5.0 * z2 - 1.0)\n + uHshW[13] * 2.6 * z * (x2 - y2)\n + uHshW[14] * x * (x2 - 3.0 * y2);\n s += uHshW[15] * 4.0 * x * y * (x2 - y2)\n + uHshW[16] * 3.0 * y * z * (3.0 * x2 - y2)\n + uHshW[17] * 1.5 * x * y * (7.0 * z2 - 1.0)\n + uHshW[18] * 0.95 * y * z * (7.0 * z2 - 3.0)\n + uHshW[19] * 0.125 * (35.0 * z2 * z2 - 30.0 * z2 + 3.0)\n + uHshW[20] * 0.95 * x * z * (7.0 * z2 - 3.0)\n + uHshW[21] * 0.78 * (x2 - y2) * (7.0 * z2 - 1.0)\n + uHshW[22] * 3.0 * x * z * (x2 - 3.0 * y2)\n + uHshW[23] * (x2 * (x2 - 3.0 * y2) - y2 * (3.0 * x2 - y2));\n return s;\n }\n\n // The resting shell: radius along direction n, in shell-local units.\n float hshRadius(vec3 n, out float field){\n field = hshSh(n);\n return uHshBound * uHshSize * (1.0 + uHshAmplitude * field);\n }\n\n // Thin-film interference, as on a soap bubble: a film of index 1.33 and\n // thickness d delays the inner reflection by 2*n*d*cos(theta_t). Each\n // wavelength interferes on its own, which splits white light into bands.\n vec3 hshFilm(float ndv, float field){\n float cosT = sqrt(max(1.0 - (1.0 - ndv * ndv) / (1.33 * 1.33), 0.0));\n float opd = 2.0 * 1.33 * uHshFilm * (1.0 + 0.35 * field) * cosT;\n vec3 film = 0.5 + 0.5 * cos(6.28318530718 * opd / vec3(650.0, 532.0, 450.0));\n float lum = dot(film, vec3(0.299, 0.587, 0.114));\n return mix(film, lum * uHshColor * 1.7 + film * 0.12, uHshTint);\n }\n\n // \u2500\u2500 the target: signed distance in shell-local units (negative inside) \u2500\u2500\n float hshSdf2(vec2 p){\n vec2 uv = p / (2.0 * uHshSdfHalf) + 0.5;\n vec2 c = clamp(uv, 0.0, 1.0);\n return texture2D(uHshSdf, c).r + length((uv - c) * 2.0 * uHshSdfHalf);\n }\n float hshSdf(vec3 p){\n if (uHshMode > 1.5) {\n // an object's own field, baked in its local space\n vec3 q = (uHshSdf3Inv * vec4(p, 1.0)).xyz;\n vec3 uvw = (q - uHshSdf3Min) / uHshSdf3Size; vec3 c = clamp(uvw, 0.0, 1.0);\n return texture(uHshSdf3, c).r + length((uvw - c) * uHshSdf3Size);\n }\n // text / shape: the 2D field extruded to \u00B1depth\n float d2 = hshSdf2(p.xy); float dz = abs(p.z) - uHshDepth;\n vec2 w = max(vec2(d2, dz), 0.0);\n return min(max(d2, dz), 0.0) + length(w);\n }\n vec3 hshSdfGrad(vec3 p){\n float e = 0.012;\n vec3 g = vec3(hshSdf(p + vec3(e, 0, 0)) - hshSdf(p - vec3(e, 0, 0)),\n hshSdf(p + vec3(0, e, 0)) - hshSdf(p - vec3(0, e, 0)),\n hshSdf(p + vec3(0, 0, e)) - hshSdf(p - vec3(0, 0, e)));\n float l = length(g); return l > 1e-6 ? g / l : vec3(0.0, 0.0, 1.0);\n }\n\n // Signed distance from a shell-local point to the shell's CURRENT surface\n // (the resting shell blended with the target by uHshMorph). 'field' is the\n // harmonic value in that direction, for the film.\n // Where a sphere vertex starts its trace: the resting shell point stretched to the\n // target's extent, so a wide word or a long object gets vertices along its whole length\n // instead of everything converging on the part nearest the centre.\n vec3 hshTraceStart(vec3 p0){\n float R0 = uHshBound * uHshSize;\n if (uHshMode > 1.5) {\n vec3 c = uHshSdf3Min + 0.5 * uHshSdf3Size; // the target's centre, in its own space\n vec3 h = uHshSdf3Size * 0.44; // half extents (the baked box is padded 12%)\n vec3 ct = (uHshSdf3Inv * vec4(0.0, 0.0, 0.0, 1.0)).xyz;\n vec3 qt = (uHshSdf3Inv * vec4(p0, 1.0)).xyz;\n qt = c + (qt - ct) * (h / R0);\n return (uHshSdf3Mat * vec4(qt, 1.0)).xyz;\n }\n return p0 * vec3(uHshSdfHalf / R0, max(uHshDepth, 0.05) / R0);\n }\n\n float hshDist(vec3 p, out float field){\n float r = length(p); vec3 n = r > 1e-5 ? p / r : vec3(0.0, 1.0, 0.0);\n float dS = r - hshRadius(n, field);\n return uHshMode > 0.5 && uHshMorph > 0.0 ? mix(dS, hshSdf(p), uHshMorph) : dS;\n }\n\n\n vec3 shellPoint(vec3 n, out float f){ return n * hshRadius(n, f); }\n\n void main(){\n vec3 n = normalize(position);\n float f, f1, f2;\n vec3 p0 = shellPoint(n, f);\n\n // normal from two nearby points on the displaced surface\n vec3 up = abs(n.y) > 0.99 ? vec3(1.0, 0.0, 0.0) : vec3(0.0, 1.0, 0.0);\n vec3 t1 = normalize(cross(n, up));\n vec3 t2 = cross(n, t1);\n vec3 p1 = shellPoint(normalize(n + t1 * 0.01), f1);\n vec3 p2 = shellPoint(normalize(n + t2 * 0.01), f2);\n vec3 nrm = normalize(cross(p1 - p0, p2 - p0));\n if (dot(nrm, n) < 0.0) nrm = -nrm;\n\n vec3 p = p0; vec3 N = nrm;\n if (uHshMode > 0.5 && uHshMorph > 0.0) {\n // trace the vertex onto the target surface along the field's gradient\n vec3 q = hshTraceStart(p0);\n for (int i = 0; i < 12; i++) { float d = hshSdf(q); q -= d * hshSdfGrad(q) * 0.85; }\n vec3 g = hshSdfGrad(q);\n // the harmonics keep breathing on the target, along its normal\n q += g * uHshBound * uHshAmplitude * uBreath * f;\n p = mix(p0, q, uHshMorph);\n N = normalize(mix(nrm, g, uHshMorph));\n }\n\n vLocal = p;\n vField = f;\n vec4 mv = modelViewMatrix * vec4(p, 1.0);\n vNormal = normalize(normalMatrix * N);\n vViewDir = normalize(-mv.xyz);\n gl_Position = projectionMatrix * mv;\n }\n";
|
|
28
|
+
export declare const SHELL_FRAG = "\n uniform float uOpacity;\n uniform float uIntensity;\n uniform float uNodalLines;\n uniform float uWeb;\n varying vec3 vNormal;\n varying vec3 vViewDir;\n varying float vField;\n varying vec3 vLocal;\n \n uniform float uHshW[24];\n uniform float uHshAmplitude; uniform float uHshBound; uniform float uHshSize;\n uniform vec3 uHshColor; uniform vec3 uHshRim; uniform float uHshFilm; uniform float uHshTint; uniform float uHshTime;\n uniform float uHshMorph; uniform float uHshMode;\n uniform sampler2D uHshSdf; uniform vec2 uHshSdfHalf; uniform float uHshDepth;\n uniform sampler3D uHshSdf3; uniform vec3 uHshSdf3Min; uniform vec3 uHshSdf3Size; uniform mat4 uHshSdf3Inv; uniform mat4 uHshSdf3Mat;\n\n\n // Real spherical harmonics for l = 1..4, as polynomials in the unit vector,\n // each scaled so its peak is about 1. The CPU animates the 24 weights.\n float hshSh(vec3 n){\n float x = n.x, y = n.y, z = n.z;\n float x2 = x * x, y2 = y * y, z2 = z * z;\n float s = 0.0;\n s += uHshW[0] * y + uHshW[1] * z + uHshW[2] * x;\n s += uHshW[3] * 2.0 * x * y + uHshW[4] * 2.0 * y * z + uHshW[5] * (1.5 * z2 - 0.5)\n + uHshW[6] * 2.0 * x * z + uHshW[7] * (x2 - y2);\n s += uHshW[8] * y * (3.0 * x2 - y2)\n + uHshW[9] * 5.2 * x * y * z\n + uHshW[10] * 0.8 * y * (5.0 * z2 - 1.0)\n + uHshW[11] * 0.5 * z * (5.0 * z2 - 3.0)\n + uHshW[12] * 0.8 * x * (5.0 * z2 - 1.0)\n + uHshW[13] * 2.6 * z * (x2 - y2)\n + uHshW[14] * x * (x2 - 3.0 * y2);\n s += uHshW[15] * 4.0 * x * y * (x2 - y2)\n + uHshW[16] * 3.0 * y * z * (3.0 * x2 - y2)\n + uHshW[17] * 1.5 * x * y * (7.0 * z2 - 1.0)\n + uHshW[18] * 0.95 * y * z * (7.0 * z2 - 3.0)\n + uHshW[19] * 0.125 * (35.0 * z2 * z2 - 30.0 * z2 + 3.0)\n + uHshW[20] * 0.95 * x * z * (7.0 * z2 - 3.0)\n + uHshW[21] * 0.78 * (x2 - y2) * (7.0 * z2 - 1.0)\n + uHshW[22] * 3.0 * x * z * (x2 - 3.0 * y2)\n + uHshW[23] * (x2 * (x2 - 3.0 * y2) - y2 * (3.0 * x2 - y2));\n return s;\n }\n\n // The resting shell: radius along direction n, in shell-local units.\n float hshRadius(vec3 n, out float field){\n field = hshSh(n);\n return uHshBound * uHshSize * (1.0 + uHshAmplitude * field);\n }\n\n // Thin-film interference, as on a soap bubble: a film of index 1.33 and\n // thickness d delays the inner reflection by 2*n*d*cos(theta_t). Each\n // wavelength interferes on its own, which splits white light into bands.\n vec3 hshFilm(float ndv, float field){\n float cosT = sqrt(max(1.0 - (1.0 - ndv * ndv) / (1.33 * 1.33), 0.0));\n float opd = 2.0 * 1.33 * uHshFilm * (1.0 + 0.35 * field) * cosT;\n vec3 film = 0.5 + 0.5 * cos(6.28318530718 * opd / vec3(650.0, 532.0, 450.0));\n float lum = dot(film, vec3(0.299, 0.587, 0.114));\n return mix(film, lum * uHshColor * 1.7 + film * 0.12, uHshTint);\n }\n\n // \u2500\u2500 the target: signed distance in shell-local units (negative inside) \u2500\u2500\n float hshSdf2(vec2 p){\n vec2 uv = p / (2.0 * uHshSdfHalf) + 0.5;\n vec2 c = clamp(uv, 0.0, 1.0);\n return texture2D(uHshSdf, c).r + length((uv - c) * 2.0 * uHshSdfHalf);\n }\n float hshSdf(vec3 p){\n if (uHshMode > 1.5) {\n // an object's own field, baked in its local space\n vec3 q = (uHshSdf3Inv * vec4(p, 1.0)).xyz;\n vec3 uvw = (q - uHshSdf3Min) / uHshSdf3Size; vec3 c = clamp(uvw, 0.0, 1.0);\n return texture(uHshSdf3, c).r + length((uvw - c) * uHshSdf3Size);\n }\n // text / shape: the 2D field extruded to \u00B1depth\n float d2 = hshSdf2(p.xy); float dz = abs(p.z) - uHshDepth;\n vec2 w = max(vec2(d2, dz), 0.0);\n return min(max(d2, dz), 0.0) + length(w);\n }\n vec3 hshSdfGrad(vec3 p){\n float e = 0.012;\n vec3 g = vec3(hshSdf(p + vec3(e, 0, 0)) - hshSdf(p - vec3(e, 0, 0)),\n hshSdf(p + vec3(0, e, 0)) - hshSdf(p - vec3(0, e, 0)),\n hshSdf(p + vec3(0, 0, e)) - hshSdf(p - vec3(0, 0, e)));\n float l = length(g); return l > 1e-6 ? g / l : vec3(0.0, 0.0, 1.0);\n }\n\n // Signed distance from a shell-local point to the shell's CURRENT surface\n // (the resting shell blended with the target by uHshMorph). 'field' is the\n // harmonic value in that direction, for the film.\n // Where a sphere vertex starts its trace: the resting shell point stretched to the\n // target's extent, so a wide word or a long object gets vertices along its whole length\n // instead of everything converging on the part nearest the centre.\n vec3 hshTraceStart(vec3 p0){\n float R0 = uHshBound * uHshSize;\n if (uHshMode > 1.5) {\n vec3 c = uHshSdf3Min + 0.5 * uHshSdf3Size; // the target's centre, in its own space\n vec3 h = uHshSdf3Size * 0.44; // half extents (the baked box is padded 12%)\n vec3 ct = (uHshSdf3Inv * vec4(0.0, 0.0, 0.0, 1.0)).xyz;\n vec3 qt = (uHshSdf3Inv * vec4(p0, 1.0)).xyz;\n qt = c + (qt - ct) * (h / R0);\n return (uHshSdf3Mat * vec4(qt, 1.0)).xyz;\n }\n return p0 * vec3(uHshSdfHalf / R0, max(uHshDepth, 0.05) / R0);\n }\n\n float hshDist(vec3 p, out float field){\n float r = length(p); vec3 n = r > 1e-5 ? p / r : vec3(0.0, 1.0, 0.0);\n float dS = r - hshRadius(n, field);\n return uHshMode > 0.5 && uHshMorph > 0.0 ? mix(dS, hshSdf(p), uHshMorph) : dS;\n }\n\n\n void main(){\n vec3 N = normalize(vNormal);\n vec3 V = normalize(vViewDir);\n float ndv = abs(dot(N, V));\n\n // the film is thicker where the harmonic pushes the shell out, so the colour bands follow the shape\n vec3 film = hshFilm(ndv, vField);\n\n float fres = pow(1.0 - ndv, 3.0);\n vec3 col = uHshColor * 0.25 + film * (0.2 + 0.8 * fres);\n col = mix(col, uHshRim, fres * 0.45);\n\n // nodal lines: where the harmonic field crosses zero\n float nd = abs(vField) / max(fwidth(vField), 1e-4);\n float nodal = (1.0 - smoothstep(0.5, 1.5, nd)) * uNodalLines;\n col += uHshRim * nodal * 1.5;\n\n float a = (0.06 + 0.9 * fres + nodal * 0.8) * uOpacity;\n // webbing between letters: pixels that are not on the target fade out as the morph completes\n if (uHshMode > 0.5 && uHshMorph > 0.0) { float d = abs(hshSdf(vLocal)); a *= 1.0 - uHshMorph * smoothstep(uWeb, uWeb * 3.0, d); }\n if (a < 0.003) discard;\n gl_FragColor = vec4(col * uIntensity, a);\n }\n";
|
|
29
|
+
export type HshUniforms = Record<string, THREE.IUniform>;
|
|
30
|
+
/** Fresh uniform objects for one shell (the shell writes them every frame; effects read them). */
|
|
31
|
+
export declare function createShellUniforms(): HshUniforms;
|
|
32
|
+
/** The keys an effect copies from a shell into its own uniforms (everything but the time, which it keeps). */
|
|
33
|
+
export declare const HSH_SHARED_KEYS: readonly ["uHshW", "uHshAmplitude", "uHshBound", "uHshSize", "uHshColor", "uHshRim", "uHshFilm", "uHshTint", "uHshMorph", "uHshMode", "uHshSdf", "uHshSdfHalf", "uHshDepth", "uHshSdf3", "uHshSdf3Min", "uHshSdf3Size", "uHshSdf3Inv", "uHshSdf3Mat"];
|
|
34
|
+
/** Copy a shell's current values into another uniform set (objects are shared by reference, scalars by value). */
|
|
35
|
+
export declare function copyShellUniforms(from: HshUniforms, to: HshUniforms): void;
|
|
36
|
+
export interface ShellField {
|
|
37
|
+
/** The shell's root group (its world matrix is the field's frame). */
|
|
38
|
+
root: Object3D;
|
|
39
|
+
uniforms: HshUniforms;
|
|
40
|
+
/** World → shell-local, refreshed by the shell every frame. */
|
|
41
|
+
inv: THREE.Matrix4;
|
|
42
|
+
/** The object the shell is wrapping right now (mode object), so it can be left out of the inside look. */
|
|
43
|
+
target: Object3D | null;
|
|
44
|
+
}
|
|
45
|
+
export declare function registerShellField(f: ShellField): void;
|
|
46
|
+
export declare function unregisterShellField(f: ShellField): void;
|
|
47
|
+
export declare const shellFields: () => ReadonlySet<ShellField>;
|
|
48
|
+
/** The shell whose centre is nearest a world point (what `inShell` reacts to), or null when the scene has none. */
|
|
49
|
+
export declare function nearestShellField(worldPos: THREE.Vector3): ShellField | null;
|
|
50
|
+
export declare const SHELL_SHAPES: readonly ["star", "heart", "ring", "hexagon", "triangle", "cross", "disc", "square", "diamond", "pentagon", "octagon", "sparkle", "hexstar", "crescent", "drop", "bolt", "arrow", "cloud", "flower", "gear", "shield", "leaf", "capsule", "infinity", "chevron", "hourglass", "clover", "spade", "sun", "halfmoon"];
|
|
51
|
+
export type ShellShape = (typeof SHELL_SHAPES)[number];
|
|
52
|
+
/** CSS font families the `font` option maps to (the page's own fonts, with system fallbacks). */
|
|
53
|
+
export declare const SHELL_FONTS: Record<string, string>;
|
|
54
|
+
export interface Sdf2 {
|
|
55
|
+
tex: THREE.DataTexture;
|
|
56
|
+
half: number;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The 2D field of a word or a shape, sized to the shell: `half` is the half-width it
|
|
60
|
+
* spans in shell-local units (the shell's resting radius, a little more).
|
|
61
|
+
* Text waits for the page font once (document.fonts), then draws.
|
|
62
|
+
*/
|
|
63
|
+
export declare function bakeShapeSdf(kind: 'text' | ShellShape, text: string, font: string, half: number): Promise<Sdf2 | null>;
|
|
64
|
+
export interface Sdf3 {
|
|
65
|
+
tex: THREE.Data3DTexture;
|
|
66
|
+
min: THREE.Vector3;
|
|
67
|
+
size: THREE.Vector3;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* The object's signed distance field in its root-local space: all its meshes merged
|
|
71
|
+
* (position only, see mergedPositions), sampled on a 48³ grid with the BVH's
|
|
72
|
+
* closest-point query, sign from the hit face. Baked in slices so the page stays
|
|
73
|
+
* responsive; cached on the root until its geometry changes. ~100–300 ms for a
|
|
74
|
+
* typical model on a desktop CPU — do it at load (pre-bake), never on trigger.
|
|
75
|
+
*/
|
|
76
|
+
export declare function bakeObjectSdf(root: Object3D): Promise<Sdf3 | null>;
|
|
77
|
+
//# sourceMappingURL=harmonicShellField.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"harmonicShellField.d.ts","sourceRoot":"","sources":["../../../src/components/effects/harmonicShellField.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAgBtC,MAAM,WAAW,aAAa;IAC1B,EAAE,EAAE,YAAY,CAAC;IAAC,EAAE,EAAE,YAAY,CAAC;IAAC,EAAE,EAAE,YAAY,CAAC;IAAC,EAAE,EAAE,YAAY,CAAC;IACvE,IAAI,EAAE,YAAY,CAAC;CACtB;AAED,2FAA2F;AAC3F,wBAAgB,mBAAmB,IAAI,aAAa,CAUnD;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAe1H;AAID,iHAAiH;AACjH,eAAO,MAAM,cAAc,0fAO1B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,eAAe,miJAiG3B,CAAC;AAEF,2FAA2F;AAC3F,eAAO,MAAM,cAAc,QAAmC,CAAC;AAE/D,2GAA2G;AAC3G,eAAO,MAAM,UAAU,y9MA2CtB,CAAC;AAEF,eAAO,MAAM,UAAU,quMAkCtB,CAAC;AAKF,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;AAczD,kGAAkG;AAClG,wBAAgB,mBAAmB,IAAI,WAAW,CAUjD;AAED,8GAA8G;AAC9G,eAAO,MAAM,eAAe,uPAGlB,CAAC;AAEX,kHAAkH;AAClH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,WAAW,QAMnE;AAID,MAAM,WAAW,UAAU;IACvB,sEAAsE;IACtE,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,WAAW,CAAC;IACtB,+DAA+D;IAC/D,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC;IACnB,0GAA0G;IAC1G,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;CAC3B;AAID,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,UAAU,QAAoB;AACpE,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,UAAU,QAAuB;AACzE,eAAO,MAAM,WAAW,QAAO,WAAW,CAAC,UAAU,CAAW,CAAC;AAGjE,mHAAmH;AACnH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,KAAK,CAAC,OAAO,GAAG,UAAU,GAAG,IAAI,CAQ5E;AAID,eAAO,MAAM,YAAY,qTAKf,CAAC;AACX,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvD,iGAAiG;AACjG,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAK9C,CAAC;AAsBF,MAAM,WAAW,IAAI;IAAG,GAAG,EAAE,KAAK,CAAC,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE;AA2G9D;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAK5H;AAID,MAAM,WAAW,IAAI;IAAG,GAAG,EAAE,KAAK,CAAC,aAAa,CAAC;IAAC,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC;IAAC,IAAI,EAAE,KAAK,CAAC,OAAO,CAAA;CAAE;AA8C3F;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CA4CxE"}
|