vireuikit 0.1.0 → 0.2.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.
- package/README.md +15 -0
- package/dist/{chunk-ZFX2MLKK.js → chunk-BV5P6MF7.js} +91 -17
- package/dist/chunk-BV5P6MF7.js.map +1 -0
- package/dist/{dynamics-BqBoj03x.d.cts → dynamics-DelasYER.d.cts} +100 -1
- package/dist/{dynamics-BqBoj03x.d.ts → dynamics-DelasYER.d.ts} +100 -1
- package/dist/index.cjs +93 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/web/index.cjs +70 -11
- package/dist/web/index.cjs.map +1 -1
- package/dist/web/index.d.cts +1 -1
- package/dist/web/index.d.ts +1 -1
- package/dist/web/index.js +8 -1
- package/dist/web/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-ZFX2MLKK.js.map +0 -1
|
@@ -59,8 +59,83 @@ type VireUIKitMediumParams = {
|
|
|
59
59
|
baseColor: VireUIKitMediumChannel;
|
|
60
60
|
/** Background weight in the Oklab mix: keeps the platform tone visible even where the smoke is dense. */
|
|
61
61
|
baseWeight: number;
|
|
62
|
+
/** Field-sample scale for the far depth plane, k ≥ 1 (see `MEDIUM_COMPOSITE_SHADER`): the far
|
|
63
|
+
* plane reads the SAME vapor/condensate field as the near plane, at `src * k + offset` instead
|
|
64
|
+
* of `src`. A larger k folds more of the field into the same screen area, so the identical
|
|
65
|
+
* evolving field reads as both finer-grained structure (features occupy fewer screen-px) and,
|
|
66
|
+
* because the field's own motion maps to screen motion as v/k, slower apparent motion — parallax
|
|
67
|
+
* and scale-of-structure from one number, with no second simulation. Measured against
|
|
68
|
+
* `check-medium.mjs`'s parallax gate (a cross-correlation best-shift, not a centroid — see the
|
|
69
|
+
* gate's own comment on why): 1.6 puts the far/near screen-speed ratio at 0.630, matching the
|
|
70
|
+
* naive prediction 1/1.6 = 0.625 almost exactly, comfortably inside a band that reads as depth
|
|
71
|
+
* without the far plane looking frozen. */
|
|
72
|
+
depthFarScale: number;
|
|
73
|
+
/** Offset into the shared field for the far plane, as a FRACTION of the grid's own width/height
|
|
74
|
+
* (per axis) rather than a fixed grid-px value — the field wraps (`gl.REPEAT`), so a fixed
|
|
75
|
+
* grid-px offset's EFFECTIVE (wrapped) distance from zero depends on whatever grid size it
|
|
76
|
+
* happens to run at, and can land close to zero by accident at a size nobody tested against.
|
|
77
|
+
* (This shipped once as `[41, 67]` grid-px: harmless at the 96px grid the depth gates tested
|
|
78
|
+
* against, but 67 wraps to just 5px at the 72px grid height this package's own README uses as
|
|
79
|
+
* its example — the far plane nearly re-read the near one, exactly the echo depth exists to
|
|
80
|
+
* avoid.) A fraction keeps the wrapped distance proportional to the grid at any size — see
|
|
81
|
+
* `depthFarOffsetPx` below and `check-medium.mjs`'s decorrelation gate, which measures this
|
|
82
|
+
* directly at the README's own 128x72. */
|
|
83
|
+
depthFarOffsetFrac: readonly [number, number];
|
|
84
|
+
/** Grid-px tap radius for the far plane's 4-tap box blur. A spatial average cannot raise local
|
|
85
|
+
* variance, so the same blur that softens the far plane's edges also, by construction, lowers
|
|
86
|
+
* its measured contrast — one mechanism for both aerial-perspective cues (no separate contrast
|
|
87
|
+
* knob exists). 0 disables it (the near plane stays sharp). Measured: reading the far plane's
|
|
88
|
+
* own scale/offset with no blur already drops contrast ~6% (a fixed offset can land on a
|
|
89
|
+
* locally denser or sparser patch of a field that isn't spatially uniform); this radius takes
|
|
90
|
+
* the total to 24% — see the aerial-perspective gate, whose two thresholds isolate blur's own
|
|
91
|
+
* share from that baseline. */
|
|
92
|
+
depthFarBlurRadius: number;
|
|
93
|
+
/** The far plane's share of the mix, relative to the near plane's implicit 1 — aerial
|
|
94
|
+
* perspective's other half: even at equal density the far plane must not compete with the near
|
|
95
|
+
* one for attention. */
|
|
96
|
+
depthFarWeight: number;
|
|
97
|
+
/** grid-px/s — a small downward drift added to VAPOR's own velocity, on top of the shared
|
|
98
|
+
* curl-noise field (condensate already settles faster via `condensateSettleSpeed`). An order of
|
|
99
|
+
* magnitude below it so the gas reads as "barely noticeable" drift rather than visibly falling.
|
|
100
|
+
* ADDS NO DENSITY: on this periodic (`gl.REPEAT`) grid a uniform drift only ever translates the
|
|
101
|
+
* field and wraps it back in at the top — it cannot accumulate mass anywhere, the way a real
|
|
102
|
+
* floor would. It is a motion cue (which way is down), not a source of "denser at the bottom" —
|
|
103
|
+
* that reading comes entirely from `gravityBottomBoost` below. See
|
|
104
|
+
* `check-medium.mjs`'s vapor-drift gate, which checks DIRECTION over a short window rather than
|
|
105
|
+
* a density ratio, for exactly this reason. */
|
|
106
|
+
gravityVaporDrift: number;
|
|
107
|
+
/** Density boost at the very bottom of the frame, added on top of 1 — a COMPOSITING-only effect:
|
|
108
|
+
* it scales how the already-conserved vapor/condensate density is DISPLAYED, never the
|
|
109
|
+
* simulated buffers themselves, so it cannot threaten the water-conservation invariant by
|
|
110
|
+
* construction, and — unlike `gravityVaporDrift` above — this is the mechanism that actually
|
|
111
|
+
* produces "denser at the bottom": it is a steady-state property of the compositing math, true
|
|
112
|
+
* from the very first frame, independent of any warmup or mixing time (see
|
|
113
|
+
* `check-medium.mjs`'s boost gate, which measures it with zero simulated steps for exactly that
|
|
114
|
+
* reason). Large in absolute terms because `baseWeight` (0.9) dominates the Oklab mix wherever
|
|
115
|
+
* gas density is thin — a small boost gets diluted into an invisible change in the final color;
|
|
116
|
+
* measured this large to register as a real, visible band against that dilution. */
|
|
117
|
+
gravityBottomBoost: number;
|
|
118
|
+
/** Fraction of frame height (0 top, 1 bottom) where the bottom boost starts ramping in via
|
|
119
|
+
* `smoothstep`. The brief asks for a denser LAYER at the floor of the chamber, not a boost that
|
|
120
|
+
* reaches halfway up the frame — kept in the bottom quarter. */
|
|
121
|
+
gravityBottomBoostStart: number;
|
|
62
122
|
};
|
|
63
123
|
declare const MEDIUM_DEFAULTS: VireUIKitMediumParams;
|
|
124
|
+
/**
|
|
125
|
+
* Resolves the far plane's fractional offset (`depthFarOffsetFrac`) into grid-px for a GIVEN grid
|
|
126
|
+
* size — pure and exported so the wrap-safety it exists for is directly testable (see
|
|
127
|
+
* `medium-depth-offset.test.ts`) without spinning up a WebGL2 context. Per-axis: `gridWidth` and
|
|
128
|
+
* `gridHeight` are independent, and a non-square grid (the README's own 128x72 example) shouldn't
|
|
129
|
+
* distort one axis relative to the other.
|
|
130
|
+
*/
|
|
131
|
+
declare function depthFarOffsetPx(offsetFrac: readonly [number, number], gridWidth: number, gridHeight: number): readonly [number, number];
|
|
132
|
+
/**
|
|
133
|
+
* How far a value lands from the nearest wrap boundary (0 or `period`) on a periodic axis of the
|
|
134
|
+
* given `period` — the quantity that actually matters for decorrelation, not the raw offset value
|
|
135
|
+
* itself (a raw offset near the period is effectively a tiny one once the field wraps). Exported
|
|
136
|
+
* for the same reason as `depthFarOffsetPx`: a plain number, testable without a GPU.
|
|
137
|
+
*/
|
|
138
|
+
declare function wrappedDistanceFromZero(value: number, period: number): number;
|
|
64
139
|
/** The material probe can't see texture finer than this: there is no gain in a denser simulation
|
|
65
140
|
* grid than the probe's own floor. */
|
|
66
141
|
declare const MEDIUM_PROBE_FLOOR_PX = 22;
|
|
@@ -115,6 +190,18 @@ declare const MEDIUM_NATURAL_JITTER = 35;
|
|
|
115
190
|
* supersaturation for a track to appear at all, so background emission is only ever visible here. */
|
|
116
191
|
declare const MEDIUM_SENSITIVE_TOP = 0.55;
|
|
117
192
|
declare const MEDIUM_SENSITIVE_BOTTOM = 0.95;
|
|
193
|
+
/** Per-emission depth range applied to a track's width (`headWidthFrac`/`tailWidthFrac`), far…near.
|
|
194
|
+
* The same knob reads as both "thin" and "soft": a Gaussian stamp's edge steepness scales with its
|
|
195
|
+
* own width (`emit-shader.ts`'s only extent parameter), so shrinking it for a far track thins and
|
|
196
|
+
* softens it in the same stroke — there is no second knob to hang an independent softness number
|
|
197
|
+
* on. Floored at 0.55: below that a far `alpha` track's head narrows past what MacCormack's own
|
|
198
|
+
* numerical smoothing already blurs it to, and the depth cue disappears into that noise floor. */
|
|
199
|
+
declare const MEDIUM_TRACK_DEPTH_WIDTH_RANGE: readonly [number, number];
|
|
200
|
+
/** Per-emission depth range applied to a track's intensity, far…near — "dim". Floored at 0.4, not
|
|
201
|
+
* lower: a far track still has to clear `MEDIUM_DEFAULTS.condensationFloor`'s excess threshold
|
|
202
|
+
* often enough to read as a track, or depth would look like tracks randomly failing to spawn
|
|
203
|
+
* rather than fading into the distance. */
|
|
204
|
+
declare const MEDIUM_TRACK_DEPTH_INTENSITY_RANGE: readonly [number, number];
|
|
118
205
|
|
|
119
206
|
type VireUIKitMediumPlaybackState = 'idle' | 'playing' | 'paused' | 'stopped';
|
|
120
207
|
type VireUIKitMediumTrackPresetName = keyof typeof MEDIUM_TRACK_PRESETS;
|
|
@@ -129,6 +216,11 @@ type VireUIKitMediumEmission = VireUIKitMediumTrackPreset & {
|
|
|
129
216
|
angle: number;
|
|
130
217
|
seed: number;
|
|
131
218
|
channel: readonly [number, number, number];
|
|
219
|
+
/** 0 (farthest) … 1 (nearest), random within the full range and deterministic from `seed` — which
|
|
220
|
+
* plane the track reads as living on. Already folded into this emission's own `headWidthFrac`/
|
|
221
|
+
* `tailWidthFrac`/`intensity` below (see `buildEmission`); carried here too so a caller inspecting
|
|
222
|
+
* an emission can see why. */
|
|
223
|
+
depth: number;
|
|
132
224
|
};
|
|
133
225
|
type VireUIKitMediumDynamicsInput = {
|
|
134
226
|
state: VireUIKitMediumPlaybackState;
|
|
@@ -150,6 +242,13 @@ type VireUIKitMediumDynamicsFrame = {
|
|
|
150
242
|
type VireUIKitMediumDynamics = {
|
|
151
243
|
step(input: VireUIKitMediumDynamicsInput): VireUIKitMediumDynamicsFrame;
|
|
152
244
|
};
|
|
245
|
+
/**
|
|
246
|
+
* Scales a track preset's width and intensity by depth (0 far … 1 near) — separated from
|
|
247
|
+
* `buildEmission` below and exported so depth's effect on a preset is directly testable with two
|
|
248
|
+
* plain numbers, independent of how a specific emission's depth got hashed from its seed. At
|
|
249
|
+
* depth=1 this returns the preset's own numbers unchanged (both ranges' upper bound is 1).
|
|
250
|
+
*/
|
|
251
|
+
declare function applyTrackDepth(preset: VireUIKitMediumTrackPreset, depth: number): VireUIKitMediumTrackPreset;
|
|
153
252
|
/**
|
|
154
253
|
* Stateful "playback dynamics" model: turbulence and emission scheduling per frame. Platform
|
|
155
254
|
* neutral — the caller supplies the clock and the playback position (in a bench, that would be
|
|
@@ -157,4 +256,4 @@ type VireUIKitMediumDynamics = {
|
|
|
157
256
|
*/
|
|
158
257
|
declare function createMediumDynamics(): VireUIKitMediumDynamics;
|
|
159
258
|
|
|
160
|
-
export { MEDIUM_DEFAULTS as M, type VireUIKitMediumChannel as V, MEDIUM_DEFAULT_CELL_PX as a, MEDIUM_DRIFT_TURBULENCE as b, MEDIUM_EMISSION_BEATS as c, MEDIUM_EMISSION_PROBABILITY as d, MEDIUM_MIN_EMISSION_INTERVAL as e, MEDIUM_NATURAL_INTERVAL as f, MEDIUM_NATURAL_JITTER as g, MEDIUM_PLAYING_TURBULENCE_RANGE as h, MEDIUM_PROBE_FLOOR_PX as i, MEDIUM_SENSITIVE_BOTTOM as j, MEDIUM_SENSITIVE_TOP as k,
|
|
259
|
+
export { wrappedDistanceFromZero as A, MEDIUM_DEFAULTS as M, type VireUIKitMediumChannel as V, MEDIUM_DEFAULT_CELL_PX as a, MEDIUM_DRIFT_TURBULENCE as b, MEDIUM_EMISSION_BEATS as c, MEDIUM_EMISSION_PROBABILITY as d, MEDIUM_MIN_EMISSION_INTERVAL as e, MEDIUM_NATURAL_INTERVAL as f, MEDIUM_NATURAL_JITTER as g, MEDIUM_PLAYING_TURBULENCE_RANGE as h, MEDIUM_PROBE_FLOOR_PX as i, MEDIUM_SENSITIVE_BOTTOM as j, MEDIUM_SENSITIVE_TOP as k, MEDIUM_TRACK_DEPTH_INTENSITY_RANGE as l, MEDIUM_TRACK_DEPTH_WIDTH_RANGE as m, MEDIUM_TRACK_PRESETS as n, MEDIUM_TURBULENCE_TAU as o, type VireUIKitMediumDynamics as p, type VireUIKitMediumDynamicsFrame as q, type VireUIKitMediumDynamicsInput as r, type VireUIKitMediumEmission as s, type VireUIKitMediumParams as t, type VireUIKitMediumPlaybackState as u, type VireUIKitMediumTrackPreset as v, type VireUIKitMediumTrackPresetName as w, applyTrackDepth as x, createMediumDynamics as y, depthFarOffsetPx as z };
|
package/dist/index.cjs
CHANGED
|
@@ -291,8 +291,22 @@ var MEDIUM_DEFAULTS = {
|
|
|
291
291
|
condensateColor: [0.94, 0.95, 0.97],
|
|
292
292
|
channelColors: neutralSpecies(BASE_COLOR),
|
|
293
293
|
baseColor: BASE_COLOR,
|
|
294
|
-
baseWeight: 0.9
|
|
294
|
+
baseWeight: 0.9,
|
|
295
|
+
depthFarScale: 1.6,
|
|
296
|
+
depthFarOffsetFrac: [0.47, 0.53],
|
|
297
|
+
depthFarBlurRadius: 5,
|
|
298
|
+
depthFarWeight: 0.5,
|
|
299
|
+
gravityVaporDrift: 0.6,
|
|
300
|
+
gravityBottomBoost: 2,
|
|
301
|
+
gravityBottomBoostStart: 0.75
|
|
295
302
|
};
|
|
303
|
+
function depthFarOffsetPx(offsetFrac, gridWidth, gridHeight) {
|
|
304
|
+
return [offsetFrac[0] * gridWidth, offsetFrac[1] * gridHeight];
|
|
305
|
+
}
|
|
306
|
+
function wrappedDistanceFromZero(value, period) {
|
|
307
|
+
const wrapped = (value % period + period) % period;
|
|
308
|
+
return Math.min(wrapped, period - wrapped);
|
|
309
|
+
}
|
|
296
310
|
var MEDIUM_PROBE_FLOOR_PX = 22;
|
|
297
311
|
var MEDIUM_DEFAULT_CELL_PX = 26;
|
|
298
312
|
var MEDIUM_TRACK_PRESETS = {
|
|
@@ -337,6 +351,8 @@ var MEDIUM_NATURAL_INTERVAL = 25;
|
|
|
337
351
|
var MEDIUM_NATURAL_JITTER = 35;
|
|
338
352
|
var MEDIUM_SENSITIVE_TOP = 0.55;
|
|
339
353
|
var MEDIUM_SENSITIVE_BOTTOM = 0.95;
|
|
354
|
+
var MEDIUM_TRACK_DEPTH_WIDTH_RANGE = [0.55, 1];
|
|
355
|
+
var MEDIUM_TRACK_DEPTH_INTENSITY_RANGE = [0.4, 1];
|
|
340
356
|
|
|
341
357
|
// src/medium/noise.ts
|
|
342
358
|
var VG_VALUE_NOISE = `
|
|
@@ -521,11 +537,16 @@ var MEDIUM_ADVECT_VELOCITY_SETTLE = `
|
|
|
521
537
|
${MEDIUM_ADVECT_VELOCITY}
|
|
522
538
|
vel.y += u_settleSpeed;
|
|
523
539
|
`;
|
|
540
|
+
var MEDIUM_ADVECT_VELOCITY_VAPOR = `
|
|
541
|
+
${MEDIUM_ADVECT_VELOCITY}
|
|
542
|
+
vel.y += u_vaporDrift;
|
|
543
|
+
`;
|
|
524
544
|
var MEDIUM_VAPOR_FORWARD_SHADER = `
|
|
525
545
|
${MEDIUM_ADVECT_UNIFORMS}
|
|
546
|
+
uniform float u_vaporDrift;
|
|
526
547
|
|
|
527
548
|
half4 main(float2 xy) {
|
|
528
|
-
${
|
|
549
|
+
${MEDIUM_ADVECT_VELOCITY_VAPOR}
|
|
529
550
|
float2 src = xy - vel * u_dt;
|
|
530
551
|
return u_dye.eval(float2(src.x, u_dyeSize.y - src.y));
|
|
531
552
|
}
|
|
@@ -561,9 +582,10 @@ var MEDIUM_MACCORMACK_CORRECT = `
|
|
|
561
582
|
var MEDIUM_VAPOR_CORRECT_SHADER = `
|
|
562
583
|
${MEDIUM_ADVECT_UNIFORMS}
|
|
563
584
|
uniform shader u_forward;
|
|
585
|
+
uniform float u_vaporDrift;
|
|
564
586
|
|
|
565
587
|
half4 main(float2 xy) {
|
|
566
|
-
${
|
|
588
|
+
${MEDIUM_ADVECT_VELOCITY_VAPOR}
|
|
567
589
|
${MEDIUM_MACCORMACK_CORRECT}
|
|
568
590
|
return limited;
|
|
569
591
|
}
|
|
@@ -683,17 +705,52 @@ uniform float3 u_labCondensate;
|
|
|
683
705
|
uniform float u_baseWeight;
|
|
684
706
|
uniform float u_condensateTint;
|
|
685
707
|
uniform float u_condensateGain;
|
|
708
|
+
uniform float u_farScale;
|
|
709
|
+
uniform float2 u_farOffset;
|
|
710
|
+
uniform float u_farBlurRadius;
|
|
711
|
+
uniform float u_farWeight;
|
|
712
|
+
uniform float u_gravityBoost;
|
|
713
|
+
uniform float u_gravityBoostStart;
|
|
686
714
|
|
|
687
715
|
${VG_OKLAB_TO_SRGB}
|
|
688
716
|
|
|
689
717
|
half4 main(float2 xy) {
|
|
690
|
-
float2
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
half4
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
718
|
+
float2 baseSrc = xy * u_dyeScale;
|
|
719
|
+
|
|
720
|
+
// Near plane \u2014 sampled exactly as before the depth work; the calibrated look lives here.
|
|
721
|
+
half4 vapor = u_vapor.eval(baseSrc);
|
|
722
|
+
half4 condensate = u_condensate.eval(baseSrc);
|
|
723
|
+
half4 track = u_track.eval(baseSrc);
|
|
724
|
+
|
|
725
|
+
// Far plane \u2014 the identical field, scaled and offset (see the file header). A 4-tap box blur
|
|
726
|
+
// (no separate center sample, the cheapest isotropic blur available) supplies aerial perspective's
|
|
727
|
+
// blur and, as a side effect of averaging, its lower contrast too.
|
|
728
|
+
float2 farSrc = baseSrc * u_farScale + u_farOffset;
|
|
729
|
+
float2 blurX = float2(u_farBlurRadius, 0.0);
|
|
730
|
+
float2 blurY = float2(0.0, u_farBlurRadius);
|
|
731
|
+
half4 vaporFar = (u_vapor.eval(farSrc + blurX) + u_vapor.eval(farSrc - blurX) +
|
|
732
|
+
u_vapor.eval(farSrc + blurY) + u_vapor.eval(farSrc - blurY)) * half4(0.25);
|
|
733
|
+
half4 condensateFar = (u_condensate.eval(farSrc + blurX) + u_condensate.eval(farSrc - blurX) +
|
|
734
|
+
u_condensate.eval(farSrc + blurY) + u_condensate.eval(farSrc - blurY)) * half4(0.25);
|
|
735
|
+
|
|
736
|
+
// Gravity's "denser layer at the bottom of the chamber" \u2014 a compositing-only boost (see
|
|
737
|
+
// MEDIUM_DEFAULTS.gravityBottomBoost): it scales how density already in the conserved buffers is
|
|
738
|
+
// DISPLAYED, never the buffers themselves, so it can't threaten water conservation. Shared by
|
|
739
|
+
// both planes: the chamber's floor is denser everywhere along its depth, not just up close.
|
|
740
|
+
//
|
|
741
|
+
// Verified with a headless render, not assumed: ramping this with xy.y directly lit up the TOP
|
|
742
|
+
// of the frame, not the bottom (contentTexture's own orientation contract flips y once more
|
|
743
|
+
// between this pass and the final scene \u2014 see the file header) \u2014 hence the 1.0 - below.
|
|
744
|
+
float bottomFrac = 1.0 - xy.y / u_resolution.y;
|
|
745
|
+
float bottomT = smoothstep(u_gravityBoostStart, 1.0, bottomFrac);
|
|
746
|
+
float bottomBoost = 1.0 + u_gravityBoost * bottomT;
|
|
747
|
+
|
|
748
|
+
float gasR = (float(vapor.r) + float(vaporFar.r) * u_farWeight) * bottomBoost;
|
|
749
|
+
float gasG = (float(vapor.g) + float(vaporFar.g) * u_farWeight) * bottomBoost;
|
|
750
|
+
float gasB = (float(vapor.b) + float(vaporFar.b) * u_farWeight) * bottomBoost;
|
|
751
|
+
float w0 = gasR + float(track.r);
|
|
752
|
+
float w1 = gasG + float(track.g);
|
|
753
|
+
float w2 = gasB + float(track.b);
|
|
697
754
|
float wBg = u_baseWeight;
|
|
698
755
|
|
|
699
756
|
// A faint condensate hint from the LOCAL vapor, not its own color (droplets scatter light
|
|
@@ -701,7 +758,8 @@ half4 main(float2 xy) {
|
|
|
701
758
|
float vaporLocal = max(w0 + w1 + w2, 1e-4);
|
|
702
759
|
float3 vaporHue = (u_lab0 * w0 + u_lab1 * w1 + u_lab2 * w2) / vaporLocal;
|
|
703
760
|
float3 condensateLab = mix(u_labCondensate, vaporHue, u_condensateTint);
|
|
704
|
-
float wCondensate =
|
|
761
|
+
float wCondensate =
|
|
762
|
+
(float(condensate.r) + float(condensateFar.r) * u_farWeight) * u_condensateGain * bottomBoost;
|
|
705
763
|
|
|
706
764
|
float total = max(w0 + w1 + w2 + wBg + wCondensate, 1e-4);
|
|
707
765
|
float3 mixLab =
|
|
@@ -761,15 +819,31 @@ function channelFromSeed(seed) {
|
|
|
761
819
|
const idx = Math.floor(hash01(seed * 1.37) * 3) % 3;
|
|
762
820
|
return idx === 0 ? [1, 0, 0] : idx === 1 ? [0, 1, 0] : [0, 0, 1];
|
|
763
821
|
}
|
|
822
|
+
function lerp(range, t) {
|
|
823
|
+
return range[0] + (range[1] - range[0]) * t;
|
|
824
|
+
}
|
|
825
|
+
function applyTrackDepth(preset, depth) {
|
|
826
|
+
const widthScale = lerp(MEDIUM_TRACK_DEPTH_WIDTH_RANGE, depth);
|
|
827
|
+
const intensityScale = lerp(MEDIUM_TRACK_DEPTH_INTENSITY_RANGE, depth);
|
|
828
|
+
return {
|
|
829
|
+
...preset,
|
|
830
|
+
headWidthFrac: preset.headWidthFrac * widthScale,
|
|
831
|
+
tailWidthFrac: preset.tailWidthFrac * widthScale,
|
|
832
|
+
intensity: preset.intensity * intensityScale
|
|
833
|
+
};
|
|
834
|
+
}
|
|
764
835
|
function buildEmission(presetName, source, seed, intensityScale) {
|
|
765
836
|
const preset = MEDIUM_TRACK_PRESETS[presetName];
|
|
837
|
+
const depth = hash01(seed * 4.63);
|
|
838
|
+
const depthApplied = applyTrackDepth(preset, depth);
|
|
766
839
|
return {
|
|
767
|
-
...
|
|
768
|
-
intensity:
|
|
840
|
+
...depthApplied,
|
|
841
|
+
intensity: depthApplied.intensity * intensityScale,
|
|
769
842
|
source,
|
|
770
843
|
angle: hash01(seed * 7.31) * Math.PI * 2,
|
|
771
844
|
seed,
|
|
772
|
-
channel: channelFromSeed(seed)
|
|
845
|
+
channel: channelFromSeed(seed),
|
|
846
|
+
depth
|
|
773
847
|
};
|
|
774
848
|
}
|
|
775
849
|
function createMediumDynamics() {
|
|
@@ -845,6 +919,8 @@ exports.MEDIUM_SPECIES_HUE_SPREAD = MEDIUM_SPECIES_HUE_SPREAD;
|
|
|
845
919
|
exports.MEDIUM_SPECIES_LIGHTNESS = MEDIUM_SPECIES_LIGHTNESS;
|
|
846
920
|
exports.MEDIUM_TIME_PERIOD = MEDIUM_TIME_PERIOD;
|
|
847
921
|
exports.MEDIUM_TRACK_ADVECT_SHADER = MEDIUM_TRACK_ADVECT_SHADER;
|
|
922
|
+
exports.MEDIUM_TRACK_DEPTH_INTENSITY_RANGE = MEDIUM_TRACK_DEPTH_INTENSITY_RANGE;
|
|
923
|
+
exports.MEDIUM_TRACK_DEPTH_WIDTH_RANGE = MEDIUM_TRACK_DEPTH_WIDTH_RANGE;
|
|
848
924
|
exports.MEDIUM_TRACK_PRESETS = MEDIUM_TRACK_PRESETS;
|
|
849
925
|
exports.MEDIUM_TURBULENCE_TAU = MEDIUM_TURBULENCE_TAU;
|
|
850
926
|
exports.MEDIUM_VAPOR_CORRECT_SHADER = MEDIUM_VAPOR_CORRECT_SHADER;
|
|
@@ -854,12 +930,14 @@ exports.OKLAB_TO_LMS = OKLAB_TO_LMS;
|
|
|
854
930
|
exports.VG_CURL_NOISE = VG_CURL_NOISE;
|
|
855
931
|
exports.VG_OKLAB_TO_SRGB = VG_OKLAB_TO_SRGB;
|
|
856
932
|
exports.VG_VALUE_NOISE = VG_VALUE_NOISE;
|
|
933
|
+
exports.applyTrackDepth = applyTrackDepth;
|
|
857
934
|
exports.atomAtRest = atomAtRest;
|
|
858
935
|
exports.atomLift = atomLift;
|
|
859
936
|
exports.atomMaterial = atomMaterial;
|
|
860
937
|
exports.characterOf = characterOf;
|
|
861
938
|
exports.createGrowthState = createGrowthState;
|
|
862
939
|
exports.createMediumDynamics = createMediumDynamics;
|
|
940
|
+
exports.depthFarOffsetPx = depthFarOffsetPx;
|
|
863
941
|
exports.growthFrame = growthFrame;
|
|
864
942
|
exports.growthInk = growthInk;
|
|
865
943
|
exports.growthPhase = growthPhase;
|
|
@@ -870,5 +948,6 @@ exports.speciesFamily = speciesFamily;
|
|
|
870
948
|
exports.srgbToOklab = srgbToOklab;
|
|
871
949
|
exports.stepGrowth = stepGrowth;
|
|
872
950
|
exports.stepSpring = stepSpring;
|
|
951
|
+
exports.wrappedDistanceFromZero = wrappedDistanceFromZero;
|
|
873
952
|
//# sourceMappingURL=index.cjs.map
|
|
874
953
|
//# sourceMappingURL=index.cjs.map
|