@vitreajs/vitrea 0.4.0 → 0.6.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.
@@ -433,6 +433,20 @@ var DEFAULT_MATERIAL_PROFILE = {
433
433
  sizeScatterGainMax: 8,
434
434
  sizeScatterFloor: 0.4,
435
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,
436
450
  // The 1x three FITTED in the renderer (W13 G1's third sweep, 44 points over
437
451
  // the calibration bed: `results/2026-09-03-w13-ramp/g1/sweep-3/g1-sweep-3.md`
438
452
  // §3); the 2x three PROVISIONAL still, because at them the excursion is
@@ -442,17 +456,18 @@ var DEFAULT_MATERIAL_PROFILE = {
442
456
  // interior optimum of both the interior objective and the interior gap. The
443
457
  // thick anchor 0.52 is G0's `rrect-md` reading and is above that cell's deep
444
458
  // sharp share of 0.481, where the joint thick fit of 0.47 would have clamped
445
- // it to nothing. The 2x pair is G0's 2x readings, every one of which is BELOW
446
- // its cell's deep value, so at dpr 2 the excursion is zero on this whole bed
447
- // — the law evaluating to no excursion, and the 2x gap located in the deep
448
- // value instead (claims §5.64 §4). See
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
449
464
  // `MaterialProfile.sizeScatterRampStartThin1x`.
450
465
  sizeScatterRampStartThin1x: 0.72,
451
466
  sizeScatterRampStartThick1x: 0.52,
452
467
  sizeScatterRampStartFar1x: 0.2,
453
468
  sizeScatterRampStartThin2x: 0.46,
454
- sizeScatterRampStartThick2x: 0.17,
455
- sizeScatterRampStartFar2x: 0.15,
469
+ sizeScatterRampStartThick2x: 0.21,
470
+ sizeScatterRampStartFar2x: 0.21,
456
471
  sizeScatterRampReach1xPx: 80,
457
472
  sizeScatterRampReach2xPx: 100,
458
473
  sizeOcclusionGain: 0.05,
@@ -715,6 +730,10 @@ var LENS_SIZE_GAIN_MAX = DEFAULT_MATERIAL_PROFILE.lensSizeGainMax;
715
730
  var SIZE_SCATTER_GAIN_MAX = DEFAULT_MATERIAL_PROFILE.sizeScatterGainMax;
716
731
  var SIZE_SCATTER_FLOOR = DEFAULT_MATERIAL_PROFILE.sizeScatterFloor;
717
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;
718
737
  var SIZE_SCATTER_RAMP_START_THIN_1X = DEFAULT_MATERIAL_PROFILE.sizeScatterRampStartThin1x;
719
738
  var SIZE_SCATTER_RAMP_START_THICK_1X = DEFAULT_MATERIAL_PROFILE.sizeScatterRampStartThick1x;
720
739
  var SIZE_SCATTER_RAMP_START_THIN_2X = DEFAULT_MATERIAL_PROFILE.sizeScatterRampStartThin2x;
@@ -778,6 +797,10 @@ function withMaterialOverrides(base, patch) {
778
797
  sizeScatterGainMax: patch.sizeScatterGainMax ?? base.sizeScatterGainMax,
779
798
  sizeScatterFloor: patch.sizeScatterFloor ?? base.sizeScatterFloor,
780
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,
781
804
  sizeScatterRampStartThin1x: patch.sizeScatterRampStartThin1x ?? base.sizeScatterRampStartThin1x,
782
805
  sizeScatterRampStartThick1x: patch.sizeScatterRampStartThick1x ?? base.sizeScatterRampStartThick1x,
783
806
  sizeScatterRampStartThin2x: patch.sizeScatterRampStartThin2x ?? base.sizeScatterRampStartThin2x,
@@ -969,13 +992,13 @@ function lensDirection(normal, offset, half, ovalization) {
969
992
  return len > 1e-9 ? [gx / len, gy / len] : [normal[0], normal[1]];
970
993
  }
971
994
  function sizeScatterSigma(sigmaPx, spanPx, profile = DEFAULT_MATERIAL_PROFILE, devicePixelRatio = 1, extentsCssPx) {
972
- return sizeScatterSigmaAt(sigmaPx, scatterThickness(spanPx, 1, profile, devicePixelRatio, extentsCssPx), profile);
995
+ return sizeScatterSigmaAt(sigmaPx, scatterThickness(spanPx, 1, profile, devicePixelRatio, extentsCssPx), profile, devicePixelRatio, spanPx);
973
996
  }
974
997
  function scatterRampStart(devicePixelRatio, profile = DEFAULT_MATERIAL_PROFILE, spanPx = 0) {
975
998
  const thin = rampAtScale(profile.sizeScatterRampStartThin1x, profile.sizeScatterRampStartThin2x, devicePixelRatio);
976
999
  const thick = rampAtScale(profile.sizeScatterRampStartThick1x, profile.sizeScatterRampStartThick2x, devicePixelRatio);
977
1000
  const far = rampAtScale(profile.sizeScatterRampStartFar1x, profile.sizeScatterRampStartFar2x, devicePixelRatio);
978
- const decline = smoothstep(profile.sizeSpanMax, profile.sizeScatterSpanMax, spanPx);
1001
+ const decline = smoothstep(profile.sizeSpanMax, scatterSpanMaxAtScale(profile, devicePixelRatio), spanPx);
979
1002
  return thin + (thick - thin) * sizeThickness(spanPx, profile) + (far - thick) * decline;
980
1003
  }
981
1004
  function scatterRampReachDevicePx(devicePixelRatio, profile = DEFAULT_MATERIAL_PROFILE) {
@@ -989,24 +1012,43 @@ function rampAtScale(at1x, at2x, devicePixelRatio) {
989
1012
  return at2x;
990
1013
  return at1x + (at2x - at1x) * t;
991
1014
  }
992
- function scatterDeepThickness(spanPx, profile = DEFAULT_MATERIAL_PROFILE) {
993
- const floor = clampUnit(profile.sizeScatterFloor);
994
- return floor + (1 - floor) * smoothstep(profile.sizeSpanMin, profile.sizeScatterSpanMax, spanPx);
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);
1027
+ }
1028
+ function scatterFloorAtScale(profile = DEFAULT_MATERIAL_PROFILE, devicePixelRatio = 1) {
1029
+ return clampUnit(rampAtScale(profile.sizeScatterFloor, profile.sizeScatterFloor2x, devicePixelRatio));
1030
+ }
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);
995
1037
  }
996
1038
  function scatterSharpShare(uDevicePx, devicePixelRatio, profile = DEFAULT_MATERIAL_PROFILE, spanPx = 0) {
997
- const deepSharp = 1 - scatterDeepThickness(spanPx, profile);
1039
+ const deepSharp = 1 - scatterDeepThickness(spanPx, profile, devicePixelRatio);
998
1040
  const start = scatterRampStart(devicePixelRatio, profile, spanPx);
999
1041
  const reach = Math.max(scatterRampReachDevicePx(devicePixelRatio, profile), 1e-6);
1000
1042
  const excursion = Math.max(start - deepSharp, 0) * Math.max(1 - Math.max(uDevicePx, 0) / reach, 0);
1001
1043
  return clampUnit(deepSharp + excursion);
1002
1044
  }
1003
1045
  function scatterThickness(spanPx, fold, profile = DEFAULT_MATERIAL_PROFILE, devicePixelRatio = 1, extentsCssPx) {
1004
- const floor = clampUnit(profile.sizeScatterFloor);
1046
+ const floor = scatterFloorAtScale(profile, devicePixelRatio);
1005
1047
  const mean = scatterRampAreaMean(spanPx, profile, devicePixelRatio, extentsCssPx);
1006
1048
  return clampUnit(floor + (mean - floor) * fold);
1007
1049
  }
1008
1050
  function scatterRampAreaMean(spanPx, profile = DEFAULT_MATERIAL_PROFILE, devicePixelRatio = 1, extentsCssPx) {
1009
- const deep = scatterDeepThickness(spanPx, profile);
1051
+ const deep = scatterDeepThickness(spanPx, profile, devicePixelRatio);
1010
1052
  const amplitude = Math.max(scatterRampStart(devicePixelRatio, profile, spanPx) - (1 - deep), 0);
1011
1053
  if (amplitude <= 0)
1012
1054
  return clampUnit(deep);
@@ -1021,9 +1063,10 @@ function scatterRampAreaMean(spanPx, profile = DEFAULT_MATERIAL_PROFILE, deviceP
1021
1063
  const triangleMean = (perimeter * limit - 4 * limit * limit - perimeter * limit * limit / (2 * reach) + 8 * limit * limit * limit / (3 * reach)) / area;
1022
1064
  return clampUnit(deep - amplitude * triangleMean);
1023
1065
  }
1024
- function sizeScatterSigmaAt(sigmaPx, scatter, profile = DEFAULT_MATERIAL_PROFILE) {
1066
+ function sizeScatterSigmaAt(sigmaPx, scatter, profile = DEFAULT_MATERIAL_PROFILE, devicePixelRatio = 1, spanPx) {
1025
1067
  const mix = clampUnit(scatter);
1026
- return sigmaPx * (1 + (profile.sizeScatterGainMax - 1) * mix);
1068
+ const gain = spanPx === void 0 ? scatterGainAtScale(profile, devicePixelRatio) : scatterGainAt(spanPx, profile, devicePixelRatio);
1069
+ return sigmaPx * (1 + (gain - 1) * mix);
1027
1070
  }
1028
1071
  function clampUnit(value) {
1029
1072
  return Math.min(1, Math.max(0, value));
@@ -2962,7 +3005,8 @@ var WGSL_OPTICS_PASS = `struct OpticsUniforms {
2962
3005
  light : vec4f,
2963
3006
  /// hasBackdrop, fieldSize.xy, fieldUpsampled
2964
3007
  flags : vec4f,
2965
- /// the size law's gains (W2): scatterGainMax, occlusionGain, shadowGainMax,
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,
2966
3010
  /// bodyChainLod \u2014 the last being the chain level whose blur already matches the
2967
3011
  /// body texture, which is what the scattering term measures its octaves from
2968
3012
  size : vec4f,
@@ -2989,7 +3033,12 @@ var WGSL_OPTICS_PASS = `struct OpticsUniforms {
2989
3033
  /// shadow's shift needs when it lands outside the texture; the body depth
2990
3034
  /// ramp's THICK start (z, W13 G1's third form \u2014 the thin end is scatter.y and
2991
3035
  /// this pass mixes the two by the pixel's own sizeThickness), and (w) the
2992
- /// padding a uniform's vec4 alignment requires
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
2993
3042
  shadowSize : vec4f,
2994
3043
  /// the backdrop tone response's anchors (W9): the three solid anchors'
2995
3044
  /// ENCODED-space means (xyz), and the backdrop's linear-space mean (w) \u2014 the
@@ -3001,7 +3050,10 @@ var WGSL_OPTICS_PASS = `struct OpticsUniforms {
3001
3050
  toneRowThin : vec4f,
3002
3051
  /// the thick row (sizeThickness saturated), same layout
3003
3052
  toneRowThick : vec4f,
3004
- /// the size law's bands: the scatter facet's floor (x) and the depth ramp's
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
3005
3057
  /// THIN start s0 at the ratio this group draws at (y, W13 G1 \u2014 the CPU has
3006
3058
  /// already interpolated it between the profile's 1x and 2x anchors; the thick
3007
3059
  /// end is shadowSize.z), and the
@@ -3019,8 +3071,9 @@ var WGSL_OPTICS_PASS = `struct OpticsUniforms {
3019
3071
  /// ramp's reach in group-local CSS px (z, W13 G1) \u2014 the profile names it in
3020
3072
  /// device px and the CPU divides by the ratio, so that this shader can read
3021
3073
  /// the field's own CSS-px depth without a second conversion; the scatter span
3022
- /// curve's band top sizeScatterSpanMax (w, W11c), which W13 G1 keeps
3023
- /// underneath the ramp as its deep value
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
3024
3077
  lensOval : vec4f,
3025
3078
  /// the outer shadow's THICK regime (W14 G1): the composite's peak linear
3026
3079
  /// occlusion at casting spans 96, 128 and 160 CSS px (xyz), already folded
@@ -3400,6 +3453,13 @@ fn fs_optics(in : FullscreenOut) -> @location(0) vec4f {
3400
3453
  * band the reference has ABOVE the body, and a span whose deep sharp share
3401
3454
  * already exceeds s0 has nothing to add rather than something to subtract.
3402
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
+ *
3403
3463
  * The fold keeps its W11c semantics on the composed law: the floor is the
3404
3464
  * frost the material has at any size and is not folded, everything above it -
3405
3465
  * the span curve's rise and the ramp's excursion alike - is depth and is, so
@@ -3473,7 +3533,13 @@ fn fs_optics(in : FullscreenOut) -> @location(0) vec4f {
3473
3533
  * by a share that is \u2248 0.4 on a small control and rises with the span. The
3474
3534
  * mix, not the level, is what the span moves.
3475
3535
  */
3476
- let scatterLod = clamp(ou.size.w + log2(max(ou.size.x, 1e-4)), 0.0, ou.lens.w);
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);
3477
3543
 
3478
3544
  var backdrop = vec3f(0.0);
3479
3545
  if (ou.flags.x > 0.5) {
@@ -4074,7 +4140,7 @@ function createPassRunner(context) {
4074
4140
  d[56] = args.outerShadowSizeGain;
4075
4141
  d[57] = args.outerShadowRectCssHeight;
4076
4142
  d[58] = args.sizeScatterRampStartThick;
4077
- d[59] = 0;
4143
+ d[59] = args.sizeScatterGainFar;
4078
4144
  d[60] = args.backdropToneAnchorX[0];
4079
4145
  d[61] = args.backdropToneAnchorX[1];
4080
4146
  d[62] = args.backdropToneAnchorX[2];
@@ -4942,7 +5008,10 @@ function createWebGPURenderer(options = {}) {
4942
5008
  const input = [...groups.values()].find((entry) => entry.input.backdropSourceId === sourceId)?.input;
4943
5009
  return input === void 0 ? "regular" : variantOf(input);
4944
5010
  };
4945
- const bodySigmaCssFor = (sourceId) => opticsUnderPolicy(material.optics[sourceVariant(sourceId)], accessibility, material).blurSigma;
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
+ };
4946
5015
  const groupTintSeed = (input) => input.surfaces.find((surface) => surface.fieldReferenceOnly !== true && (surface.tint?.strength ?? 0) > 0)?.tint?.color;
4947
5016
  const stateOf = (input, resolution) => {
4948
5017
  const resolved = resolution?.groups.find((group) => group.groupId === input.groupId);
@@ -5000,9 +5069,14 @@ function createWebGPURenderer(options = {}) {
5000
5069
  sourceId: request.sourceId,
5001
5070
  epoch: request.epoch,
5002
5071
  resolution: request.resolution,
5003
- // The body's sharp width in CSS px at every device scale (W13 Decision
5004
- // Log 8); the heavy component rides the same conversion — its level
5005
- // is this one plus log2(sizeScatterGainMax) octaves.
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.
5006
5080
  bodySigmaCss: bodySigmaCssFor(request.sourceId),
5007
5081
  viewportCss: [viewport.widthCss, viewport.heightCss],
5008
5082
  ...isUsablePlacement(placement) ? { placement } : {}
@@ -5159,7 +5233,20 @@ function createWebGPURenderer(options = {}) {
5159
5233
  // group whose members are not one size. `bodyChainLod` is where the body
5160
5234
  // blur already sits on the chain — only the pyramid that built it knows
5161
5235
  // the CSS-px-to-texel conversion, so it publishes the σ and this converts.
5162
- sizeScatterGainMax: material.sizeScatterGainMax,
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),
5163
5250
  sizeOcclusionGain: material.sizeOcclusionGain,
5164
5251
  sizeShadowGainMax: material.sizeShadowGainMax,
5165
5252
  bodyChainLod: chainLodForSigma(pyramid?.bodySigmaTexels ?? 0),
@@ -5168,8 +5255,12 @@ function createWebGPURenderer(options = {}) {
5168
5255
  // same fold `sizeThicknessUnderPolicy` applies on the CPU.
5169
5256
  sizeSpanMin: material.sizeSpanMin,
5170
5257
  sizeSpanMax: material.sizeSpanMax,
5171
- sizeScatterFloor: material.sizeScatterFloor,
5172
- sizeScatterSpanMax: material.sizeScatterSpanMax,
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),
5173
5264
  // The body's depth ramp (W13 G1, claims §5.61 §2, §5.64 §5), resolved
5174
5265
  // from the viewport's own ratio: the profile anchors the start's thin
5175
5266
  // and thick ends and the reach at dpr 1 and dpr 2, and the CPU
@@ -5182,7 +5273,7 @@ function createWebGPURenderer(options = {}) {
5182
5273
  sizeScatterRampStartThick: scatterRampStart(dpr, material, material.sizeSpanMax),
5183
5274
  // The fourth form's far end, at the scatter span curve's top; the shader
5184
5275
  // declines from the thick end to it along that curve.
5185
- sizeScatterRampStartFar: scatterRampStart(dpr, material, material.sizeScatterSpanMax),
5276
+ sizeScatterRampStartFar: scatterRampStart(dpr, material, scatterSpanMaxAtScale(material, dpr)),
5186
5277
  sizeScatterRampReachCssPx: scatterRampReachDevicePx(dpr, material) / dpr,
5187
5278
  sizeFold: material.refractionScale[accessibilityRefractionCap(policy)],
5188
5279
  backdropTone,
@@ -5518,6 +5609,6 @@ function createWebGPURenderer(options = {}) {
5518
5609
  };
5519
5610
  }
5520
5611
 
5521
- 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_GAIN_MAX, 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_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, scatterRampAreaMean, scatterRampReachDevicePx, scatterRampStart, scatterSharpShare, scatterThickness, shadowDepthPx, sizeOcclusionAlpha, sizeOcclusionAlphaAt, sizeOuterShadowOcclusion, sizeOuterShadowOcclusionAt, sizeScatterSigma, sizeScatterSigmaAt, sizeShadowDepth, sizeShadowDepthAt, sizeThickness, sizeThicknessUnderPolicy, snapRectToDevicePixels, srgbToLinear, srgbToLinearChannel, statsFromBuffer, supportsTimestamps, texelsPerCssPx, tintShade, tintShadeLayer, tintToneAdaptation, tintedMaterialColour, validateAppTexture, withMaterialOverrides };
5522
- //# sourceMappingURL=dist-RGWZAZ6J.js.map
5523
- //# sourceMappingURL=dist-RGWZAZ6J.js.map
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