@vitreajs/vitrea 0.3.0 → 0.5.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-MVSXUP3K.js → dist-OHNXYEZP.js} +873 -132
- package/dist/dist-OHNXYEZP.js.map +1 -0
- package/dist/index.d.ts +576 -43
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/dist-MVSXUP3K.js.map +0 -1
|
@@ -348,6 +348,8 @@ var DEFAULT_MATERIAL_PROFILE = {
|
|
|
348
348
|
*/
|
|
349
349
|
sizeSpanMin: 32,
|
|
350
350
|
sizeSpanMax: 96,
|
|
351
|
+
// The inner shadow's depth gain since W12 G2 (the lens reads its own law
|
|
352
|
+
// below); the value is W2's, unchanged.
|
|
351
353
|
lensSizeGainMax: 2.6,
|
|
352
354
|
/*
|
|
353
355
|
* MEASURED (W2), each with its own status — see the fields' notes.
|
|
@@ -431,16 +433,79 @@ var DEFAULT_MATERIAL_PROFILE = {
|
|
|
431
433
|
sizeScatterGainMax: 8,
|
|
432
434
|
sizeScatterFloor: 0.4,
|
|
433
435
|
sizeScatterSpanMax: 256,
|
|
436
|
+
// The second scale, FITTED by W15 G1's runtime sweep at dpr 2 and landed
|
|
437
|
+
// (claims §5.70 §2 and §8): the gain 4.8 — a heavy width of 6 device px, the
|
|
438
|
+
// sweep's own interior minimum and narrower than G0's Gaussian estimate — the
|
|
439
|
+
// deep value fully heavy, and the span top left at the 1x value because a
|
|
440
|
+
// floor of 1 leaves it nothing to rise to. Read only above dpr 1, so the 1x
|
|
441
|
+
// material is byte-identical to the W13 bed (see
|
|
442
|
+
// `MaterialProfile.sizeScatterFloor2x`).
|
|
443
|
+
sizeScatterGainMax2x: 4.8,
|
|
444
|
+
sizeScatterFloor2x: 1,
|
|
445
|
+
sizeScatterSpanMax2x: 256,
|
|
446
|
+
// The 2x gain's own span grading (W15 G1's re-form, claims §5.70 §8; W15
|
|
447
|
+
// Decision Log 3), set from G0's independent per-span reading — 8 device px
|
|
448
|
+
// at span 96 and 11 at 160 — and not from the holdout row it fixes.
|
|
449
|
+
sizeScatterGainFar2x: 9.9,
|
|
450
|
+
// The 1x three FITTED in the renderer (W13 G1's third sweep, 44 points over
|
|
451
|
+
// the calibration bed: `results/2026-09-03-w13-ramp/g1/sweep-3/g1-sweep-3.md`
|
|
452
|
+
// §3); the 2x three PROVISIONAL still, because at them the excursion is
|
|
453
|
+
// bit-exactly zero on the whole bed and a sweep cannot fit what does not
|
|
454
|
+
// move (§4). The thin anchor 0.72 is above G0's read-off of 0.637–0.642: the
|
|
455
|
+
// grid runs 0.60 / 0.64 / 0.68 and the refinement 0.72 / 0.76, and 0.72 is an
|
|
456
|
+
// interior optimum of both the interior objective and the interior gap. The
|
|
457
|
+
// thick anchor 0.52 is G0's `rrect-md` reading and is above that cell's deep
|
|
458
|
+
// sharp share of 0.481, where the joint thick fit of 0.47 would have clamped
|
|
459
|
+
// it to nothing. The 2x three are G0's readings, LANDED by W15 G1 (claims
|
|
460
|
+
// §5.70 §2): thin is G0's u 6 reading and stage 2b's best, thick and far are
|
|
461
|
+
// one number because G0 read the 2x start flat across the thick spans, and
|
|
462
|
+
// with the deep value now fully heavy at that ratio the ramp is the whole
|
|
463
|
+
// body above it rather than the null W13 recorded. See
|
|
464
|
+
// `MaterialProfile.sizeScatterRampStartThin1x`.
|
|
465
|
+
sizeScatterRampStartThin1x: 0.72,
|
|
466
|
+
sizeScatterRampStartThick1x: 0.52,
|
|
467
|
+
sizeScatterRampStartFar1x: 0.2,
|
|
468
|
+
sizeScatterRampStartThin2x: 0.46,
|
|
469
|
+
sizeScatterRampStartThick2x: 0.21,
|
|
470
|
+
sizeScatterRampStartFar2x: 0.21,
|
|
471
|
+
sizeScatterRampReach1xPx: 80,
|
|
472
|
+
sizeScatterRampReach2xPx: 100,
|
|
434
473
|
sizeOcclusionGain: 0.05,
|
|
435
474
|
sizeShadowGainMax: 1,
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
475
|
+
/*
|
|
476
|
+
* The lens (W12 G2, claims §5.51; Decision Log 3 of the W12 spec).
|
|
477
|
+
*
|
|
478
|
+
* MEASURED from the reference's own layer tree (§5.50): the inner refraction
|
|
479
|
+
* amount and height laws, verified on spans 32 / 44 / 48 … 112 / 128 / 160
|
|
480
|
+
* and the nested base at 130, and the ovalization's knee (0 through span 64,
|
|
481
|
+
* 0.5 from 72). FITTED on the pixels (§5.51, a 2-D band renderer validated on
|
|
482
|
+
* this renderer's own capture): the gain, the extent and the exponent, on
|
|
483
|
+
* `rrect-md` + `-ml` at 1x pitches 16 and 32 with `rrect-lg` held out at both
|
|
484
|
+
* scales; the ovalization's effective value on the box-inscribed ellipse.
|
|
485
|
+
* Predicted before the landing capture: checkerboard SSIM 1x md / ml / lg
|
|
486
|
+
* 0.954 / 0.931 / 0.929 → 0.970 / 0.946 / 0.942, 2x 0.939 / 0.902 / 0.901 →
|
|
487
|
+
* 0.950 / 0.918 / 0.918, the capsule 0.977 → 0.985.
|
|
488
|
+
*
|
|
489
|
+
* What W11c G2 recorded here — 1.6 lens depths on the square profile, fitted
|
|
490
|
+
* per depth shell — was the band's mean, not its shape (§5.48, §5.49); it is
|
|
491
|
+
* kept in git history and in claims §5.43.
|
|
492
|
+
*/
|
|
493
|
+
lensRefractionGain: 0.745,
|
|
494
|
+
lensHeightPerSpan: 0.25,
|
|
495
|
+
lensHeightMax: 20,
|
|
496
|
+
lensAmountPerSpan: 0.8,
|
|
497
|
+
lensAmountMax: 60,
|
|
498
|
+
lensThicknessReference: 8,
|
|
499
|
+
lensExtentGain: 1.337,
|
|
500
|
+
lensProfileExponent: 3.69,
|
|
501
|
+
// 0.8 by eye (W12 Decision Log 6). The pixels preferred 0.6 by a hair — every
|
|
502
|
+
// texture row that can see ω scores 0.001–0.002 lower at 0.8 (claims §5.54) —
|
|
503
|
+
// but the field's measured tilt sits at 0.8–1.0 (§5.49 §3) and the user read
|
|
504
|
+
// the 0.8 sheet as much closer to macOS; the eye overrides a margin that small,
|
|
505
|
+
// and §5.54 §1's rows are the recorded cost.
|
|
506
|
+
lensOvalization: 0.8,
|
|
507
|
+
lensOvalizationSpanMin: 64,
|
|
508
|
+
lensOvalizationSpanMax: 72,
|
|
444
509
|
reducedTransparencyFrost: 1.75,
|
|
445
510
|
increasedOcclusionLift: INCREASED_OCCLUSION_LIFT,
|
|
446
511
|
strongBorderRim: { rimWidth: 2, rimAlpha: 0.95 },
|
|
@@ -568,14 +633,18 @@ var DEFAULT_MATERIAL_PROFILE = {
|
|
|
568
633
|
* instrument's independent shadow axis agrees, so the geometry stands on those
|
|
569
634
|
* two and this objective is not asked to re-decide it.
|
|
570
635
|
*
|
|
571
|
-
* `reducedTransparencyOcclusion` 0.70
|
|
572
|
-
* 3.83× spread over 0.45…0.95 with a clear minimum. It also
|
|
636
|
+
* `reducedTransparencyOcclusion` 0.70 was sharp where the amplitude was flat: a
|
|
637
|
+
* 3.83× spread over 0.45…0.95 with a clear minimum. It also reconciled two
|
|
573
638
|
* routes — 0.285 × 0.70 = 0.1995 against the reference's directly measured
|
|
574
639
|
* reduce-transparency amplitude of 0.203 at a 44 px span. W8's 0.566 was the
|
|
575
|
-
* ratio of the reference's two amplitudes;
|
|
640
|
+
* ratio of the reference's two amplitudes; that was the ratio that made
|
|
576
641
|
* vitrea's shadow match the reference's under the preference, and the two
|
|
577
|
-
*
|
|
578
|
-
* is much wider in the standard profile than under reduced transparency.
|
|
642
|
+
* differed because the base amplitude is a compromise across scenes whose
|
|
643
|
+
* spread is much wider in the standard profile than under reduced transparency.
|
|
644
|
+
* **W14 G1 re-forms the constant as the LEVEL those routes were reconciling
|
|
645
|
+
* to** — 0.197 absolute, replacing both regimes rather than scaling them — for
|
|
646
|
+
* the reason its doc comment gives: a ratio and a level stopped being the same
|
|
647
|
+
* thing when the single amplitude became six unequal anchors.
|
|
579
648
|
*
|
|
580
649
|
* Extracted from the active bed's native fixtures directly —
|
|
581
650
|
* `results/2026-08-31-active-bed-stage0.json` measured the gap, these numbers
|
|
@@ -614,13 +683,33 @@ var DEFAULT_MATERIAL_PROFILE = {
|
|
|
614
683
|
* 44 px span — the dark material's shadow is nearly invisible) and lands as a
|
|
615
684
|
* profile patch, not as a branch here. Both are stated in the claims doc as the
|
|
616
685
|
* open question the cascade's fit inherits.
|
|
686
|
+
*
|
|
687
|
+
* **W14 G1 answers that open question and replaces `occlusion`.** "Over the
|
|
688
|
+
* flat near-white `light-solid` backdrop the same fit reads 0.123 rather than
|
|
689
|
+
* 0.33 … it is not a function of the backdrop's luminance" was read on a bed
|
|
690
|
+
* that had no backdrop between `hc-text` (linear 0.74) and `light-solid`
|
|
691
|
+
* (0.891): it IS a function of the backdrop's luminance, and the whole factor
|
|
692
|
+
* of 2.6 happens inside the gap the bed cannot see (claims §5.62 §5). The
|
|
693
|
+
* single amplitude becomes six anchors on two regimes — three in backdrop
|
|
694
|
+
* luminance below the knee, three in span above it — plus the lift's four
|
|
695
|
+
* constants. The three lengths are untouched; G0 re-read them free on both
|
|
696
|
+
* terms and they came back at W8's values.
|
|
617
697
|
*/
|
|
618
698
|
outerShadow: {
|
|
619
699
|
offsetPx: 7.95,
|
|
620
700
|
sigmaPx: 15.55,
|
|
621
701
|
spreadPx: 3.1,
|
|
622
|
-
|
|
623
|
-
|
|
702
|
+
thinOcclusionDark: 0,
|
|
703
|
+
thinOcclusionMid: 0.33,
|
|
704
|
+
thinOcclusionBright: 0.127,
|
|
705
|
+
thickOcclusionAt96: 0.37,
|
|
706
|
+
thickOcclusionAt128: 0.448,
|
|
707
|
+
thickOcclusionAt160: 0.479,
|
|
708
|
+
liftAmplitude: 0.01,
|
|
709
|
+
liftSpanMin: 64,
|
|
710
|
+
liftSpanFull: 118,
|
|
711
|
+
liftBlurSigmaCss: 40,
|
|
712
|
+
reducedTransparencyOcclusion: 0.197,
|
|
624
713
|
sizeGain: 0
|
|
625
714
|
},
|
|
626
715
|
lightDirection: [-0.3714, -0.9285],
|
|
@@ -641,9 +730,29 @@ var LENS_SIZE_GAIN_MAX = DEFAULT_MATERIAL_PROFILE.lensSizeGainMax;
|
|
|
641
730
|
var SIZE_SCATTER_GAIN_MAX = DEFAULT_MATERIAL_PROFILE.sizeScatterGainMax;
|
|
642
731
|
var SIZE_SCATTER_FLOOR = DEFAULT_MATERIAL_PROFILE.sizeScatterFloor;
|
|
643
732
|
var SIZE_SCATTER_SPAN_MAX = DEFAULT_MATERIAL_PROFILE.sizeScatterSpanMax;
|
|
733
|
+
var SIZE_SCATTER_GAIN_MAX_2X = DEFAULT_MATERIAL_PROFILE.sizeScatterGainMax2x;
|
|
734
|
+
var SIZE_SCATTER_GAIN_FAR_2X = DEFAULT_MATERIAL_PROFILE.sizeScatterGainFar2x;
|
|
735
|
+
var SIZE_SCATTER_FLOOR_2X = DEFAULT_MATERIAL_PROFILE.sizeScatterFloor2x;
|
|
736
|
+
var SIZE_SCATTER_SPAN_MAX_2X = DEFAULT_MATERIAL_PROFILE.sizeScatterSpanMax2x;
|
|
737
|
+
var SIZE_SCATTER_RAMP_START_THIN_1X = DEFAULT_MATERIAL_PROFILE.sizeScatterRampStartThin1x;
|
|
738
|
+
var SIZE_SCATTER_RAMP_START_THICK_1X = DEFAULT_MATERIAL_PROFILE.sizeScatterRampStartThick1x;
|
|
739
|
+
var SIZE_SCATTER_RAMP_START_THIN_2X = DEFAULT_MATERIAL_PROFILE.sizeScatterRampStartThin2x;
|
|
740
|
+
var SIZE_SCATTER_RAMP_START_THICK_2X = DEFAULT_MATERIAL_PROFILE.sizeScatterRampStartThick2x;
|
|
741
|
+
var SIZE_SCATTER_RAMP_START_FAR_1X = DEFAULT_MATERIAL_PROFILE.sizeScatterRampStartFar1x;
|
|
742
|
+
var SIZE_SCATTER_RAMP_START_FAR_2X = DEFAULT_MATERIAL_PROFILE.sizeScatterRampStartFar2x;
|
|
743
|
+
var SIZE_SCATTER_RAMP_REACH_1X_PX = DEFAULT_MATERIAL_PROFILE.sizeScatterRampReach1xPx;
|
|
744
|
+
var SIZE_SCATTER_RAMP_REACH_2X_PX = DEFAULT_MATERIAL_PROFILE.sizeScatterRampReach2xPx;
|
|
644
745
|
var SIZE_OCCLUSION_GAIN = DEFAULT_MATERIAL_PROFILE.sizeOcclusionGain;
|
|
645
746
|
var SIZE_SHADOW_GAIN_MAX = DEFAULT_MATERIAL_PROFILE.sizeShadowGainMax;
|
|
646
747
|
var LENS_REFRACTION_GAIN = DEFAULT_MATERIAL_PROFILE.lensRefractionGain;
|
|
748
|
+
var LENS_HEIGHT_PER_SPAN = DEFAULT_MATERIAL_PROFILE.lensHeightPerSpan;
|
|
749
|
+
var LENS_HEIGHT_MAX = DEFAULT_MATERIAL_PROFILE.lensHeightMax;
|
|
750
|
+
var LENS_AMOUNT_PER_SPAN = DEFAULT_MATERIAL_PROFILE.lensAmountPerSpan;
|
|
751
|
+
var LENS_AMOUNT_MAX = DEFAULT_MATERIAL_PROFILE.lensAmountMax;
|
|
752
|
+
var LENS_THICKNESS_REFERENCE = DEFAULT_MATERIAL_PROFILE.lensThicknessReference;
|
|
753
|
+
var LENS_EXTENT_GAIN = DEFAULT_MATERIAL_PROFILE.lensExtentGain;
|
|
754
|
+
var LENS_PROFILE_EXPONENT = DEFAULT_MATERIAL_PROFILE.lensProfileExponent;
|
|
755
|
+
var LENS_OVALIZATION = DEFAULT_MATERIAL_PROFILE.lensOvalization;
|
|
647
756
|
var BACKDROP_TONE_MAX = DEFAULT_MATERIAL_PROFILE.backdropToneMax;
|
|
648
757
|
var BACKDROP_TONE_LOW = DEFAULT_MATERIAL_PROFILE.backdropToneLow;
|
|
649
758
|
var BACKDROP_TONE_HIGH = DEFAULT_MATERIAL_PROFILE.backdropToneHigh;
|
|
@@ -653,7 +762,20 @@ DEFAULT_MATERIAL_PROFILE.backdropToneResponseThin;
|
|
|
653
762
|
DEFAULT_MATERIAL_PROFILE.backdropToneResponseThick;
|
|
654
763
|
DEFAULT_MATERIAL_PROFILE.backdropToneResponseStrength;
|
|
655
764
|
var OUTER_SHADOW = DEFAULT_MATERIAL_PROFILE.outerShadow;
|
|
765
|
+
var RETIRED_OUTER_SHADOW_LEAVES = {
|
|
766
|
+
occlusion: "the six amplitude anchors (thinOcclusionDark, thinOcclusionMid, thinOcclusionBright, thickOcclusionAt96, thickOcclusionAt128, thickOcclusionAt160) and liftAmplitude for the second term"
|
|
767
|
+
};
|
|
768
|
+
function rejectRetiredOuterShadowLeaves(patch) {
|
|
769
|
+
if (patch === void 0)
|
|
770
|
+
return;
|
|
771
|
+
for (const [leaf, replacement] of Object.entries(RETIRED_OUTER_SHADOW_LEAVES)) {
|
|
772
|
+
if (!(leaf in patch))
|
|
773
|
+
continue;
|
|
774
|
+
throw new Error(`outerShadow.${leaf} was retired by W14 G1 (claims \xA75.62) and is replaced by ${replacement}. Applying this patch would have rendered the default shadow while recording itself as configured. It is refused rather than mapped: a single span-flat amplitude is the material the measurement retired.`);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
656
777
|
function withMaterialOverrides(base, patch) {
|
|
778
|
+
rejectRetiredOuterShadowLeaves(patch.outerShadow);
|
|
657
779
|
const optics = {};
|
|
658
780
|
for (const variant of MATERIAL_VARIANTS) {
|
|
659
781
|
optics[variant] = { ...base.optics[variant], ...patch.optics?.[variant] };
|
|
@@ -675,9 +797,31 @@ function withMaterialOverrides(base, patch) {
|
|
|
675
797
|
sizeScatterGainMax: patch.sizeScatterGainMax ?? base.sizeScatterGainMax,
|
|
676
798
|
sizeScatterFloor: patch.sizeScatterFloor ?? base.sizeScatterFloor,
|
|
677
799
|
sizeScatterSpanMax: patch.sizeScatterSpanMax ?? base.sizeScatterSpanMax,
|
|
800
|
+
sizeScatterGainMax2x: patch.sizeScatterGainMax2x ?? base.sizeScatterGainMax2x,
|
|
801
|
+
sizeScatterFloor2x: patch.sizeScatterFloor2x ?? base.sizeScatterFloor2x,
|
|
802
|
+
sizeScatterSpanMax2x: patch.sizeScatterSpanMax2x ?? base.sizeScatterSpanMax2x,
|
|
803
|
+
sizeScatterGainFar2x: patch.sizeScatterGainFar2x ?? base.sizeScatterGainFar2x,
|
|
804
|
+
sizeScatterRampStartThin1x: patch.sizeScatterRampStartThin1x ?? base.sizeScatterRampStartThin1x,
|
|
805
|
+
sizeScatterRampStartThick1x: patch.sizeScatterRampStartThick1x ?? base.sizeScatterRampStartThick1x,
|
|
806
|
+
sizeScatterRampStartThin2x: patch.sizeScatterRampStartThin2x ?? base.sizeScatterRampStartThin2x,
|
|
807
|
+
sizeScatterRampStartThick2x: patch.sizeScatterRampStartThick2x ?? base.sizeScatterRampStartThick2x,
|
|
808
|
+
sizeScatterRampStartFar1x: patch.sizeScatterRampStartFar1x ?? base.sizeScatterRampStartFar1x,
|
|
809
|
+
sizeScatterRampStartFar2x: patch.sizeScatterRampStartFar2x ?? base.sizeScatterRampStartFar2x,
|
|
810
|
+
sizeScatterRampReach1xPx: patch.sizeScatterRampReach1xPx ?? base.sizeScatterRampReach1xPx,
|
|
811
|
+
sizeScatterRampReach2xPx: patch.sizeScatterRampReach2xPx ?? base.sizeScatterRampReach2xPx,
|
|
678
812
|
sizeOcclusionGain: patch.sizeOcclusionGain ?? base.sizeOcclusionGain,
|
|
679
813
|
sizeShadowGainMax: patch.sizeShadowGainMax ?? base.sizeShadowGainMax,
|
|
680
814
|
lensRefractionGain: patch.lensRefractionGain ?? base.lensRefractionGain,
|
|
815
|
+
lensHeightPerSpan: patch.lensHeightPerSpan ?? base.lensHeightPerSpan,
|
|
816
|
+
lensHeightMax: patch.lensHeightMax ?? base.lensHeightMax,
|
|
817
|
+
lensAmountPerSpan: patch.lensAmountPerSpan ?? base.lensAmountPerSpan,
|
|
818
|
+
lensAmountMax: patch.lensAmountMax ?? base.lensAmountMax,
|
|
819
|
+
lensThicknessReference: patch.lensThicknessReference ?? base.lensThicknessReference,
|
|
820
|
+
lensExtentGain: patch.lensExtentGain ?? base.lensExtentGain,
|
|
821
|
+
lensProfileExponent: patch.lensProfileExponent ?? base.lensProfileExponent,
|
|
822
|
+
lensOvalization: patch.lensOvalization ?? base.lensOvalization,
|
|
823
|
+
lensOvalizationSpanMin: patch.lensOvalizationSpanMin ?? base.lensOvalizationSpanMin,
|
|
824
|
+
lensOvalizationSpanMax: patch.lensOvalizationSpanMax ?? base.lensOvalizationSpanMax,
|
|
681
825
|
reducedTransparencyFrost: patch.reducedTransparencyFrost ?? base.reducedTransparencyFrost,
|
|
682
826
|
increasedOcclusionLift: patch.increasedOcclusionLift ?? base.increasedOcclusionLift,
|
|
683
827
|
strongBorderRim: { ...base.strongBorderRim, ...patch.strongBorderRim },
|
|
@@ -810,15 +954,122 @@ function lensSizeGain(spanPx, profile = DEFAULT_MATERIAL_PROFILE) {
|
|
|
810
954
|
function lensSizeGainFromThickness(thickness, profile = DEFAULT_MATERIAL_PROFILE) {
|
|
811
955
|
return 1 + (profile.lensSizeGainMax - 1) * thickness;
|
|
812
956
|
}
|
|
813
|
-
function
|
|
814
|
-
return
|
|
957
|
+
function shadowDepthPx(thicknessPx, spanPx, thickness, profile = DEFAULT_MATERIAL_PROFILE) {
|
|
958
|
+
return Math.min(Math.max(thicknessPx, 0) * lensSizeGainFromThickness(thickness, profile), spanPx * 0.5);
|
|
959
|
+
}
|
|
960
|
+
function lensHeightBasePx(spanPx, profile = DEFAULT_MATERIAL_PROFILE) {
|
|
961
|
+
return Math.min(profile.lensHeightPerSpan * Math.max(spanPx, 0), profile.lensHeightMax);
|
|
962
|
+
}
|
|
963
|
+
function lensAmountBasePx(spanPx, profile = DEFAULT_MATERIAL_PROFILE) {
|
|
964
|
+
return Math.min(profile.lensAmountPerSpan * Math.max(spanPx, 0), profile.lensAmountMax);
|
|
965
|
+
}
|
|
966
|
+
function lensOvalizationAt(spanPx, profile = DEFAULT_MATERIAL_PROFILE) {
|
|
967
|
+
return profile.lensOvalization * smoothstep(profile.lensOvalizationSpanMin, profile.lensOvalizationSpanMax, spanPx);
|
|
968
|
+
}
|
|
969
|
+
function lensMagnitudePx(thicknessPx, spanPx, profile = DEFAULT_MATERIAL_PROFILE, fold = 1) {
|
|
970
|
+
const thick = Math.max(thicknessPx, 0);
|
|
971
|
+
if (thick <= 0)
|
|
972
|
+
return 0;
|
|
973
|
+
const scale = thick / profile.lensThicknessReference;
|
|
974
|
+
const unclamped = thick + (lensHeightBasePx(spanPx, profile) * scale - thick) * fold;
|
|
975
|
+
const depth = lensDepthPx(thicknessPx, spanPx, profile, fold);
|
|
976
|
+
const amount = thick + (lensAmountBasePx(spanPx, profile) * scale - thick) * fold;
|
|
977
|
+
return profile.lensRefractionGain * amount * (unclamped > 0 ? depth / unclamped : 0);
|
|
978
|
+
}
|
|
979
|
+
function lensExtentPx(thicknessPx, spanPx, profile = DEFAULT_MATERIAL_PROFILE, fold = 1) {
|
|
980
|
+
return profile.lensExtentGain * lensDepthPx(thicknessPx, spanPx, profile, fold);
|
|
981
|
+
}
|
|
982
|
+
function lensDirection(normal, offset, half, ovalization) {
|
|
983
|
+
const a = Math.max(half[0], 1e-6);
|
|
984
|
+
const b = Math.max(half[1], 1e-6);
|
|
985
|
+
const ex = offset[0] / (a * a);
|
|
986
|
+
const ey = offset[1] / (b * b);
|
|
987
|
+
const r = Math.max(Math.hypot(offset[0] / a, offset[1] / b), 1e-6);
|
|
988
|
+
const scale = Math.min(a, b) / r;
|
|
989
|
+
const gx = (1 - ovalization) * normal[0] + ovalization * ex * scale;
|
|
990
|
+
const gy = (1 - ovalization) * normal[1] + ovalization * ey * scale;
|
|
991
|
+
const len = Math.hypot(gx, gy);
|
|
992
|
+
return len > 1e-9 ? [gx / len, gy / len] : [normal[0], normal[1]];
|
|
993
|
+
}
|
|
994
|
+
function sizeScatterSigma(sigmaPx, spanPx, profile = DEFAULT_MATERIAL_PROFILE, devicePixelRatio = 1, extentsCssPx) {
|
|
995
|
+
return sizeScatterSigmaAt(sigmaPx, scatterThickness(spanPx, 1, profile, devicePixelRatio, extentsCssPx), profile, devicePixelRatio, spanPx);
|
|
996
|
+
}
|
|
997
|
+
function scatterRampStart(devicePixelRatio, profile = DEFAULT_MATERIAL_PROFILE, spanPx = 0) {
|
|
998
|
+
const thin = rampAtScale(profile.sizeScatterRampStartThin1x, profile.sizeScatterRampStartThin2x, devicePixelRatio);
|
|
999
|
+
const thick = rampAtScale(profile.sizeScatterRampStartThick1x, profile.sizeScatterRampStartThick2x, devicePixelRatio);
|
|
1000
|
+
const far = rampAtScale(profile.sizeScatterRampStartFar1x, profile.sizeScatterRampStartFar2x, devicePixelRatio);
|
|
1001
|
+
const decline = smoothstep(profile.sizeSpanMax, scatterSpanMaxAtScale(profile, devicePixelRatio), spanPx);
|
|
1002
|
+
return thin + (thick - thin) * sizeThickness(spanPx, profile) + (far - thick) * decline;
|
|
1003
|
+
}
|
|
1004
|
+
function scatterRampReachDevicePx(devicePixelRatio, profile = DEFAULT_MATERIAL_PROFILE) {
|
|
1005
|
+
return rampAtScale(profile.sizeScatterRampReach1xPx, profile.sizeScatterRampReach2xPx, devicePixelRatio);
|
|
1006
|
+
}
|
|
1007
|
+
function rampAtScale(at1x, at2x, devicePixelRatio) {
|
|
1008
|
+
const t = devicePixelRatio - 1;
|
|
1009
|
+
if (t <= 0)
|
|
1010
|
+
return at1x;
|
|
1011
|
+
if (t >= 1)
|
|
1012
|
+
return at2x;
|
|
1013
|
+
return at1x + (at2x - at1x) * t;
|
|
1014
|
+
}
|
|
1015
|
+
function scatterGainAtScale(profile = DEFAULT_MATERIAL_PROFILE, devicePixelRatio = 1) {
|
|
1016
|
+
return rampAtScale(profile.sizeScatterGainMax, profile.sizeScatterGainMax2x, devicePixelRatio);
|
|
1017
|
+
}
|
|
1018
|
+
function scatterGainFarAtScale(profile = DEFAULT_MATERIAL_PROFILE, devicePixelRatio = 1) {
|
|
1019
|
+
return rampAtScale(profile.sizeScatterGainMax, profile.sizeScatterGainFar2x, devicePixelRatio);
|
|
1020
|
+
}
|
|
1021
|
+
function scatterGainAt(spanPx, profile = DEFAULT_MATERIAL_PROFILE, devicePixelRatio = 1) {
|
|
1022
|
+
const near = scatterGainAtScale(profile, devicePixelRatio);
|
|
1023
|
+
const far = scatterGainFarAtScale(profile, devicePixelRatio);
|
|
1024
|
+
if (far === near)
|
|
1025
|
+
return near;
|
|
1026
|
+
return near + (far - near) * smoothstep(profile.sizeSpanMax, scatterSpanMaxAtScale(profile, devicePixelRatio), spanPx);
|
|
815
1027
|
}
|
|
816
|
-
function
|
|
817
|
-
|
|
818
|
-
return floor + (1 - floor) * smoothstep(profile.sizeSpanMin, profile.sizeScatterSpanMax, spanPx) * fold;
|
|
1028
|
+
function scatterFloorAtScale(profile = DEFAULT_MATERIAL_PROFILE, devicePixelRatio = 1) {
|
|
1029
|
+
return clampUnit(rampAtScale(profile.sizeScatterFloor, profile.sizeScatterFloor2x, devicePixelRatio));
|
|
819
1030
|
}
|
|
820
|
-
function
|
|
821
|
-
return
|
|
1031
|
+
function scatterSpanMaxAtScale(profile = DEFAULT_MATERIAL_PROFILE, devicePixelRatio = 1) {
|
|
1032
|
+
return rampAtScale(profile.sizeScatterSpanMax, profile.sizeScatterSpanMax2x, devicePixelRatio);
|
|
1033
|
+
}
|
|
1034
|
+
function scatterDeepThickness(spanPx, profile = DEFAULT_MATERIAL_PROFILE, devicePixelRatio = 1) {
|
|
1035
|
+
const floor = scatterFloorAtScale(profile, devicePixelRatio);
|
|
1036
|
+
return floor + (1 - floor) * smoothstep(profile.sizeSpanMin, scatterSpanMaxAtScale(profile, devicePixelRatio), spanPx);
|
|
1037
|
+
}
|
|
1038
|
+
function scatterSharpShare(uDevicePx, devicePixelRatio, profile = DEFAULT_MATERIAL_PROFILE, spanPx = 0) {
|
|
1039
|
+
const deepSharp = 1 - scatterDeepThickness(spanPx, profile, devicePixelRatio);
|
|
1040
|
+
const start = scatterRampStart(devicePixelRatio, profile, spanPx);
|
|
1041
|
+
const reach = Math.max(scatterRampReachDevicePx(devicePixelRatio, profile), 1e-6);
|
|
1042
|
+
const excursion = Math.max(start - deepSharp, 0) * Math.max(1 - Math.max(uDevicePx, 0) / reach, 0);
|
|
1043
|
+
return clampUnit(deepSharp + excursion);
|
|
1044
|
+
}
|
|
1045
|
+
function scatterThickness(spanPx, fold, profile = DEFAULT_MATERIAL_PROFILE, devicePixelRatio = 1, extentsCssPx) {
|
|
1046
|
+
const floor = scatterFloorAtScale(profile, devicePixelRatio);
|
|
1047
|
+
const mean = scatterRampAreaMean(spanPx, profile, devicePixelRatio, extentsCssPx);
|
|
1048
|
+
return clampUnit(floor + (mean - floor) * fold);
|
|
1049
|
+
}
|
|
1050
|
+
function scatterRampAreaMean(spanPx, profile = DEFAULT_MATERIAL_PROFILE, devicePixelRatio = 1, extentsCssPx) {
|
|
1051
|
+
const deep = scatterDeepThickness(spanPx, profile, devicePixelRatio);
|
|
1052
|
+
const amplitude = Math.max(scatterRampStart(devicePixelRatio, profile, spanPx) - (1 - deep), 0);
|
|
1053
|
+
if (amplitude <= 0)
|
|
1054
|
+
return clampUnit(deep);
|
|
1055
|
+
const width = Math.max(extentsCssPx?.[0] ?? spanPx, 0);
|
|
1056
|
+
const height = Math.max(extentsCssPx?.[1] ?? spanPx, 0);
|
|
1057
|
+
const area = width * height;
|
|
1058
|
+
if (area <= 0)
|
|
1059
|
+
return clampUnit(deep - amplitude);
|
|
1060
|
+
const reach = Math.max(scatterRampReachDevicePx(devicePixelRatio, profile) / Math.max(devicePixelRatio, 1e-3), 1e-6);
|
|
1061
|
+
const perimeter = 2 * (width + height);
|
|
1062
|
+
const limit = Math.min(reach, Math.min(width, height) / 2);
|
|
1063
|
+
const triangleMean = (perimeter * limit - 4 * limit * limit - perimeter * limit * limit / (2 * reach) + 8 * limit * limit * limit / (3 * reach)) / area;
|
|
1064
|
+
return clampUnit(deep - amplitude * triangleMean);
|
|
1065
|
+
}
|
|
1066
|
+
function sizeScatterSigmaAt(sigmaPx, scatter, profile = DEFAULT_MATERIAL_PROFILE, devicePixelRatio = 1, spanPx) {
|
|
1067
|
+
const mix = clampUnit(scatter);
|
|
1068
|
+
const gain = spanPx === void 0 ? scatterGainAtScale(profile, devicePixelRatio) : scatterGainAt(spanPx, profile, devicePixelRatio);
|
|
1069
|
+
return sigmaPx * (1 + (gain - 1) * mix);
|
|
1070
|
+
}
|
|
1071
|
+
function clampUnit(value) {
|
|
1072
|
+
return Math.min(1, Math.max(0, value));
|
|
822
1073
|
}
|
|
823
1074
|
function sizeOcclusionAlpha(alpha, spanPx, profile = DEFAULT_MATERIAL_PROFILE) {
|
|
824
1075
|
return sizeOcclusionAlphaAt(alpha, sizeThickness(spanPx, profile), profile);
|
|
@@ -838,15 +1089,79 @@ function sizeOuterShadowOcclusion(occlusion, spanPx, profile = DEFAULT_MATERIAL_
|
|
|
838
1089
|
function sizeOuterShadowOcclusionAt(occlusion, thickness, profile = DEFAULT_MATERIAL_PROFILE) {
|
|
839
1090
|
return Math.min(1, occlusion + profile.outerShadow.sizeGain * thickness * (1 - occlusion));
|
|
840
1091
|
}
|
|
1092
|
+
var OUTER_SHADOW_THIN_L = {
|
|
1093
|
+
/** At and below this backdrop luminance the black term is `thinOcclusionDark`. */
|
|
1094
|
+
inert: 0.02,
|
|
1095
|
+
/** From here the mid plateau holds. */
|
|
1096
|
+
midFrom: 0.06,
|
|
1097
|
+
/** To here — `hc-text`'s own linear luminance. */
|
|
1098
|
+
midTo: 0.74,
|
|
1099
|
+
/** `light-solid`'s linear luminance, where 0.127 was measured; held above. */
|
|
1100
|
+
bright: 0.891
|
|
1101
|
+
};
|
|
1102
|
+
var OUTER_SHADOW_UNMEASURED_BACKDROP_LUMINANCE = 0.3;
|
|
1103
|
+
function outerShadowThinOcclusion(backdropLuminance, shadow) {
|
|
1104
|
+
const l = backdropLuminance ?? OUTER_SHADOW_UNMEASURED_BACKDROP_LUMINANCE;
|
|
1105
|
+
const { inert, midFrom, midTo, bright } = OUTER_SHADOW_THIN_L;
|
|
1106
|
+
if (l <= inert)
|
|
1107
|
+
return shadow.thinOcclusionDark;
|
|
1108
|
+
if (l < midFrom) {
|
|
1109
|
+
const t2 = (l - inert) / (midFrom - inert);
|
|
1110
|
+
const s = t2 * t2 * (3 - 2 * t2);
|
|
1111
|
+
return shadow.thinOcclusionDark + (shadow.thinOcclusionMid - shadow.thinOcclusionDark) * s;
|
|
1112
|
+
}
|
|
1113
|
+
if (l <= midTo)
|
|
1114
|
+
return shadow.thinOcclusionMid;
|
|
1115
|
+
if (l >= bright)
|
|
1116
|
+
return shadow.thinOcclusionBright;
|
|
1117
|
+
const t = (l - midTo) / (bright - midTo);
|
|
1118
|
+
return shadow.thinOcclusionMid + (shadow.thinOcclusionBright - shadow.thinOcclusionMid) * t;
|
|
1119
|
+
}
|
|
1120
|
+
var OUTER_SHADOW_THICK_SPANS = [96, 128, 160];
|
|
1121
|
+
function outerShadowThickOcclusion(spanPx, shadow) {
|
|
1122
|
+
const [s0, s1, s2] = OUTER_SHADOW_THICK_SPANS;
|
|
1123
|
+
const y0 = shadow.thickOcclusionAt96;
|
|
1124
|
+
const y1 = shadow.thickOcclusionAt128;
|
|
1125
|
+
const y2 = shadow.thickOcclusionAt160;
|
|
1126
|
+
if (spanPx <= s0)
|
|
1127
|
+
return y0;
|
|
1128
|
+
if (spanPx >= s2)
|
|
1129
|
+
return y2;
|
|
1130
|
+
if (spanPx <= s1)
|
|
1131
|
+
return y0 + (y1 - y0) * (spanPx - s0) / (s1 - s0);
|
|
1132
|
+
return y1 + (y2 - y1) * (spanPx - s1) / (s2 - s1);
|
|
1133
|
+
}
|
|
1134
|
+
function outerShadowOcclusionAt(shadow, backdropLuminance, spanPx, thickness, profile = DEFAULT_MATERIAL_PROFILE) {
|
|
1135
|
+
const thin = outerShadowThinOcclusion(backdropLuminance, shadow);
|
|
1136
|
+
const thick = outerShadowThickOcclusion(spanPx, shadow);
|
|
1137
|
+
const k = Math.min(1, Math.max(0, thickness));
|
|
1138
|
+
const blend = k * k * (3 - 2 * k);
|
|
1139
|
+
return sizeOuterShadowOcclusionAt(thin + (thick - thin) * blend, thickness, profile);
|
|
1140
|
+
}
|
|
1141
|
+
function outerShadowLiftRise(spanPx, shadow) {
|
|
1142
|
+
return smoothstep(shadow.liftSpanMin, shadow.liftSpanFull, spanPx);
|
|
1143
|
+
}
|
|
841
1144
|
function outerShadowUnderPolicy(policy, profile = DEFAULT_MATERIAL_PROFILE) {
|
|
842
1145
|
const shadow = profile.outerShadow;
|
|
843
1146
|
if (policy.glass === "none" || policy.frost === "none")
|
|
844
|
-
return
|
|
1147
|
+
return flatOuterShadow(shadow, 0);
|
|
845
1148
|
if (policy.frost === "increased") {
|
|
846
|
-
return
|
|
1149
|
+
return flatOuterShadow(shadow, shadow.reducedTransparencyOcclusion);
|
|
847
1150
|
}
|
|
848
1151
|
return shadow;
|
|
849
1152
|
}
|
|
1153
|
+
function flatOuterShadow(shadow, amplitude) {
|
|
1154
|
+
return {
|
|
1155
|
+
...shadow,
|
|
1156
|
+
thinOcclusionDark: amplitude,
|
|
1157
|
+
thinOcclusionMid: amplitude,
|
|
1158
|
+
thinOcclusionBright: amplitude,
|
|
1159
|
+
thickOcclusionAt96: amplitude,
|
|
1160
|
+
thickOcclusionAt128: amplitude,
|
|
1161
|
+
thickOcclusionAt160: amplitude,
|
|
1162
|
+
liftAmplitude: 0
|
|
1163
|
+
};
|
|
1164
|
+
}
|
|
850
1165
|
var SRGB_ENCODING_EXPONENT = 2.4;
|
|
851
1166
|
function outerShadowFalloff(signedDistancePx, sigmaPx) {
|
|
852
1167
|
const x = -signedDistancePx / Math.max(sigmaPx, 1e-4);
|
|
@@ -856,8 +1171,8 @@ function outerShadowAlpha(occlusion) {
|
|
|
856
1171
|
const occ = Math.min(1, Math.max(0, occlusion));
|
|
857
1172
|
return 1 - Math.pow(1 - occ, 1 / SRGB_ENCODING_EXPONENT);
|
|
858
1173
|
}
|
|
859
|
-
function outerShadowReachPx(shadow) {
|
|
860
|
-
const alpha = outerShadowAlpha(
|
|
1174
|
+
function outerShadowReachPx(shadow, occlusion) {
|
|
1175
|
+
const alpha = outerShadowAlpha(occlusion);
|
|
861
1176
|
if (!(alpha > 0))
|
|
862
1177
|
return 0;
|
|
863
1178
|
const cutoff = 1 / 255 / alpha;
|
|
@@ -875,15 +1190,18 @@ function outerShadowReachPx(shadow) {
|
|
|
875
1190
|
}
|
|
876
1191
|
return Math.max(0, hi + Math.abs(shadow.offsetPx) + shadow.spreadPx);
|
|
877
1192
|
}
|
|
878
|
-
function lensDepthPx(thicknessPx, spanPx, profile = DEFAULT_MATERIAL_PROFILE) {
|
|
879
|
-
const
|
|
880
|
-
|
|
1193
|
+
function lensDepthPx(thicknessPx, spanPx, profile = DEFAULT_MATERIAL_PROFILE, fold = 1) {
|
|
1194
|
+
const thick = Math.max(thicknessPx, 0);
|
|
1195
|
+
const scale = thick / profile.lensThicknessReference;
|
|
1196
|
+
const depth = thick + (lensHeightBasePx(spanPx, profile) * scale - thick) * fold;
|
|
1197
|
+
return Math.min(Math.max(depth, 0), spanPx * 0.5);
|
|
881
1198
|
}
|
|
882
|
-
function lensDisplacementPx(depthPx,
|
|
883
|
-
|
|
1199
|
+
function lensDisplacementPx(depthPx, spanPx, thicknessPx, refractionScale, profile = DEFAULT_MATERIAL_PROFILE, fold = 1) {
|
|
1200
|
+
const extent = lensExtentPx(thicknessPx, spanPx, profile, fold);
|
|
1201
|
+
if (extent <= 0)
|
|
884
1202
|
return 0;
|
|
885
|
-
const
|
|
886
|
-
return
|
|
1203
|
+
const t = Math.max(0, 1 - Math.max(depthPx, 0) / extent);
|
|
1204
|
+
return lensMagnitudePx(thicknessPx, spanPx, profile, fold) * Math.pow(t, profile.lensProfileExponent) * refractionScale;
|
|
887
1205
|
}
|
|
888
1206
|
|
|
889
1207
|
// ../renderer-webgpu/dist/analysis.js
|
|
@@ -1721,7 +2039,8 @@ var poolKey = {
|
|
|
1721
2039
|
backdropBodyScratch: (sourceId) => `backdrop:${sourceId}:body-scratch`,
|
|
1722
2040
|
backdropUpload: (sourceId) => `backdrop:${sourceId}:upload`,
|
|
1723
2041
|
groupField: (groupId) => `group:${groupId}:field`,
|
|
1724
|
-
groupAux: (groupId) => `group:${groupId}:aux
|
|
2042
|
+
groupAux: (groupId) => `group:${groupId}:aux`,
|
|
2043
|
+
groupAux2: (groupId) => `group:${groupId}:aux2`
|
|
1725
2044
|
};
|
|
1726
2045
|
|
|
1727
2046
|
// ../renderer-webgpu/dist/gpu-context.js
|
|
@@ -1878,7 +2197,7 @@ function resolveSurfaces(group, family, profile = DEFAULT_MATERIAL_PROFILE, poli
|
|
|
1878
2197
|
centre: [fieldSource.channels.center[0], fieldSource.channels.center[1]],
|
|
1879
2198
|
channels,
|
|
1880
2199
|
spanPx,
|
|
1881
|
-
lensDepthPx:
|
|
2200
|
+
lensDepthPx: lensDepthPx(shape.channels.thickness, spanPx, profile, profile.refractionScale[accessibilityRefractionCap(policy)]),
|
|
1882
2201
|
sizeThickness: thickness,
|
|
1883
2202
|
tintStrength: Math.min(1, Math.max(0, surface.tint?.strength ?? 0))
|
|
1884
2203
|
};
|
|
@@ -1943,7 +2262,7 @@ function packInstances(surfaces, origin, into) {
|
|
|
1943
2262
|
data[o + 11] = s.shape.channels.thickness;
|
|
1944
2263
|
data[o + 12] = s.channels.press;
|
|
1945
2264
|
data[o + 13] = s.channels.glow;
|
|
1946
|
-
data[o + 14] = s.
|
|
2265
|
+
data[o + 14] = Math.max(s.shape.channels.thickness, 0) * Math.min(1, Math.max(0, s.channels.lensStrength));
|
|
1947
2266
|
data[o + 15] = s.tintStrength;
|
|
1948
2267
|
data[o + 16] = s.spanPx;
|
|
1949
2268
|
data[o + 17] = 0;
|
|
@@ -2221,7 +2540,7 @@ var WGSL_INSTANCE_STRUCT = `struct Instance {
|
|
|
2221
2540
|
thick : f32, // 44 material thickness, CSS px
|
|
2222
2541
|
press : f32, // 48 interaction channel value, 0..1
|
|
2223
2542
|
glow : f32, // 52 interaction channel value, 0..1
|
|
2224
|
-
|
|
2543
|
+
lensThick : f32, // 56 authored thickness in CSS px times the lensStrength channel (W12 G2): both depths are evaluated from it per pixel
|
|
2225
2544
|
tintK : f32, // 60 author tint strength, 0..1 \u2014 the seed itself is a group uniform
|
|
2226
2545
|
span : f32, // 64 the surface's shorter extent, CSS px \u2014 the size law's input, both curves (W11c)
|
|
2227
2546
|
_pad : f32, // 68 stride padding: vec2f aligns the struct to 8, so 72 is the next legal size
|
|
@@ -2389,7 +2708,8 @@ var WGSL_FIELD_PASS = `struct FieldUniforms {
|
|
|
2389
2708
|
// field but its members are not one size, and carrying them per pixel is what
|
|
2390
2709
|
// lets a 40 px button and a 320 px platter share a field pass and still lens by
|
|
2391
2710
|
// their own depth \u2014 parent acceptance #2 inside a GlassEffectContainer.
|
|
2392
|
-
// aux
|
|
2711
|
+
// aux = (lensThickPx, glow, spanPx, tintStrength)
|
|
2712
|
+
// aux2 = (p \u2212 centre, half.x, half.y) \u2014 W12 G2, group-local CSS px
|
|
2393
2713
|
//
|
|
2394
2714
|
// The third and fourth slots both changed hands, and neither of the values they
|
|
2395
2715
|
// used to carry was ever read by a fragment stage. 'thick' left because
|
|
@@ -2423,14 +2743,25 @@ var WGSL_FIELD_PASS = `struct FieldUniforms {
|
|
|
2423
2743
|
// their SPANS and evaluates the curves on the blend, where it used to blend the
|
|
2424
2744
|
// evaluated thicknesses. Inside either member the two are identical; only the
|
|
2425
2745
|
// smooth-min neck differs, and nothing measured claims a value there.
|
|
2746
|
+
//
|
|
2747
|
+
// 'aux2' (W12 G2) carries the pixel's offset from the owning surface's centre
|
|
2748
|
+
// and that surface's half-extents, which
|
|
2749
|
+
// the optics pass needs for the lens's direction: the reference displaces along
|
|
2750
|
+
// the gradient of its SDF blended toward the oval inscribed in the shape's box
|
|
2751
|
+
// (claims \xA75.50's 'gradientOvalization'), and an oval is a property of the box,
|
|
2752
|
+
// not of the distance field. Blended by the same weight as 'aux' in a neck, so
|
|
2753
|
+
// two merged members share one oval there \u2014 a value nothing measured either.
|
|
2754
|
+
// For a concentric child the box is the parent's (the child is the parent's
|
|
2755
|
+
// level set), which is the same centre and a slightly larger oval.
|
|
2426
2756
|
|
|
2427
2757
|
@group(0) @binding(0) var<uniform> fu : FieldUniforms;
|
|
2428
2758
|
@group(0) @binding(1) var<storage, read> instances : array<Instance>;
|
|
2429
2759
|
|
|
2430
2760
|
struct Member {
|
|
2431
|
-
d
|
|
2432
|
-
g
|
|
2433
|
-
aux
|
|
2761
|
+
d : f32,
|
|
2762
|
+
g : vec2f,
|
|
2763
|
+
aux : vec4f,
|
|
2764
|
+
aux2 : vec4f,
|
|
2434
2765
|
};
|
|
2435
2766
|
|
|
2436
2767
|
fn eval_instance(i : u32, p : vec2f) -> Member {
|
|
@@ -2443,13 +2774,17 @@ fn eval_instance(i : u32, p : vec2f) -> Member {
|
|
|
2443
2774
|
var m : Member;
|
|
2444
2775
|
m.d = f.d + s.inset;
|
|
2445
2776
|
m.g = f.g;
|
|
2446
|
-
m.aux = vec4f(s.
|
|
2777
|
+
m.aux = vec4f(s.lensThick, s.glow, s.span, s.tintK);
|
|
2778
|
+
// The pixel relative to the surface's centre, and the half-extents: what the
|
|
2779
|
+
// oval's gradient needs, written here where the group-local position is exact.
|
|
2780
|
+
m.aux2 = vec4f(p - s.centre, s.half);
|
|
2447
2781
|
return m;
|
|
2448
2782
|
}
|
|
2449
2783
|
|
|
2450
2784
|
struct FieldOut {
|
|
2451
2785
|
@location(0) field : vec4f,
|
|
2452
2786
|
@location(1) aux : vec4f,
|
|
2787
|
+
@location(2) aux2 : vec4f,
|
|
2453
2788
|
};
|
|
2454
2789
|
|
|
2455
2790
|
@fragment
|
|
@@ -2467,6 +2802,7 @@ fn fs_field(in : FullscreenOut) -> FieldOut {
|
|
|
2467
2802
|
// coverage downstream is not reliably zero.
|
|
2468
2803
|
out.field = vec4f(65000.0, 0.0, -1.0, 0.0);
|
|
2469
2804
|
out.aux = vec4f(0.0);
|
|
2805
|
+
out.aux2 = vec4f(0.0);
|
|
2470
2806
|
return out;
|
|
2471
2807
|
}
|
|
2472
2808
|
|
|
@@ -2479,6 +2815,7 @@ fn fs_field(in : FullscreenOut) -> FieldOut {
|
|
|
2479
2815
|
acc.d = blend.x;
|
|
2480
2816
|
acc.g = mix(s.g, acc.g, h);
|
|
2481
2817
|
acc.aux = mix(s.aux, acc.aux, h);
|
|
2818
|
+
acc.aux2 = mix(s.aux2, acc.aux2, h);
|
|
2482
2819
|
nearest = min(nearest, s.d);
|
|
2483
2820
|
}
|
|
2484
2821
|
// One clamp at the end of the fold, so |union - min| <= maxBulge holds for any
|
|
@@ -2491,6 +2828,7 @@ fn fs_field(in : FullscreenOut) -> FieldOut {
|
|
|
2491
2828
|
|
|
2492
2829
|
out.field = vec4f(acc.d, normal.x, normal.y, coverage);
|
|
2493
2830
|
out.aux = acc.aux;
|
|
2831
|
+
out.aux2 = acc.aux2;
|
|
2494
2832
|
return out;
|
|
2495
2833
|
}`;
|
|
2496
2834
|
function fieldPassSource(family) {
|
|
@@ -2646,7 +2984,9 @@ var WGSL_OPTICS_PASS = `struct OpticsUniforms {
|
|
|
2646
2984
|
screen : vec4f,
|
|
2647
2985
|
/// backdrop uv transform on viewport-normalised coords: scale (xy), offset (zw)
|
|
2648
2986
|
fit : vec4f,
|
|
2649
|
-
/// refractionScale, lensRefractionGain (
|
|
2987
|
+
/// refractionScale, lensRefractionGain (W12 G2: the gain on the reference's
|
|
2988
|
+
/// amount law), the inner shadow's depth gain (W2's lensSizeGainMax, which the
|
|
2989
|
+
/// shadow keeps), chainMaxLod
|
|
2650
2990
|
lens : vec4f,
|
|
2651
2991
|
/// fixed tint colour, linear light (xyz), tint alpha (w)
|
|
2652
2992
|
tint : vec4f,
|
|
@@ -2665,7 +3005,8 @@ var WGSL_OPTICS_PASS = `struct OpticsUniforms {
|
|
|
2665
3005
|
light : vec4f,
|
|
2666
3006
|
/// hasBackdrop, fieldSize.xy, fieldUpsampled
|
|
2667
3007
|
flags : vec4f,
|
|
2668
|
-
/// the size law's gains (W2): scatterGainMax
|
|
3008
|
+
/// the size law's gains (W2): scatterGainMax \u2014 resolved at the group's device
|
|
3009
|
+
/// ratio since W15 G1, claims 5.69 section 1 \u2014 occlusionGain, shadowGainMax,
|
|
2669
3010
|
/// bodyChainLod \u2014 the last being the chain level whose blur already matches the
|
|
2670
3011
|
/// body texture, which is what the scattering term measures its octaves from
|
|
2671
3012
|
size : vec4f,
|
|
@@ -2677,17 +3018,27 @@ var WGSL_OPTICS_PASS = `struct OpticsUniforms {
|
|
|
2677
3018
|
toneAdapt : vec4f,
|
|
2678
3019
|
/// the backdrop source's own average colour, linear (xyz), and its luminance (w)
|
|
2679
3020
|
toneColour : vec4f,
|
|
2680
|
-
/// the outer shadow (W8
|
|
2681
|
-
///
|
|
2682
|
-
///
|
|
2683
|
-
///
|
|
2684
|
-
///
|
|
2685
|
-
///
|
|
3021
|
+
/// the outer shadow (W8, amplitude re-read by W14 G1): the THIN regime's peak
|
|
3022
|
+
/// LINEAR occlusion, already resolved at this group's backdrop luminance and
|
|
3023
|
+
/// already folded under the accessibility policy (x) \u2014 a linear occlusion and
|
|
3024
|
+
/// no longer a compositing alpha, because the shader now blends two regimes
|
|
3025
|
+
/// before it converts; the blur's sigma in group-local CSS px (y), the
|
|
3026
|
+
/// silhouette's outward spread in the same units (z), and the downward offset
|
|
3027
|
+
/// expressed in field-texture UV (w) \u2014 a UV so the offset silhouette is one
|
|
3028
|
+
/// texture read rather than a gradient extrapolation, which is exact only on a
|
|
3029
|
+
/// straight edge and a capsule is mostly not one
|
|
2686
3030
|
shadow : vec4f,
|
|
2687
3031
|
/// the outer shadow's size-law gain (x), read against the casting surface's own
|
|
2688
3032
|
/// thickness, and the field rect's height in CSS px (y) \u2014 the conversion the
|
|
2689
|
-
/// shadow's shift needs when it lands outside the texture;
|
|
2690
|
-
///
|
|
3033
|
+
/// shadow's shift needs when it lands outside the texture; the body depth
|
|
3034
|
+
/// ramp's THICK start (z, W13 G1's third form \u2014 the thin end is scatter.y and
|
|
3035
|
+
/// this pass mixes the two by the pixel's own sizeThickness), and (w) the
|
|
3036
|
+
/// heavy width's gain at the TOP of the scatter span curve, resolved at this
|
|
3037
|
+
/// group's device ratio (W15 G1's re-form, claims \xA75.70 \xA74 and \xA77) \u2014 the
|
|
3038
|
+
/// padding slot this vec4's alignment already required, taken because the
|
|
3039
|
+
/// gain's near end (size.x) has no free neighbour and this vec4 already
|
|
3040
|
+
/// carries the body ramp's other span-graded end; at dpr 1 it equals size.x
|
|
3041
|
+
/// and the grading is flat
|
|
2691
3042
|
shadowSize : vec4f,
|
|
2692
3043
|
/// the backdrop tone response's anchors (W9): the three solid anchors'
|
|
2693
3044
|
/// ENCODED-space means (xyz), and the backdrop's linear-space mean (w) \u2014 the
|
|
@@ -2699,10 +3050,45 @@ var WGSL_OPTICS_PASS = `struct OpticsUniforms {
|
|
|
2699
3050
|
toneRowThin : vec4f,
|
|
2700
3051
|
/// the thick row (sizeThickness saturated), same layout
|
|
2701
3052
|
toneRowThick : vec4f,
|
|
2702
|
-
/// the size law's bands
|
|
2703
|
-
///
|
|
2704
|
-
///
|
|
3053
|
+
/// the size law's bands: the scatter facet's floor (x, resolved at the
|
|
3054
|
+
/// group's device ratio since W15 G1 \u2014 the deep value is per-scale, claims
|
|
3055
|
+
/// 5.69 section 2 \u2014 and read here for both the deep curve and the fold) and
|
|
3056
|
+
/// the depth ramp's
|
|
3057
|
+
/// THIN start s0 at the ratio this group draws at (y, W13 G1 \u2014 the CPU has
|
|
3058
|
+
/// already interpolated it between the profile's 1x and 2x anchors; the thick
|
|
3059
|
+
/// end is shadowSize.z), and the
|
|
3060
|
+
/// thickness curve's sizeSpanMin (z) and sizeSpanMax (w); the fold both
|
|
3061
|
+
/// multiply by is tone.w. The scatter span curve's own band top is lensOval.w,
|
|
3062
|
+
/// and it starts at the same z.
|
|
2705
3063
|
scatter : vec4f,
|
|
3064
|
+
/// the lens law (W12 G2): the reference's height law (lensHeightPerSpan,
|
|
3065
|
+
/// lensHeightMax) and amount law (lensAmountPerSpan, lensAmountMax)
|
|
3066
|
+
lensLaw : vec4f,
|
|
3067
|
+
/// the lens profile (W12 G2): lensExtentGain, lensProfileExponent,
|
|
3068
|
+
/// lensOvalization, lensThicknessReference
|
|
3069
|
+
lensShape : vec4f,
|
|
3070
|
+
/// the ovalization's knee (W12 G2): spanMin (x), spanMax (y); the body depth
|
|
3071
|
+
/// ramp's reach in group-local CSS px (z, W13 G1) \u2014 the profile names it in
|
|
3072
|
+
/// device px and the CPU divides by the ratio, so that this shader can read
|
|
3073
|
+
/// the field's own CSS-px depth without a second conversion; the scatter span
|
|
3074
|
+
/// curve's band top sizeScatterSpanMax (w, W11c, resolved at the group's
|
|
3075
|
+
/// device ratio since W15 G1), which W13 G1 keeps underneath the ramp as its
|
|
3076
|
+
/// deep value
|
|
3077
|
+
lensOval : vec4f,
|
|
3078
|
+
/// the outer shadow's THICK regime (W14 G1): the composite's peak linear
|
|
3079
|
+
/// occlusion at casting spans 96, 128 and 160 CSS px (xyz), already folded
|
|
3080
|
+
/// under the accessibility policy, piecewise-linear between and held outside;
|
|
3081
|
+
/// and (w) the body depth ramp's FAR start (W13 G1's fourth form) \u2014 the start
|
|
3082
|
+
/// at span >= sizeScatterSpanMax, which the ramp declines to from the thick
|
|
3083
|
+
/// end along the scatter span curve, in the slot W14 left free
|
|
3084
|
+
shadowThick : vec4f,
|
|
3085
|
+
/// the lift (W14 G1), the shadow's second term and GPU-tier only: its peak
|
|
3086
|
+
/// amplitude in LINEAR light as a fraction of the blurred backdrop's own
|
|
3087
|
+
/// luminance (x), the span rise's foot and top in CSS px (yz), and the chain
|
|
3088
|
+
/// LOD whose blur is the profile's liftBlurSigmaCss \u2014 resolved on the CPU,
|
|
3089
|
+
/// which is the only place the CSS-px-to-texel conversion is knowable, and
|
|
3090
|
+
/// already clamped to chainMaxLod (w)
|
|
3091
|
+
shadowLift : vec4f,
|
|
2706
3092
|
};
|
|
2707
3093
|
|
|
2708
3094
|
@group(0) @binding(0) var<uniform> ou : OpticsUniforms;
|
|
@@ -2712,6 +3098,9 @@ var WGSL_OPTICS_PASS = `struct OpticsUniforms {
|
|
|
2712
3098
|
@group(0) @binding(4) var backdropChain : texture_2d<f32>;
|
|
2713
3099
|
@group(0) @binding(5) var backdropBody : texture_2d<f32>;
|
|
2714
3100
|
@group(0) @binding(6) var fieldSampler : sampler;
|
|
3101
|
+
/// The owning surface's box per pixel (W12 G2): the pixel's offset from its
|
|
3102
|
+
/// centre (xy) and its half-extents (zw), group-local CSS px \u2014 the lens's oval.
|
|
3103
|
+
@group(0) @binding(7) var aux2Texture : texture_2d<f32>;
|
|
2715
3104
|
|
|
2716
3105
|
/// One encoded sRGB channel from a linear one \u2014 the space the backdrop tone
|
|
2717
3106
|
/// response's anchors live in (W9). Mirrors material.ts's 'linearToSrgbChannel'.
|
|
@@ -2767,7 +3156,33 @@ fn outer_shadow_falloff(signedDistance : f32, sigma : f32) -> f32 {
|
|
|
2767
3156
|
return 0.5 * (1.0 + tanh(0.7978845608028654 * (x + 0.044715 * x * x * x)));
|
|
2768
3157
|
}
|
|
2769
3158
|
|
|
2770
|
-
///
|
|
3159
|
+
/// What one pixel's outer shadow is made of: the compositing-space alpha the
|
|
3160
|
+
/// black term composites at, the falloff BOTH terms ride, and the span of the
|
|
3161
|
+
/// surface that cast it \u2014 which the lift's own rise is read from.
|
|
3162
|
+
///
|
|
3163
|
+
/// One struct rather than three calls because there is one falloff (W14 G0,
|
|
3164
|
+
/// claims \xA75.62 \xA74): the black term's free fit and the lift's free fit return
|
|
3165
|
+
/// the same sigma, offset and spread, so evaluating a second geometry for the
|
|
3166
|
+
/// second term would be asserting a difference the measurement denies.
|
|
3167
|
+
struct ShadowSample {
|
|
3168
|
+
alpha : f32,
|
|
3169
|
+
falloff : f32,
|
|
3170
|
+
castSpanCss : f32,
|
|
3171
|
+
};
|
|
3172
|
+
|
|
3173
|
+
/// The composite's peak linear occlusion above the knee, at a casting span \u2014
|
|
3174
|
+
/// piecewise linear through the three measured anchors (96 / 128 / 160 CSS px)
|
|
3175
|
+
/// and held flat outside them. Mirrors material.ts's
|
|
3176
|
+
/// 'outerShadowThickOcclusion'.
|
|
3177
|
+
fn outer_shadow_thick(spanCss : f32) -> f32 {
|
|
3178
|
+
let y = ou.shadowThick.xyz;
|
|
3179
|
+
if (spanCss <= 96.0) { return y.x; }
|
|
3180
|
+
if (spanCss >= 160.0) { return y.z; }
|
|
3181
|
+
if (spanCss <= 128.0) { return y.x + (y.y - y.x) * (spanCss - 96.0) / 32.0; }
|
|
3182
|
+
return y.y + (y.z - y.y) * (spanCss - 128.0) / 32.0;
|
|
3183
|
+
}
|
|
3184
|
+
|
|
3185
|
+
/// The outer shadow's alpha at this pixel (W8, amplitude W14 G1).
|
|
2771
3186
|
///
|
|
2772
3187
|
/// The shadow is the group's OWN field, translated down by 'shadow.w' in field
|
|
2773
3188
|
/// UV and outset by 'shadow.z', then blurred. Reading the offset silhouette from
|
|
@@ -2778,9 +3193,13 @@ fn outer_shadow_falloff(signedDistance : f32, sigma : f32) -> f32 {
|
|
|
2778
3193
|
/// The value is an ALPHA on pure black, so what it composites to is the backdrop
|
|
2779
3194
|
/// times '1 - alpha' \u2014 multiplicative occlusion, and exactly zero over black,
|
|
2780
3195
|
/// with no branch for it.
|
|
2781
|
-
fn
|
|
2782
|
-
|
|
2783
|
-
|
|
3196
|
+
fn outer_shadow(uv : vec2f, upsampled : f32, fieldSize : vec2f) -> ShadowSample {
|
|
3197
|
+
var out : ShadowSample;
|
|
3198
|
+
out.alpha = 0.0;
|
|
3199
|
+
out.falloff = 0.0;
|
|
3200
|
+
out.castSpanCss = 0.0;
|
|
3201
|
+
if (ou.shadow.x <= 0.0 && ou.shadowThick.x <= 0.0) {
|
|
3202
|
+
return out;
|
|
2784
3203
|
}
|
|
2785
3204
|
/*
|
|
2786
3205
|
* The shift can leave the field texture, and clamping alone reads a lie there.
|
|
@@ -2816,14 +3235,11 @@ fn outer_shadow_alpha(uv : vec2f, upsampled : f32, fieldSize : vec2f) -> f32 {
|
|
|
2816
3235
|
}
|
|
2817
3236
|
|
|
2818
3237
|
// The size law reaches the amplitude and nothing else \u2014 the reference's three
|
|
2819
|
-
// lengths are span-invariant across 32\u2026160 px.
|
|
2820
|
-
// the
|
|
2821
|
-
// position rather than the one under the pixel
|
|
3238
|
+
// lengths are span-invariant across 32\u2026160 px. Every span-keyed quantity here
|
|
3239
|
+
// rides the thickness and the span of the surface that CAST the shadow, which
|
|
3240
|
+
// is the one read at the offset position rather than the one under the pixel
|
|
3241
|
+
// being shaded.
|
|
2822
3242
|
//
|
|
2823
|
-
// Applied on '1 - alpha' rather than on the alpha: the gain is a fraction of
|
|
2824
|
-
// the remaining transparency in LINEAR light ('sizeOuterShadowOcclusionAt'),
|
|
2825
|
-
// and (1 - occ) is exactly what raising to 1/2.4 turns into (1 - alpha), so the
|
|
2826
|
-
// two forms are the same law and neither needs the other's space.
|
|
2827
3243
|
// The thickness curve off the casting surface's span (W11c: the span rides
|
|
2828
3244
|
// the field), written as the body's own is below.
|
|
2829
3245
|
let castT = clamp(
|
|
@@ -2832,10 +3248,65 @@ fn outer_shadow_alpha(uv : vec2f, upsampled : f32, fieldSize : vec2f) -> f32 {
|
|
|
2832
3248
|
1.0,
|
|
2833
3249
|
);
|
|
2834
3250
|
let sizeK = clamp(castT * castT * (3.0 - 2.0 * castT) * clamp(ou.tone.w, 0.0, 1.0), 0.0, 1.0);
|
|
2835
|
-
let sizeFold = pow(max(1.0 - ou.shadowSize.x * sizeK, 0.0), 1.0 / 2.4);
|
|
2836
|
-
let alpha = 1.0 - (1.0 - ou.shadow.x) * sizeFold;
|
|
2837
3251
|
|
|
2838
|
-
|
|
3252
|
+
// The two regimes and the knee between them (W14 G1). The thin amplitude is
|
|
3253
|
+
// resolved on the CPU because its key \u2014 the backdrop's luminance \u2014 is one
|
|
3254
|
+
// number for the whole group; the thick one is a span law and has to be per
|
|
3255
|
+
// pixel. The blend is the smoothstep of 'sizeK', which is exactly the curve
|
|
3256
|
+
// 'tone_response' blends its own thin and thick rows across: one knee for the
|
|
3257
|
+
// face and the shadow, which is the charter's third binding rule read from
|
|
3258
|
+
// the other side.
|
|
3259
|
+
let blend = sizeK * sizeK * (3.0 - 2.0 * sizeK);
|
|
3260
|
+
let regime = mix(ou.shadow.x, outer_shadow_thick(shadowAux.z), blend);
|
|
3261
|
+
// The size gain, on the relative form 'sizeOuterShadowOcclusionAt' uses, in
|
|
3262
|
+
// LINEAR light; then the one conversion to the canvas's compositing space,
|
|
3263
|
+
// which is where the black term is actually composited.
|
|
3264
|
+
let occ = clamp(regime + ou.shadowSize.x * sizeK * (1.0 - regime), 0.0, 1.0);
|
|
3265
|
+
out.alpha = 1.0 - pow(1.0 - occ, 1.0 / 2.4);
|
|
3266
|
+
out.falloff = outer_shadow_falloff(shadowField.x + clampedOffCss - ou.shadow.z, ou.shadow.y);
|
|
3267
|
+
out.castSpanCss = shadowAux.z;
|
|
3268
|
+
return out;
|
|
3269
|
+
}
|
|
3270
|
+
|
|
3271
|
+
/// The lift (W14 G1) \u2014 the shadow's second term, in LINEAR light, premultiplied
|
|
3272
|
+
/// by nothing yet: a blurred copy of the backdrop's own light, at an amplitude
|
|
3273
|
+
/// that is zero below the knee and saturating above it, on the SAME falloff the
|
|
3274
|
+
/// black term rides.
|
|
3275
|
+
///
|
|
3276
|
+
/// 'V' is the backdrop chain read at the pixel's OWN position \u2014 the copy is of
|
|
3277
|
+
/// the backdrop beneath the shadow, not of the backdrop under the surface \u2014 at
|
|
3278
|
+
/// the LOD whose blur is the profile's 40 CSS px. The chain's uv transform is on
|
|
3279
|
+
/// viewport-normalised coordinates, so every pixel this pass draws has a valid
|
|
3280
|
+
/// sample: unlike the field texture, which the offset shift can walk off the top
|
|
3281
|
+
/// of, the chain covers the whole viewport and there is nothing to reconstruct.
|
|
3282
|
+
///
|
|
3283
|
+
/// Zero over black by construction (V = 0), zero below the knee by the rise, and
|
|
3284
|
+
/// zero where the group has no backdrop to copy \u2014 so 'dark-solid', 'impulse',
|
|
3285
|
+
/// every thin cell and every unsampled group stay byte-for-byte what they were.
|
|
3286
|
+
fn outer_shadow_lift(viewport01 : vec2f, shadow : ShadowSample) -> vec3f {
|
|
3287
|
+
if (ou.flags.x <= 0.5 || ou.shadowLift.x <= 0.0 || shadow.falloff <= 0.0) {
|
|
3288
|
+
return vec3f(0.0);
|
|
3289
|
+
}
|
|
3290
|
+
// Written out rather than through WGSL's 'smoothstep', which is undefined
|
|
3291
|
+
// where the two edges coincide \u2014 a profile is entitled to set the rise's foot
|
|
3292
|
+
// and top to one number, and material.ts's own 'smoothstep' degrades that to a
|
|
3293
|
+
// step rather than to a NaN. Same rule as the tone band's divide above.
|
|
3294
|
+
let riseT = clamp(
|
|
3295
|
+
(shadow.castSpanCss - ou.shadowLift.y) / max(ou.shadowLift.z - ou.shadowLift.y, 1e-6),
|
|
3296
|
+
0.0,
|
|
3297
|
+
1.0,
|
|
3298
|
+
);
|
|
3299
|
+
let rise = riseT * riseT * (3.0 - 2.0 * riseT);
|
|
3300
|
+
if (rise <= 0.0) {
|
|
3301
|
+
return vec3f(0.0);
|
|
3302
|
+
}
|
|
3303
|
+
let uv = clamp(viewport01 * ou.fit.xy + ou.fit.zw, vec2f(0.0), vec2f(1.0));
|
|
3304
|
+
let chainSample = textureSampleLevel(backdropChain, backdropSampler, uv, ou.shadowLift.w);
|
|
3305
|
+
// Premultiplied linear in, straight colour out \u2014 the same unpremultiply the
|
|
3306
|
+
// body's own samples take, for the same reason: a partially transparent
|
|
3307
|
+
// backdrop must not darken what the material adds back.
|
|
3308
|
+
let v = chainSample.rgb / max(chainSample.a, 1e-6);
|
|
3309
|
+
return v * (ou.shadowLift.x * rise * shadow.falloff);
|
|
2839
3310
|
}
|
|
2840
3311
|
|
|
2841
3312
|
@fragment
|
|
@@ -2847,13 +3318,16 @@ fn fs_optics(in : FullscreenOut) -> @location(0) vec4f {
|
|
|
2847
3318
|
// contour to the coarse grid and the rim would step along it.
|
|
2848
3319
|
var field : vec4f;
|
|
2849
3320
|
var aux : vec4f;
|
|
3321
|
+
var aux2 : vec4f;
|
|
2850
3322
|
if (ou.flags.w > 0.5) {
|
|
2851
3323
|
field = textureSampleLevel(fieldTexture, fieldSampler, in.uv, 0.0);
|
|
2852
3324
|
aux = textureSampleLevel(auxTexture, fieldSampler, in.uv, 0.0);
|
|
3325
|
+
aux2 = textureSampleLevel(aux2Texture, fieldSampler, in.uv, 0.0);
|
|
2853
3326
|
} else {
|
|
2854
3327
|
let texel = vec2i(in.uv * ou.flags.yz);
|
|
2855
3328
|
field = textureLoad(fieldTexture, texel, 0);
|
|
2856
3329
|
aux = textureLoad(auxTexture, texel, 0);
|
|
3330
|
+
aux2 = textureLoad(aux2Texture, texel, 0);
|
|
2857
3331
|
}
|
|
2858
3332
|
|
|
2859
3333
|
let d = field.x;
|
|
@@ -2869,46 +3343,179 @@ fn fs_optics(in : FullscreenOut) -> @location(0) vec4f {
|
|
|
2869
3343
|
* is clipped out of its own border box, and the two meet across the coverage
|
|
2870
3344
|
* ramp with no seam because it is one expression.
|
|
2871
3345
|
*/
|
|
2872
|
-
let
|
|
3346
|
+
let shadowSample = outer_shadow(in.uv, ou.flags.w, ou.flags.yz);
|
|
3347
|
+
let shadowAlpha = shadowSample.alpha * shadowSample.falloff;
|
|
3348
|
+
let viewport01 = in.position.xy / ou.screen.xy;
|
|
3349
|
+
// Resolved once, for both sides of the coverage ramp: the lift fills exactly
|
|
3350
|
+
// what the surface's coverage leaves, on the same rule and in the same
|
|
3351
|
+
// expression as the black term's alpha at the bottom of this function, so the
|
|
3352
|
+
// two meet across the ramp with no seam. At full coverage it is not computed
|
|
3353
|
+
// at all and the chain read is not taken.
|
|
3354
|
+
var liftEncoded = vec3f(0.0);
|
|
3355
|
+
if (coverage < 1.0) {
|
|
3356
|
+
let lift = outer_shadow_lift(viewport01, shadowSample);
|
|
3357
|
+
liftEncoded = min(linear_to_srgb(max(lift, vec3f(0.0))), vec3f(shadowAlpha));
|
|
3358
|
+
}
|
|
2873
3359
|
if (coverage <= 0.0) {
|
|
2874
|
-
|
|
3360
|
+
/*
|
|
3361
|
+
* The lift (W14 G1) makes this a premultiplied COMPOSITE rather than an
|
|
3362
|
+
* alpha on black: the compositor produces 'page\xB7(1 \u2212 shadowAlpha) + lift',
|
|
3363
|
+
* which is the reference's own two-term form outside the coverage.
|
|
3364
|
+
*
|
|
3365
|
+
* The added light is computed in linear light and encoded on its own before
|
|
3366
|
+
* it is emitted, because it IS its own light \u2014 over the checkerboard's black
|
|
3367
|
+
* squares, where the multiply is inert, what the reference adds is exactly
|
|
3368
|
+
* this term and nothing else, and encoding it alone reproduces G0's measured
|
|
3369
|
+
* +0.048 encoded at span 160 from its +0.0038 linear (claims \xA75.62 \xA72, \xA73).
|
|
3370
|
+
* Where the backdrop is bright the encoding of a sum and the sum of the
|
|
3371
|
+
* encodings differ, and this term is the second: the residual is bounded by
|
|
3372
|
+
* the term's own size and is well inside the bed's \xB14/255, on the same
|
|
3373
|
+
* honest-floor argument 'outerShadowAlpha' makes for the black term's space.
|
|
3374
|
+
*
|
|
3375
|
+
* A premultiplied layer may not carry a channel above its own alpha, so the
|
|
3376
|
+
* emitted light is capped at 'shadowAlpha'. At the profile's constants the
|
|
3377
|
+
* cap is inert everywhere on the falloff: both terms are proportional to the
|
|
3378
|
+
* same F, and the lift's encoded value stays at about a sixth of the black
|
|
3379
|
+
* term's alpha from the deepest ring out to where the shadow stops moving a
|
|
3380
|
+
* code. It is here so that a swept amplitude produces a valid layer rather
|
|
3381
|
+
* than an implementation-defined one.
|
|
3382
|
+
*/
|
|
3383
|
+
return vec4f(liftEncoded, shadowAlpha);
|
|
2875
3384
|
}
|
|
2876
3385
|
|
|
2877
|
-
let viewport01 = in.position.xy / ou.screen.xy;
|
|
2878
3386
|
let viewportCss = ou.screen.xy * ou.screen.z;
|
|
2879
3387
|
|
|
2880
|
-
// Per-pixel, unioned through the field pass. See the module note.
|
|
2881
|
-
|
|
2882
|
-
//
|
|
3388
|
+
// Per-pixel, unioned through the field pass. See the module note. 'aux.x' is
|
|
3389
|
+
// the authored thickness times the lensStrength channel (W12 G2); the lens's
|
|
3390
|
+
// depth and the inner shadow's are both evaluated from it and the span below.
|
|
3391
|
+
let lensThick = max(aux.x, 0.0);
|
|
3392
|
+
// The thickness curve off the span of whichever surface owns this pixel
|
|
2883
3393
|
// (W11c). 'sizeK' is the thickness factor, 0..1: zero on anything at or below
|
|
2884
3394
|
// the profile's 'sizeSpanMin', saturated at 'sizeSpanMax', folded under the
|
|
2885
3395
|
// 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.
|
|
2887
|
-
//
|
|
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
|
|
3396
|
+
// by it, so a small control takes the pre-law path exactly. Written out as a
|
|
3397
|
+
// smoothstep with a guarded denominator, so a profile that collapses the band
|
|
2891
3398
|
// degrades to a step rather than to NaN.
|
|
2892
3399
|
let span = aux.z;
|
|
2893
3400
|
let fold = clamp(ou.tone.w, 0.0, 1.0);
|
|
2894
3401
|
let thickT = clamp((span - ou.scatter.z) / max(ou.scatter.w - ou.scatter.z, 1e-6), 0.0, 1.0);
|
|
2895
|
-
|
|
3402
|
+
// The unfolded curve, which the depth ramp's start grades along below, and the
|
|
3403
|
+
// folded factor every other facet multiplies by. One smoothstep, two readings.
|
|
3404
|
+
let sizeThick = thickT * thickT * (3.0 - 2.0 * thickT);
|
|
3405
|
+
let sizeK = clamp(sizeThick * fold, 0.0, 1.0);
|
|
3406
|
+
/*
|
|
3407
|
+
* The body's mix is the span curve W11c fitted with a ramp in DEPTH riding on
|
|
3408
|
+
* top of it near the contour (W13 G1, from the measurement of claims \xA75.61 \xA72
|
|
3409
|
+
* and the re-forming its first runtime sweep forced):
|
|
3410
|
+
*
|
|
3411
|
+
* kDeep = floor + (1 - floor) * smoothstep(sizeSpanMin, sizeScatterSpanMax, span)
|
|
3412
|
+
* s0 = startThin + (startThick - startThin) * sizeThickness(span)
|
|
3413
|
+
* + (startFar - startThick) * smoothstep(sizeSpanMax, sizeScatterSpanMax, span)
|
|
3414
|
+
* s(u) = (1 - kDeep) + max(0, s0 - (1 - kDeep)) * max(0, 1 - u / U)
|
|
3415
|
+
* k(u) = 1 - s(u)
|
|
3416
|
+
*
|
|
3417
|
+
* The first form of the ramp replaced the span curve outright and its sweep
|
|
3418
|
+
* measured what that cost: the ramp's own projection onto one number per
|
|
3419
|
+
* surface runs 0.43-0.56 where the curve it replaced runs 0.41-1.00, so the
|
|
3420
|
+
* family is nearly span-flat where the bed is strongly span-graded and no
|
|
3421
|
+
* point in 81 reached the wave's stops. So the curve stays as the deep value
|
|
3422
|
+
* and the ramp is what it always measured as - a near-contour excursion.
|
|
3423
|
+
*
|
|
3424
|
+
* The SECOND form gave that excursion one start per scale, and its own sweep
|
|
3425
|
+
* refuted that arithmetically (claims \xA75.64 \xA72): the smallest cell's span is
|
|
3426
|
+
* exactly sizeSpanMin, so its deep sharp share is exactly 1 - floor and no
|
|
3427
|
+
* start at or below it can touch that cell, while the next span up only
|
|
3428
|
+
* improves BELOW a start lower still. So the start grades with span too, and
|
|
3429
|
+
* along the material's own thin/thick curve rather than a new one - the same
|
|
3430
|
+
* smoothstep 'sizeK' is built from above, unfolded, because s0 is a share the
|
|
3431
|
+
* reference has and not a rise a preference removes. The fold below still
|
|
3432
|
+
* applies once, on the composed mix.
|
|
3433
|
+
*
|
|
3434
|
+
* The THIRD form stopped there and its holdout failed on one row for the
|
|
3435
|
+
* form's own arithmetic (claims \xA75.67 \xA74): sizeThickness saturates at
|
|
3436
|
+
* sizeSpanMax, so every thick span got the same start while the reference's
|
|
3437
|
+
* start keeps FALLING across the thick spans, and because kDeep keeps rising
|
|
3438
|
+
* to sizeScatterSpanMax the excursion grew with span where the reference's
|
|
3439
|
+
* shrinks. So the FOURTH form declines the start past the knee along the
|
|
3440
|
+
* scatter span curve itself \u2014 the same smoothstep 'deepT' is built from, on
|
|
3441
|
+
* the band above the thickness knee \u2014 from the thick anchor to startFar at
|
|
3442
|
+
* sizeScatterSpanMax. Same curve read twice, no new span statistic.
|
|
3443
|
+
*
|
|
3444
|
+
* '-d' is the depth, in group-local CSS px and unclamped - the same quantity
|
|
3445
|
+
* the lens's 'lensT' and the inner shadow's 'shadowT' are evaluated from,
|
|
3446
|
+
* except that those two divide it by a depth of their own while this reads it
|
|
3447
|
+
* absolutely, because the reach measured as a LENGTH rather than as a fraction
|
|
3448
|
+
* of the span. The reach reaches this shader already divided by the device
|
|
3449
|
+
* ratio ('lensOval.z'), so the ratio 'u / U' is the same number in CSS px as
|
|
3450
|
+
* it is in the device px the profile names.
|
|
3451
|
+
*
|
|
3452
|
+
* 'max(s0 - sDeep, 0)' rather than a signed difference: the excursion is the
|
|
3453
|
+
* band the reference has ABOVE the body, and a span whose deep sharp share
|
|
3454
|
+
* already exceeds s0 has nothing to add rather than something to subtract.
|
|
3455
|
+
*
|
|
3456
|
+
* The floor and the span top arrive resolved at the ratio this group draws at
|
|
3457
|
+
* (W15 G1, claims 5.69 section 2): the reference's 2x deep interior is fully
|
|
3458
|
+
* heavy on the largest spans where this curve fitted at 1x leaves a sharp
|
|
3459
|
+
* share of 0.24-0.36, so kDeep is a per-scale law. Nothing about the
|
|
3460
|
+
* arithmetic here changes - the uniforms carry a different number, not a
|
|
3461
|
+
* different shape - and on the landed material the two anchors are equal.
|
|
3462
|
+
*
|
|
3463
|
+
* The fold keeps its W11c semantics on the composed law: the floor is the
|
|
3464
|
+
* frost the material has at any size and is not folded, everything above it -
|
|
3465
|
+
* the span curve's rise and the ramp's excursion alike - is depth and is, so
|
|
3466
|
+
* fold 1 renders the law and fold 0 sits at the floor exactly.
|
|
3467
|
+
*/
|
|
2896
3468
|
let scatterFloor = clamp(ou.scatter.x, 0.0, 1.0);
|
|
2897
|
-
let
|
|
2898
|
-
let
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
);
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
let
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
//
|
|
2910
|
-
//
|
|
2911
|
-
|
|
3469
|
+
let deepT = clamp((span - ou.scatter.z) / max(ou.lensOval.w - ou.scatter.z, 1e-6), 0.0, 1.0);
|
|
3470
|
+
let kDeep = scatterFloor + (1.0 - scatterFloor) * deepT * deepT * (3.0 - 2.0 * deepT);
|
|
3471
|
+
let sDeep = 1.0 - kDeep;
|
|
3472
|
+
let rampT = max(1.0 - max(-d, 0.0) / max(ou.lensOval.z, 1e-6), 0.0);
|
|
3473
|
+
let farT = clamp((span - ou.scatter.w) / max(ou.lensOval.w - ou.scatter.w, 1e-6), 0.0, 1.0);
|
|
3474
|
+
let farS = farT * farT * (3.0 - 2.0 * farT);
|
|
3475
|
+
let rampStart = ou.scatter.y + (ou.shadowSize.z - ou.scatter.y) * sizeThick
|
|
3476
|
+
+ (ou.shadowThick.w - ou.shadowSize.z) * farS;
|
|
3477
|
+
let sharpShare = clamp(sDeep + max(rampStart - sDeep, 0.0) * rampT, 0.0, 1.0);
|
|
3478
|
+
let kScatter = clamp(scatterFloor + ((1.0 - sharpShare) - scatterFloor) * fold, 0.0, 1.0);
|
|
3479
|
+
// The inner shadow's depth and profile: W2's law, byte for byte \u2014 the
|
|
3480
|
+
// thickness times the size gain (folded through 'sizeK'), clamped to the
|
|
3481
|
+
// shorter half extent, and a square on it. The lens ran on this until W12 G2;
|
|
3482
|
+
// the occlusion keeps it, because nothing measured it as wrong, and every
|
|
3483
|
+
// solid-backdrop cell renders exactly as it did.
|
|
3484
|
+
let shadowLensDepth = max(min(lensThick * (1.0 + (ou.lens.z - 1.0) * sizeK), span * 0.5), 1e-4);
|
|
3485
|
+
let shadowT = clamp(-d / shadowLensDepth, 0.0, 1.0);
|
|
3486
|
+
let shadowProfile = (1.0 - shadowT) * (1.0 - shadowT);
|
|
3487
|
+
|
|
3488
|
+
// The lens (W12 G2, claims \xA75.51) \u2014 see the module note. The reference's
|
|
3489
|
+
// height and amount laws off the span, scaled by the thickness over the
|
|
3490
|
+
// reference's unit, folded like the W2 law (at fold 0 the depth is the
|
|
3491
|
+
// authored thickness and nothing more), and clamped to the shorter half
|
|
3492
|
+
// extent with the magnitude clamped by the same ratio.
|
|
3493
|
+
let thickScale = lensThick / max(ou.lensShape.w, 1e-4);
|
|
3494
|
+
let heightBase = min(ou.lensLaw.x * span, ou.lensLaw.y);
|
|
3495
|
+
let amountBase = min(ou.lensLaw.z * span, ou.lensLaw.w);
|
|
3496
|
+
let depthUnclamped = mix(lensThick, heightBase * thickScale, fold);
|
|
3497
|
+
let lensDepth = clamp(depthUnclamped, 0.0, span * 0.5);
|
|
3498
|
+
let clampRatio = select(0.0, lensDepth / depthUnclamped, depthUnclamped > 1e-6);
|
|
3499
|
+
let magnitude = ou.lens.y * mix(lensThick, amountBase * thickScale, fold) * clampRatio;
|
|
3500
|
+
let extent = max(ou.lensShape.x * lensDepth, 1e-4);
|
|
3501
|
+
// One steep power over the extent; '-d' is depth inside the surface.
|
|
3502
|
+
let lensT = max(1.0 - max(-d, 0.0) / extent, 0.0);
|
|
3503
|
+
let displacementCss = magnitude * pow(lensT, ou.lensShape.y) * ou.lens.x;
|
|
3504
|
+
|
|
3505
|
+
// The direction: the field's gradient blended toward the oval inscribed in
|
|
3506
|
+
// the surface's box (the reference's 'gradientOvalization'), on from the
|
|
3507
|
+
// knee, with the magnitude fixed \u2014 which tilts the displacement toward the
|
|
3508
|
+
// edge's midpoint and magnifies the band along the edge.
|
|
3509
|
+
let ovalT = clamp((span - ou.lensOval.x) / max(ou.lensOval.y - ou.lensOval.x, 1e-6), 0.0, 1.0);
|
|
3510
|
+
let omega = ou.lensShape.z * ovalT * ovalT * (3.0 - 2.0 * ovalT);
|
|
3511
|
+
let halfExt = max(aux2.zw, vec2f(1e-4));
|
|
3512
|
+
let rel = aux2.xy;
|
|
3513
|
+
let unitR = max(length(rel / halfExt), 1e-6);
|
|
3514
|
+
let ovalGrad = (rel / (halfExt * halfExt)) * (min(halfExt.x, halfExt.y) / unitR);
|
|
3515
|
+
let blended = (1.0 - omega) * normal + omega * ovalGrad;
|
|
3516
|
+
let blendLen = length(blended);
|
|
3517
|
+
let direction = select(normal, blended / blendLen, blendLen > 1e-6);
|
|
3518
|
+
let displaceCss = -direction * displacementCss;
|
|
2912
3519
|
let refracted01 = viewport01 + displaceCss / viewportCss;
|
|
2913
3520
|
|
|
2914
3521
|
let refractedUv = clamp(refracted01 * ou.fit.xy + ou.fit.zw, vec2f(0.0), vec2f(1.0));
|
|
@@ -2926,7 +3533,13 @@ fn fs_optics(in : FullscreenOut) -> @location(0) vec4f {
|
|
|
2926
3533
|
* by a share that is \u2248 0.4 on a small control and rises with the span. The
|
|
2927
3534
|
* mix, not the level, is what the span moves.
|
|
2928
3535
|
*/
|
|
2929
|
-
|
|
3536
|
+
// The gain is per PIXEL and not one number per group since W15 G1's re-form
|
|
3537
|
+
// (claims \xA75.70 \xA74 and \xA77): the reference's heavy kernel grows with the span,
|
|
3538
|
+
// so the gain rises from its near end 'size.x' to its far end 'shadowSize.w'
|
|
3539
|
+
// along 'farS' \u2014 the very smoothstep the ramp's far anchor already declines
|
|
3540
|
+
// along, computed above with the share. One curve, two quantities.
|
|
3541
|
+
let gainEff = ou.size.x + (ou.shadowSize.w - ou.size.x) * farS;
|
|
3542
|
+
let scatterLod = clamp(ou.size.w + log2(max(gainEff, 1e-4)), 0.0, ou.lens.w);
|
|
2930
3543
|
|
|
2931
3544
|
var backdrop = vec3f(0.0);
|
|
2932
3545
|
if (ou.flags.x > 0.5) {
|
|
@@ -3160,7 +3773,7 @@ fn fs_optics(in : FullscreenOut) -> @location(0) vec4f {
|
|
|
3160
3773
|
// A multiplicative occlusion of the whole composite. As a layer that is the
|
|
3161
3774
|
// colour scaled and the alpha raised \u2014 (k\xB7a\xB7c, 1 \u2212 k\xB7(1 \u2212 a)) composites to
|
|
3162
3775
|
// k times what (a\xB7c, a) would \u2014 and at bodyAlpha 1 it is the plain product.
|
|
3163
|
-
let shadowKeep = 1.0 -
|
|
3776
|
+
let shadowKeep = 1.0 - shadowProfile * shadowDepth * ou.light.w * present;
|
|
3164
3777
|
let shadowedAlpha = 1.0 - shadowKeep * (1.0 - bodyAlpha);
|
|
3165
3778
|
colour = colour * (shadowKeep * bodyAlpha / max(shadowedAlpha, 1e-6));
|
|
3166
3779
|
bodyAlpha = shadowedAlpha;
|
|
@@ -3198,7 +3811,10 @@ fn fs_optics(in : FullscreenOut) -> @location(0) vec4f {
|
|
|
3198
3811
|
// an opaque body the two are the same quantity, which is how the shadow was
|
|
3199
3812
|
// first written and why the difference only surfaced with the layer form.)
|
|
3200
3813
|
let body = encode_output(max(colour, vec3f(0.0)), coverage * bodyAlpha);
|
|
3201
|
-
return vec4f(
|
|
3814
|
+
return vec4f(
|
|
3815
|
+
body.rgb + liftEncoded * (1.0 - coverage),
|
|
3816
|
+
body.a + shadowAlpha * (1.0 - coverage),
|
|
3817
|
+
);
|
|
3202
3818
|
}`;
|
|
3203
3819
|
|
|
3204
3820
|
// ../renderer-webgpu/dist/wgsl/prelude.js
|
|
@@ -3326,7 +3942,11 @@ function createPassRunner(context) {
|
|
|
3326
3942
|
fragment: {
|
|
3327
3943
|
module,
|
|
3328
3944
|
entryPoint: "fs_field",
|
|
3329
|
-
targets: [
|
|
3945
|
+
targets: [
|
|
3946
|
+
{ format: WORKING_TEXTURE_FORMAT },
|
|
3947
|
+
{ format: WORKING_TEXTURE_FORMAT },
|
|
3948
|
+
{ format: WORKING_TEXTURE_FORMAT }
|
|
3949
|
+
]
|
|
3330
3950
|
},
|
|
3331
3951
|
primitive: { topology: "triangle-list" }
|
|
3332
3952
|
};
|
|
@@ -3390,6 +4010,13 @@ function createPassRunner(context) {
|
|
|
3390
4010
|
usage: fieldUsage(),
|
|
3391
4011
|
label: `vitrea:group:${args.groupId}:aux`
|
|
3392
4012
|
});
|
|
4013
|
+
const aux2 = pool.acquire(poolKey.groupAux2(args.groupId), {
|
|
4014
|
+
width,
|
|
4015
|
+
height,
|
|
4016
|
+
format: WORKING_TEXTURE_FORMAT,
|
|
4017
|
+
usage: fieldUsage(),
|
|
4018
|
+
label: `vitrea:group:${args.groupId}:aux2`
|
|
4019
|
+
});
|
|
3393
4020
|
const slot = uniformSlot(`field:${args.groupId}`, 12);
|
|
3394
4021
|
slot.data[0] = rectWidth;
|
|
3395
4022
|
slot.data[1] = rectHeight;
|
|
@@ -3423,6 +4050,12 @@ function createPassRunner(context) {
|
|
|
3423
4050
|
loadOp: "clear",
|
|
3424
4051
|
storeOp: "store",
|
|
3425
4052
|
clearValue: { r: 0, g: 0, b: 0, a: 0 }
|
|
4053
|
+
},
|
|
4054
|
+
{
|
|
4055
|
+
view: aux2.createView(),
|
|
4056
|
+
loadOp: "clear",
|
|
4057
|
+
storeOp: "store",
|
|
4058
|
+
clearValue: { r: 0, g: 0, b: 0, a: 0 }
|
|
3426
4059
|
}
|
|
3427
4060
|
]
|
|
3428
4061
|
});
|
|
@@ -3439,13 +4072,14 @@ function createPassRunner(context) {
|
|
|
3439
4072
|
return {
|
|
3440
4073
|
field,
|
|
3441
4074
|
aux,
|
|
4075
|
+
aux2,
|
|
3442
4076
|
width,
|
|
3443
4077
|
height,
|
|
3444
4078
|
upsampled: width !== rectWidth || height !== rectHeight
|
|
3445
4079
|
};
|
|
3446
4080
|
},
|
|
3447
4081
|
opticsPass(encoder, args) {
|
|
3448
|
-
const slot = uniformSlot(`optics:${args.groupId}`,
|
|
4082
|
+
const slot = uniformSlot(`optics:${args.groupId}`, 96);
|
|
3449
4083
|
const d = slot.data;
|
|
3450
4084
|
d[0] = args.viewportDevice[0];
|
|
3451
4085
|
d[1] = args.viewportDevice[1];
|
|
@@ -3457,7 +4091,7 @@ function createPassRunner(context) {
|
|
|
3457
4091
|
d[7] = args.fit[3];
|
|
3458
4092
|
d[8] = args.refractionScale;
|
|
3459
4093
|
d[9] = args.lensRefractionGain;
|
|
3460
|
-
d[10] =
|
|
4094
|
+
d[10] = args.shadowDepthSizeGainMax;
|
|
3461
4095
|
d[11] = args.chainMaxLod;
|
|
3462
4096
|
d[12] = args.tint[0];
|
|
3463
4097
|
d[13] = args.tint[1];
|
|
@@ -3505,8 +4139,8 @@ function createPassRunner(context) {
|
|
|
3505
4139
|
d[55] = args.outerShadow[3];
|
|
3506
4140
|
d[56] = args.outerShadowSizeGain;
|
|
3507
4141
|
d[57] = args.outerShadowRectCssHeight;
|
|
3508
|
-
d[58] =
|
|
3509
|
-
d[59] =
|
|
4142
|
+
d[58] = args.sizeScatterRampStartThick;
|
|
4143
|
+
d[59] = args.sizeScatterGainFar;
|
|
3510
4144
|
d[60] = args.backdropToneAnchorX[0];
|
|
3511
4145
|
d[61] = args.backdropToneAnchorX[1];
|
|
3512
4146
|
d[62] = args.backdropToneAnchorX[2];
|
|
@@ -3520,9 +4154,29 @@ function createPassRunner(context) {
|
|
|
3520
4154
|
d[70] = args.backdropToneResponseThick[2];
|
|
3521
4155
|
d[71] = 0;
|
|
3522
4156
|
d[72] = args.sizeScatterFloor;
|
|
3523
|
-
d[73] = args.
|
|
4157
|
+
d[73] = args.sizeScatterRampStartThin;
|
|
3524
4158
|
d[74] = args.sizeSpanMin;
|
|
3525
4159
|
d[75] = args.sizeSpanMax;
|
|
4160
|
+
d[76] = args.lensHeightPerSpan;
|
|
4161
|
+
d[77] = args.lensHeightMax;
|
|
4162
|
+
d[78] = args.lensAmountPerSpan;
|
|
4163
|
+
d[79] = args.lensAmountMax;
|
|
4164
|
+
d[80] = args.lensExtentGain;
|
|
4165
|
+
d[81] = args.lensProfileExponent;
|
|
4166
|
+
d[82] = args.lensOvalization;
|
|
4167
|
+
d[83] = args.lensThicknessReference;
|
|
4168
|
+
d[84] = args.lensOvalizationSpanMin;
|
|
4169
|
+
d[85] = args.lensOvalizationSpanMax;
|
|
4170
|
+
d[86] = args.sizeScatterRampReachCssPx;
|
|
4171
|
+
d[87] = args.sizeScatterSpanMax;
|
|
4172
|
+
d[88] = args.outerShadowThick[0];
|
|
4173
|
+
d[89] = args.outerShadowThick[1];
|
|
4174
|
+
d[90] = args.outerShadowThick[2];
|
|
4175
|
+
d[91] = args.sizeScatterRampStartFar;
|
|
4176
|
+
d[92] = args.outerShadowLift[0];
|
|
4177
|
+
d[93] = args.outerShadowLift[1];
|
|
4178
|
+
d[94] = args.outerShadowLift[2];
|
|
4179
|
+
d[95] = args.outerShadowLift[3];
|
|
3526
4180
|
slot.write();
|
|
3527
4181
|
const chain = args.backdrop?.chain ?? placeholderView;
|
|
3528
4182
|
const body = args.backdrop?.body ?? placeholderView;
|
|
@@ -3545,7 +4199,8 @@ function createPassRunner(context) {
|
|
|
3545
4199
|
{ binding: 5, resource: body },
|
|
3546
4200
|
// Linear, no mips: the field targets have one level, and this is only
|
|
3547
4201
|
// read at all when the governor shrank them below the group's rect.
|
|
3548
|
-
{ binding: 6, resource: context.flatSampler }
|
|
4202
|
+
{ binding: 6, resource: context.flatSampler },
|
|
4203
|
+
{ binding: 7, resource: args.fields.aux2.createView() }
|
|
3549
4204
|
]
|
|
3550
4205
|
}));
|
|
3551
4206
|
pass.draw(3);
|
|
@@ -3628,6 +4283,7 @@ function createPassRunner(context) {
|
|
|
3628
4283
|
forget(groupId) {
|
|
3629
4284
|
pool.release(poolKey.groupField(groupId));
|
|
3630
4285
|
pool.release(poolKey.groupAux(groupId));
|
|
4286
|
+
pool.release(poolKey.groupAux2(groupId));
|
|
3631
4287
|
storages.get(groupId)?.destroy();
|
|
3632
4288
|
storages.delete(groupId);
|
|
3633
4289
|
for (const key of [`field:${groupId}`, `optics:${groupId}`, `highlight:${groupId}`]) {
|
|
@@ -3773,10 +4429,8 @@ function createRebuildLedger() {
|
|
|
3773
4429
|
// ../renderer-webgpu/dist/pyramid.js
|
|
3774
4430
|
var chainUsage = () => GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING;
|
|
3775
4431
|
var densityOf = (source, request) => texelsPerCssPx(source.width, source.height, request.placement, request.viewportCss[0], request.viewportCss[1]);
|
|
3776
|
-
var
|
|
3777
|
-
|
|
3778
|
-
return Math.abs(next - existing.texelsPerCss) <= 1e-6 * Math.max(1, existing.texelsPerCss);
|
|
3779
|
-
};
|
|
4432
|
+
var same = (next, existing) => Math.abs(next - existing) <= 1e-6 * Math.max(1, Math.abs(existing));
|
|
4433
|
+
var sameBody = (existing, request) => same(densityOf({ width: existing.sourceWidth, height: existing.sourceHeight }, request), existing.texelsPerCss) && same(request.bodySigmaCss, existing.bodySigmaCss);
|
|
3780
4434
|
function createPyramidStore(context) {
|
|
3781
4435
|
const { device, pool, cache } = context;
|
|
3782
4436
|
const resources = /* @__PURE__ */ new Map();
|
|
@@ -3852,7 +4506,7 @@ function createPyramidStore(context) {
|
|
|
3852
4506
|
return target;
|
|
3853
4507
|
};
|
|
3854
4508
|
const mipView = (texture, level) => texture.createView({ baseMipLevel: level, mipLevelCount: 1, dimension: "2d" });
|
|
3855
|
-
function allocate(sourceId, plan, bodyLevel, sizeEpoch, builtEpoch, bodySigmaTexels, density) {
|
|
4509
|
+
function allocate(sourceId, plan, bodyLevel, sizeEpoch, builtEpoch, bodySigmaTexels, density, bodySigmaCss) {
|
|
3856
4510
|
const existing = resources.get(sourceId);
|
|
3857
4511
|
const bodyWidth = (plan.levels[bodyLevel] ?? plan.levels[0]).width;
|
|
3858
4512
|
const bodyHeight = (plan.levels[bodyLevel] ?? plan.levels[0]).height;
|
|
@@ -3893,7 +4547,8 @@ function createPyramidStore(context) {
|
|
|
3893
4547
|
bodySigmaTexels,
|
|
3894
4548
|
texelsPerCss: density.texelsPerCss,
|
|
3895
4549
|
sourceWidth: density.sourceWidth,
|
|
3896
|
-
sourceHeight: density.sourceHeight
|
|
4550
|
+
sourceHeight: density.sourceHeight,
|
|
4551
|
+
bodySigmaCss
|
|
3897
4552
|
};
|
|
3898
4553
|
resources.set(sourceId, next);
|
|
3899
4554
|
return next;
|
|
@@ -4081,7 +4736,7 @@ function createPyramidStore(context) {
|
|
|
4081
4736
|
},
|
|
4082
4737
|
build(request, provider, encoder) {
|
|
4083
4738
|
const existing = liveResources(request.sourceId);
|
|
4084
|
-
if (existing !== void 0 && existing.builtEpoch >= request.epoch && !provider.isDirty() &&
|
|
4739
|
+
if (existing !== void 0 && existing.builtEpoch >= request.epoch && !provider.isDirty() && sameBody(existing, request)) {
|
|
4085
4740
|
ledger.recordClean();
|
|
4086
4741
|
return { status: "clean", resources: existing };
|
|
4087
4742
|
}
|
|
@@ -4103,7 +4758,7 @@ function createPyramidStore(context) {
|
|
|
4103
4758
|
const planScale = frame.width > 0 ? plan.width / frame.width : 1;
|
|
4104
4759
|
const bodySigmaTexels = request.bodySigmaCss * texelsPerCss * planScale;
|
|
4105
4760
|
const bodyPlan = bodyBlurPlan(bodySigmaTexels, plan);
|
|
4106
|
-
const target = allocate(request.sourceId, plan, bodyPlan.level, frame.sizeEpoch, request.epoch, bodySigmaTexels, { texelsPerCss, sourceWidth: frame.width, sourceHeight: frame.height });
|
|
4761
|
+
const target = allocate(request.sourceId, plan, bodyPlan.level, frame.sizeEpoch, request.epoch, bodySigmaTexels, { texelsPerCss, sourceWidth: frame.width, sourceHeight: frame.height }, request.bodySigmaCss);
|
|
4107
4762
|
runImport(encoder, request.sourceId, frame, target.chain);
|
|
4108
4763
|
runChain(encoder, request.sourceId, plan, target.chain);
|
|
4109
4764
|
runBodyBlur(encoder, request.sourceId, plan, target.chain, target.body, bodyPlan.level, bodyPlan.residualSigmaTexels);
|
|
@@ -4316,7 +4971,14 @@ function createWebGPURenderer(options = {}) {
|
|
|
4316
4971
|
const placement = placements.get(sourceId);
|
|
4317
4972
|
return isUsablePlacement(placement) ? placementFit(placement, viewport.widthCss, viewport.heightCss) : coverFit(pyramid.plan.width, pyramid.plan.height, viewport.widthCss, viewport.heightCss);
|
|
4318
4973
|
};
|
|
4319
|
-
const
|
|
4974
|
+
const liftChainLod = (sigmaCss, pyramid) => {
|
|
4975
|
+
if (pyramid === void 0 || !(sigmaCss > 0))
|
|
4976
|
+
return 0;
|
|
4977
|
+
const planScale = pyramid.sourceWidth > 0 ? pyramid.plan.width / pyramid.sourceWidth : 1;
|
|
4978
|
+
const sigmaTexels = sigmaCss * pyramid.texelsPerCss * planScale;
|
|
4979
|
+
return Math.min(chainLodForSigma(sigmaTexels), pyramid.plan.maxLod);
|
|
4980
|
+
};
|
|
4981
|
+
const staleBodyRebuilds = (explicit, pyramids) => {
|
|
4320
4982
|
const requests = [];
|
|
4321
4983
|
const named = new Set(explicit.map((request) => request.sourceId));
|
|
4322
4984
|
for (const sourceId of providers.keys()) {
|
|
@@ -4326,7 +4988,10 @@ function createWebGPURenderer(options = {}) {
|
|
|
4326
4988
|
if (existing === void 0)
|
|
4327
4989
|
continue;
|
|
4328
4990
|
const next = texelsPerCssPx(existing.sourceWidth, existing.sourceHeight, placements.get(sourceId), viewport.widthCss, viewport.heightCss);
|
|
4329
|
-
|
|
4991
|
+
const sameDensity = Math.abs(next - existing.texelsPerCss) <= 1e-6 * Math.max(1, existing.texelsPerCss);
|
|
4992
|
+
const sigmaCss = bodySigmaCssFor(sourceId);
|
|
4993
|
+
const sameSigma = Math.abs(sigmaCss - existing.bodySigmaCss) <= 1e-6 * Math.max(1, existing.bodySigmaCss);
|
|
4994
|
+
if (sameDensity && sameSigma)
|
|
4330
4995
|
continue;
|
|
4331
4996
|
requests.push({
|
|
4332
4997
|
sourceId,
|
|
@@ -4339,6 +5004,14 @@ function createWebGPURenderer(options = {}) {
|
|
|
4339
5004
|
};
|
|
4340
5005
|
const unionOf = (input) => input.union ?? DEFAULT_GROUP_UNION;
|
|
4341
5006
|
const variantOf = (input) => input.variant ?? "regular";
|
|
5007
|
+
const sourceVariant = (sourceId) => {
|
|
5008
|
+
const input = [...groups.values()].find((entry) => entry.input.backdropSourceId === sourceId)?.input;
|
|
5009
|
+
return input === void 0 ? "regular" : variantOf(input);
|
|
5010
|
+
};
|
|
5011
|
+
const bodySigmaCssFor = (sourceId) => {
|
|
5012
|
+
const optics = opticsUnderPolicy(material.optics[sourceVariant(sourceId)], accessibility, material);
|
|
5013
|
+
return optics.blurSigma / Math.max(viewport.devicePixelRatio, 1e-3);
|
|
5014
|
+
};
|
|
4342
5015
|
const groupTintSeed = (input) => input.surfaces.find((surface) => surface.fieldReferenceOnly !== true && (surface.tint?.strength ?? 0) > 0)?.tint?.color;
|
|
4343
5016
|
const stateOf = (input, resolution) => {
|
|
4344
5017
|
const resolved = resolution?.groups.find((group) => group.groupId === input.groupId);
|
|
@@ -4353,8 +5026,8 @@ function createWebGPURenderer(options = {}) {
|
|
|
4353
5026
|
function rebuildRequests(explicit) {
|
|
4354
5027
|
const base = explicit ?? standaloneRequests();
|
|
4355
5028
|
const { store: pyramids } = ensureContext();
|
|
4356
|
-
const
|
|
4357
|
-
return
|
|
5029
|
+
const stale = staleBodyRebuilds(base, pyramids);
|
|
5030
|
+
return stale.length === 0 ? base : [...base, ...stale];
|
|
4358
5031
|
}
|
|
4359
5032
|
function standaloneRequests() {
|
|
4360
5033
|
const sampled = /* @__PURE__ */ new Set();
|
|
@@ -4390,15 +5063,21 @@ function createWebGPURenderer(options = {}) {
|
|
|
4390
5063
|
unbuilt.push(request.sourceId);
|
|
4391
5064
|
continue;
|
|
4392
5065
|
}
|
|
4393
|
-
const variant = variantOf([...groups.values()].find((entry) => entry.input.backdropSourceId === request.sourceId)?.input ?? { });
|
|
4394
|
-
const optics = opticsUnderPolicy(material.optics[variant], accessibility, material);
|
|
4395
5066
|
lastResolution.set(request.sourceId, request.resolution);
|
|
4396
5067
|
const placement = placements.get(request.sourceId);
|
|
4397
5068
|
const outcome = pyramids.build({
|
|
4398
5069
|
sourceId: request.sourceId,
|
|
4399
5070
|
epoch: request.epoch,
|
|
4400
5071
|
resolution: request.resolution,
|
|
4401
|
-
|
|
5072
|
+
// The body's sharp width is a DEVICE-pixel quantity (W12 G3, claims
|
|
5073
|
+
// §5.56; restored by W15 G1): `blurSigma` device px, so
|
|
5074
|
+
// `blurSigma / dpr` in the CSS px the pyramid converts to source
|
|
5075
|
+
// texels with. The heavy component rides the same conversion — its
|
|
5076
|
+
// level is this one plus log2(the gain at this ratio) octaves — so it
|
|
5077
|
+
// is the gain's own multiple of the sharp width in device px at every
|
|
5078
|
+
// scale. At dpr 1 this is `optics.blurSigma` unchanged, which is what
|
|
5079
|
+
// leaves the 1x law exactly as landed.
|
|
5080
|
+
bodySigmaCss: bodySigmaCssFor(request.sourceId),
|
|
4402
5081
|
viewportCss: [viewport.widthCss, viewport.heightCss],
|
|
4403
5082
|
...isUsablePlacement(placement) ? { placement } : {}
|
|
4404
5083
|
}, provider, encoder);
|
|
@@ -4449,11 +5128,12 @@ function createWebGPURenderer(options = {}) {
|
|
|
4449
5128
|
}
|
|
4450
5129
|
const union = unionOf(input);
|
|
4451
5130
|
const shadow = outerShadowUnderPolicy(policy, material);
|
|
4452
|
-
|
|
5131
|
+
const shadowBackdropLuminance = input.backdropTone === void 0 ? void 0 : input.backdropToneLevel ?? relativeLuminance(input.backdropTone);
|
|
5132
|
+
let reachOcclusion = outerShadowThinOcclusion(shadowBackdropLuminance, shadow);
|
|
4453
5133
|
for (const surface of surfaces) {
|
|
4454
|
-
reachOcclusion = Math.max(reachOcclusion,
|
|
5134
|
+
reachOcclusion = Math.max(reachOcclusion, outerShadowOcclusionAt(shadow, shadowBackdropLuminance, surface.spanPx, surface.sizeThickness, material));
|
|
4455
5135
|
}
|
|
4456
|
-
const shadowReachPx = outerShadowReachPx(
|
|
5136
|
+
const shadowReachPx = outerShadowReachPx(shadow, reachOcclusion);
|
|
4457
5137
|
const snapped = snapRectToDevicePixels(groupFieldRect(surfaces, union, void 0, shadowReachPx), dpr);
|
|
4458
5138
|
const rectDevice = clipFieldRectToCanvas(snapped, dpr, viewportDevice);
|
|
4459
5139
|
if (rectDevice === void 0)
|
|
@@ -4493,7 +5173,7 @@ function createWebGPURenderer(options = {}) {
|
|
|
4493
5173
|
backdropToneSizeBiasUnderPolicy(policy, material),
|
|
4494
5174
|
input.backdropTone === void 0 ? 0 : backdropToneUnderPolicy(policy, material) * material.backdropToneMax
|
|
4495
5175
|
];
|
|
4496
|
-
const backdropToneLevel =
|
|
5176
|
+
const backdropToneLevel = shadowBackdropLuminance ?? 0;
|
|
4497
5177
|
const refraction = effectiveRefraction(accessibilityRefractionCap(policy), state.refraction);
|
|
4498
5178
|
const refractionScale = material.refractionScale[refraction];
|
|
4499
5179
|
passes.opticsPass(encoder, {
|
|
@@ -4508,6 +5188,19 @@ function createWebGPURenderer(options = {}) {
|
|
|
4508
5188
|
fit: pyramid === void 0 || sourceId === void 0 ? [1, 1, 0, 0] : fitFor(sourceId, pyramid),
|
|
4509
5189
|
refractionScale,
|
|
4510
5190
|
lensRefractionGain: material.lensRefractionGain,
|
|
5191
|
+
// The lens law (W12 G2) and the inner shadow's own depth gain, both
|
|
5192
|
+
// evaluated per pixel in the shader from the thickness and the span.
|
|
5193
|
+
lensHeightPerSpan: material.lensHeightPerSpan,
|
|
5194
|
+
lensHeightMax: material.lensHeightMax,
|
|
5195
|
+
lensAmountPerSpan: material.lensAmountPerSpan,
|
|
5196
|
+
lensAmountMax: material.lensAmountMax,
|
|
5197
|
+
lensThicknessReference: material.lensThicknessReference,
|
|
5198
|
+
lensExtentGain: material.lensExtentGain,
|
|
5199
|
+
lensProfileExponent: material.lensProfileExponent,
|
|
5200
|
+
lensOvalization: material.lensOvalization,
|
|
5201
|
+
lensOvalizationSpanMin: material.lensOvalizationSpanMin,
|
|
5202
|
+
lensOvalizationSpanMax: material.lensOvalizationSpanMax,
|
|
5203
|
+
shadowDepthSizeGainMax: material.lensSizeGainMax,
|
|
4511
5204
|
chainMaxLod: pyramid?.plan.maxLod ?? 0,
|
|
4512
5205
|
tint: optics.tint,
|
|
4513
5206
|
tintAlpha: optics.tintAlpha,
|
|
@@ -4540,7 +5233,20 @@ function createWebGPURenderer(options = {}) {
|
|
|
4540
5233
|
// group whose members are not one size. `bodyChainLod` is where the body
|
|
4541
5234
|
// blur already sits on the chain — only the pyramid that built it knows
|
|
4542
5235
|
// the CSS-px-to-texel conversion, so it publishes the σ and this converts.
|
|
4543
|
-
|
|
5236
|
+
// The gain at the ratio this group draws at (W15 G1, claims §5.69 §1):
|
|
5237
|
+
// the shader's scatter level is `bodyChainLod + log2(gain)`, so this is
|
|
5238
|
+
// where the heavy width's second scale acts. `sizeScatterGainMax2x`
|
|
5239
|
+
// defaults to the 1x gain, so this is `material.sizeScatterGainMax`
|
|
5240
|
+
// until the sweep fits it.
|
|
5241
|
+
sizeScatterGainMax: scatterGainAtScale(material, dpr),
|
|
5242
|
+
// The gain's far end at the same ratio (W15 G1's re-form, claims §5.70
|
|
5243
|
+
// §4 and §7): the shader grades the gain from the near end at
|
|
5244
|
+
// `sizeSpanMax` to this at `sizeScatterSpanMax`, because the reference's
|
|
5245
|
+
// heavy kernel grows with the span and one gain left the largest span's
|
|
5246
|
+
// deep interior too structured. `sizeScatterGainFar2x` defaults to the
|
|
5247
|
+
// 2x gain's own default and interpolates from the 1x gain, so this is
|
|
5248
|
+
// `sizeScatterGainMax` at dpr 1 whatever the profile says.
|
|
5249
|
+
sizeScatterGainFar: scatterGainFarAtScale(material, dpr),
|
|
4544
5250
|
sizeOcclusionGain: material.sizeOcclusionGain,
|
|
4545
5251
|
sizeShadowGainMax: material.sizeShadowGainMax,
|
|
4546
5252
|
bodyChainLod: chainLodForSigma(pyramid?.bodySigmaTexels ?? 0),
|
|
@@ -4549,8 +5255,26 @@ function createWebGPURenderer(options = {}) {
|
|
|
4549
5255
|
// same fold `sizeThicknessUnderPolicy` applies on the CPU.
|
|
4550
5256
|
sizeSpanMin: material.sizeSpanMin,
|
|
4551
5257
|
sizeSpanMax: material.sizeSpanMax,
|
|
4552
|
-
|
|
4553
|
-
|
|
5258
|
+
// The deep value's floor and span top at this ratio (W15 G1, claims
|
|
5259
|
+
// §5.69 §2). Same two uniform slots the shader has always read for
|
|
5260
|
+
// `kDeep` and for the fold; only the value is resolved from the
|
|
5261
|
+
// viewport now, as the ramp's anchors already were.
|
|
5262
|
+
sizeScatterFloor: scatterFloorAtScale(material, dpr),
|
|
5263
|
+
sizeScatterSpanMax: scatterSpanMaxAtScale(material, dpr),
|
|
5264
|
+
// The body's depth ramp (W13 G1, claims §5.61 §2, §5.64 §5), resolved
|
|
5265
|
+
// from the viewport's own ratio: the profile anchors the start's thin
|
|
5266
|
+
// and thick ends and the reach at dpr 1 and dpr 2, and the CPU
|
|
5267
|
+
// interpolates all three in dpr. The start's SPAN grading stays in the
|
|
5268
|
+
// shader, because a group's members are not one size and the span
|
|
5269
|
+
// arrives per pixel on the field's own channel — so the two anchors go
|
|
5270
|
+
// over and `sizeThickness` is evaluated there. The reach is handed over
|
|
5271
|
+
// in CSS px because the field's depth is in CSS px.
|
|
5272
|
+
sizeScatterRampStartThin: scatterRampStart(dpr, material, 0),
|
|
5273
|
+
sizeScatterRampStartThick: scatterRampStart(dpr, material, material.sizeSpanMax),
|
|
5274
|
+
// The fourth form's far end, at the scatter span curve's top; the shader
|
|
5275
|
+
// declines from the thick end to it along that curve.
|
|
5276
|
+
sizeScatterRampStartFar: scatterRampStart(dpr, material, scatterSpanMaxAtScale(material, dpr)),
|
|
5277
|
+
sizeScatterRampReachCssPx: scatterRampReachDevicePx(dpr, material) / dpr,
|
|
4554
5278
|
sizeFold: material.refractionScale[accessibilityRefractionCap(policy)],
|
|
4555
5279
|
backdropTone,
|
|
4556
5280
|
backdropToneColour: [
|
|
@@ -4579,7 +5303,11 @@ function createWebGPURenderer(options = {}) {
|
|
|
4579
5303
|
backdropToneResponseStrength: backdropToneUnderPolicy(policy, material) >= 0.999 ? material.backdropToneResponseStrength : 0,
|
|
4580
5304
|
backdropToneLinearMean: input.backdropToneLinearLuminance ?? backdropToneLevel,
|
|
4581
5305
|
outerShadow: [
|
|
4582
|
-
|
|
5306
|
+
// The thin regime's LINEAR occlusion at this group's backdrop (W14
|
|
5307
|
+
// G1). The conversion to the compositing space moved into the shader
|
|
5308
|
+
// with the regimes: it is not linear, so converting one regime here
|
|
5309
|
+
// and blending in the shader would not be the same law.
|
|
5310
|
+
outerShadowThinOcclusion(shadowBackdropLuminance, shadow),
|
|
4583
5311
|
shadow.sigmaPx,
|
|
4584
5312
|
shadow.spreadPx,
|
|
4585
5313
|
// The offset in field-texture UV. The field spans exactly this rect, so
|
|
@@ -4587,6 +5315,19 @@ function createWebGPURenderer(options = {}) {
|
|
|
4587
5315
|
// scaled the rasterisation.
|
|
4588
5316
|
shadow.offsetPx / Math.max(rectDevice.height * cssPerDevice, 1e-6)
|
|
4589
5317
|
],
|
|
5318
|
+
outerShadowThick: [
|
|
5319
|
+
shadow.thickOcclusionAt96,
|
|
5320
|
+
shadow.thickOcclusionAt128,
|
|
5321
|
+
shadow.thickOcclusionAt160
|
|
5322
|
+
],
|
|
5323
|
+
outerShadowLift: [
|
|
5324
|
+
// The lift needs a chain to copy; without one there is nothing to add,
|
|
5325
|
+
// and `hasBackdrop` in the shader says the same thing a second time.
|
|
5326
|
+
pyramid === void 0 || policy.glass === "none" ? 0 : shadow.liftAmplitude,
|
|
5327
|
+
shadow.liftSpanMin,
|
|
5328
|
+
shadow.liftSpanFull,
|
|
5329
|
+
liftChainLod(shadow.liftBlurSigmaCss, pyramid)
|
|
5330
|
+
],
|
|
4590
5331
|
outerShadowSizeGain: shadow.sizeGain,
|
|
4591
5332
|
outerShadowRectCssHeight: rectDevice.height * cssPerDevice,
|
|
4592
5333
|
backdrop: pyramid === void 0 || policy.glass === "none" ? void 0 : {
|
|
@@ -4868,6 +5609,6 @@ function createWebGPURenderer(options = {}) {
|
|
|
4868
5609
|
};
|
|
4869
5610
|
}
|
|
4870
5611
|
|
|
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-
|
|
4873
|
-
//# sourceMappingURL=dist-
|
|
5612
|
+
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_AMOUNT_MAX, LENS_AMOUNT_PER_SPAN, LENS_EXTENT_GAIN, LENS_HEIGHT_MAX, LENS_HEIGHT_PER_SPAN, LENS_OVALIZATION, LENS_PROFILE_EXPONENT, LENS_REFRACTION_GAIN, LENS_SIZE_GAIN_MAX, LENS_THICKNESS_REFERENCE, LUMINANCE_WEIGHTS, MATERIAL_OPTICS, MATERIAL_VARIANTS, MAX_CHAIN_LEVELS, MIN_LEVEL_EXTENT, NOMINAL_GOVERNOR, NOMINAL_MATERIAL_POLICY, OPTICS_PASS_ID, OUTER_SHADOW, OUTER_SHADOW_THICK_SPANS, OUTER_SHADOW_THIN_L, OUTER_SHADOW_UNMEASURED_BACKDROP_LUMINANCE, OUTPUT_TEXTURE_FORMAT, PASS_LABEL, REFRACTION_LADDER, REFRACTION_SCALE, RENDERER_ERROR_CODES, RENDERER_PASS_IDS, RendererError, SIZE_OCCLUSION_GAIN, SIZE_SCATTER_FLOOR, SIZE_SCATTER_FLOOR_2X, SIZE_SCATTER_GAIN_FAR_2X, SIZE_SCATTER_GAIN_MAX, SIZE_SCATTER_GAIN_MAX_2X, SIZE_SCATTER_RAMP_REACH_1X_PX, SIZE_SCATTER_RAMP_REACH_2X_PX, SIZE_SCATTER_RAMP_START_FAR_1X, SIZE_SCATTER_RAMP_START_FAR_2X, SIZE_SCATTER_RAMP_START_THICK_1X, SIZE_SCATTER_RAMP_START_THICK_2X, SIZE_SCATTER_RAMP_START_THIN_1X, SIZE_SCATTER_RAMP_START_THIN_2X, SIZE_SCATTER_SPAN_MAX, SIZE_SCATTER_SPAN_MAX_2X, 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, lensAmountBasePx, lensDepthPx, lensDirection, lensDisplacementPx, lensExtentPx, lensHeightBasePx, lensMagnitudePx, lensOvalizationAt, lensSizeGain, lensSizeGainFromThickness, linearGradientStops, linearToSrgb, linearToSrgbChannel, occlusionAlphaUnderPolicy, opticsModule, opticsUnderPolicy, outerShadowAlpha, outerShadowFalloff, outerShadowLiftRise, outerShadowOcclusionAt, outerShadowReachPx, outerShadowThickOcclusion, outerShadowThinOcclusion, outerShadowUnderPolicy, packInstances, pipelineKey, placementFit, planPyramid, poolKey, readbackDue, refractionRank, relativeLuminance, rendererError, resolveSurfaces, samePlacement, scatterDeepThickness, scatterFloorAtScale, scatterGainAt, scatterGainAtScale, scatterGainFarAtScale, scatterRampAreaMean, scatterRampReachDevicePx, scatterRampStart, scatterSharpShare, scatterSpanMaxAtScale, scatterThickness, shadowDepthPx, sizeOcclusionAlpha, sizeOcclusionAlphaAt, sizeOuterShadowOcclusion, sizeOuterShadowOcclusionAt, sizeScatterSigma, sizeScatterSigmaAt, sizeShadowDepth, sizeShadowDepthAt, sizeThickness, sizeThicknessUnderPolicy, snapRectToDevicePixels, srgbToLinear, srgbToLinearChannel, statsFromBuffer, supportsTimestamps, texelsPerCssPx, tintShade, tintShadeLayer, tintToneAdaptation, tintedMaterialColour, validateAppTexture, withMaterialOverrides };
|
|
5613
|
+
//# sourceMappingURL=dist-OHNXYEZP.js.map
|
|
5614
|
+
//# sourceMappingURL=dist-OHNXYEZP.js.map
|