@vitreajs/vitrea 0.2.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/dist/{dist-RQ4ZMA3D.js → dist-MVSXUP3K.js} +612 -198
- package/dist/dist-MVSXUP3K.js.map +1 -0
- package/dist/index.d.ts +388 -173
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/dist-RQ4ZMA3D.js.map +0 -1
|
@@ -17,6 +17,7 @@ var WGSL_ANALYSIS_PASS = `struct AnalysisUniforms {
|
|
|
17
17
|
@group(0) @binding(3) var<storage, read_write> stats : array<f32>;
|
|
18
18
|
|
|
19
19
|
var<workgroup> partialLum : array<f32, ${ANALYSIS_WORKGROUP}>;
|
|
20
|
+
var<workgroup> partialTone : array<f32, ${ANALYSIS_WORKGROUP}>;
|
|
20
21
|
var<workgroup> partialSq : array<f32, ${ANALYSIS_WORKGROUP}>;
|
|
21
22
|
var<workgroup> partialEdge : array<f32, ${ANALYSIS_WORKGROUP}>;
|
|
22
23
|
|
|
@@ -32,6 +33,7 @@ fn lum_at(uv : vec2f) -> f32 {
|
|
|
32
33
|
fn cs_analysis(@builtin(local_invocation_index) lane : u32) {
|
|
33
34
|
let total = u32(au.grid.x * au.grid.y);
|
|
34
35
|
var sumLum = 0.0;
|
|
36
|
+
var sumTone = 0.0;
|
|
35
37
|
var sumSq = 0.0;
|
|
36
38
|
var sumEdge = 0.0;
|
|
37
39
|
var n = 0.0;
|
|
@@ -47,7 +49,15 @@ fn cs_analysis(@builtin(local_invocation_index) lane : u32) {
|
|
|
47
49
|
let dx = lum_at(uv + vec2f(au.texel.x, 0.0)) - lum_at(uv - vec2f(au.texel.x, 0.0));
|
|
48
50
|
let dy = lum_at(uv + vec2f(0.0, au.texel.y)) - lum_at(uv - vec2f(0.0, au.texel.y));
|
|
49
51
|
|
|
52
|
+
// The published luminance accumulates ENCODED \u2014 the W9 model (claims
|
|
53
|
+
// 5.31): the reference's tone responses track the encoded-space mean of a
|
|
54
|
+
// structured backdrop, not its linear mean. The host decodes the mean once
|
|
55
|
+
// on readback, so every consumer downstream still speaks linear light.
|
|
56
|
+
// The linear sum stays, because the variance fold below needs a mean in
|
|
57
|
+
// the same space as its squares; variance and edge density stay linear \u2014
|
|
58
|
+
// nothing tonal reads them.
|
|
50
59
|
sumLum = sumLum + c;
|
|
60
|
+
sumTone = sumTone + linear_to_srgb(vec3f(c, c, c)).x;
|
|
51
61
|
sumSq = sumSq + c * c;
|
|
52
62
|
sumEdge = sumEdge + length(vec2f(dx, dy)) * 0.5;
|
|
53
63
|
n = n + 1.0;
|
|
@@ -56,6 +66,7 @@ fn cs_analysis(@builtin(local_invocation_index) lane : u32) {
|
|
|
56
66
|
}
|
|
57
67
|
|
|
58
68
|
partialLum[lane] = sumLum;
|
|
69
|
+
partialTone[lane] = sumTone;
|
|
59
70
|
partialSq[lane] = sumSq;
|
|
60
71
|
partialEdge[lane] = sumEdge;
|
|
61
72
|
workgroupBarrier();
|
|
@@ -66,6 +77,7 @@ fn cs_analysis(@builtin(local_invocation_index) lane : u32) {
|
|
|
66
77
|
if (stride == 0u) { break; }
|
|
67
78
|
if (lane < stride) {
|
|
68
79
|
partialLum[lane] = partialLum[lane] + partialLum[lane + stride];
|
|
80
|
+
partialTone[lane] = partialTone[lane] + partialTone[lane + stride];
|
|
69
81
|
partialSq[lane] = partialSq[lane] + partialSq[lane + stride];
|
|
70
82
|
partialEdge[lane] = partialEdge[lane] + partialEdge[lane + stride];
|
|
71
83
|
}
|
|
@@ -76,7 +88,8 @@ fn cs_analysis(@builtin(local_invocation_index) lane : u32) {
|
|
|
76
88
|
if (lane == 0u) {
|
|
77
89
|
let count = max(f32(total), 1.0);
|
|
78
90
|
let mean = partialLum[0] / count;
|
|
79
|
-
stats[0]
|
|
91
|
+
// stats[0] is the ENCODED-space mean; the host decodes it once (W9).
|
|
92
|
+
stats[0] = partialTone[0] / count;
|
|
80
93
|
stats[1] = max(partialSq[0] / count - mean * mean, 0.0);
|
|
81
94
|
stats[2] = partialEdge[0] / count;
|
|
82
95
|
stats[3] = count;
|
|
@@ -233,7 +246,12 @@ var DEFAULT_MATERIAL_PROFILE = {
|
|
|
233
246
|
* the same 44 px span, which is the lerp-versus-multiply question C9a
|
|
234
247
|
* recorded and nobody has acted on. Claims §5.13.
|
|
235
248
|
*/
|
|
236
|
-
|
|
249
|
+
// 1.25 since W11c G1 (claims §5.41): the reference's interior over
|
|
250
|
+
// structured content is a sharp component near σ 1.25 plus a heavy one
|
|
251
|
+
// the scatter facet now supplies (`sizeScatterFloor`); the cascade's 3
|
|
252
|
+
// (claims §5.16) was the one Gaussian that best split the difference on
|
|
253
|
+
// a bed with no pitch axis to tell the two apart.
|
|
254
|
+
blurSigma: 1.25,
|
|
237
255
|
tint: srgbToLinear(SRGB_WHITE_TINT),
|
|
238
256
|
tintAlpha: 0.46,
|
|
239
257
|
rimWidth: 1.5,
|
|
@@ -405,41 +423,45 @@ var DEFAULT_MATERIAL_PROFILE = {
|
|
|
405
423
|
* on. The grid 1.0/1.4/2.2 is flat to 0.2%. Carrying a fitted-looking 1.4 whose
|
|
406
424
|
* measurement no longer exists would be the worse of the two errors.
|
|
407
425
|
*/
|
|
408
|
-
|
|
426
|
+
// MEASURED (W11c G1, claims §5.41): the scatter facet off the identity, on
|
|
427
|
+
// its own curve — see `MaterialProfile.sizeScatterFloor`. The paragraph
|
|
428
|
+
// above records why the identity was the right answer while `blurSigma`
|
|
429
|
+
// was 3 and the bed had no pitch axis; the W9 probe's pitch axis is what
|
|
430
|
+
// identified the two-component interior this expresses.
|
|
431
|
+
sizeScatterGainMax: 8,
|
|
432
|
+
sizeScatterFloor: 0.4,
|
|
433
|
+
sizeScatterSpanMax: 256,
|
|
409
434
|
sizeOcclusionGain: 0.05,
|
|
410
435
|
sizeShadowGainMax: 1,
|
|
411
|
-
|
|
412
|
-
|
|
436
|
+
// MEASURED (W11c G2, claims §5.43): the reference band's inward source
|
|
437
|
+
// displacement is 33 px at the contour on a 20.8 px lens depth — 1.6 lens
|
|
438
|
+
// depths — following the shader's own (1 − depth)² profile (per-shell fits
|
|
439
|
+
// on the probe's pitch-32/64 cells, spans 96/128 fitted, 160 held out; the
|
|
440
|
+
// gain's sweep 1.4/1.6/1.8/2.4 reads 0.0487/0.0478/0.0516/0.0567 at the
|
|
441
|
+
// shader's depth and profile). The band folds the plate from 1.6 lens depths
|
|
442
|
+
// inward; nothing in it is sharper or darker than the interior.
|
|
443
|
+
lensRefractionGain: 1.6,
|
|
413
444
|
reducedTransparencyFrost: 1.75,
|
|
414
445
|
increasedOcclusionLift: INCREASED_OCCLUSION_LIFT,
|
|
415
446
|
strongBorderRim: { rimWidth: 2, rimAlpha: 0.95 },
|
|
416
447
|
reducedTintAdaptation: 0.35,
|
|
417
|
-
// ADVISORY (W3). The span 0.02 … 0.65 covers most of the canonical backdrop
|
|
418
|
-
// range (0.003 … 0.891 linear), and the two ends are deliberately symmetric —
|
|
419
|
-
// 0.45 of the seed's brightness at the dark end, 0.45 of the way to white at
|
|
420
|
-
// the bright end — so the seed itself sits mid-range and the excursion reads
|
|
421
|
-
// as glass rather than as two different colours.
|
|
422
448
|
/*
|
|
423
|
-
*
|
|
424
|
-
*
|
|
425
|
-
*
|
|
426
|
-
*
|
|
427
|
-
*
|
|
428
|
-
*
|
|
449
|
+
* MEASURED (W10, 2026-09-02) — per-pixel least squares of the reference's
|
|
450
|
+
* tinted pixel against its own untinted pixel on the five W9-probe tinted
|
|
451
|
+
* checkerboard cells (pitch 4…64 px, 17 700 px): shade = 0.5289 + 0.4886·u,
|
|
452
|
+
* RMS 0.0035. Out of sample on the canonical bed the orange cells fit at
|
|
453
|
+
* RMS 0.003 with zero bias; blue sits 0.011 darker (the second hue's
|
|
454
|
+
* residual, recorded and unmodelled). The light end extrapolates past 1 and
|
|
455
|
+
* is clamped in `tintShade`. Claims §5.36.
|
|
429
456
|
*
|
|
430
|
-
*
|
|
431
|
-
*
|
|
432
|
-
*
|
|
433
|
-
*
|
|
434
|
-
* its tinted interior is the declared seed EXACTLY in linear light, at a
|
|
435
|
-
* per-channel standard deviation of 0.000. Apple's tint is the author's
|
|
436
|
-
* colour, not a range of tones mapped to it; S219's wording described the
|
|
437
|
-
* inactive material's residue. Claims §5.13.
|
|
457
|
+
* §5.13's earlier "the curve is the identity" fit was made on the material's
|
|
458
|
+
* MEAN over solid backdrops, where u is either ~0.97 (shade 1.0) or the
|
|
459
|
+
* collapse has already folded the shade out — the identity was the law's
|
|
460
|
+
* two endpoints, seen without anything between them.
|
|
438
461
|
*/
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
tintToneHigh: 0.65,
|
|
462
|
+
tintShadeDark: 0.5289,
|
|
463
|
+
tintShadeLight: 1.0175,
|
|
464
|
+
tintShadeStrength: 1,
|
|
443
465
|
/*
|
|
444
466
|
* MEASURED (W7), against the settled apple-macos-26.5 bed, on the light and
|
|
445
467
|
* dark standard profiles jointly and at both scales.
|
|
@@ -472,9 +494,21 @@ var DEFAULT_MATERIAL_PROFILE = {
|
|
|
472
494
|
* is a VALIDATION cell and was not fitted to: the calibration set predicts
|
|
473
495
|
* 0.34 there and the cell reads 0.356.
|
|
474
496
|
*/
|
|
497
|
+
/*
|
|
498
|
+
* RE-SCOPED (W9, claims §5.33). The four mix constants now own TEXTURE
|
|
499
|
+
* COLLAPSE alone — the interior mean moved to the response law below — and
|
|
500
|
+
* their band contracts to the one domain the collapse is real in: full at
|
|
501
|
+
* `dark-solid` (0.0117) for thin surfaces including the 44 px capsule
|
|
502
|
+
* (byte-identical collapse, arg 0.0164 ≤ low with margin), zero by
|
|
503
|
+
* `mid-dark-solid` (0.0595), where the reference's small surface keeps a
|
|
504
|
+
* textured body at 0.4561 and the old band's partial collapse (k = 0.81)
|
|
505
|
+
* was the measured 0.1375-vs-0.4561 overshoot. `impulse__rrect-md`
|
|
506
|
+
* (arg 0.0539) keeps k ≈ 0.003 against the old band's 0.008 — the same
|
|
507
|
+
* unadapted render that cell validated.
|
|
508
|
+
*/
|
|
475
509
|
backdropToneMax: 1,
|
|
476
|
-
backdropToneLow: 0.
|
|
477
|
-
backdropToneHigh: 0.
|
|
510
|
+
backdropToneLow: 0.02,
|
|
511
|
+
backdropToneHigh: 0.055,
|
|
478
512
|
/*
|
|
479
513
|
* REFITTED 0.09 → 0.13 (2026-08-31, active bed). The law's SHAPE is untouched
|
|
480
514
|
* (Decision Log 13 stands; the two-axis rework is next wave) and `max`, `low`
|
|
@@ -493,8 +527,26 @@ var DEFAULT_MATERIAL_PROFILE = {
|
|
|
493
527
|
* estimator's algebra assumes the two colour schemes share one tint alpha,
|
|
494
528
|
* and this profile pair does not (0.46 against 0.97) — recorded in §5.13 as
|
|
495
529
|
* the reason it is no longer the primary evidence for this constant.
|
|
530
|
+
*
|
|
531
|
+
* RE-SCOPED 0.13 → 0.05 with the band above (W9): the bias's one remaining
|
|
532
|
+
* job is keeping the thick surface's collapse argument above `high` at the
|
|
533
|
+
* dark anchor (0.0117 + 0.05 = 0.0617 > 0.055) while the thin capsule's
|
|
534
|
+
* stays below `low` — the near-binary size snap the probe measured
|
|
535
|
+
* (claims §5.33). The smooth size trend on structured backdrops, which the
|
|
536
|
+
* old wide band tried and failed to carry, belongs to the response law's
|
|
537
|
+
* thickness axis now.
|
|
496
538
|
*/
|
|
497
|
-
backdropToneSizeBias: 0.
|
|
539
|
+
backdropToneSizeBias: 0.05,
|
|
540
|
+
/*
|
|
541
|
+
* MEASURED (W9 probe, claims §5.30–§5.33): the anchors are the probe bed's
|
|
542
|
+
* settled reference levels, frequency-settled over seven attested runs,
|
|
543
|
+
* under the probe's own native-mask interior. Thick rows pool the 96 px
|
|
544
|
+
* and 160 px rrects (±0.012). Not tuned; re-measured only by a new probe.
|
|
545
|
+
*/
|
|
546
|
+
backdropToneAnchorX: [0.1104, 0.2706, 0.9505],
|
|
547
|
+
backdropToneResponseThin: [0.0126, 0.4561, 0.9713],
|
|
548
|
+
backdropToneResponseThick: [0.4953, 0.5744, 0.9358],
|
|
549
|
+
backdropToneResponseStrength: 1,
|
|
498
550
|
/*
|
|
499
551
|
* FITTED (recalibration cascade, 2026-08-31). W8's geometry SURVIVES the fit
|
|
500
552
|
* unchanged — `sigmaPx` 15.55, `offsetPx` 7.95, `spreadPx` 3.10 — and only the
|
|
@@ -587,14 +639,19 @@ var SIZE_SPAN_MIN = DEFAULT_MATERIAL_PROFILE.sizeSpanMin;
|
|
|
587
639
|
var SIZE_SPAN_MAX = DEFAULT_MATERIAL_PROFILE.sizeSpanMax;
|
|
588
640
|
var LENS_SIZE_GAIN_MAX = DEFAULT_MATERIAL_PROFILE.lensSizeGainMax;
|
|
589
641
|
var SIZE_SCATTER_GAIN_MAX = DEFAULT_MATERIAL_PROFILE.sizeScatterGainMax;
|
|
642
|
+
var SIZE_SCATTER_FLOOR = DEFAULT_MATERIAL_PROFILE.sizeScatterFloor;
|
|
643
|
+
var SIZE_SCATTER_SPAN_MAX = DEFAULT_MATERIAL_PROFILE.sizeScatterSpanMax;
|
|
590
644
|
var SIZE_OCCLUSION_GAIN = DEFAULT_MATERIAL_PROFILE.sizeOcclusionGain;
|
|
591
645
|
var SIZE_SHADOW_GAIN_MAX = DEFAULT_MATERIAL_PROFILE.sizeShadowGainMax;
|
|
592
|
-
var
|
|
593
|
-
var LENS_RIM_LOD_BIAS = DEFAULT_MATERIAL_PROFILE.lensRimLodBias;
|
|
646
|
+
var LENS_REFRACTION_GAIN = DEFAULT_MATERIAL_PROFILE.lensRefractionGain;
|
|
594
647
|
var BACKDROP_TONE_MAX = DEFAULT_MATERIAL_PROFILE.backdropToneMax;
|
|
595
648
|
var BACKDROP_TONE_LOW = DEFAULT_MATERIAL_PROFILE.backdropToneLow;
|
|
596
649
|
var BACKDROP_TONE_HIGH = DEFAULT_MATERIAL_PROFILE.backdropToneHigh;
|
|
597
650
|
var BACKDROP_TONE_SIZE_BIAS = DEFAULT_MATERIAL_PROFILE.backdropToneSizeBias;
|
|
651
|
+
DEFAULT_MATERIAL_PROFILE.backdropToneAnchorX;
|
|
652
|
+
DEFAULT_MATERIAL_PROFILE.backdropToneResponseThin;
|
|
653
|
+
DEFAULT_MATERIAL_PROFILE.backdropToneResponseThick;
|
|
654
|
+
DEFAULT_MATERIAL_PROFILE.backdropToneResponseStrength;
|
|
598
655
|
var OUTER_SHADOW = DEFAULT_MATERIAL_PROFILE.outerShadow;
|
|
599
656
|
function withMaterialOverrides(base, patch) {
|
|
600
657
|
const optics = {};
|
|
@@ -616,22 +673,26 @@ function withMaterialOverrides(base, patch) {
|
|
|
616
673
|
sizeSpanMax: patch.sizeSpanMax ?? base.sizeSpanMax,
|
|
617
674
|
lensSizeGainMax: patch.lensSizeGainMax ?? base.lensSizeGainMax,
|
|
618
675
|
sizeScatterGainMax: patch.sizeScatterGainMax ?? base.sizeScatterGainMax,
|
|
676
|
+
sizeScatterFloor: patch.sizeScatterFloor ?? base.sizeScatterFloor,
|
|
677
|
+
sizeScatterSpanMax: patch.sizeScatterSpanMax ?? base.sizeScatterSpanMax,
|
|
619
678
|
sizeOcclusionGain: patch.sizeOcclusionGain ?? base.sizeOcclusionGain,
|
|
620
679
|
sizeShadowGainMax: patch.sizeShadowGainMax ?? base.sizeShadowGainMax,
|
|
621
|
-
|
|
622
|
-
lensRimLodBias: patch.lensRimLodBias ?? base.lensRimLodBias,
|
|
680
|
+
lensRefractionGain: patch.lensRefractionGain ?? base.lensRefractionGain,
|
|
623
681
|
reducedTransparencyFrost: patch.reducedTransparencyFrost ?? base.reducedTransparencyFrost,
|
|
624
682
|
increasedOcclusionLift: patch.increasedOcclusionLift ?? base.increasedOcclusionLift,
|
|
625
683
|
strongBorderRim: { ...base.strongBorderRim, ...patch.strongBorderRim },
|
|
626
684
|
reducedTintAdaptation: patch.reducedTintAdaptation ?? base.reducedTintAdaptation,
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
tintToneHigh: patch.tintToneHigh ?? base.tintToneHigh,
|
|
685
|
+
tintShadeDark: patch.tintShadeDark ?? base.tintShadeDark,
|
|
686
|
+
tintShadeLight: patch.tintShadeLight ?? base.tintShadeLight,
|
|
687
|
+
tintShadeStrength: patch.tintShadeStrength ?? base.tintShadeStrength,
|
|
631
688
|
backdropToneMax: patch.backdropToneMax ?? base.backdropToneMax,
|
|
632
689
|
backdropToneLow: patch.backdropToneLow ?? base.backdropToneLow,
|
|
633
690
|
backdropToneHigh: patch.backdropToneHigh ?? base.backdropToneHigh,
|
|
634
691
|
backdropToneSizeBias: patch.backdropToneSizeBias ?? base.backdropToneSizeBias,
|
|
692
|
+
backdropToneAnchorX: patch.backdropToneAnchorX ?? base.backdropToneAnchorX,
|
|
693
|
+
backdropToneResponseThin: patch.backdropToneResponseThin ?? base.backdropToneResponseThin,
|
|
694
|
+
backdropToneResponseThick: patch.backdropToneResponseThick ?? base.backdropToneResponseThick,
|
|
695
|
+
backdropToneResponseStrength: patch.backdropToneResponseStrength ?? base.backdropToneResponseStrength,
|
|
635
696
|
outerShadow: { ...base.outerShadow, ...patch.outerShadow },
|
|
636
697
|
lightDirection: patch.lightDirection ?? base.lightDirection,
|
|
637
698
|
sweepBandRadians: patch.sweepBandRadians ?? base.sweepBandRadians,
|
|
@@ -677,28 +738,29 @@ function tintToneAdaptation(policy, profile = DEFAULT_MATERIAL_PROFILE) {
|
|
|
677
738
|
return 0;
|
|
678
739
|
}
|
|
679
740
|
}
|
|
680
|
-
function
|
|
681
|
-
const
|
|
682
|
-
const
|
|
741
|
+
function tintShade(materialLuminance, grip, profile = DEFAULT_MATERIAL_PROFILE) {
|
|
742
|
+
const u = Math.min(1, Math.max(0, materialLuminance));
|
|
743
|
+
const shade = Math.min(1, Math.max(0, profile.tintShadeDark + (profile.tintShadeLight - profile.tintShadeDark) * u));
|
|
744
|
+
const k = Math.min(1, Math.max(0, grip));
|
|
745
|
+
return 1 + (shade - 1) * k;
|
|
746
|
+
}
|
|
747
|
+
function tintShadeLayer(seed, materialLuminance, grip, profile = DEFAULT_MATERIAL_PROFILE) {
|
|
748
|
+
const shade = tintShade(materialLuminance, grip, profile);
|
|
749
|
+
return [seed[0] * shade, seed[1] * shade, seed[2] * shade];
|
|
750
|
+
}
|
|
751
|
+
function tintedMaterialColour(material, tint, grip, profile = DEFAULT_MATERIAL_PROFILE) {
|
|
752
|
+
if (tint === void 0 || tint.strength <= 0)
|
|
753
|
+
return material;
|
|
754
|
+
const s = Math.min(1, Math.max(0, tint.strength));
|
|
755
|
+
const layer = tintShadeLayer(tint.color, relativeLuminance(material), grip, profile);
|
|
756
|
+
const clamp01 = (value) => Math.min(1, Math.max(0, value));
|
|
683
757
|
const channel = (index) => {
|
|
684
|
-
const
|
|
685
|
-
const
|
|
686
|
-
|
|
687
|
-
return s + (low + (high - low) * t - s) * k;
|
|
758
|
+
const from = linearToSrgbChannel(clamp01(material[index]));
|
|
759
|
+
const to = linearToSrgbChannel(clamp01(layer[index]));
|
|
760
|
+
return srgbToLinearChannel(from + (to - from) * s);
|
|
688
761
|
};
|
|
689
762
|
return [channel(0), channel(1), channel(2)];
|
|
690
763
|
}
|
|
691
|
-
function tintedTintColour(neutral, tint, backdropLuminance, toneAdaptation, profile = DEFAULT_MATERIAL_PROFILE) {
|
|
692
|
-
if (tint === void 0)
|
|
693
|
-
return neutral;
|
|
694
|
-
const tone = tintTone(tint.color, backdropLuminance, toneAdaptation, profile);
|
|
695
|
-
const k = Math.min(1, Math.max(0, tint.strength));
|
|
696
|
-
return [
|
|
697
|
-
neutral[0] + (tone[0] - neutral[0]) * k,
|
|
698
|
-
neutral[1] + (tone[1] - neutral[1]) * k,
|
|
699
|
-
neutral[2] + (tone[2] - neutral[2]) * k
|
|
700
|
-
];
|
|
701
|
-
}
|
|
702
764
|
function backdropToneAdaptation(backdropLuminance, thickness, profile = DEFAULT_MATERIAL_PROFILE) {
|
|
703
765
|
const x = backdropLuminance + profile.backdropToneSizeBias * Math.min(1, Math.max(0, thickness));
|
|
704
766
|
const span = Math.max(profile.backdropToneHigh - profile.backdropToneLow, 1e-6);
|
|
@@ -749,10 +811,14 @@ function lensSizeGainFromThickness(thickness, profile = DEFAULT_MATERIAL_PROFILE
|
|
|
749
811
|
return 1 + (profile.lensSizeGainMax - 1) * thickness;
|
|
750
812
|
}
|
|
751
813
|
function sizeScatterSigma(sigmaPx, spanPx, profile = DEFAULT_MATERIAL_PROFILE) {
|
|
752
|
-
return sizeScatterSigmaAt(sigmaPx,
|
|
814
|
+
return sizeScatterSigmaAt(sigmaPx, scatterThickness(spanPx, 1, profile), profile);
|
|
753
815
|
}
|
|
754
|
-
function
|
|
755
|
-
|
|
816
|
+
function scatterThickness(spanPx, fold, profile = DEFAULT_MATERIAL_PROFILE) {
|
|
817
|
+
const floor = Math.min(1, Math.max(0, profile.sizeScatterFloor));
|
|
818
|
+
return floor + (1 - floor) * smoothstep(profile.sizeSpanMin, profile.sizeScatterSpanMax, spanPx) * fold;
|
|
819
|
+
}
|
|
820
|
+
function sizeScatterSigmaAt(sigmaPx, scatter, profile = DEFAULT_MATERIAL_PROFILE) {
|
|
821
|
+
return sigmaPx * (1 + (profile.sizeScatterGainMax - 1) * scatter);
|
|
756
822
|
}
|
|
757
823
|
function sizeOcclusionAlpha(alpha, spanPx, profile = DEFAULT_MATERIAL_PROFILE) {
|
|
758
824
|
return sizeOcclusionAlphaAt(alpha, sizeThickness(spanPx, profile), profile);
|
|
@@ -813,8 +879,11 @@ function lensDepthPx(thicknessPx, spanPx, profile = DEFAULT_MATERIAL_PROFILE) {
|
|
|
813
879
|
const gain = lensSizeGain(spanPx, profile);
|
|
814
880
|
return Math.min(Math.max(thicknessPx, 0) * gain, spanPx * 0.5);
|
|
815
881
|
}
|
|
816
|
-
function
|
|
817
|
-
|
|
882
|
+
function lensDisplacementPx(depthPx, lensDepthPx2, refractionScale, profile = DEFAULT_MATERIAL_PROFILE) {
|
|
883
|
+
if (lensDepthPx2 <= 0)
|
|
884
|
+
return 0;
|
|
885
|
+
const depth = Math.min(Math.max(depthPx / lensDepthPx2, 0), 1);
|
|
886
|
+
return lensDepthPx2 * (1 - depth) * (1 - depth) * refractionScale * profile.lensRefractionGain;
|
|
818
887
|
}
|
|
819
888
|
|
|
820
889
|
// ../renderer-webgpu/dist/analysis.js
|
|
@@ -826,7 +895,11 @@ var ZERO_STATS = {
|
|
|
826
895
|
};
|
|
827
896
|
function statsFromBuffer(values) {
|
|
828
897
|
return {
|
|
829
|
-
|
|
898
|
+
// The reduction publishes the ENCODED-space mean (the W9 model, claims
|
|
899
|
+
// §5.31) and this is its one decode: every consumer downstream — the
|
|
900
|
+
// drivers, `adaptiveTint`'s fitted thresholds — still speaks linear light,
|
|
901
|
+
// and a solid backdrop reads identically under either convention.
|
|
902
|
+
luminance: srgbToLinearChannel(values[0] ?? 0),
|
|
830
903
|
variance: values[1] ?? 0,
|
|
831
904
|
edgeDensity: values[2] ?? 0,
|
|
832
905
|
sampleCount: values[3] ?? 0
|
|
@@ -1300,6 +1373,44 @@ function createAppTextureProvider(options) {
|
|
|
1300
1373
|
};
|
|
1301
1374
|
}
|
|
1302
1375
|
|
|
1376
|
+
// ../renderer-webgpu/dist/backdrop-fit.js
|
|
1377
|
+
var IDENTITY_FIT = [1, 1, 0, 0];
|
|
1378
|
+
function isUsablePlacement(placement) {
|
|
1379
|
+
return placement !== void 0 && Number.isFinite(placement.x) && Number.isFinite(placement.y) && placement.width > 0 && placement.height > 0;
|
|
1380
|
+
}
|
|
1381
|
+
function coverFit(sourceWidth, sourceHeight, viewportWidthCss, viewportHeightCss) {
|
|
1382
|
+
const viewportAspect = viewportHeightCss > 0 ? viewportWidthCss / viewportHeightCss : 1;
|
|
1383
|
+
const sourceAspect = sourceHeight > 0 ? sourceWidth / sourceHeight : 1;
|
|
1384
|
+
if (sourceAspect > viewportAspect) {
|
|
1385
|
+
const scaleX = viewportAspect / sourceAspect;
|
|
1386
|
+
return [scaleX, 1, (1 - scaleX) / 2, 0];
|
|
1387
|
+
}
|
|
1388
|
+
const scaleY = sourceAspect / viewportAspect;
|
|
1389
|
+
return [1, scaleY, 0, (1 - scaleY) / 2];
|
|
1390
|
+
}
|
|
1391
|
+
function placementFit(placement, viewportWidthCss, viewportHeightCss) {
|
|
1392
|
+
return [
|
|
1393
|
+
viewportWidthCss / placement.width,
|
|
1394
|
+
viewportHeightCss / placement.height,
|
|
1395
|
+
-placement.x / placement.width,
|
|
1396
|
+
-placement.y / placement.height
|
|
1397
|
+
];
|
|
1398
|
+
}
|
|
1399
|
+
function backdropFit(sourceWidth, sourceHeight, placement, viewportWidthCss, viewportHeightCss) {
|
|
1400
|
+
return isUsablePlacement(placement) ? placementFit(placement, viewportWidthCss, viewportHeightCss) : coverFit(sourceWidth, sourceHeight, viewportWidthCss, viewportHeightCss);
|
|
1401
|
+
}
|
|
1402
|
+
function texelsPerCssPx(sourceWidth, sourceHeight, placement, viewportWidthCss, viewportHeightCss) {
|
|
1403
|
+
if (isUsablePlacement(placement)) {
|
|
1404
|
+
return sourceWidth > 0 ? sourceWidth / placement.width : 1;
|
|
1405
|
+
}
|
|
1406
|
+
return viewportWidthCss > 0 && viewportHeightCss > 0 ? Math.max(sourceWidth / viewportWidthCss, sourceHeight / viewportHeightCss) : 1;
|
|
1407
|
+
}
|
|
1408
|
+
function samePlacement(a, b) {
|
|
1409
|
+
if (a === void 0 || b === void 0)
|
|
1410
|
+
return a === b;
|
|
1411
|
+
return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1303
1414
|
// ../renderer-webgpu/dist/device.js
|
|
1304
1415
|
function createDeviceHost(options = {}) {
|
|
1305
1416
|
let status = {
|
|
@@ -1834,7 +1945,7 @@ function packInstances(surfaces, origin, into) {
|
|
|
1834
1945
|
data[o + 13] = s.channels.glow;
|
|
1835
1946
|
data[o + 14] = s.lensDepthPx * Math.min(1, Math.max(0, s.channels.lensStrength));
|
|
1836
1947
|
data[o + 15] = s.tintStrength;
|
|
1837
|
-
data[o + 16] = s.
|
|
1948
|
+
data[o + 16] = s.spanPx;
|
|
1838
1949
|
data[o + 17] = 0;
|
|
1839
1950
|
}
|
|
1840
1951
|
return { data, count: surfaces.length };
|
|
@@ -2112,7 +2223,7 @@ var WGSL_INSTANCE_STRUCT = `struct Instance {
|
|
|
2112
2223
|
glow : f32, // 52 interaction channel value, 0..1
|
|
2113
2224
|
lensDepth : f32, // 56 CPU-resolved lens depth in CSS px (material.ts), already scaled by the lensStrength channel
|
|
2114
2225
|
tintK : f32, // 60 author tint strength, 0..1 \u2014 the seed itself is a group uniform
|
|
2115
|
-
|
|
2226
|
+
span : f32, // 64 the surface's shorter extent, CSS px \u2014 the size law's input, both curves (W11c)
|
|
2116
2227
|
_pad : f32, // 68 stride padding: vec2f aligns the struct to 8, so 72 is the next legal size
|
|
2117
2228
|
};`;
|
|
2118
2229
|
var WGSL_FIELD_SAMPLE = `struct FieldSample {
|
|
@@ -2278,7 +2389,7 @@ var WGSL_FIELD_PASS = `struct FieldUniforms {
|
|
|
2278
2389
|
// field but its members are not one size, and carrying them per pixel is what
|
|
2279
2390
|
// lets a 40 px button and a 320 px platter share a field pass and still lens by
|
|
2280
2391
|
// their own depth \u2014 parent acceptance #2 inside a GlassEffectContainer.
|
|
2281
|
-
// aux = (lensDepthPx, glow,
|
|
2392
|
+
// aux = (lensDepthPx, glow, spanPx, tintStrength)
|
|
2282
2393
|
//
|
|
2283
2394
|
// The third and fourth slots both changed hands, and neither of the values they
|
|
2284
2395
|
// used to carry was ever read by a fragment stage. 'thick' left because
|
|
@@ -2292,15 +2403,26 @@ var WGSL_FIELD_PASS = `struct FieldUniforms {
|
|
|
2292
2403
|
// because a group is one field pass and one optics pass while its members are
|
|
2293
2404
|
// not one thing:
|
|
2294
2405
|
//
|
|
2295
|
-
// '
|
|
2296
|
-
// 320 px platter in one container
|
|
2297
|
-
// shadow by their own size.
|
|
2406
|
+
// 'spanPx' \u2014 the size law's input (W2): the surface's shorter extent in
|
|
2407
|
+
// CSS px. A 40 px button and a 320 px platter in one container
|
|
2408
|
+
// lens, scatter, occlude and shadow by their own size. Until
|
|
2409
|
+
// W11c this slot carried the folded thickness factor itself;
|
|
2410
|
+
// the scatter facet now rides a curve with a different band
|
|
2411
|
+
// top, so the span travels and each consumer evaluates its own
|
|
2412
|
+
// curve from it with the bands and the fold as uniforms.
|
|
2298
2413
|
// 'tintStrength' \u2014 the author tint's strength (W3). One emphasised control
|
|
2299
2414
|
// among plain ones is the composition Apple's guidance
|
|
2300
2415
|
// describes; the seed itself is a group uniform.
|
|
2301
2416
|
//
|
|
2302
2417
|
// They compose rather than compete: the size law moves what the material does to
|
|
2303
2418
|
// the backdrop, the tint moves what colour it does it in.
|
|
2419
|
+
//
|
|
2420
|
+
// One consequence of carrying the span rather than the thickness, stated so it
|
|
2421
|
+
// is not mistaken for a drift: the union below blends 'aux' linearly between two
|
|
2422
|
+
// members, so a pixel in the neck between a button and a platter now blends
|
|
2423
|
+
// their SPANS and evaluates the curves on the blend, where it used to blend the
|
|
2424
|
+
// evaluated thicknesses. Inside either member the two are identical; only the
|
|
2425
|
+
// smooth-min neck differs, and nothing measured claims a value there.
|
|
2304
2426
|
|
|
2305
2427
|
@group(0) @binding(0) var<uniform> fu : FieldUniforms;
|
|
2306
2428
|
@group(0) @binding(1) var<storage, read> instances : array<Instance>;
|
|
@@ -2321,7 +2443,7 @@ fn eval_instance(i : u32, p : vec2f) -> Member {
|
|
|
2321
2443
|
var m : Member;
|
|
2322
2444
|
m.d = f.d + s.inset;
|
|
2323
2445
|
m.g = f.g;
|
|
2324
|
-
m.aux = vec4f(s.lensDepth, s.glow, s.
|
|
2446
|
+
m.aux = vec4f(s.lensDepth, s.glow, s.span, s.tintK);
|
|
2325
2447
|
return m;
|
|
2326
2448
|
}
|
|
2327
2449
|
|
|
@@ -2409,7 +2531,10 @@ var WGSL_HIGHLIGHT_PASS = `struct HighlightUniforms {
|
|
|
2409
2531
|
/// accessibility refraction cap, and the resolved strength \u2014 zero where there
|
|
2410
2532
|
/// is no measured backdrop tone, which stands the fade down
|
|
2411
2533
|
toneAdapt : vec4f,
|
|
2412
|
-
/// the backdrop source's own average luminance (x)
|
|
2534
|
+
/// the backdrop source's own average luminance (x), and the size law's band
|
|
2535
|
+
/// and fold \u2014 sizeSpanMin (y), sizeSpanMax (z), the accessibility fold (w) \u2014
|
|
2536
|
+
/// so this pass evaluates the thickness curve off the per-pixel span exactly
|
|
2537
|
+
/// as the optics pass does (W11c: the span rides the field, not the factor)
|
|
2413
2538
|
toneLevel : vec4f,
|
|
2414
2539
|
/// this pass's uv into the FIELD texture's uv: scale (xy), offset (zw).
|
|
2415
2540
|
///
|
|
@@ -2489,7 +2614,14 @@ fn fs_highlight(in : FullscreenOut) -> @location(0) vec4f {
|
|
|
2489
2614
|
* around a surface the optics pass had made invisible \u2014 measured at 60 pixels
|
|
2490
2615
|
* past 2/255, peaking at 97/255, on exactly that cell.
|
|
2491
2616
|
*/
|
|
2492
|
-
|
|
2617
|
+
// The thickness curve off the per-pixel span, written out as the optics pass
|
|
2618
|
+
// writes it, so the two passes fade on one curve and not on two copies.
|
|
2619
|
+
let spanT = clamp(
|
|
2620
|
+
(aux.z - hu.toneLevel.y) / max(hu.toneLevel.z - hu.toneLevel.y, 1e-6),
|
|
2621
|
+
0.0,
|
|
2622
|
+
1.0,
|
|
2623
|
+
);
|
|
2624
|
+
let sizeK = clamp(spanT * spanT * (3.0 - 2.0 * spanT) * hu.toneLevel.w, 0.0, 1.0);
|
|
2493
2625
|
var toneAdapt = 0.0;
|
|
2494
2626
|
if (hu.toneAdapt.w > 0.0) {
|
|
2495
2627
|
let toneX = hu.toneLevel.x + hu.toneAdapt.z * sizeK;
|
|
@@ -2514,7 +2646,7 @@ var WGSL_OPTICS_PASS = `struct OpticsUniforms {
|
|
|
2514
2646
|
screen : vec4f,
|
|
2515
2647
|
/// backdrop uv transform on viewport-normalised coords: scale (xy), offset (zw)
|
|
2516
2648
|
fit : vec4f,
|
|
2517
|
-
/// refractionScale,
|
|
2649
|
+
/// refractionScale, lensRefractionGain (W11c G2), unused, chainMaxLod
|
|
2518
2650
|
lens : vec4f,
|
|
2519
2651
|
/// fixed tint colour, linear light (xyz), tint alpha (w)
|
|
2520
2652
|
tint : vec4f,
|
|
@@ -2522,7 +2654,10 @@ var WGSL_OPTICS_PASS = `struct OpticsUniforms {
|
|
|
2522
2654
|
adapt : vec4f,
|
|
2523
2655
|
/// author tint seed, linear light (xyz), tone adaptation under the contrast regime (w)
|
|
2524
2656
|
seed : vec4f,
|
|
2525
|
-
///
|
|
2657
|
+
/// the author tint's shade law (W10): tintShadeDark, tintShadeLight,
|
|
2658
|
+
/// tintShadeStrength (the profile's provenance gate); and (w) the size law's
|
|
2659
|
+
/// accessibility fold \u2014 the refraction ladder read at the preference's cap,
|
|
2660
|
+
/// which every facet's span-dependent rise is multiplied by (W11c)
|
|
2526
2661
|
tone : vec4f,
|
|
2527
2662
|
/// rimWidthPx, rimAlpha, specularPower, specularGain
|
|
2528
2663
|
rim : vec4f,
|
|
@@ -2551,9 +2686,23 @@ var WGSL_OPTICS_PASS = `struct OpticsUniforms {
|
|
|
2551
2686
|
shadow : vec4f,
|
|
2552
2687
|
/// the outer shadow's size-law gain (x), read against the casting surface's own
|
|
2553
2688
|
/// thickness, and the field rect's height in CSS px (y) \u2014 the conversion the
|
|
2554
|
-
/// shadow's shift needs when it lands outside the texture;
|
|
2555
|
-
///
|
|
2689
|
+
/// shadow's shift needs when it lands outside the texture; zw are the padding
|
|
2690
|
+
/// a uniform's vec4 alignment requires
|
|
2556
2691
|
shadowSize : vec4f,
|
|
2692
|
+
/// the backdrop tone response's anchors (W9): the three solid anchors'
|
|
2693
|
+
/// ENCODED-space means (xyz), and the backdrop's linear-space mean (w) \u2014 the
|
|
2694
|
+
/// quantity the response solve composites against
|
|
2695
|
+
toneAnchor : vec4f,
|
|
2696
|
+
/// the response law's thin row: the reference's settled interior levels at the
|
|
2697
|
+
/// three anchors for a surface of sizeThickness 0 (xyz); w is the law's
|
|
2698
|
+
/// per-profile authority \u2014 0 on dark profiles, whose response is unmeasured
|
|
2699
|
+
toneRowThin : vec4f,
|
|
2700
|
+
/// the thick row (sizeThickness saturated), same layout
|
|
2701
|
+
toneRowThick : vec4f,
|
|
2702
|
+
/// the size law's bands (W11c): the scatter facet's floor (x) and its own
|
|
2703
|
+
/// band top (y), and the thickness curve's sizeSpanMin (z) and sizeSpanMax
|
|
2704
|
+
/// (w) \u2014 both curves start at z; the fold they multiply by is tone.w
|
|
2705
|
+
scatter : vec4f,
|
|
2557
2706
|
};
|
|
2558
2707
|
|
|
2559
2708
|
@group(0) @binding(0) var<uniform> ou : OpticsUniforms;
|
|
@@ -2564,6 +2713,42 @@ var WGSL_OPTICS_PASS = `struct OpticsUniforms {
|
|
|
2564
2713
|
@group(0) @binding(5) var backdropBody : texture_2d<f32>;
|
|
2565
2714
|
@group(0) @binding(6) var fieldSampler : sampler;
|
|
2566
2715
|
|
|
2716
|
+
/// One encoded sRGB channel from a linear one \u2014 the space the backdrop tone
|
|
2717
|
+
/// response's anchors live in (W9). Mirrors material.ts's 'linearToSrgbChannel'.
|
|
2718
|
+
fn srgb_encode(c : f32) -> f32 {
|
|
2719
|
+
let x = clamp(c, 0.0, 1.0);
|
|
2720
|
+
return select(1.055 * pow(x, 1.0 / 2.4) - 0.055, x * 12.92, x <= 0.0031308);
|
|
2721
|
+
}
|
|
2722
|
+
|
|
2723
|
+
/// The backdrop tone response R(encodedInput, sizeK) (W9): monotone
|
|
2724
|
+
/// (Fritsch\u2013Carlson) interpolation through the three anchors, clamped to their
|
|
2725
|
+
/// span; smoothstep between the thin and thick rows. Mirrors material.ts's
|
|
2726
|
+
/// 'backdropToneResponse' term for term \u2014 the constants are authored there.
|
|
2727
|
+
fn tone_response(x : f32, sizeK : f32) -> f32 {
|
|
2728
|
+
let f = sizeK * sizeK * (3.0 - 2.0 * sizeK);
|
|
2729
|
+
let ys = mix(ou.toneRowThin.xyz, ou.toneRowThick.xyz, vec3f(f));
|
|
2730
|
+
let xs = ou.toneAnchor.xyz;
|
|
2731
|
+
let xc = clamp(x, xs.x, xs.z);
|
|
2732
|
+
let h0 = max(xs.y - xs.x, 1e-4);
|
|
2733
|
+
let h1 = max(xs.z - xs.y, 1e-4);
|
|
2734
|
+
let d0 = (ys.y - ys.x) / h0;
|
|
2735
|
+
let d1 = (ys.z - ys.y) / h1;
|
|
2736
|
+
// The interior slope: the harmonic mean where the secants agree, 0 across a
|
|
2737
|
+
// sign change \u2014 what keeps the curve monotone between monotone anchors.
|
|
2738
|
+
var m1 = 0.0;
|
|
2739
|
+
if (d0 * d1 > 0.0) { m1 = 2.0 * d0 * d1 / (d0 + d1); }
|
|
2740
|
+
var h = h0; var t = (xc - xs.x) / h0;
|
|
2741
|
+
var y0 = ys.x; var y1 = ys.y; var s0 = d0; var s1 = m1;
|
|
2742
|
+
if (xc > xs.y) {
|
|
2743
|
+
h = h1; t = (xc - xs.y) / h1;
|
|
2744
|
+
y0 = ys.y; y1 = ys.z; s0 = m1; s1 = d1;
|
|
2745
|
+
}
|
|
2746
|
+
return y0 * (1.0 + 2.0 * t) * (1.0 - t) * (1.0 - t)
|
|
2747
|
+
+ s0 * h * t * (1.0 - t) * (1.0 - t)
|
|
2748
|
+
+ y1 * t * t * (3.0 - 2.0 * t)
|
|
2749
|
+
+ s1 * h * t * t * (t - 1.0);
|
|
2750
|
+
}
|
|
2751
|
+
|
|
2567
2752
|
/// Rim proximity: 1 exactly on the contour, falling to 0 by 'width' on either
|
|
2568
2753
|
/// side. Symmetric, so the rim is a band on the boundary rather than a plateau
|
|
2569
2754
|
/// that keeps burning outward where coverage has already faded.
|
|
@@ -2639,7 +2824,14 @@ fn outer_shadow_alpha(uv : vec2f, upsampled : f32, fieldSize : vec2f) -> f32 {
|
|
|
2639
2824
|
// the remaining transparency in LINEAR light ('sizeOuterShadowOcclusionAt'),
|
|
2640
2825
|
// and (1 - occ) is exactly what raising to 1/2.4 turns into (1 - alpha), so the
|
|
2641
2826
|
// two forms are the same law and neither needs the other's space.
|
|
2642
|
-
|
|
2827
|
+
// The thickness curve off the casting surface's span (W11c: the span rides
|
|
2828
|
+
// the field), written as the body's own is below.
|
|
2829
|
+
let castT = clamp(
|
|
2830
|
+
(shadowAux.z - ou.scatter.z) / max(ou.scatter.w - ou.scatter.z, 1e-6),
|
|
2831
|
+
0.0,
|
|
2832
|
+
1.0,
|
|
2833
|
+
);
|
|
2834
|
+
let sizeK = clamp(castT * castT * (3.0 - 2.0 * castT) * clamp(ou.tone.w, 0.0, 1.0), 0.0, 1.0);
|
|
2643
2835
|
let sizeFold = pow(max(1.0 - ou.shadowSize.x * sizeK, 0.0), 1.0 / 2.4);
|
|
2644
2836
|
let alpha = 1.0 - (1.0 - ou.shadow.x) * sizeFold;
|
|
2645
2837
|
|
|
@@ -2687,51 +2879,66 @@ fn fs_optics(in : FullscreenOut) -> @location(0) vec4f {
|
|
|
2687
2879
|
|
|
2688
2880
|
// Per-pixel, unioned through the field pass. See the module note.
|
|
2689
2881
|
let lensDepth = max(aux.x, 1e-4);
|
|
2690
|
-
// The size law's
|
|
2691
|
-
//
|
|
2692
|
-
//
|
|
2693
|
-
|
|
2882
|
+
// The size law's two curves off the span of whichever surface owns this pixel
|
|
2883
|
+
// (W11c). 'sizeK' is the thickness factor, 0..1: zero on anything at or below
|
|
2884
|
+
// the profile's 'sizeSpanMin', saturated at 'sizeSpanMax', folded under the
|
|
2885
|
+
// preference \u2014 the occlusion, the inner shadow and the tone response multiply
|
|
2886
|
+
// by it, so a small control takes the pre-law path exactly. 'kScatter' is the
|
|
2887
|
+
// scatter facet's own mix, floor + (1 - floor) \xB7 smoothstep to
|
|
2888
|
+
// 'sizeScatterSpanMax' \xB7 fold: the floor is the material's frost at any size
|
|
2889
|
+
// and is not folded, the rise is depth and is. Both written out as
|
|
2890
|
+
// smoothsteps with a guarded denominator, so a profile that collapses a band
|
|
2891
|
+
// degrades to a step rather than to NaN.
|
|
2892
|
+
let span = aux.z;
|
|
2893
|
+
let fold = clamp(ou.tone.w, 0.0, 1.0);
|
|
2894
|
+
let thickT = clamp((span - ou.scatter.z) / max(ou.scatter.w - ou.scatter.z, 1e-6), 0.0, 1.0);
|
|
2895
|
+
let sizeK = clamp(thickT * thickT * (3.0 - 2.0 * thickT) * fold, 0.0, 1.0);
|
|
2896
|
+
let scatterFloor = clamp(ou.scatter.x, 0.0, 1.0);
|
|
2897
|
+
let scatterT = clamp((span - ou.scatter.z) / max(ou.scatter.y - ou.scatter.z, 1e-6), 0.0, 1.0);
|
|
2898
|
+
let kScatter = clamp(
|
|
2899
|
+
scatterFloor + (1.0 - scatterFloor) * scatterT * scatterT * (3.0 - 2.0 * scatterT) * fold,
|
|
2900
|
+
0.0,
|
|
2901
|
+
1.0,
|
|
2902
|
+
);
|
|
2694
2903
|
// '-d' is depth inside the surface, so the profile runs 1 at the contour to 0
|
|
2695
2904
|
// at 'lensDepth' inward, and the square makes the falloff read as curvature
|
|
2696
2905
|
// rather than as a linear ramp.
|
|
2697
2906
|
let depth = clamp(-d / lensDepth, 0.0, 1.0);
|
|
2698
2907
|
let profile = (1.0 - depth) * (1.0 - depth);
|
|
2699
2908
|
|
|
2700
|
-
|
|
2909
|
+
// W11c G2: the lens is the body read from further inside. 'lens.y' is the
|
|
2910
|
+
// profile's refraction gain, the contour displacement in lens depths.
|
|
2911
|
+
let displaceCss = -normal * lensDepth * profile * ou.lens.x * ou.lens.y;
|
|
2701
2912
|
let refracted01 = viewport01 + displaceCss / viewportCss;
|
|
2702
2913
|
|
|
2703
|
-
let straightUv = clamp(viewport01 * ou.fit.xy + ou.fit.zw, vec2f(0.0), vec2f(1.0));
|
|
2704
2914
|
let refractedUv = clamp(refracted01 * ou.fit.xy + ou.fit.zw, vec2f(0.0), vec2f(1.0));
|
|
2705
2915
|
|
|
2706
|
-
let bodyLod = clamp(lensDepth * ou.lens.y, 0.0, ou.lens.w);
|
|
2707
|
-
let lod = clamp(bodyLod - ou.lens.z * profile, 0.0, ou.lens.w);
|
|
2708
|
-
|
|
2709
2916
|
/*
|
|
2710
2917
|
* The scattering facet of the size law: "a softer scattering of light".
|
|
2711
2918
|
*
|
|
2712
2919
|
* The body texture is one blur for the whole backdrop source \u2014 it is built once
|
|
2713
2920
|
* per source per frame, so it cannot be per-surface. The chain beside it can:
|
|
2714
2921
|
* 'size.w' is the chain level whose blur already matches that body, so
|
|
2715
|
-
* 'size.w + log2(
|
|
2716
|
-
* wider
|
|
2717
|
-
*
|
|
2718
|
-
*
|
|
2922
|
+
* 'size.w + log2(sizeScatterGainMax)' is the level whose blur is the gain times
|
|
2923
|
+
* wider \u2014 the HEAVY component, at one fixed level \u2014 and lerping the body toward
|
|
2924
|
+
* it by 'kScatter' is what the reference's interior measures (W11c, claims
|
|
2925
|
+
* \xA75.41): a sharp component near the body's \u03C3 and a heavy one near \u03C3 10, mixed
|
|
2926
|
+
* by a share that is \u2248 0.4 on a small control and rises with the span. The
|
|
2927
|
+
* mix, not the level, is what the span moves.
|
|
2719
2928
|
*/
|
|
2720
|
-
let
|
|
2721
|
-
let scatterLod = clamp(ou.size.w + log2(max(scatterGain, 1e-4)), 0.0, ou.lens.w);
|
|
2929
|
+
let scatterLod = clamp(ou.size.w + log2(max(ou.size.x, 1e-4)), 0.0, ou.lens.w);
|
|
2722
2930
|
|
|
2723
2931
|
var backdrop = vec3f(0.0);
|
|
2724
2932
|
if (ou.flags.x > 0.5) {
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
let
|
|
2933
|
+
// Both components at the refracted position (W11c G2): the band and the
|
|
2934
|
+
// interior are one body, and the displacement alone is the lens.
|
|
2935
|
+
let bodySample = textureSampleLevel(backdropBody, backdropSampler, refractedUv, 0.0);
|
|
2936
|
+
let scatterSample = textureSampleLevel(backdropChain, backdropSampler, refractedUv, scatterLod);
|
|
2728
2937
|
// Premultiplied linear in, straight colour out: the material composites over
|
|
2729
2938
|
// whatever is behind it, so a partially transparent backdrop must not darken
|
|
2730
2939
|
// the glass.
|
|
2731
|
-
let lensColour = lensSample.rgb / max(lensSample.a, 1e-6);
|
|
2732
2940
|
let scatterColour = scatterSample.rgb / max(scatterSample.a, 1e-6);
|
|
2733
|
-
|
|
2734
|
-
backdrop = mix(bodyColour, lensColour, profile);
|
|
2941
|
+
backdrop = mix(bodySample.rgb / max(bodySample.a, 1e-6), scatterColour, kScatter);
|
|
2735
2942
|
}
|
|
2736
2943
|
|
|
2737
2944
|
// Adaptive tint. 'adapt.w' is the strength the accessibility policy and the
|
|
@@ -2739,8 +2946,6 @@ fn fs_optics(in : FullscreenOut) -> @location(0) vec4f {
|
|
|
2739
2946
|
// is what a 'hint' or 'none' group gets.
|
|
2740
2947
|
let neutral = mix(ou.tint.rgb, ou.adapt.rgb, ou.adapt.w);
|
|
2741
2948
|
|
|
2742
|
-
let backdropLuma = dot(backdrop, vec3f(0.2126, 0.7152, 0.0722));
|
|
2743
|
-
|
|
2744
2949
|
/*
|
|
2745
2950
|
* Backdrop tone adaptation (W7) \u2014 step two of the composition contract, between
|
|
2746
2951
|
* the colour scheme's neutral and the author's tint.
|
|
@@ -2785,57 +2990,148 @@ fn fs_optics(in : FullscreenOut) -> @location(0) vec4f {
|
|
|
2785
2990
|
* and 'adaptedTintAlpha' in material.ts.
|
|
2786
2991
|
*/
|
|
2787
2992
|
let sizedAlpha = ou.tint.w + ou.size.y * sizeK * (1.0 - ou.tint.w);
|
|
2788
|
-
|
|
2789
|
-
|
|
2993
|
+
|
|
2994
|
+
/*
|
|
2995
|
+
* The backdrop tone response solve (W9) \u2014 the law that owns the interior
|
|
2996
|
+
* MEAN, where the collapse below owns texture and nothing else (claims
|
|
2997
|
+
* \xA75.33). The composite under this mechanism reduces exactly to
|
|
2998
|
+
* mean = (1 \u2212 k)\xB7M\u2080 + k\xB7toneLuma with M\u2080 = (1 \u2212 \u03B1)\xB7bgLinear + \u03B1\xB7L(neutral),
|
|
2999
|
+
* so the neutral's tone is solved in closed form: shift its luma so the
|
|
3000
|
+
* post-collapse mean lands on R(encodedInput, sizeK), the reference's own
|
|
3001
|
+
* measured response. Chroma is untouched \u2014 the shift is achromatic \u2014 and
|
|
3002
|
+
* the author tint still displaces the result per the composition contract.
|
|
3003
|
+
*
|
|
3004
|
+
* Three stand-downs, each measured rather than defensive: the whole axis is
|
|
3005
|
+
* off where no backdrop tone was measured (same gate as the collapse); the
|
|
3006
|
+
* solve's authority fades to zero below the dark anchor, where the only
|
|
3007
|
+
* evidence is the impulse cell the collapse constants were fitted on; and
|
|
3008
|
+
* at k \u2192 1 the collapse owns the pixel outright, so the solve's
|
|
3009
|
+
* extrapolation is never evaluated against a vanishing (1 \u2212 k).
|
|
3010
|
+
*/
|
|
3011
|
+
var solvedNeutral = neutral;
|
|
3012
|
+
var solvedAlpha = sizedAlpha;
|
|
3013
|
+
if (ou.flags.x > 0.5 && ou.toneAdapt.w > 0.0 && ou.toneRowThin.w > 0.0 &&
|
|
3014
|
+
sizedAlpha > 1e-3 && toneAdapt < 0.995) {
|
|
3015
|
+
let encodedInput = srgb_encode(ou.toneColour.w);
|
|
3016
|
+
let anchor = max(ou.toneAnchor.x, 1e-4);
|
|
3017
|
+
let authority =
|
|
3018
|
+
smoothstep(anchor * 0.5, anchor, encodedInput) * clamp(ou.toneRowThin.w, 0.0, 1.0);
|
|
3019
|
+
if (authority > 0.0) {
|
|
3020
|
+
let response = tone_response(encodedInput, sizeK);
|
|
3021
|
+
// The collapse's mean pull is toward L(toneColour.rgb) \u2014 the LINEAR
|
|
3022
|
+
// mean, which toneAnchor.w carries \u2014 not toward the encoded level.
|
|
3023
|
+
let preCollapse = (response - toneAdapt * ou.toneAnchor.w) / (1.0 - toneAdapt);
|
|
3024
|
+
let neutralLuma = dot(neutral, vec3f(0.2126, 0.7152, 0.0722));
|
|
3025
|
+
let nominal = (1.0 - sizedAlpha) * ou.toneAnchor.w + sizedAlpha * neutralLuma;
|
|
3026
|
+
let shift = (preCollapse - nominal) / sizedAlpha * authority * ou.toneAdapt.w;
|
|
3027
|
+
solvedNeutral = clamp(neutral + vec3f(shift), vec3f(0.0), vec3f(1.0));
|
|
3028
|
+
/*
|
|
3029
|
+
* The light attractor needs OPACITY. The light scheme's neutral is
|
|
3030
|
+
* already at white, so an upward shift clamps to nothing \u2014 and the
|
|
3031
|
+
* reference's light-adapted state is the material gone opaque-bright,
|
|
3032
|
+
* the same "an adapting material stops transmitting" the collapse's
|
|
3033
|
+
* alpha half was measured on. Whatever the clamp truncated is carried
|
|
3034
|
+
* by the alpha, solved against the same composite and folded by the
|
|
3035
|
+
* same authority. One-sided by design: darkward opacity is the
|
|
3036
|
+
* collapse's own axis with its own fitted constants.
|
|
3037
|
+
*/
|
|
3038
|
+
let solvedLuma = dot(solvedNeutral, vec3f(0.2126, 0.7152, 0.0722));
|
|
3039
|
+
let achieved = (1.0 - sizedAlpha) * ou.toneAnchor.w + sizedAlpha * solvedLuma;
|
|
3040
|
+
if (preCollapse > achieved + 1e-4 && solvedLuma > ou.toneAnchor.w + 1e-3) {
|
|
3041
|
+
let alphaTarget = clamp(
|
|
3042
|
+
(preCollapse - ou.toneAnchor.w) / (solvedLuma - ou.toneAnchor.w),
|
|
3043
|
+
sizedAlpha,
|
|
3044
|
+
1.0,
|
|
3045
|
+
);
|
|
3046
|
+
solvedAlpha = mix(sizedAlpha, alphaTarget, authority * ou.toneAdapt.w);
|
|
3047
|
+
}
|
|
3048
|
+
}
|
|
3049
|
+
}
|
|
3050
|
+
|
|
3051
|
+
let adaptedAlpha = solvedAlpha + toneAdapt * (1.0 - solvedAlpha);
|
|
3052
|
+
var adapted = solvedNeutral;
|
|
2790
3053
|
if (toneAdapt > 0.0 && adaptedAlpha > 0.0) {
|
|
2791
3054
|
adapted =
|
|
2792
|
-
(
|
|
3055
|
+
(solvedNeutral * ((1.0 - toneAdapt) * solvedAlpha) + ou.toneColour.rgb * toneAdapt) /
|
|
3056
|
+
adaptedAlpha;
|
|
2793
3057
|
}
|
|
2794
3058
|
|
|
2795
|
-
// The
|
|
2796
|
-
//
|
|
2797
|
-
//
|
|
2798
|
-
//
|
|
2799
|
-
//
|
|
2800
|
-
//
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
3059
|
+
// The material, untinted: the adapted neutral over what this pixel looks
|
|
3060
|
+
// through, at the material's own occlusion. That alpha is what reduced
|
|
3061
|
+
// transparency lifts and what the size law thickens ("a larger size is more
|
|
3062
|
+
// opaque" is a statement about how much material there is), and the backdrop
|
|
3063
|
+
// adaptation moved it above. The author's colour never touches it \u2014 but not
|
|
3064
|
+
// for the reason the composition contract first gave (claims \xA75.36).
|
|
3065
|
+
var colour = mix(backdrop, adapted, adaptedAlpha);
|
|
3066
|
+
/*
|
|
3067
|
+
* How much of this pixel the SURFACE owns, as the canvas will composite it.
|
|
3068
|
+
*
|
|
3069
|
+
* With a backdrop the composite above is the whole pixel \u2014 the pass sampled
|
|
3070
|
+
* what is behind the surface and mixed it in, so the output is opaque inside
|
|
3071
|
+
* the contour and the page beneath the canvas never shows. With no backdrop
|
|
3072
|
+
* the composite is not here to make: a 'css-backdrop' group's blurred
|
|
3073
|
+
* backdrop is a DOM proxy under this canvas, a 'none' group's is the page
|
|
3074
|
+
* itself, and the browser is the compositor. So the material leaves as a
|
|
3075
|
+
* LAYER \u2014 the adapted colour at the material's own alpha, premultiplied on
|
|
3076
|
+
* the way out \u2014 and every term below that would have shaped the composite
|
|
3077
|
+
* shapes the layer instead, in the form that composites to the same thing.
|
|
3078
|
+
*
|
|
3079
|
+
* Before W11a this path mixed the material over a black backdrop and wrote
|
|
3080
|
+
* it opaque: a nested surface over glass rendered as a flat 0.468 where the
|
|
3081
|
+
* reference reads 0.89 (claims \xA75.38 \xA75). The rows that floored were that
|
|
3082
|
+
* grey. The pair the layer is written at is the host's
|
|
3083
|
+
* ('GroupRenderInput.unsampledMaterial'): the browser composites this canvas
|
|
3084
|
+
* in encoded sRGB, so the alpha is the CSS tier's, not the linear profile's.
|
|
3085
|
+
*/
|
|
3086
|
+
var bodyAlpha = 1.0;
|
|
3087
|
+
if (ou.flags.x <= 0.5) {
|
|
3088
|
+
colour = adapted;
|
|
3089
|
+
bodyAlpha = adaptedAlpha;
|
|
2824
3090
|
}
|
|
2825
3091
|
|
|
2826
|
-
// The tint
|
|
2827
|
-
//
|
|
2828
|
-
//
|
|
2829
|
-
//
|
|
3092
|
+
// The author tint (W10). 'aux.w' is the per-pixel strength, unioned in the
|
|
3093
|
+
// field pass, so a toolbar can carry one tinted control among plain ones; the
|
|
3094
|
+
// seed is a group uniform. At strength 0 this is the identity and the material
|
|
3095
|
+
// is the one the calibration bed measures, byte for byte.
|
|
2830
3096
|
//
|
|
2831
|
-
//
|
|
2832
|
-
//
|
|
2833
|
-
//
|
|
2834
|
-
// the
|
|
2835
|
-
//
|
|
3097
|
+
// Apple's mechanism, measured per pixel: the tinted material is an OPAQUE
|
|
3098
|
+
// layer of the seed at a shade that is linear in the luminance the untinted
|
|
3099
|
+
// material shows at this pixel \u2014 about half the seed's light over black
|
|
3100
|
+
// content, the seed itself over white, the seed's chromaticity intact
|
|
3101
|
+
// throughout. That layer composites over the material at the AUTHOR's
|
|
3102
|
+
// opacity in the encoded space, which is what a CALayer with 'opacity' does
|
|
3103
|
+
// and how the reference's half-strength cell measures. So a tinted surface
|
|
3104
|
+
// over a checkerboard shows the checker as light and dark ORANGE, not as
|
|
3105
|
+
// orange glass with the checker behind it \u2014 the material's own alpha is not
|
|
3106
|
+
// what a tinted surface shows.
|
|
2836
3107
|
//
|
|
2837
|
-
//
|
|
2838
|
-
|
|
3108
|
+
// 'seed.w' is the contrast regime's grip on the excursion, never on the hue;
|
|
3109
|
+
// 'tone.z' is the profile's provenance gate (the dark scheme renders the pure
|
|
3110
|
+
// seed); and '1 \u2212 toneAdapt' folds the shade out where the collapse has made
|
|
3111
|
+
// the material a dark body, where the reference renders the pure seed too.
|
|
3112
|
+
// At zero grip the layer is the bare seed \u2014 the author's colour, flat.
|
|
3113
|
+
if (aux.w > 0.0) {
|
|
3114
|
+
// The untinted material's luminance at this pixel. Over a backdrop that is
|
|
3115
|
+
// the composite; as a layer it is the layer over the tone the host measured
|
|
3116
|
+
// for the group \u2014 zero where nothing was measured, the same reference-level
|
|
3117
|
+
// convention the CSS tier's 'materialLuminance' takes (within its 0.02).
|
|
3118
|
+
let u = bodyAlpha * dot(colour, vec3f(0.2126, 0.7152, 0.0722)) + (1.0 - bodyAlpha) * ou.toneColour.w;
|
|
3119
|
+
let grip = clamp(ou.seed.w, 0.0, 1.0) * clamp(ou.tone.z, 0.0, 1.0) * (1.0 - toneAdapt);
|
|
3120
|
+
let shade = mix(1.0, clamp(mix(ou.tone.x, ou.tone.y, clamp(u, 0.0, 1.0)), 0.0, 1.0), grip);
|
|
3121
|
+
let layer = ou.seed.rgb * shade;
|
|
3122
|
+
let s = clamp(aux.w, 0.0, 1.0);
|
|
3123
|
+
let encodedMaterial = linear_to_srgb(clamp(colour, vec3f(0.0), vec3f(1.0)));
|
|
3124
|
+
let encodedLayer = linear_to_srgb(layer);
|
|
3125
|
+
if (ou.flags.x > 0.5) {
|
|
3126
|
+
colour = srgb_to_linear(mix(encodedMaterial, encodedLayer, vec3f(s)));
|
|
3127
|
+
} else {
|
|
3128
|
+
// The opaque layer at the author's opacity over the material's own layer,
|
|
3129
|
+
// premultiplied: the fold 'tintedCssOptics' makes into one rgba.
|
|
3130
|
+
let premultiplied = (1.0 - s) * bodyAlpha * encodedMaterial + s * encodedLayer;
|
|
3131
|
+
bodyAlpha = 1.0 - (1.0 - s) * (1.0 - bodyAlpha);
|
|
3132
|
+
colour = srgb_to_linear(premultiplied / max(bodyAlpha, 1e-6));
|
|
3133
|
+
}
|
|
3134
|
+
}
|
|
2839
3135
|
|
|
2840
3136
|
/*
|
|
2841
3137
|
* Everything below is the surface's own APPEARANCE \u2014 the marks that say a
|
|
@@ -2861,21 +3157,48 @@ fn fs_optics(in : FullscreenOut) -> @location(0) vec4f {
|
|
|
2861
3157
|
// strongest, which is what makes a thicker surface read as heavier \u2014 and the
|
|
2862
3158
|
// shadow facet of the size law deepens it further with the span.
|
|
2863
3159
|
let shadowDepth = ou.light.z * (1.0 + (ou.size.z - 1.0) * sizeK);
|
|
2864
|
-
|
|
3160
|
+
// A multiplicative occlusion of the whole composite. As a layer that is the
|
|
3161
|
+
// colour scaled and the alpha raised \u2014 (k\xB7a\xB7c, 1 \u2212 k\xB7(1 \u2212 a)) composites to
|
|
3162
|
+
// k times what (a\xB7c, a) would \u2014 and at bodyAlpha 1 it is the plain product.
|
|
3163
|
+
let shadowKeep = 1.0 - profile * shadowDepth * ou.light.w * present;
|
|
3164
|
+
let shadowedAlpha = 1.0 - shadowKeep * (1.0 - bodyAlpha);
|
|
3165
|
+
colour = colour * (shadowKeep * bodyAlpha / max(shadowedAlpha, 1e-6));
|
|
3166
|
+
bodyAlpha = shadowedAlpha;
|
|
2865
3167
|
|
|
2866
3168
|
// Rim and specular from the gradient. The rim is unlit ambient edge brightness;
|
|
2867
3169
|
// the specular term is the same edge lit from 'light.xy'.
|
|
2868
3170
|
let rw = rim_weight(d, ou.rim.x);
|
|
2869
3171
|
let facing = dot(normal, ou.light.xy);
|
|
2870
3172
|
let spec = pow(clamp(facing, 0.0, 1.0), max(ou.rim.z, 1e-3)) * ou.rim.w;
|
|
2871
|
-
|
|
3173
|
+
let rim = rw * (ou.rim.y + spec) * present;
|
|
3174
|
+
if (ou.flags.x > 0.5) {
|
|
3175
|
+
colour = colour + vec3f(rim);
|
|
3176
|
+
} else {
|
|
3177
|
+
// Added light has no premultiplied form of its own \u2014 a canvas colour may
|
|
3178
|
+
// not exceed its alpha \u2014 so the layer carries the light in its opacity:
|
|
3179
|
+
// (a\xB7c + rim, a + rim) composites to dst\xB7(1 \u2212 a) + a\xB7c + rim \u2212 rim\xB7dst,
|
|
3180
|
+
// the additive term short of rim \xD7 dst. Exact wherever the layer is
|
|
3181
|
+
// opaque (an author tint at full strength, or a lit contour pixel whose
|
|
3182
|
+
// alpha the rim fills), and never further off than the rim times what
|
|
3183
|
+
// shows through \u2014 where the mix form ("white over the layer at the rim's
|
|
3184
|
+
// weight") is short by the rim times the whole composite, which halved
|
|
3185
|
+
// the rim on a tinted panel.
|
|
3186
|
+
let carried = clamp(bodyAlpha + rim, 0.0, 1.0);
|
|
3187
|
+
colour = min((colour * bodyAlpha + vec3f(rim)) / max(carried, 1e-6), vec3f(1.0));
|
|
3188
|
+
bodyAlpha = carried;
|
|
3189
|
+
}
|
|
2872
3190
|
|
|
2873
3191
|
// The material over its own shadow over the page, in one premultiplied vector:
|
|
2874
|
-
// the colour is the surface's, weighted by its coverage
|
|
2875
|
-
// the
|
|
2876
|
-
// the shadow
|
|
2877
|
-
|
|
2878
|
-
|
|
3192
|
+
// the colour is the surface's, weighted by its coverage and by however much of
|
|
3193
|
+
// the pixel the layer owns, and the alpha is what the two of them together
|
|
3194
|
+
// leave the page. The shadow fills only what the surface's COVERAGE leaves \u2014
|
|
3195
|
+
// clipped out of the silhouette exactly as a 'box-shadow' is clipped out of
|
|
3196
|
+
// its border box \u2014 never what the layer's own transparency leaves: a
|
|
3197
|
+
// translucent surface shows the page through it, not its own shadow. (With
|
|
3198
|
+
// an opaque body the two are the same quantity, which is how the shadow was
|
|
3199
|
+
// first written and why the difference only surfaced with the layer form.)
|
|
3200
|
+
let body = encode_output(max(colour, vec3f(0.0)), coverage * bodyAlpha);
|
|
3201
|
+
return vec4f(body.rgb, body.a + shadowAlpha * (1.0 - coverage));
|
|
2879
3202
|
}`;
|
|
2880
3203
|
|
|
2881
3204
|
// ../renderer-webgpu/dist/wgsl/prelude.js
|
|
@@ -3122,7 +3445,7 @@ function createPassRunner(context) {
|
|
|
3122
3445
|
};
|
|
3123
3446
|
},
|
|
3124
3447
|
opticsPass(encoder, args) {
|
|
3125
|
-
const slot = uniformSlot(`optics:${args.groupId}`,
|
|
3448
|
+
const slot = uniformSlot(`optics:${args.groupId}`, 76);
|
|
3126
3449
|
const d = slot.data;
|
|
3127
3450
|
d[0] = args.viewportDevice[0];
|
|
3128
3451
|
d[1] = args.viewportDevice[1];
|
|
@@ -3133,8 +3456,8 @@ function createPassRunner(context) {
|
|
|
3133
3456
|
d[6] = args.fit[2];
|
|
3134
3457
|
d[7] = args.fit[3];
|
|
3135
3458
|
d[8] = args.refractionScale;
|
|
3136
|
-
d[9] = args.
|
|
3137
|
-
d[10] =
|
|
3459
|
+
d[9] = args.lensRefractionGain;
|
|
3460
|
+
d[10] = 0;
|
|
3138
3461
|
d[11] = args.chainMaxLod;
|
|
3139
3462
|
d[12] = args.tint[0];
|
|
3140
3463
|
d[13] = args.tint[1];
|
|
@@ -3148,10 +3471,10 @@ function createPassRunner(context) {
|
|
|
3148
3471
|
d[21] = args.tintSeed[1];
|
|
3149
3472
|
d[22] = args.tintSeed[2];
|
|
3150
3473
|
d[23] = args.tintToneAdaptation;
|
|
3151
|
-
d[24] = args.
|
|
3152
|
-
d[25] = args.
|
|
3153
|
-
d[26] = args.
|
|
3154
|
-
d[27] =
|
|
3474
|
+
d[24] = args.tintShade[0];
|
|
3475
|
+
d[25] = args.tintShade[1];
|
|
3476
|
+
d[26] = args.tintShade[2];
|
|
3477
|
+
d[27] = args.sizeFold;
|
|
3155
3478
|
d[28] = args.rimWidth;
|
|
3156
3479
|
d[29] = args.rimAlpha;
|
|
3157
3480
|
d[30] = args.specularPower;
|
|
@@ -3184,6 +3507,22 @@ function createPassRunner(context) {
|
|
|
3184
3507
|
d[57] = args.outerShadowRectCssHeight;
|
|
3185
3508
|
d[58] = 0;
|
|
3186
3509
|
d[59] = 0;
|
|
3510
|
+
d[60] = args.backdropToneAnchorX[0];
|
|
3511
|
+
d[61] = args.backdropToneAnchorX[1];
|
|
3512
|
+
d[62] = args.backdropToneAnchorX[2];
|
|
3513
|
+
d[63] = args.backdropToneLinearMean;
|
|
3514
|
+
d[64] = args.backdropToneResponseThin[0];
|
|
3515
|
+
d[65] = args.backdropToneResponseThin[1];
|
|
3516
|
+
d[66] = args.backdropToneResponseThin[2];
|
|
3517
|
+
d[67] = args.backdropToneResponseStrength;
|
|
3518
|
+
d[68] = args.backdropToneResponseThick[0];
|
|
3519
|
+
d[69] = args.backdropToneResponseThick[1];
|
|
3520
|
+
d[70] = args.backdropToneResponseThick[2];
|
|
3521
|
+
d[71] = 0;
|
|
3522
|
+
d[72] = args.sizeScatterFloor;
|
|
3523
|
+
d[73] = args.sizeScatterSpanMax;
|
|
3524
|
+
d[74] = args.sizeSpanMin;
|
|
3525
|
+
d[75] = args.sizeSpanMax;
|
|
3187
3526
|
slot.write();
|
|
3188
3527
|
const chain = args.backdrop?.chain ?? placeholderView;
|
|
3189
3528
|
const body = args.backdrop?.body ?? placeholderView;
|
|
@@ -3240,9 +3579,9 @@ function createPassRunner(context) {
|
|
|
3240
3579
|
d[22] = args.backdropTone[2];
|
|
3241
3580
|
d[23] = args.backdropTone[3];
|
|
3242
3581
|
d[24] = args.backdropToneLevel;
|
|
3243
|
-
d[25] =
|
|
3244
|
-
d[26] =
|
|
3245
|
-
d[27] =
|
|
3582
|
+
d[25] = args.sizeSpanMin;
|
|
3583
|
+
d[26] = args.sizeSpanMax;
|
|
3584
|
+
d[27] = args.sizeFold;
|
|
3246
3585
|
const fieldRect = args.fieldRectDevice;
|
|
3247
3586
|
d[28] = args.rectDevice.width / Math.max(fieldRect.width, 1e-6);
|
|
3248
3587
|
d[29] = args.rectDevice.height / Math.max(fieldRect.height, 1e-6);
|
|
@@ -3433,6 +3772,11 @@ function createRebuildLedger() {
|
|
|
3433
3772
|
|
|
3434
3773
|
// ../renderer-webgpu/dist/pyramid.js
|
|
3435
3774
|
var chainUsage = () => GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING;
|
|
3775
|
+
var densityOf = (source, request) => texelsPerCssPx(source.width, source.height, request.placement, request.viewportCss[0], request.viewportCss[1]);
|
|
3776
|
+
var sameDensity = (existing, request) => {
|
|
3777
|
+
const next = densityOf({ width: existing.sourceWidth, height: existing.sourceHeight }, request);
|
|
3778
|
+
return Math.abs(next - existing.texelsPerCss) <= 1e-6 * Math.max(1, existing.texelsPerCss);
|
|
3779
|
+
};
|
|
3436
3780
|
function createPyramidStore(context) {
|
|
3437
3781
|
const { device, pool, cache } = context;
|
|
3438
3782
|
const resources = /* @__PURE__ */ new Map();
|
|
@@ -3508,7 +3852,7 @@ function createPyramidStore(context) {
|
|
|
3508
3852
|
return target;
|
|
3509
3853
|
};
|
|
3510
3854
|
const mipView = (texture, level) => texture.createView({ baseMipLevel: level, mipLevelCount: 1, dimension: "2d" });
|
|
3511
|
-
function allocate(sourceId, plan, bodyLevel, sizeEpoch, builtEpoch, bodySigmaTexels) {
|
|
3855
|
+
function allocate(sourceId, plan, bodyLevel, sizeEpoch, builtEpoch, bodySigmaTexels, density) {
|
|
3512
3856
|
const existing = resources.get(sourceId);
|
|
3513
3857
|
const bodyWidth = (plan.levels[bodyLevel] ?? plan.levels[0]).width;
|
|
3514
3858
|
const bodyHeight = (plan.levels[bodyLevel] ?? plan.levels[0]).height;
|
|
@@ -3546,7 +3890,10 @@ function createPyramidStore(context) {
|
|
|
3546
3890
|
stats,
|
|
3547
3891
|
sizeEpoch,
|
|
3548
3892
|
builtEpoch,
|
|
3549
|
-
bodySigmaTexels
|
|
3893
|
+
bodySigmaTexels,
|
|
3894
|
+
texelsPerCss: density.texelsPerCss,
|
|
3895
|
+
sourceWidth: density.sourceWidth,
|
|
3896
|
+
sourceHeight: density.sourceHeight
|
|
3550
3897
|
};
|
|
3551
3898
|
resources.set(sourceId, next);
|
|
3552
3899
|
return next;
|
|
@@ -3734,7 +4081,7 @@ function createPyramidStore(context) {
|
|
|
3734
4081
|
},
|
|
3735
4082
|
build(request, provider, encoder) {
|
|
3736
4083
|
const existing = liveResources(request.sourceId);
|
|
3737
|
-
if (existing !== void 0 && existing.builtEpoch >= request.epoch && !provider.isDirty()) {
|
|
4084
|
+
if (existing !== void 0 && existing.builtEpoch >= request.epoch && !provider.isDirty() && sameDensity(existing, request)) {
|
|
3738
4085
|
ledger.recordClean();
|
|
3739
4086
|
return { status: "clean", resources: existing };
|
|
3740
4087
|
}
|
|
@@ -3752,12 +4099,11 @@ function createPyramidStore(context) {
|
|
|
3752
4099
|
}
|
|
3753
4100
|
pendingRelease.push(provider);
|
|
3754
4101
|
const plan = planPyramid(frame.width, frame.height, request.resolution);
|
|
3755
|
-
const
|
|
3756
|
-
const cover = viewportW > 0 && viewportH > 0 ? Math.max(frame.width / viewportW, frame.height / viewportH) : 1;
|
|
4102
|
+
const texelsPerCss = densityOf(frame, request);
|
|
3757
4103
|
const planScale = frame.width > 0 ? plan.width / frame.width : 1;
|
|
3758
|
-
const bodySigmaTexels = request.bodySigmaCss *
|
|
4104
|
+
const bodySigmaTexels = request.bodySigmaCss * texelsPerCss * planScale;
|
|
3759
4105
|
const bodyPlan = bodyBlurPlan(bodySigmaTexels, plan);
|
|
3760
|
-
const target = allocate(request.sourceId, plan, bodyPlan.level, frame.sizeEpoch, request.epoch, bodySigmaTexels);
|
|
4106
|
+
const target = allocate(request.sourceId, plan, bodyPlan.level, frame.sizeEpoch, request.epoch, bodySigmaTexels, { texelsPerCss, sourceWidth: frame.width, sourceHeight: frame.height });
|
|
3761
4107
|
runImport(encoder, request.sourceId, frame, target.chain);
|
|
3762
4108
|
runChain(encoder, request.sourceId, plan, target.chain);
|
|
3763
4109
|
runBodyBlur(encoder, request.sourceId, plan, target.chain, target.body, bodyPlan.level, bodyPlan.residualSigmaTexels);
|
|
@@ -3873,8 +4219,11 @@ var RENDERER_PASS_IDS = [
|
|
|
3873
4219
|
OPTICS_PASS_ID,
|
|
3874
4220
|
HIGHLIGHT_PASS_ID
|
|
3875
4221
|
];
|
|
4222
|
+
var STANDALONE_RESOLUTION = { scale: 1, maxDimension: 2048 };
|
|
3876
4223
|
function createWebGPURenderer(options = {}) {
|
|
3877
4224
|
const providers = /* @__PURE__ */ new Map();
|
|
4225
|
+
const placements = /* @__PURE__ */ new Map();
|
|
4226
|
+
const lastResolution = /* @__PURE__ */ new Map();
|
|
3878
4227
|
const groups = /* @__PURE__ */ new Map();
|
|
3879
4228
|
const adaptation = /* @__PURE__ */ new Map();
|
|
3880
4229
|
const lastReadbackAt = /* @__PURE__ */ new Map();
|
|
@@ -3963,15 +4312,30 @@ function createWebGPURenderer(options = {}) {
|
|
|
3963
4312
|
}
|
|
3964
4313
|
return state;
|
|
3965
4314
|
};
|
|
3966
|
-
const
|
|
3967
|
-
const
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
4315
|
+
const fitFor = (sourceId, pyramid) => {
|
|
4316
|
+
const placement = placements.get(sourceId);
|
|
4317
|
+
return isUsablePlacement(placement) ? placementFit(placement, viewport.widthCss, viewport.heightCss) : coverFit(pyramid.plan.width, pyramid.plan.height, viewport.widthCss, viewport.heightCss);
|
|
4318
|
+
};
|
|
4319
|
+
const densityRebuilds = (explicit, pyramids) => {
|
|
4320
|
+
const requests = [];
|
|
4321
|
+
const named = new Set(explicit.map((request) => request.sourceId));
|
|
4322
|
+
for (const sourceId of providers.keys()) {
|
|
4323
|
+
if (named.has(sourceId))
|
|
4324
|
+
continue;
|
|
4325
|
+
const existing = pyramids.resources(sourceId);
|
|
4326
|
+
if (existing === void 0)
|
|
4327
|
+
continue;
|
|
4328
|
+
const next = texelsPerCssPx(existing.sourceWidth, existing.sourceHeight, placements.get(sourceId), viewport.widthCss, viewport.heightCss);
|
|
4329
|
+
if (Math.abs(next - existing.texelsPerCss) <= 1e-6 * Math.max(1, existing.texelsPerCss))
|
|
4330
|
+
continue;
|
|
4331
|
+
requests.push({
|
|
4332
|
+
sourceId,
|
|
4333
|
+
epoch: existing.builtEpoch,
|
|
4334
|
+
resolution: lastResolution.get(sourceId) ?? STANDALONE_RESOLUTION,
|
|
4335
|
+
groupIds: [...groups.values()].filter((entry) => entry.input.backdropSourceId === sourceId).map((entry) => entry.input.groupId)
|
|
4336
|
+
});
|
|
3972
4337
|
}
|
|
3973
|
-
|
|
3974
|
-
return [1, scaleY, 0, (1 - scaleY) / 2];
|
|
4338
|
+
return requests;
|
|
3975
4339
|
};
|
|
3976
4340
|
const unionOf = (input) => input.union ?? DEFAULT_GROUP_UNION;
|
|
3977
4341
|
const variantOf = (input) => input.variant ?? "regular";
|
|
@@ -3987,8 +4351,12 @@ function createWebGPURenderer(options = {}) {
|
|
|
3987
4351
|
};
|
|
3988
4352
|
};
|
|
3989
4353
|
function rebuildRequests(explicit) {
|
|
3990
|
-
|
|
3991
|
-
|
|
4354
|
+
const base = explicit ?? standaloneRequests();
|
|
4355
|
+
const { store: pyramids } = ensureContext();
|
|
4356
|
+
const density = densityRebuilds(base, pyramids);
|
|
4357
|
+
return density.length === 0 ? base : [...base, ...density];
|
|
4358
|
+
}
|
|
4359
|
+
function standaloneRequests() {
|
|
3992
4360
|
const sampled = /* @__PURE__ */ new Set();
|
|
3993
4361
|
for (const entry of groups.values()) {
|
|
3994
4362
|
const id = entry.input.backdropSourceId;
|
|
@@ -4006,7 +4374,7 @@ function createWebGPURenderer(options = {}) {
|
|
|
4006
4374
|
// keeping the books, so a monotonically rising number is the honest
|
|
4007
4375
|
// stand-in and it keeps `build`'s clean-skip from firing spuriously.
|
|
4008
4376
|
epoch: framesDrawn + 1,
|
|
4009
|
-
resolution:
|
|
4377
|
+
resolution: STANDALONE_RESOLUTION,
|
|
4010
4378
|
groupIds: [...groups.values()].filter((entry) => entry.input.backdropSourceId === sourceId).map((entry) => entry.input.groupId)
|
|
4011
4379
|
});
|
|
4012
4380
|
}
|
|
@@ -4024,12 +4392,15 @@ function createWebGPURenderer(options = {}) {
|
|
|
4024
4392
|
}
|
|
4025
4393
|
const variant = variantOf([...groups.values()].find((entry) => entry.input.backdropSourceId === request.sourceId)?.input ?? { });
|
|
4026
4394
|
const optics = opticsUnderPolicy(material.optics[variant], accessibility, material);
|
|
4395
|
+
lastResolution.set(request.sourceId, request.resolution);
|
|
4396
|
+
const placement = placements.get(request.sourceId);
|
|
4027
4397
|
const outcome = pyramids.build({
|
|
4028
4398
|
sourceId: request.sourceId,
|
|
4029
4399
|
epoch: request.epoch,
|
|
4030
4400
|
resolution: request.resolution,
|
|
4031
4401
|
bodySigmaCss: optics.blurSigma,
|
|
4032
|
-
viewportCss: [viewport.widthCss, viewport.heightCss]
|
|
4402
|
+
viewportCss: [viewport.widthCss, viewport.heightCss],
|
|
4403
|
+
...isUsablePlacement(placement) ? { placement } : {}
|
|
4033
4404
|
}, provider, encoder);
|
|
4034
4405
|
if (outcome.status === "built")
|
|
4035
4406
|
built += 1;
|
|
@@ -4107,19 +4478,24 @@ function createWebGPURenderer(options = {}) {
|
|
|
4107
4478
|
});
|
|
4108
4479
|
const state = stateOf(input, resolution);
|
|
4109
4480
|
const variant = variantOf(input);
|
|
4110
|
-
const
|
|
4481
|
+
const sourceId = input.backdropSourceId;
|
|
4482
|
+
const pyramid = sourceId === void 0 ? void 0 : pyramids.resources(sourceId);
|
|
4483
|
+
const adapt = sourceId === void 0 ? void 0 : adaptationFor(sourceId).values;
|
|
4484
|
+
const nominalOptics = material.optics[variant];
|
|
4485
|
+
const optics = opticsUnderPolicy(pyramid === void 0 && input.unsampledMaterial !== void 0 ? {
|
|
4486
|
+
...nominalOptics,
|
|
4487
|
+
tint: input.unsampledMaterial.tint,
|
|
4488
|
+
tintAlpha: input.unsampledMaterial.tintAlpha
|
|
4489
|
+
} : nominalOptics, policy, material);
|
|
4111
4490
|
const backdropTone = [
|
|
4112
4491
|
material.backdropToneLow,
|
|
4113
4492
|
material.backdropToneHigh,
|
|
4114
4493
|
backdropToneSizeBiasUnderPolicy(policy, material),
|
|
4115
4494
|
input.backdropTone === void 0 ? 0 : backdropToneUnderPolicy(policy, material) * material.backdropToneMax
|
|
4116
4495
|
];
|
|
4117
|
-
const backdropToneLevel = input.backdropTone === void 0 ? 0 : relativeLuminance(input.backdropTone);
|
|
4496
|
+
const backdropToneLevel = input.backdropTone === void 0 ? 0 : input.backdropToneLevel ?? relativeLuminance(input.backdropTone);
|
|
4118
4497
|
const refraction = effectiveRefraction(accessibilityRefractionCap(policy), state.refraction);
|
|
4119
4498
|
const refractionScale = material.refractionScale[refraction];
|
|
4120
|
-
const sourceId = input.backdropSourceId;
|
|
4121
|
-
const pyramid = sourceId === void 0 ? void 0 : pyramids.resources(sourceId);
|
|
4122
|
-
const adapt = sourceId === void 0 ? void 0 : adaptationFor(sourceId).values;
|
|
4123
4499
|
passes.opticsPass(encoder, {
|
|
4124
4500
|
groupId: input.groupId,
|
|
4125
4501
|
target: active.optics,
|
|
@@ -4129,10 +4505,9 @@ function createWebGPURenderer(options = {}) {
|
|
|
4129
4505
|
viewportDevice,
|
|
4130
4506
|
cssPerDevice,
|
|
4131
4507
|
coverageRampCss: cssPerDevice,
|
|
4132
|
-
fit: pyramid === void 0 ? [1, 1, 0, 0] :
|
|
4508
|
+
fit: pyramid === void 0 || sourceId === void 0 ? [1, 1, 0, 0] : fitFor(sourceId, pyramid),
|
|
4133
4509
|
refractionScale,
|
|
4134
|
-
|
|
4135
|
-
rimLodBias: material.lensRimLodBias,
|
|
4510
|
+
lensRefractionGain: material.lensRefractionGain,
|
|
4136
4511
|
chainMaxLod: pyramid?.plan.maxLod ?? 0,
|
|
4137
4512
|
tint: optics.tint,
|
|
4138
4513
|
tintAlpha: optics.tintAlpha,
|
|
@@ -4152,12 +4527,7 @@ function createWebGPURenderer(options = {}) {
|
|
|
4152
4527
|
*/
|
|
4153
4528
|
tintSeed: (policy.glass === "none" ? void 0 : groupTintSeed(input)) ?? optics.tint,
|
|
4154
4529
|
tintToneAdaptation: tintToneAdaptation(policy, material),
|
|
4155
|
-
|
|
4156
|
-
material.tintToneFloor,
|
|
4157
|
-
material.tintToneCeilMix,
|
|
4158
|
-
material.tintToneLow,
|
|
4159
|
-
material.tintToneHigh
|
|
4160
|
-
],
|
|
4530
|
+
tintShade: [material.tintShadeDark, material.tintShadeLight, material.tintShadeStrength],
|
|
4161
4531
|
rimWidth: optics.rimWidth,
|
|
4162
4532
|
rimAlpha: optics.rimAlpha,
|
|
4163
4533
|
specularPower: optics.specularPower,
|
|
@@ -4174,6 +4544,14 @@ function createWebGPURenderer(options = {}) {
|
|
|
4174
4544
|
sizeOcclusionGain: material.sizeOcclusionGain,
|
|
4175
4545
|
sizeShadowGainMax: material.sizeShadowGainMax,
|
|
4176
4546
|
bodyChainLod: chainLodForSigma(pyramid?.bodySigmaTexels ?? 0),
|
|
4547
|
+
// The size law's curves (W11c): the shader evaluates the thickness and
|
|
4548
|
+
// the scatter mix per pixel from the span the field carries, under the
|
|
4549
|
+
// same fold `sizeThicknessUnderPolicy` applies on the CPU.
|
|
4550
|
+
sizeSpanMin: material.sizeSpanMin,
|
|
4551
|
+
sizeSpanMax: material.sizeSpanMax,
|
|
4552
|
+
sizeScatterFloor: material.sizeScatterFloor,
|
|
4553
|
+
sizeScatterSpanMax: material.sizeScatterSpanMax,
|
|
4554
|
+
sizeFold: material.refractionScale[accessibilityRefractionCap(policy)],
|
|
4177
4555
|
backdropTone,
|
|
4178
4556
|
backdropToneColour: [
|
|
4179
4557
|
input.backdropTone?.[0] ?? 0,
|
|
@@ -4181,6 +4559,25 @@ function createWebGPURenderer(options = {}) {
|
|
|
4181
4559
|
input.backdropTone?.[2] ?? 0,
|
|
4182
4560
|
backdropToneLevel
|
|
4183
4561
|
],
|
|
4562
|
+
// The response law's anchors (W9) and the linear backdrop mean its
|
|
4563
|
+
// solve composites against. The shader re-derives the encoded input
|
|
4564
|
+
// from `backdropToneLevel`, so the fourth slot carries the linear
|
|
4565
|
+
// mean — the one quantity the solve needs that the tone colour does
|
|
4566
|
+
// not already hold.
|
|
4567
|
+
backdropToneAnchorX: material.backdropToneAnchorX,
|
|
4568
|
+
backdropToneResponseThin: material.backdropToneResponseThin,
|
|
4569
|
+
backdropToneResponseThick: material.backdropToneResponseThick,
|
|
4570
|
+
// The response law rides only the UN-DEGRADED regime, for the same
|
|
4571
|
+
// provenance reason the dark profiles set its strength to 0: the
|
|
4572
|
+
// anchors are standard-light-reference measurements, and the
|
|
4573
|
+
// accessibility references behave differently (nearly opaque, flat in
|
|
4574
|
+
// span — W2). Measured before this gate existed: reduced-transparency
|
|
4575
|
+
// photo ΔE p95 0.007 → 0.048 and cross-tier ratio 0.99 → 0.86. Where
|
|
4576
|
+
// any policy fold touches the tone axis, the solve stands down and the
|
|
4577
|
+
// collapse alone runs, exactly the pre-W9 behaviour those profiles
|
|
4578
|
+
// were fitted on.
|
|
4579
|
+
backdropToneResponseStrength: backdropToneUnderPolicy(policy, material) >= 0.999 ? material.backdropToneResponseStrength : 0,
|
|
4580
|
+
backdropToneLinearMean: input.backdropToneLinearLuminance ?? backdropToneLevel,
|
|
4184
4581
|
outerShadow: [
|
|
4185
4582
|
outerShadowAlpha(shadow.occlusion),
|
|
4186
4583
|
shadow.sigmaPx,
|
|
@@ -4219,9 +4616,13 @@ function createWebGPURenderer(options = {}) {
|
|
|
4219
4616
|
glowGain: material.glowGain,
|
|
4220
4617
|
colour: optics.highlight,
|
|
4221
4618
|
// The same four numbers the optics pass took, so the two passes fade
|
|
4222
|
-
// the surface on one curve rather than on two copies of it
|
|
4619
|
+
// the surface on one curve rather than on two copies of it — and the
|
|
4620
|
+
// same band and fold, since the curve is evaluated off the span.
|
|
4223
4621
|
backdropTone,
|
|
4224
|
-
backdropToneLevel
|
|
4622
|
+
backdropToneLevel,
|
|
4623
|
+
sizeSpanMin: material.sizeSpanMin,
|
|
4624
|
+
sizeSpanMax: material.sizeSpanMax,
|
|
4625
|
+
sizeFold: material.refractionScale[accessibilityRefractionCap(policy)]
|
|
4225
4626
|
});
|
|
4226
4627
|
}
|
|
4227
4628
|
drawn += 1;
|
|
@@ -4286,6 +4687,8 @@ function createWebGPURenderer(options = {}) {
|
|
|
4286
4687
|
providers.set(provider.id, provider);
|
|
4287
4688
|
},
|
|
4288
4689
|
unregisterBackdrop(sourceId) {
|
|
4690
|
+
placements.delete(sourceId);
|
|
4691
|
+
lastResolution.delete(sourceId);
|
|
4289
4692
|
const provider = providers.get(sourceId);
|
|
4290
4693
|
if (provider === void 0)
|
|
4291
4694
|
return;
|
|
@@ -4298,6 +4701,17 @@ function createWebGPURenderer(options = {}) {
|
|
|
4298
4701
|
backdrop(sourceId) {
|
|
4299
4702
|
return providers.get(sourceId);
|
|
4300
4703
|
},
|
|
4704
|
+
setBackdropPlacement(sourceId, placement) {
|
|
4705
|
+
if (samePlacement(placements.get(sourceId), placement))
|
|
4706
|
+
return;
|
|
4707
|
+
if (placement === void 0)
|
|
4708
|
+
placements.delete(sourceId);
|
|
4709
|
+
else
|
|
4710
|
+
placements.set(sourceId, { ...placement });
|
|
4711
|
+
},
|
|
4712
|
+
backdropPlacement(sourceId) {
|
|
4713
|
+
return placements.get(sourceId);
|
|
4714
|
+
},
|
|
4301
4715
|
setViewport(next) {
|
|
4302
4716
|
const changed = next.widthCss !== viewport.widthCss || next.heightCss !== viewport.heightCss || next.devicePixelRatio !== viewport.devicePixelRatio;
|
|
4303
4717
|
viewport = next;
|
|
@@ -4454,6 +4868,6 @@ function createWebGPURenderer(options = {}) {
|
|
|
4454
4868
|
};
|
|
4455
4869
|
}
|
|
4456
4870
|
|
|
4457
|
-
export { ADAPTIVE_LUMINANCE_HIGH, ADAPTIVE_LUMINANCE_LOW, ADAPTIVE_TINT_DARK, ADAPTIVE_TINT_LIGHT, ANALYSIS_DISPATCH, ANALYSIS_GRID, ANALYSIS_PASS_ID, ANALYSIS_STATS_FLOATS, ANALYSIS_TARGET_EXTENT, ANALYSIS_WORKGROUP, BACKDROP_ALPHA_MODES, BACKDROP_COLOR_SPACES, BACKDROP_KINDS, BACKDROP_PASS_ID, BACKDROP_TONE_HIGH, BACKDROP_TONE_LOW, BACKDROP_TONE_MAX, BACKDROP_TONE_SIZE_BIAS, CANVAS_FORMAT, CHAIN_SIGMA_AT_LEVEL_1, CROSS_CHECK_SHAPE_FLOATS, CROSS_CHECK_WORKGROUP, DEFAULT_MATERIAL_PROFILE, FAMILY_C_CROSS_CHECK, FIELD_PASS_ID, GOVERNOR_LADDER, HIGHLIGHT_PASS_ID, IDLE_CHANNELS, INCREASED_OCCLUSION_LIFT, INSTANCE_BYTES, INSTANCE_FLOATS,
|
|
4458
|
-
//# sourceMappingURL=dist-
|
|
4459
|
-
//# sourceMappingURL=dist-
|
|
4871
|
+
export { ADAPTIVE_LUMINANCE_HIGH, ADAPTIVE_LUMINANCE_LOW, ADAPTIVE_TINT_DARK, ADAPTIVE_TINT_LIGHT, ANALYSIS_DISPATCH, ANALYSIS_GRID, ANALYSIS_PASS_ID, ANALYSIS_STATS_FLOATS, ANALYSIS_TARGET_EXTENT, ANALYSIS_WORKGROUP, BACKDROP_ALPHA_MODES, BACKDROP_COLOR_SPACES, BACKDROP_KINDS, BACKDROP_PASS_ID, BACKDROP_TONE_HIGH, BACKDROP_TONE_LOW, BACKDROP_TONE_MAX, BACKDROP_TONE_SIZE_BIAS, CANVAS_FORMAT, CHAIN_SIGMA_AT_LEVEL_1, CROSS_CHECK_SHAPE_FLOATS, CROSS_CHECK_WORKGROUP, DEFAULT_MATERIAL_PROFILE, FAMILY_C_CROSS_CHECK, FIELD_PASS_ID, GOVERNOR_LADDER, HIGHLIGHT_PASS_ID, IDENTITY_FIT, IDLE_CHANNELS, INCREASED_OCCLUSION_LIFT, INSTANCE_BYTES, INSTANCE_FLOATS, LENS_REFRACTION_GAIN, LENS_SIZE_GAIN_MAX, LUMINANCE_WEIGHTS, MATERIAL_OPTICS, MATERIAL_VARIANTS, MAX_CHAIN_LEVELS, MIN_LEVEL_EXTENT, NOMINAL_GOVERNOR, NOMINAL_MATERIAL_POLICY, OPTICS_PASS_ID, OUTER_SHADOW, OUTPUT_TEXTURE_FORMAT, PASS_LABEL, REFRACTION_LADDER, REFRACTION_SCALE, RENDERER_ERROR_CODES, RENDERER_PASS_IDS, RendererError, SIZE_OCCLUSION_GAIN, SIZE_SCATTER_FLOOR, SIZE_SCATTER_GAIN_MAX, SIZE_SCATTER_SPAN_MAX, SIZE_SHADOW_GAIN_MAX, SIZE_SPAN_MAX, SIZE_SPAN_MIN, SRGB_ENCODING_EXPONENT, SUPPORTED_APP_TEXTURE_FORMATS, WGSL_ANALYSIS_PASS, WGSL_CROSS_CHECK_PASS, WGSL_DOWNSAMPLE_PASS, WGSL_FIELD_KERNELS, WGSL_FIELD_PASS, WGSL_FIELD_SAMPLE, WGSL_HIGHLIGHT_PASS, WGSL_IMPORT_PASS, WGSL_INSTANCE_STRUCT, WGSL_OPTICS_PASS, WGSL_PRELUDE, WGSL_RSUPN_GRAD, WGSL_RSUP_GRAD, WGSL_SMOOTH_UNION, WORKING_TEXTURE_FORMAT, ZERO_STATS, accessibilityRefractionCap, adaptationStrength, adaptedTintAlpha, adaptedTintColour, adaptiveTint, allShaderSource, alphaNormalisationMode, analysisModule, backdropFit, backdropToneAdaptation, backdropToneSizeBiasUnderPolicy, backdropToneUnderPolicy, bodyBlurPlan, chainModule, clipFieldRectToCanvas, coverFit, createAdaptationState, createAppTextureProvider, createCopyProvider, createDeviceHost, createGovernor, createGpuContext, createGradientProvider, createPassRunner, createPipelineCache, createPyramidStore, createRebuildLedger, createStorageSlot, createTexturePool, createTimingCollector, createUniformSlot, createVideoProvider, createWebGPURenderer, crossCheckKernelModule, displayP3ToSrgbLinear, effectiveRefraction, encodeOutput, encodeOutputBytes, fieldModule, fieldPassSource, groupFieldRect, highlightModule, importColorMatrix, importModule, importPassSource, isUsablePlacement, lensDepthPx, lensDisplacementPx, lensSizeGain, lensSizeGainFromThickness, linearGradientStops, linearToSrgb, linearToSrgbChannel, occlusionAlphaUnderPolicy, opticsModule, opticsUnderPolicy, outerShadowAlpha, outerShadowFalloff, outerShadowReachPx, outerShadowUnderPolicy, packInstances, pipelineKey, placementFit, planPyramid, poolKey, readbackDue, refractionRank, relativeLuminance, rendererError, resolveSurfaces, samePlacement, scatterThickness, sizeOcclusionAlpha, sizeOcclusionAlphaAt, sizeOuterShadowOcclusion, sizeOuterShadowOcclusionAt, sizeScatterSigma, sizeScatterSigmaAt, sizeShadowDepth, sizeShadowDepthAt, sizeThickness, sizeThicknessUnderPolicy, snapRectToDevicePixels, srgbToLinear, srgbToLinearChannel, statsFromBuffer, supportsTimestamps, texelsPerCssPx, tintShade, tintShadeLayer, tintToneAdaptation, tintedMaterialColour, validateAppTexture, withMaterialOverrides };
|
|
4872
|
+
//# sourceMappingURL=dist-MVSXUP3K.js.map
|
|
4873
|
+
//# sourceMappingURL=dist-MVSXUP3K.js.map
|