vireuikit 0.1.0 → 0.3.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 +27 -0
- package/dist/{chunk-ZFX2MLKK.js → chunk-2AFVQ65S.js} +240 -18
- package/dist/chunk-2AFVQ65S.js.map +1 -0
- package/dist/dynamics-7Bq3LLds.d.cts +271 -0
- package/dist/dynamics-7Bq3LLds.d.ts +271 -0
- package/dist/index.cjs +224 -15
- 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 +340 -126
- 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 +21 -7
- package/dist/web/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-ZFX2MLKK.js.map +0 -1
- package/dist/dynamics-BqBoj03x.d.cts +0 -160
- package/dist/dynamics-BqBoj03x.d.ts +0 -160
package/README.md
CHANGED
|
@@ -76,6 +76,33 @@ Turbulence and light-track emission in response to playback come from `createMed
|
|
|
76
76
|
which is platform-neutral: feed `step()` a playback state, BPM and a deterministic position, and
|
|
77
77
|
pass the `emissions` it returns on as `MediumFrame.emissions`.
|
|
78
78
|
|
|
79
|
+
The backdrop reads as a chamber seen from the side, not a flat field, without a second simulation:
|
|
80
|
+
the composite samples the same vapor/condensate grid twice, once at identity and once at a larger
|
|
81
|
+
scale and a fixed offset (`MEDIUM_DEFAULTS.depthFarScale`/`depthFarOffsetFrac`, the offset as a
|
|
82
|
+
fraction of the grid so it stays clear of the wrap at any grid size) — the far read is both slower
|
|
83
|
+
on screen and finer-grained, since the field's own motion maps to screen motion as `v / scale`. An
|
|
84
|
+
8-tap rotated ring blur on that far read (`depthFarBlurRadius`) supplies aerial perspective's
|
|
85
|
+
softness and lower contrast in the same pass, weighted down (`depthFarWeight`) so it never competes
|
|
86
|
+
with the near plane; the ring (not an axis-aligned box) keeps that blur from turning a residual
|
|
87
|
+
seam or resize artifact into parallel straight lines. Gravity is two separate properties: a small
|
|
88
|
+
downward drift on vapor's own velocity (`gravityVaporDrift`, a motion cue only — a uniform drift on
|
|
89
|
+
this periodic grid cannot itself accumulate density) and a compositing-only density boost near the
|
|
90
|
+
bottom of the frame (`gravityBottomBoost`/`gravityBottomBoostStart`, a steady-state property that
|
|
91
|
+
cannot affect the conserved water total) that actually produces the "denser at the bottom" reading.
|
|
92
|
+
Condensate settling adds a second, always-on share of the ambient curl field's own sideways push
|
|
93
|
+
(`condensateSettleWiggle`) so a droplet falling at a constant speed doesn't trace a dead-straight
|
|
94
|
+
vertical line when turbulence is otherwise low. Each emitted track also carries a `depth` (0 far …
|
|
95
|
+
1 near) that scales its own width and intensity, so a track reads thinner, dimmer and softer the
|
|
96
|
+
farther back it lives.
|
|
97
|
+
|
|
98
|
+
The curl-noise potential itself is periodic over the simulation grid in space, the same way it
|
|
99
|
+
already was in time: each octave's spatial frequency is adjusted, per grid size, to the nearest
|
|
100
|
+
value that makes a full grid width (or height) an exact integer number of lattice cells
|
|
101
|
+
(`computeMediumSpatialPeriods`) — so velocity never jumps at the domain's own wrap, and the far
|
|
102
|
+
plane's tile boundary reads as an ordinary point in the field rather than a seam. The near plane's
|
|
103
|
+
vapor and condensate reads use cubic B-spline reconstruction (4 bilinear taps) instead of plain
|
|
104
|
+
bilinear, so the simulation grid's own cells don't read as squares at typical (coarse) grid sizes.
|
|
105
|
+
|
|
79
106
|
## License
|
|
80
107
|
|
|
81
108
|
Apache-2.0. See [LICENSE](./LICENSE) and [NOTICE](./NOTICE).
|
|
@@ -98,14 +98,29 @@ var MEDIUM_DEFAULTS = {
|
|
|
98
98
|
condensationFloor: 0.12,
|
|
99
99
|
evaporationRate: 0.2,
|
|
100
100
|
condensateSettleSpeed: 8,
|
|
101
|
+
condensateSettleWiggle: 0.4,
|
|
101
102
|
condensateSpreadRate: 0.08,
|
|
102
103
|
condensateTint: 0.2,
|
|
103
104
|
condensateGain: 5,
|
|
104
105
|
condensateColor: [0.94, 0.95, 0.97],
|
|
105
106
|
channelColors: neutralSpecies(BASE_COLOR),
|
|
106
107
|
baseColor: BASE_COLOR,
|
|
107
|
-
baseWeight: 0.9
|
|
108
|
+
baseWeight: 0.9,
|
|
109
|
+
depthFarScale: 1.6,
|
|
110
|
+
depthFarOffsetFrac: [0.47, 0.53],
|
|
111
|
+
depthFarBlurRadius: 5,
|
|
112
|
+
depthFarWeight: 0.5,
|
|
113
|
+
gravityVaporDrift: 0.6,
|
|
114
|
+
gravityBottomBoost: 2,
|
|
115
|
+
gravityBottomBoostStart: 0.75
|
|
108
116
|
};
|
|
117
|
+
function depthFarOffsetPx(offsetFrac, gridWidth, gridHeight) {
|
|
118
|
+
return [offsetFrac[0] * gridWidth, offsetFrac[1] * gridHeight];
|
|
119
|
+
}
|
|
120
|
+
function wrappedDistanceFromZero(value, period) {
|
|
121
|
+
const wrapped = (value % period + period) % period;
|
|
122
|
+
return Math.min(wrapped, period - wrapped);
|
|
123
|
+
}
|
|
109
124
|
var MEDIUM_PROBE_FLOOR_PX = 22;
|
|
110
125
|
var MEDIUM_DEFAULT_CELL_PX = 26;
|
|
111
126
|
var MEDIUM_TRACK_PRESETS = {
|
|
@@ -150,6 +165,8 @@ var MEDIUM_NATURAL_INTERVAL = 25;
|
|
|
150
165
|
var MEDIUM_NATURAL_JITTER = 35;
|
|
151
166
|
var MEDIUM_SENSITIVE_TOP = 0.55;
|
|
152
167
|
var MEDIUM_SENSITIVE_BOTTOM = 0.95;
|
|
168
|
+
var MEDIUM_TRACK_DEPTH_WIDTH_RANGE = [0.55, 1];
|
|
169
|
+
var MEDIUM_TRACK_DEPTH_INTENSITY_RANGE = [0.4, 1];
|
|
153
170
|
|
|
154
171
|
// src/medium/noise.ts
|
|
155
172
|
var VG_VALUE_NOISE = `
|
|
@@ -281,7 +298,90 @@ float2 vgCurlVelocity(float2 p, float phase) {
|
|
|
281
298
|
float py0 = vgPotential(p - float2(0.0, e), phase);
|
|
282
299
|
return float2(py1 - py0, -(px1 - px0)) / (2.0 * e);
|
|
283
300
|
}
|
|
301
|
+
|
|
302
|
+
// vgPotential above is periodic in TIME only: the simulation domain is a torus (gl.REPEAT), but
|
|
303
|
+
// the lattice itself never wraps in x/y, so velocity jumps at the domain seam and dye on either
|
|
304
|
+
// side of it is transported differently \u2014 a straight seam, made worse wherever something re-reads
|
|
305
|
+
// the field at a different scale (the far depth plane's tile boundary). vgValueNoise3Seamless
|
|
306
|
+
// wraps ALL THREE lattice axes: xyPeriod/zPeriod fold BOTH neighbors of a cell into their own
|
|
307
|
+
// period before hashing, so the field is EXACTLY the same one period over, in space exactly as it
|
|
308
|
+
// already was in time.
|
|
309
|
+
//
|
|
310
|
+
// The period has to be an integer number of lattice cells, or the wrap would jump instead of
|
|
311
|
+
// closing seamlessly (the same requirement MEDIUM_TIME_OCTAVES/mediumOctavePeriod enforces for
|
|
312
|
+
// time) \u2014 but unlike time, the spatial period depends on the GRID SIZE, which this shader has no
|
|
313
|
+
// notion of. So xyPeriod travels together with a per-octave spatial frequency the CPU computes to
|
|
314
|
+
// make gridSize * frequency land on that exact integer (see computeMediumSpatialPeriods below);
|
|
315
|
+
// both arrive together as one uniform per octave (u_spatialN.xy = frequency, .zw = period),
|
|
316
|
+
// recomputed by web/medium.ts whenever the grid resizes.
|
|
317
|
+
float vgValueNoise3Seamless(float3 p, float2 xyPeriod, float zPeriod) {
|
|
318
|
+
float3 i = floor(p);
|
|
319
|
+
float3 f = fract(p);
|
|
320
|
+
float3 u = f * f * (3.0 - 2.0 * f);
|
|
321
|
+
float x0 = mod(i.x, xyPeriod.x);
|
|
322
|
+
float x1 = mod(i.x + 1.0, xyPeriod.x);
|
|
323
|
+
float y0 = mod(i.y, xyPeriod.y);
|
|
324
|
+
float y1 = mod(i.y + 1.0, xyPeriod.y);
|
|
325
|
+
float z0 = mod(i.z, zPeriod);
|
|
326
|
+
float z1 = mod(i.z + 1.0, zPeriod);
|
|
327
|
+
float n000 = vgHash31(float3(x0, y0, z0));
|
|
328
|
+
float n100 = vgHash31(float3(x1, y0, z0));
|
|
329
|
+
float n010 = vgHash31(float3(x0, y1, z0));
|
|
330
|
+
float n110 = vgHash31(float3(x1, y1, z0));
|
|
331
|
+
float n001 = vgHash31(float3(x0, y0, z1));
|
|
332
|
+
float n101 = vgHash31(float3(x1, y0, z1));
|
|
333
|
+
float n011 = vgHash31(float3(x0, y1, z1));
|
|
334
|
+
float n111 = vgHash31(float3(x1, y1, z1));
|
|
335
|
+
float nx00 = mix(n000, n100, u.x);
|
|
336
|
+
float nx10 = mix(n010, n110, u.x);
|
|
337
|
+
float nx01 = mix(n001, n101, u.x);
|
|
338
|
+
float nx11 = mix(n011, n111, u.x);
|
|
339
|
+
float nxy0 = mix(nx00, nx10, u.y);
|
|
340
|
+
float nxy1 = mix(nx01, nx11, u.y);
|
|
341
|
+
return mix(nxy0, nxy1, u.z) * 2.0 - 1.0;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Same four-octave fbm as vgPotential, but every octave reads the seamless lattice above at its
|
|
345
|
+
// OWN (frequency, period) pair \u2014 sN.xy replaces that octave's compile-time \`scale\` constant (it
|
|
346
|
+
// has to be a uniform now: the adjustment depends on the live grid size), sN.zw is the matching
|
|
347
|
+
// integer period. \`p\` is still curlFreq-scaled exactly as vgPotential's caller scales it \u2014 only
|
|
348
|
+
// the per-octave multiplier changed from a constant to a grid-dependent uniform, so the finite
|
|
349
|
+
// difference in vgCurlVelocitySeamless below needs no epsilon change of its own.
|
|
350
|
+
float vgPotentialSeamless(float2 p, float phase, float4 s0, float4 s1, float4 s2, float4 s3) {
|
|
351
|
+
float v = vgValueNoise3Seamless(float3(p * s0.xy, phase * ${VG_OCT0.rate} + ${VG_OCT0.offset}), s0.zw, ${VG_OCT0.period}) * 0.5;
|
|
352
|
+
v += vgValueNoise3Seamless(float3(p * s1.xy, phase * ${VG_OCT1.rate} + ${VG_OCT1.offset}), s1.zw, ${VG_OCT1.period}) * 0.25;
|
|
353
|
+
v += vgValueNoise3Seamless(float3(p * s2.xy, phase * ${VG_OCT2.rate} + ${VG_OCT2.offset}), s2.zw, ${VG_OCT2.period}) * 0.125;
|
|
354
|
+
float macro = vgValueNoise3Seamless(float3(p * s3.xy, phase * ${VG_OCT3.rate} + ${VG_OCT3.offset}), s3.zw, ${VG_OCT3.period});
|
|
355
|
+
return v * (0.7 + 0.3 * macro);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
float2 vgCurlVelocitySeamless(float2 p, float phase, float4 s0, float4 s1, float4 s2, float4 s3) {
|
|
359
|
+
float e = 0.6;
|
|
360
|
+
float px1 = vgPotentialSeamless(p + float2(e, 0.0), phase, s0, s1, s2, s3);
|
|
361
|
+
float px0 = vgPotentialSeamless(p - float2(e, 0.0), phase, s0, s1, s2, s3);
|
|
362
|
+
float py1 = vgPotentialSeamless(p + float2(0.0, e), phase, s0, s1, s2, s3);
|
|
363
|
+
float py0 = vgPotentialSeamless(p - float2(0.0, e), phase, s0, s1, s2, s3);
|
|
364
|
+
return float2(py1 - py0, -(px1 - px0)) / (2.0 * e);
|
|
365
|
+
}
|
|
284
366
|
`;
|
|
367
|
+
var MIN_SPATIAL_PERIOD = 2;
|
|
368
|
+
function computeMediumSpatialPeriods(gridWidth, gridHeight, curlFreq) {
|
|
369
|
+
const freq = Math.max(curlFreq, 1e-6);
|
|
370
|
+
return MEDIUM_TIME_OCTAVES.map((o) => {
|
|
371
|
+
const rawX = gridWidth * freq * o.scale;
|
|
372
|
+
const rawY = gridHeight * freq * o.scale;
|
|
373
|
+
const minRaw = Math.min(rawX, rawY);
|
|
374
|
+
const scaleUp = minRaw > 0 && minRaw < MIN_SPATIAL_PERIOD ? MIN_SPATIAL_PERIOD / minRaw : 1;
|
|
375
|
+
const periodX = Math.max(MIN_SPATIAL_PERIOD, Math.round(rawX * scaleUp));
|
|
376
|
+
const periodY = Math.max(MIN_SPATIAL_PERIOD, Math.round(rawY * scaleUp));
|
|
377
|
+
return {
|
|
378
|
+
freqX: periodX / (gridWidth * freq),
|
|
379
|
+
freqY: periodY / (gridHeight * freq),
|
|
380
|
+
periodX,
|
|
381
|
+
periodY
|
|
382
|
+
};
|
|
383
|
+
});
|
|
384
|
+
}
|
|
285
385
|
|
|
286
386
|
// src/medium/seed-shader.ts
|
|
287
387
|
var MEDIUM_SEED_SHADER = `
|
|
@@ -324,21 +424,36 @@ uniform float u_phase;
|
|
|
324
424
|
uniform float u_curlFreq;
|
|
325
425
|
uniform float u_advectSpeed;
|
|
326
426
|
uniform float u_turbulence;
|
|
427
|
+
// One float4 per MEDIUM_TIME_OCTAVES entry: xy is that octave's seamless spatial frequency, zw its
|
|
428
|
+
// matching integer lattice period \u2014 both grid-size-dependent, so they can't be compile-time
|
|
429
|
+
// constants (see vgPotentialSeamless/computeMediumSpatialPeriods in noise.ts). Recomputed by
|
|
430
|
+
// web/medium.ts's bindVelocityUniforms every frame from the live grid dimensions.
|
|
431
|
+
uniform float4 u_spatial0;
|
|
432
|
+
uniform float4 u_spatial1;
|
|
433
|
+
uniform float4 u_spatial2;
|
|
434
|
+
uniform float4 u_spatial3;
|
|
327
435
|
|
|
328
436
|
${VG_CURL_NOISE}
|
|
329
437
|
`;
|
|
330
438
|
var MEDIUM_ADVECT_VELOCITY = `
|
|
331
|
-
float2
|
|
439
|
+
float2 curl = vgCurlVelocitySeamless(xy * u_curlFreq, u_phase, u_spatial0, u_spatial1, u_spatial2, u_spatial3);
|
|
440
|
+
float2 vel = curl * u_advectSpeed * u_turbulence;
|
|
332
441
|
`;
|
|
333
442
|
var MEDIUM_ADVECT_VELOCITY_SETTLE = `
|
|
334
443
|
${MEDIUM_ADVECT_VELOCITY}
|
|
335
444
|
vel.y += u_settleSpeed;
|
|
445
|
+
vel.x += curl.x * u_advectSpeed * u_settleWiggle;
|
|
446
|
+
`;
|
|
447
|
+
var MEDIUM_ADVECT_VELOCITY_VAPOR = `
|
|
448
|
+
${MEDIUM_ADVECT_VELOCITY}
|
|
449
|
+
vel.y += u_vaporDrift;
|
|
336
450
|
`;
|
|
337
451
|
var MEDIUM_VAPOR_FORWARD_SHADER = `
|
|
338
452
|
${MEDIUM_ADVECT_UNIFORMS}
|
|
453
|
+
uniform float u_vaporDrift;
|
|
339
454
|
|
|
340
455
|
half4 main(float2 xy) {
|
|
341
|
-
${
|
|
456
|
+
${MEDIUM_ADVECT_VELOCITY_VAPOR}
|
|
342
457
|
float2 src = xy - vel * u_dt;
|
|
343
458
|
return u_dye.eval(float2(src.x, u_dyeSize.y - src.y));
|
|
344
459
|
}
|
|
@@ -346,6 +461,7 @@ ${MEDIUM_ADVECT_VELOCITY}
|
|
|
346
461
|
var MEDIUM_CONDENSATE_FORWARD_SHADER = `
|
|
347
462
|
${MEDIUM_ADVECT_UNIFORMS}
|
|
348
463
|
uniform float u_settleSpeed;
|
|
464
|
+
uniform float u_settleWiggle;
|
|
349
465
|
|
|
350
466
|
half4 main(float2 xy) {
|
|
351
467
|
${MEDIUM_ADVECT_VELOCITY_SETTLE}
|
|
@@ -374,9 +490,10 @@ var MEDIUM_MACCORMACK_CORRECT = `
|
|
|
374
490
|
var MEDIUM_VAPOR_CORRECT_SHADER = `
|
|
375
491
|
${MEDIUM_ADVECT_UNIFORMS}
|
|
376
492
|
uniform shader u_forward;
|
|
493
|
+
uniform float u_vaporDrift;
|
|
377
494
|
|
|
378
495
|
half4 main(float2 xy) {
|
|
379
|
-
${
|
|
496
|
+
${MEDIUM_ADVECT_VELOCITY_VAPOR}
|
|
380
497
|
${MEDIUM_MACCORMACK_CORRECT}
|
|
381
498
|
return limited;
|
|
382
499
|
}
|
|
@@ -384,6 +501,7 @@ ${MEDIUM_MACCORMACK_CORRECT}
|
|
|
384
501
|
var MEDIUM_CONDENSATE_CORRECT_SHADER = `
|
|
385
502
|
${MEDIUM_ADVECT_UNIFORMS}
|
|
386
503
|
uniform float u_settleSpeed;
|
|
504
|
+
uniform float u_settleWiggle;
|
|
387
505
|
uniform shader u_forward;
|
|
388
506
|
|
|
389
507
|
half4 main(float2 xy) {
|
|
@@ -483,6 +601,39 @@ ${MEDIUM_ADVECT_VELOCITY}
|
|
|
483
601
|
`;
|
|
484
602
|
|
|
485
603
|
// src/medium/composite-shader.ts
|
|
604
|
+
var VG_CUBIC_WEIGHTS = `
|
|
605
|
+
float4 vgCubicWeights(float v) {
|
|
606
|
+
float4 n = float4(1.0, 2.0, 3.0, 4.0) - v;
|
|
607
|
+
float4 s = n * n * n;
|
|
608
|
+
float x = s.x;
|
|
609
|
+
float y = s.y - 4.0 * s.x;
|
|
610
|
+
float z = s.z - 4.0 * s.y + 6.0 * s.x;
|
|
611
|
+
float w = 6.0 - x - y - z;
|
|
612
|
+
return float4(x, y, z, w) * (1.0 / 6.0);
|
|
613
|
+
}
|
|
614
|
+
`;
|
|
615
|
+
function vgBicubicSampler(fnName, textureName) {
|
|
616
|
+
return `
|
|
617
|
+
half4 ${fnName}(float2 px) {
|
|
618
|
+
float2 shifted = px - 0.5;
|
|
619
|
+
float2 fxy = fract(shifted);
|
|
620
|
+
float2 base = shifted - fxy;
|
|
621
|
+
float4 xw = vgCubicWeights(fxy.x);
|
|
622
|
+
float4 yw = vgCubicWeights(fxy.y);
|
|
623
|
+
float2 sx = float2(xw.x + xw.y, xw.z + xw.w);
|
|
624
|
+
float2 sy = float2(yw.x + yw.y, yw.z + yw.w);
|
|
625
|
+
float2 offsetX = float2(base.x - 0.5 + xw.y / sx.x, base.x + 1.5 + xw.w / sx.y);
|
|
626
|
+
float2 offsetY = float2(base.y - 0.5 + yw.y / sy.x, base.y + 1.5 + yw.w / sy.y);
|
|
627
|
+
half4 s00 = ${textureName}.eval(float2(offsetX.x, offsetY.x));
|
|
628
|
+
half4 s10 = ${textureName}.eval(float2(offsetX.y, offsetY.x));
|
|
629
|
+
half4 s01 = ${textureName}.eval(float2(offsetX.x, offsetY.y));
|
|
630
|
+
half4 s11 = ${textureName}.eval(float2(offsetX.y, offsetY.y));
|
|
631
|
+
float wx = sx.x / (sx.x + sx.y);
|
|
632
|
+
float wy = sy.x / (sy.x + sy.y);
|
|
633
|
+
return mix(mix(s11, s01, wx), mix(s10, s00, wx), wy);
|
|
634
|
+
}
|
|
635
|
+
`;
|
|
636
|
+
}
|
|
486
637
|
var MEDIUM_COMPOSITE_SHADER = `
|
|
487
638
|
uniform shader u_vapor;
|
|
488
639
|
uniform shader u_condensate;
|
|
@@ -496,17 +647,71 @@ uniform float3 u_labCondensate;
|
|
|
496
647
|
uniform float u_baseWeight;
|
|
497
648
|
uniform float u_condensateTint;
|
|
498
649
|
uniform float u_condensateGain;
|
|
650
|
+
uniform float u_farScale;
|
|
651
|
+
uniform float2 u_farOffset;
|
|
652
|
+
uniform float u_farBlurRadius;
|
|
653
|
+
uniform float u_farWeight;
|
|
654
|
+
uniform float u_gravityBoost;
|
|
655
|
+
uniform float u_gravityBoostStart;
|
|
499
656
|
|
|
500
657
|
${VG_OKLAB_TO_SRGB}
|
|
658
|
+
${VG_CUBIC_WEIGHTS}
|
|
659
|
+
${vgBicubicSampler("vgBicubicVapor", "u_vapor")}
|
|
660
|
+
${vgBicubicSampler("vgBicubicCondensate", "u_condensate")}
|
|
501
661
|
|
|
502
662
|
half4 main(float2 xy) {
|
|
503
|
-
float2
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
663
|
+
float2 baseSrc = xy * u_dyeScale;
|
|
664
|
+
|
|
665
|
+
// Near plane \u2014 the calibrated look lives here. Bicubic (see the file header) on vapor and
|
|
666
|
+
// condensate; track stays a plain bilinear read.
|
|
667
|
+
half4 vapor = vgBicubicVapor(baseSrc);
|
|
668
|
+
half4 condensate = vgBicubicCondensate(baseSrc);
|
|
669
|
+
half4 track = u_track.eval(baseSrc);
|
|
670
|
+
|
|
671
|
+
// Far plane \u2014 the identical field, scaled and offset (see the file header). An 8-tap ring, not
|
|
672
|
+
// the near plane's \xB1x/\xB1y box: axis-aligned taps read the SAME column/row on both sides of any
|
|
673
|
+
// residual axis-aligned structure (a seam, a resize artifact), which is exactly how a straight
|
|
674
|
+
// line gets doubled into four parallel copies instead of softened away \u2014 measured directly via
|
|
675
|
+
// check-medium.mjs's isotropy gate (see its report). The ring is rotated a half-step (22.5\xB0) off
|
|
676
|
+
// the axes so no tap direction ever coincides with a grid row or column. Radius stays in the same
|
|
677
|
+
// far-plane texture-space unit as before (u_farBlurRadius, grid-px) \u2014 only the tap PATTERN
|
|
678
|
+
// changed, not what it measures in.
|
|
679
|
+
float2 farSrc = baseSrc * u_farScale + u_farOffset;
|
|
680
|
+
float2 ring0 = float2( 0.9238795, 0.3826834) * u_farBlurRadius;
|
|
681
|
+
float2 ring1 = float2( 0.3826834, 0.9238795) * u_farBlurRadius;
|
|
682
|
+
float2 ring2 = float2(-0.3826834, 0.9238795) * u_farBlurRadius;
|
|
683
|
+
float2 ring3 = float2(-0.9238795, 0.3826834) * u_farBlurRadius;
|
|
684
|
+
float2 ring4 = float2(-0.9238795, -0.3826834) * u_farBlurRadius;
|
|
685
|
+
float2 ring5 = float2(-0.3826834, -0.9238795) * u_farBlurRadius;
|
|
686
|
+
float2 ring6 = float2( 0.3826834, -0.9238795) * u_farBlurRadius;
|
|
687
|
+
float2 ring7 = float2( 0.9238795, -0.3826834) * u_farBlurRadius;
|
|
688
|
+
half4 vaporFar = (u_vapor.eval(farSrc + ring0) + u_vapor.eval(farSrc + ring1) +
|
|
689
|
+
u_vapor.eval(farSrc + ring2) + u_vapor.eval(farSrc + ring3) +
|
|
690
|
+
u_vapor.eval(farSrc + ring4) + u_vapor.eval(farSrc + ring5) +
|
|
691
|
+
u_vapor.eval(farSrc + ring6) + u_vapor.eval(farSrc + ring7)) * half4(0.125);
|
|
692
|
+
half4 condensateFar = (u_condensate.eval(farSrc + ring0) + u_condensate.eval(farSrc + ring1) +
|
|
693
|
+
u_condensate.eval(farSrc + ring2) + u_condensate.eval(farSrc + ring3) +
|
|
694
|
+
u_condensate.eval(farSrc + ring4) + u_condensate.eval(farSrc + ring5) +
|
|
695
|
+
u_condensate.eval(farSrc + ring6) + u_condensate.eval(farSrc + ring7)) * half4(0.125);
|
|
696
|
+
|
|
697
|
+
// Gravity's "denser layer at the bottom of the chamber" \u2014 a compositing-only boost (see
|
|
698
|
+
// MEDIUM_DEFAULTS.gravityBottomBoost): it scales how density already in the conserved buffers is
|
|
699
|
+
// DISPLAYED, never the buffers themselves, so it can't threaten water conservation. Shared by
|
|
700
|
+
// both planes: the chamber's floor is denser everywhere along its depth, not just up close.
|
|
701
|
+
//
|
|
702
|
+
// Verified with a headless render, not assumed: ramping this with xy.y directly lit up the TOP
|
|
703
|
+
// of the frame, not the bottom (contentTexture's own orientation contract flips y once more
|
|
704
|
+
// between this pass and the final scene \u2014 see the file header) \u2014 hence the 1.0 - below.
|
|
705
|
+
float bottomFrac = 1.0 - xy.y / u_resolution.y;
|
|
706
|
+
float bottomT = smoothstep(u_gravityBoostStart, 1.0, bottomFrac);
|
|
707
|
+
float bottomBoost = 1.0 + u_gravityBoost * bottomT;
|
|
708
|
+
|
|
709
|
+
float gasR = (float(vapor.r) + float(vaporFar.r) * u_farWeight) * bottomBoost;
|
|
710
|
+
float gasG = (float(vapor.g) + float(vaporFar.g) * u_farWeight) * bottomBoost;
|
|
711
|
+
float gasB = (float(vapor.b) + float(vaporFar.b) * u_farWeight) * bottomBoost;
|
|
712
|
+
float w0 = gasR + float(track.r);
|
|
713
|
+
float w1 = gasG + float(track.g);
|
|
714
|
+
float w2 = gasB + float(track.b);
|
|
510
715
|
float wBg = u_baseWeight;
|
|
511
716
|
|
|
512
717
|
// A faint condensate hint from the LOCAL vapor, not its own color (droplets scatter light
|
|
@@ -514,7 +719,8 @@ half4 main(float2 xy) {
|
|
|
514
719
|
float vaporLocal = max(w0 + w1 + w2, 1e-4);
|
|
515
720
|
float3 vaporHue = (u_lab0 * w0 + u_lab1 * w1 + u_lab2 * w2) / vaporLocal;
|
|
516
721
|
float3 condensateLab = mix(u_labCondensate, vaporHue, u_condensateTint);
|
|
517
|
-
float wCondensate =
|
|
722
|
+
float wCondensate =
|
|
723
|
+
(float(condensate.r) + float(condensateFar.r) * u_farWeight) * u_condensateGain * bottomBoost;
|
|
518
724
|
|
|
519
725
|
float total = max(w0 + w1 + w2 + wBg + wCondensate, 1e-4);
|
|
520
726
|
float3 mixLab =
|
|
@@ -574,15 +780,31 @@ function channelFromSeed(seed) {
|
|
|
574
780
|
const idx = Math.floor(hash01(seed * 1.37) * 3) % 3;
|
|
575
781
|
return idx === 0 ? [1, 0, 0] : idx === 1 ? [0, 1, 0] : [0, 0, 1];
|
|
576
782
|
}
|
|
783
|
+
function lerp(range, t) {
|
|
784
|
+
return range[0] + (range[1] - range[0]) * t;
|
|
785
|
+
}
|
|
786
|
+
function applyTrackDepth(preset, depth) {
|
|
787
|
+
const widthScale = lerp(MEDIUM_TRACK_DEPTH_WIDTH_RANGE, depth);
|
|
788
|
+
const intensityScale = lerp(MEDIUM_TRACK_DEPTH_INTENSITY_RANGE, depth);
|
|
789
|
+
return {
|
|
790
|
+
...preset,
|
|
791
|
+
headWidthFrac: preset.headWidthFrac * widthScale,
|
|
792
|
+
tailWidthFrac: preset.tailWidthFrac * widthScale,
|
|
793
|
+
intensity: preset.intensity * intensityScale
|
|
794
|
+
};
|
|
795
|
+
}
|
|
577
796
|
function buildEmission(presetName, source, seed, intensityScale) {
|
|
578
797
|
const preset = MEDIUM_TRACK_PRESETS[presetName];
|
|
798
|
+
const depth = hash01(seed * 4.63);
|
|
799
|
+
const depthApplied = applyTrackDepth(preset, depth);
|
|
579
800
|
return {
|
|
580
|
-
...
|
|
581
|
-
intensity:
|
|
801
|
+
...depthApplied,
|
|
802
|
+
intensity: depthApplied.intensity * intensityScale,
|
|
582
803
|
source,
|
|
583
804
|
angle: hash01(seed * 7.31) * Math.PI * 2,
|
|
584
805
|
seed,
|
|
585
|
-
channel: channelFromSeed(seed)
|
|
806
|
+
channel: channelFromSeed(seed),
|
|
807
|
+
depth
|
|
586
808
|
};
|
|
587
809
|
}
|
|
588
810
|
function createMediumDynamics() {
|
|
@@ -630,6 +852,6 @@ function createMediumDynamics() {
|
|
|
630
852
|
return { step };
|
|
631
853
|
}
|
|
632
854
|
|
|
633
|
-
export { LINEAR_TO_LMS, LMS_TO_LINEAR, LMS_TO_OKLAB, MEDIUM_COMPOSITE_SHADER, MEDIUM_CONDENSATE_CORRECT_SHADER, MEDIUM_CONDENSATE_FORWARD_SHADER, MEDIUM_CONDENSATE_REACT_SHADER, MEDIUM_DEFAULTS, MEDIUM_DEFAULT_CELL_PX, MEDIUM_DRIFT_TURBULENCE, MEDIUM_EMISSION_BEATS, MEDIUM_EMISSION_PROBABILITY, MEDIUM_EMIT_SHADER, MEDIUM_MIN_EMISSION_INTERVAL, MEDIUM_NATURAL_INTERVAL, MEDIUM_NATURAL_JITTER, MEDIUM_PLAYING_TURBULENCE_RANGE, MEDIUM_PROBE_FLOOR_PX, MEDIUM_RESAMPLE_SHADER, MEDIUM_SEED_SHADER, MEDIUM_SENSITIVE_BOTTOM, MEDIUM_SENSITIVE_TOP, MEDIUM_SPECIES_HUE_SPREAD, MEDIUM_SPECIES_LIGHTNESS, MEDIUM_TIME_PERIOD, MEDIUM_TRACK_ADVECT_SHADER, MEDIUM_TRACK_PRESETS, MEDIUM_TURBULENCE_TAU, MEDIUM_VAPOR_CORRECT_SHADER, MEDIUM_VAPOR_FORWARD_SHADER, MEDIUM_VAPOR_REACT_SHADER, OKLAB_TO_LMS, VG_CURL_NOISE, VG_OKLAB_TO_SRGB, VG_VALUE_NOISE, characterOf, createMediumDynamics, neutralSpecies, oklabToSrgb, speciesFamily, srgbToOklab };
|
|
634
|
-
//# sourceMappingURL=chunk-
|
|
635
|
-
//# sourceMappingURL=chunk-
|
|
855
|
+
export { LINEAR_TO_LMS, LMS_TO_LINEAR, LMS_TO_OKLAB, MEDIUM_COMPOSITE_SHADER, MEDIUM_CONDENSATE_CORRECT_SHADER, MEDIUM_CONDENSATE_FORWARD_SHADER, MEDIUM_CONDENSATE_REACT_SHADER, MEDIUM_DEFAULTS, MEDIUM_DEFAULT_CELL_PX, MEDIUM_DRIFT_TURBULENCE, MEDIUM_EMISSION_BEATS, MEDIUM_EMISSION_PROBABILITY, MEDIUM_EMIT_SHADER, MEDIUM_MIN_EMISSION_INTERVAL, MEDIUM_NATURAL_INTERVAL, MEDIUM_NATURAL_JITTER, MEDIUM_PLAYING_TURBULENCE_RANGE, MEDIUM_PROBE_FLOOR_PX, MEDIUM_RESAMPLE_SHADER, MEDIUM_SEED_SHADER, MEDIUM_SENSITIVE_BOTTOM, MEDIUM_SENSITIVE_TOP, MEDIUM_SPECIES_HUE_SPREAD, MEDIUM_SPECIES_LIGHTNESS, MEDIUM_TIME_PERIOD, MEDIUM_TRACK_ADVECT_SHADER, MEDIUM_TRACK_DEPTH_INTENSITY_RANGE, MEDIUM_TRACK_DEPTH_WIDTH_RANGE, MEDIUM_TRACK_PRESETS, MEDIUM_TURBULENCE_TAU, MEDIUM_VAPOR_CORRECT_SHADER, MEDIUM_VAPOR_FORWARD_SHADER, MEDIUM_VAPOR_REACT_SHADER, OKLAB_TO_LMS, VG_CURL_NOISE, VG_OKLAB_TO_SRGB, VG_VALUE_NOISE, applyTrackDepth, characterOf, computeMediumSpatialPeriods, createMediumDynamics, depthFarOffsetPx, neutralSpecies, oklabToSrgb, speciesFamily, srgbToOklab, wrappedDistanceFromZero };
|
|
856
|
+
//# sourceMappingURL=chunk-2AFVQ65S.js.map
|
|
857
|
+
//# sourceMappingURL=chunk-2AFVQ65S.js.map
|