Imagination pass 3: envelope-then-noise fields, scenarios with empty space, the tile pre-pass, the split Phacelle

Revision 2 specs for the two CultLib cuts of cultmath-tapes, as follow-ups on
hands/cultmath-interval-ops (c9bd003b) and hands/cultmath-glsl-lowering
(5ae207e8), under the rulings operator-tile-amortized-march,
operator-split-phacelle, operator-compiler-downloads and
self-exp-measure-in-site, and the operator's challenge to the march.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-10-02 22:39:33 +02:00
commit 070a58d2aa
3 changed files with 601 additions and 9 deletions

View file

@ -0,0 +1,200 @@
{
"campaign": "cultmath-tapes",
"cut": "glsl-lowering",
"revision": 2,
"title": "GLSL lowering r2: Phacelle as its own MPL-2.0 file so CultMath.glsl is MIT, notices corrected, iv_exp in the fixture, fixture regenerated, glslang and dxc run",
"repo": "GameCult/CultLib",
"branch": "hands/cultmath-glsl-lowering",
"base": "c9bd003b",
"depends_on": [
"cultmath-tapes:cut_spec:cut-interval-ops.r2"
],
"first": [
"Follow-up on cut report cultmath-tapes:cut_report:cut-glsl-lowering.h1 (head 5ae207e8, worktree F:\\Projects\\CultLib-glsl). Rulings in force: operator-split-phacelle, operator-compiler-downloads, self-exp-measure-in-site. Map: docs/cultmath-interval-ground-cut.md, section 'Pass 3'. First action: rebase hands/cultmath-glsl-lowering onto the head of hands/cultmath-interval-ops after its r2 lands (the base above is r1's head; the report records the real base). Regenerate the GLSL and the fixture on the rebased HLSL before any edit, so the first commit is the regeneration alone and shows what interval-ops r2 changed (cultmath_iv_frustum_ball is new; its fixture family is exact-tolerance geometry).",
"The split (operator-split-phacelle). Transformation step 1 becomes: each #include is inlined in place, except an include whose file is under its own licence, which is lowered on its own into its own output file. The list of such includes is one table in GlslLowering, SeparateFiles = { \"CultMath.Phacelle.hlsl\" -> \"CultMath.Phacelle.glsl\" }, with the reason beside the entry (file-level MPL-2.0). That is an enumerated rule of the lowering, documented as step 1 in design.md; it is not a per-function special case, and nothing else is keyed by name. Lower(root) returns the two texts; Lower(string) stays for the inlined body if that is simpler, but there is one resolver (ReadShaderSource, generalised to accept the include predicate), never a second.",
"shaders/CultMath.glsl: the provenance line says it is generated from CultMath.hlsl and CultMath.Interval.hlsl, MIT, and that cultmath_phacelle is in CultMath.Phacelle.glsl, to be concatenated after this file by consumers that call it. The dropped include line leaves nothing behind. shaders/CultMath.Phacelle.glsl: its own provenance line (generated from CultMath.Phacelle.hlsl, MPL-2.0, requires CultMath.glsl before it), the MPL-2.0 header and attribution exactly as the HLSL carries them, the CultPhasor struct and cultmath_phacelle, guard CULTMATH_PHACELLE_GLSL. Both files pinned by CommittedGlslEqualsLowering; CULTMATH_WRITE_GLSL=1 writes both.",
"Pin tests over both files: NoHlslOnlyTokensSurvive runs on each; EveryPublicFunctionIsLowered takes the union of both files' definitions against the HLSL (minus texture samplers) and additionally asserts cultmath_phacelle and CultPhasor appear only in the Phacelle file; a new PhacelleFileCompilesOnlyAfterTheLibrary is not needed, the compile check below proves it.",
"The fixture (self-exp-measure-in-site). The site measures WebGL2 exp against the fixture, so iv_exp joins it; its C# bits come from the platform exp and differ by OS, so its entry carries tolerance \"ulp-bounded, platform exp\" and names the OS that generated it. GoldenFixtureMatchesCSharp parses the committed JSON (System.Text.Json, BCL) and compares family by family: text-equal for every family except those whose tolerance names the platform, which compare case by case within 1 ulp and print the maximum observed. One fixture, one writer, one tolerance field already in the format; no second fixture. The phacelle family stays in the fixture; the site's parity harness concatenates both GLSL files for that run (the site ships only CultMath.glsl), say so in design.md.",
"Notices: packages/cultmath/THIRD-PARTY-NOTICES.md :35-43 lists the MPL-2.0 files as math.Phacelle.cs, CultPhasor.cs, CultMath.Phacelle.hlsl with its Unity copy, and shaders/CultMath.Phacelle.glsl; CultMath.glsl is MIT and says so in its header. The package licence expressions (MIT AND MPL-2.0) do not change: the NuGet still ships the HLSL include. README :36-37 and :44-49 name both GLSL files and when a consumer needs the second. design.md :136-192: step 1 rewritten, the second file, the wrapper runs, the fixture's platform tolerance. Unity ships neither GLSL file.",
"Compile checks on Starfire (operator-compiler-downloads), one job each, detached with a log, polled in the foreground: tools/get-glslang.ps1 (16.6.0, the pinned SHA already in the script; if the published digest differs, stop and report, do not edit the pin to match); tools/compile-glsl.ps1 gains a -Phacelle switch and runs twice: the MIT-only wrapper over CultMath.glsl calling every function in it, and the full wrapper over both files calling every function including cultmath_phacelle. Negative controls, both must fail: -Body 'float x = lerp(0.0, 1.0, 0.5);' and the MIT-only wrapper with -Body calling cultmath_phacelle (proves Phacelle is not in CultMath.glsl). Then tools/get-dxc.ps1 and dxc -T lib_6_3 -HV 2021 on both HLSL copies, exit 0. The browser-pane WebGL2 probe of r1 is repeated on the two-file wrapper.",
"Carried from r1 and still out of scope, name them in the report: the fixture's ulp bounds on WebGL2 are measured by the site cut; Stryker is not re-run unless packages/cultmath/src changes in this range (it should not; the lowering lives in tests). Deviations r1 reported that stand: the anchored token regex; the iv_exp absence is now resolved by this revision; the HLSL edits (damp splat, pcg3d order) stay."
],
"deletes": [
{
"path": "packages/cultmath/shaders/CultMath.glsl",
"lines": 100,
"note": "the inlined Phacelle block (:510-612 at 5ae207e8: MPL header, CultPhasor, cultmath_phacelle) leaves this file; it is regenerated, not edited."
},
{
"path": "packages/cultmath/THIRD-PARTY-NOTICES.md",
"lines": 3,
"note": ":39-41 the sentence making CultMath.glsl MPL-2.0 as a whole."
}
],
"keeps_moves": [
"Transformation steps 2-9 and their order: unchanged; the cast scanner, saturate lowering, the guard and provenance mechanism stay.",
"Both tracked CultMath.hlsl copies stay one blob; no HLSL edit is expected in this revision (a dialect gap found by the two-file compile is fixed in the HLSL in the common subset, bit-identical, listed in the report).",
"HlslSourceCompatibilityTests' mirror and FindCultMathRoot: the resolver generalises in place; no second include resolver.",
"tests/CultMath.Tests/fixtures/glsl-parity.json: the same file, regenerated; families keep their order so the diff shows only the two additions."
],
"adds": [
"packages/cultmath/shaders/CultMath.Phacelle.glsl: generated, MPL-2.0, guard CULTMATH_PHACELLE_GLSL.",
"GlslLowering: the SeparateFiles table and the two-text Lower; GlslMirrorTests: the second file in CommittedGlslEqualsLowering, NoHlslOnlyTokensSurvive and EveryPublicFunctionIsLowered; GoldenFixtureMatchesCSharp's per-family comparison with the platform tolerance.",
"fixture families: cultmath_iv_frustum_ball (exact) and cultmath_iv_exp (ulp-bounded, platform exp).",
"tools/compile-glsl.ps1: -Phacelle switch (appends CultMath.Phacelle.glsl after the library and includes cultmath_phacelle in the calls).",
"design.md and README: the two files, the concatenation order, the fixture tolerance."
],
"file_changes": [
{
"location": {
"path": "packages/cultmath/tests/CultMath.Tests/GlslLowering.cs",
"line": 24
},
"change": "Lower takes the root (or the resolver's two texts) and returns both outputs; SeparateFiles table with the MPL reason; ProvenanceLine per file."
},
{
"location": {
"path": "packages/cultmath/tests/CultMath.Tests/HlslSourceCompatibilityTests.cs",
"line": 320
},
"change": "ReadShaderSource accepts a predicate (or returns the separated include's text beside the inlined body); the C# mirror keeps inlining everything."
},
{
"location": {
"path": "packages/cultmath/tests/CultMath.Tests/GlslMirrorTests.cs",
"line": 30
},
"change": "CommittedGlslEqualsLowering over both files; :45 NoHlslOnlyTokensSurvive over both; :54 EveryPublicFunctionIsLowered union plus the phacelle-only-in-its-file assertion; :64 GoldenFixtureMatchesCSharp per family with the platform tolerance."
},
{
"location": {
"path": "packages/cultmath/tools/compile-glsl.ps1",
"line": 1
},
"change": "-Phacelle switch; the library text is CultMath.glsl, plus CultMath.Phacelle.glsl when set; the call list covers whichever functions are present."
},
{
"location": {
"path": "packages/cultmath/THIRD-PARTY-NOTICES.md",
"line": 35
},
"change": "MPL-2.0 file list: the two C# files, the HLSL include and its Unity copy, shaders/CultMath.Phacelle.glsl. CultMath.glsl is MIT."
},
{
"location": {
"path": "packages/cultmath/README.md",
"line": 44
},
"change": "both GLSL files; a consumer concatenates CultMath.Phacelle.glsl after CultMath.glsl only when it calls cultmath_phacelle."
},
{
"location": {
"path": "packages/cultmath/docs/design.md",
"line": 150
},
"change": "step 1 rewritten for the separated include; :182-192 the two wrapper runs and the fixture's platform tolerance; the site's parity run concatenates both files."
}
],
"authority_map": {
"owner": "GlslLowering owns the HLSL -> GLSL transformation and now emits two derived files, one per licence; both are pinned by GlslMirrorTests and never edited. The fixture stays owned by GoldenFixtureMatchesCSharp with one tolerance field per family. THIRD-PARTY-NOTICES.md states which files are MPL-2.0 and is the one place that does.",
"inputs": [
"shaders/CultMath.hlsl, CultMath.Interval.hlsl and CultMath.Phacelle.hlsl text at the rebased head",
"C# math for the fixture values, including the platform exp for iv_exp"
],
"outputs": [
"shaders/CultMath.glsl (MIT) and shaders/CultMath.Phacelle.glsl (MPL-2.0)",
"tests/CultMath.Tests/fixtures/glsl-parity.json with cultmath_iv_frustum_ball and cultmath_iv_exp families"
],
"derived_state": [
"both GLSL files are derived; a hand edit to either fails CommittedGlslEqualsLowering",
"the fixture is derived from C#; the iv_exp family is derived from the generating platform's exp and compared within 1 ulp, every other family bit for bit"
],
"forbidden_writers": [
"no per-function special case in the lowering; the only name-keyed rule is the SeparateFiles table, one entry, licence-justified",
"no second include resolver, no PowerShell or JS copy of the transformation",
"no GLSL in the Unity package; no precision statement in either library file",
"no edit to the pinned glslang or dxc hashes to make a download pass"
],
"shared_paths": [
"the same dotnet test run pins the C# mirror, both GLSL files and the fixture",
"compile-glsl.ps1 is the one wrapper writer for the MIT-only run, the two-file run and both negative controls"
],
"deletion_line": "The Phacelle block leaves CultMath.glsl by regeneration and the MPL sentence leaves the notices before the new file is added. Net: one more generated file and one switch; what it buys is an MIT CultMath.glsl the site can vendor without carrying MPL text, and the compile checks r1 could not run."
},
"verification": {
"builds": [
"Yggdrasil: dotnet test packages/cultmath/tests/CultMath.Tests green on the rebased branch; paste counts; GlslMirrorTests 4/4 with the per-family fixture line printed",
"Starfire: tools/get-glslang.ps1 installs 16.6.0 with the pinned SHA matching; tools/compile-glsl.ps1 exits 0 on the MIT-only wrapper and on -Phacelle; both negative controls fail with the error quoted",
"Starfire: tools/get-dxc.ps1 then dxc -T lib_6_3 -HV 2021 on both HLSL copies, exit 0; FXC cs_5_0 still green",
"Browser pane WebGL2: the two-file wrapper compiles with an empty log"
],
"tests": [
{
"name": "GlslMirrorTests.CommittedGlslEqualsLowering",
"pins": "CultMath.glsl and CultMath.Phacelle.glsl are exactly the lowering of the committed HLSL; the regeneration path is the same code."
},
{
"name": "GlslMirrorTests.EveryPublicFunctionIsLowered",
"pins": "the union of both files equals the HLSL's cultmath_* set minus texture samplers; cultmath_phacelle and CultPhasor live only in the Phacelle file."
},
{
"name": "GlslMirrorTests.NoHlslOnlyTokensSurvive",
"pins": "no HLSL-only token in either file."
},
{
"name": "GlslMirrorTests.GoldenFixtureMatchesCSharp",
"pins": "every family is C#'s current output bit for bit, except iv_exp within 1 ulp of the platform exp; iv_frustum_ball is present and exact."
},
{
"name": "HlslSourceCompatibilityTests.EveryMirrorFunctionMatchesCSharpMath (existing)",
"pins": "no HLSL edit in this range changed a bit."
}
],
"negative": [
{
"pattern": "Mozilla|MPL|Johansen|CultPhasor|cultmath_phacelle\\(",
"scope": "packages/cultmath/shaders/CultMath.glsl: 0 matches; the header's file-name pointer to CultMath.Phacelle.glsl is the only mention of Phacelle."
},
{
"pattern": "^CultPhasor cultmath_phacelle\\(",
"scope": "packages/cultmath/shaders/CultMath.Phacelle.glsl: exactly 1 match."
},
{
"pattern": "CultMath\\.glsl|Phacelle\\.glsl",
"scope": "packages/cultmath/unity/**: 0 matches."
},
{
"pattern": "\\b(float|u?int|bool)[234]\\b|\\blerp\\(|\\bfrac\\(|\\bsaturate\\(|\\brsqrt\\(|\\basuint\\(|\\basfloat\\(|static const|#include|precision (high|medium|low)p",
"scope": "both GLSL files: 0 matches (the anchored regex r1 settled on)."
},
{
"pattern": "CultMath\\.glsl",
"scope": "packages/cultmath/THIRD-PARTY-NOTICES.md: 0 matches in the MPL-2.0 file list; CultMath.Phacelle.glsl appears there once."
},
{
"pattern": "git diff <rebased base>..HEAD -- packages/cultmath/src",
"scope": "empty; the lowering and the fixture live in tests, so interval-ops r2's Stryker run stands for this range."
}
],
"operator": []
},
"estimate": {
"lines_added": 320,
"lines_removed": 130,
"dependencies_added": [],
"dependencies_removed": [],
"formats_added": [
"GLSL ES 3.00 library file (generated, MPL-2.0): shaders/CultMath.Phacelle.glsl"
],
"formats_removed": [],
"targets_added": [],
"targets_removed": []
},
"rulings": [
"cultmath-tapes:ruling:operator-split-phacelle",
"cultmath-tapes:ruling:operator-compiler-downloads",
"cultmath-tapes:ruling:self-exp-measure-in-site",
"cultmath-tapes:ruling:operator-campaign-home"
],
"questions": []
}

View file

@ -0,0 +1,216 @@
{
"campaign": "cultmath-tapes",
"cut": "interval-ops",
"revision": 2,
"title": "Interval ops r2: envelope-then-noise fields, scenarios with authored empty space, the tile march on cell ranges, iv_frustum_ball, wasted probes removed, dxc run",
"repo": "GameCult/CultLib",
"branch": "hands/cultmath-interval-ops",
"base": "c9bd003b",
"depends_on": [],
"first": [
"Follow-up on cut report cultmath-tapes:cut_report:cut-interval-ops.h1 (head c9bd003b, worktree F:\\Projects\\CultLib-iv). Rulings in force: operator-tile-amortized-march (the bound stays; one probe per screen tile), operator-compiler-downloads. The operator's challenge, verbatim in the map's Pass 3 section: r1 measured a uniform noise slab with no empty space to skip, the worst case for intervals, and the density that matters is an analytic envelope (a pre-distortion SDF) modulated by noise, where the envelope's interval is tight and proves whole ray lengths empty in one probe. Map: docs/cultmath-interval-ground-cut.md, 'Pass 3'. r1's ops and bounds stay; this revision touches math.Interval.cs, CultMath.Interval.hlsl (both tracked copies), NoiseBoundTests.cs, design.md and README only.",
"The field, from Aetheria (Assets/Shaders/Volumetric.cginc:165-221, :91-102; Scripts/ServerShared/Zone.cs:369-397 GetHeight, :426 PowerPulse; Resources/Settings.asset DefaultEnvironment and PlanetSettings). Pre-distortion SDF s(p) = p.y + h(p.xz), h >= 0 the well map: h = PowerPulse(|p.xz| / (2R), 2) * 64 (the zone bowl) + sum over wells of PowerPulse(|p.xz - c_b| / r_b, 16) * d_b, with PowerPulse(x, e) = (1 - 4x^2)^e for x in [0, 1/2] and 0 beyond, r_b = 500 M^0.25, d_b = 30 M^0.175 (Aetheria's GravityRadius/GravityDepth curves).",
"(Field, continued.) Noise only where s < S (SafetyDistance 30): fade(s) = 1 - smoothstep(0.75 S, S, s); displaced height s' = s + A fade(s) n(p) with n the warped two-phase coverage noise plus half the detail octave (r1's field shape); density = max(0, (F - s') / B) (Aetheria's floor: FloorOffset F = -20, FloorBlend B = 10; the patch band and the 1e-9 fill are left out). Above the safety distance density is exactly 0, and so is the noise cost: Aetheria's dense march gates the noise pointwise (`if (dist < _SafetyDistance)`); the test's dense march does the same.",
"The composed bound over a tile's frustum slice [z0, z1], every step an existing iv_* op. Envelope over the slice's exact box, not the ball: with slopes m in [m_c - s, m_c + s] per axis and z in [z0, z1], y = iv_mul([z0, z1], m_y) and x = iv_mul([z0, z1], m_x) (plus the tile's translation o); h's interval over the xz box is the sum over bowls of [pulse(far), pulse(near)], near and far the least and greatest distance from the box to the bowl's centre (pulse is monotone decreasing in distance, so each term is exact; the only slack is the sum).",
"(Bound, continued.) iv_s = iv_add(y, iv_h); iv_fade = the ordered endpoints of fade over iv_s (monotone); iv_n = iv_snoise_ball(ball.xyz * F0, ball.w * F0) with ball = iv_frustum_ball(..., warp = D); iv_s' = iv_add(iv_s, iv_scale(iv_mul(iv_fade, iv_n), A)); the slice is provably empty iff iv_s'.lo >= F. When iv_fade.hi == 0 the noise term is identically zero and iv_snoise_ball is not evaluated: the envelope alone proves the slice empty at no snoise cost. That short-circuit is the compounding the ruling expects from tiles.",
"Where the warp lives in the bound. Aetheria warps only the noise argument (triNoise3d(pos + fl * ...)); the surface height is read at the unwarped xz and the displacement is applied to y afterwards. So the envelope is evaluated on the pre-distortion SDF over the unwarped slice (no D), and only the noise ball is enlarged by D (iv_frustum_ball's warp argument). A warp applied to the envelope's argument would be unsound without enlarging the box by D; the field is defined so that it is not. The test EnvelopeBoundEnclosesDensity pins this: 2,000 seeded slices x 64 warped points, the composed bound encloses density(p) with no tolerance (every op is monotone or already widened in r1; if a seeded case fails at an endpoint, widen in the implementation, never the test).",
"iv_frustum_ball(float2 centreSlope, float z0, float z1, float footprintPerDepth, float warp) -> float4(centre, radius), in C# and both HLSL copies, is the one new CultMath function: the noise ball of a tile over a depth segment. Camera: pinhole at the origin looking +z, focal length f pixels; a pixel's ray is p(z') = (m z', z'); a tile's slopes lie in m_c + [-N/(2f), N/(2f)]^2 (full footprint, so sub-pixel jitter is covered), s = N / (sqrt(2) f). Centre c = (m_c z_m, z_m), z_m = (z0 + z1) / 2; radius r = ((z1 - z0) / 2) |(m_c, 1)| + z1 s + warp. Proof: a point of slope m_c + e (|e| <= s) at depth z' is c + (z' - z_m)(m_c, 1) + z'(e, 0), the terms bounded by the two radius terms, and the warp moves it by at most D (WarpedPointsStayEnclosed). The slice is a convex polytope, so every point between rays is enclosed too. The two vectors are never parallel, so the inequality is strict and float32 cannot cross it.",
"(Tile ball, continued.) Test TileBallEnclosesEveryRaySegment: 2,000 seeded tiles (N in {1, 4, 8, 16}, f in [500, 3000], |m_c| <= 1.1, segments across each scenario's z range), 64 points each with sub-pixel offsets, both depth ends and a flow of length <= warp; no tolerance. Hand mutations that must die, listed in the file header: z0 for z1 in the lateral term; |m_c| for |(m_c, 1)|; the warp dropped.",
"Implementation review of r1's march (NoiseBoundTests.cs:344-376), each item fixed here: (1) every probe was charged one snoise per ray; the tile shares it (ruling). (2) Coarse probes were attempted where the ball could never prove emptiness: iv_snoise_ball's hi is n(c) + L r clamped to 1, so a probe with L r F0 >= 1 + cutoff is a guaranteed failure; at r1's StepMax = 2 that is L r F0 = 3.7, so every coarse probe was a wasted evaluation (the 0.74x row). r2 attempts a noise probe only when L * radius * F0 < 1 - (the cutoff the probe needs), otherwise the level is 'maybe' at no cost. (3) Growth was capped at StepMax = 8 Step0, so a ray through clear space paid Depth / StepMax probes; r2 grows to the remaining range (the hierarchy is over cell-index ranges, halving on maybe, doubling on skip, capped only by the grid's end).",
"(Review, continued.) (4) The dense sample evaluated the detail octave even where occupancy was 0; r2's dense sample evaluates coverage first and the detail only when the envelope and coverage leave density > 0, as Aetheria's `if (dist < _SafetyDistance)` does; the dense baseline gets cheaper and the comparison honest. (5) The finest-level probe before integrating a cell stays: amortized over the tile it costs 1/N^2 and can skip 3 snoise per ray. (6) The step never regrew after a dense cell until a skip; with (3) it regrows from the next skip, which is enough.",
"The march, on cell ranges. The dense grid is a scenario-owned list of cell boundaries z_0 < ... < z_J (uniform for the slab scenarios; Aetheria's quadratic spacing z_j = near + (j/J)^2 (far - near), J = 256, for the Aetheria-like ones, from CloudShader.shader:116). MarchTile takes a range [j0, j1): the envelope probe over the slice (z_j0, z_j1); if empty, skip and double the next range; else if the noise probe is affordable (item 2) and proves empty, skip; else if the range is more than one cell, halve; else the cell is dense. It runs to the grid's end once per tile (a pre-pass does not know any ray's transmittance) and yields the dense cells; each of the N^2 rays integrates exactly those cells in order with the dense march's own Integrate and early-out at 0.02. Counted separately: snoise evaluations and envelope evaluations (pointwise in the dense march; interval probes in the tile march), per ray with probes divided by N^2.",
"The cost model, printed and reasoned about in snoise units: a pointwise envelope evaluation or an envelope probe costs e = 0.2 (a handful of pulses or a height-map fetch against ~150 flops of snoise; stated as an estimate, both counts printed so any weight can be applied). Per scenario the test prints: dense snoise and envelope per ray, tile snoise and envelope probes per ray, the combined cost ratio, and the probe-free ceiling (dense cost over the cost of the dense cells alone). The 2x contract is on the combined cost at N = 8 for scenarios (a) and (c); (b) and (d) are printed as the worst cases and not asserted.",
"Scenarios (IV-REPORT lines, each with 64 tiles at N in {1, 4, 8, 16}, warp in {0, D}): (a) height fog: h = 0, camera at y = S + 30, rays with slope m_y in [0, 0.5] (level and up), Aetheria's grid, expected near-total skip (0 snoise either way; the envelope count carries the ratio). (b) inside the fog: h = 0, camera at y = F - 10, rays level, expected near 1x: every ray saturates within a few cells and the pre-pass still probes the whole grid. (c) Aetheria-like: the zone bowl R = 2000 and four wells of mass {100, 1000, 10000, 1000} at seeded positions inside R, camera at a seeded xz in the bowl at y = max(0, S + 10 - h(camera)), rays gazing across with m_y in [-0.25, 0.1], Aetheria's units (noise scale 414 so F0 = 1/414, D = 15 * 8 / 2 = 60, A = 20 with snoise in [-1, 1]), Aetheria's grid. (d) the uniform slab: r1's field and parameters, kept as the reported worst case. The headline is (c) at N = 8, 200 tiles.",
"Imagination's predictions for the report to confirm or refute, N = 8, combined cost: (a) above 50x (dense pays 256 envelope evaluations per ray, the tile about ten probes shared by 64 rays). (b) 0.8-1.0x. (c) 2-4x: the clear cells before the fog surface collapse to a few probes per tile, the fog cells cost the same on both sides; the noise ball proves nothing at Aetheria's scale (L r F0 >= 1.6 at the finest cell with D = 60), so item 2 keeps it from being evaluated and the saving is the envelope's. (d) 1.3-1.5x: the ceiling at L = 10.099261 with the cheaper dense baseline. If (a) or (c) misses 2x, skip the assertion the way r1 did, naming a new question saving-2x-scenarios with the printed numbers; do not tune the fields, the grids or e.",
"Stryker on Yggdrasil from a plain clone: --since:c9bd003b -m math.Interval.cs; report timeouts separately from kills (a timeout is not a demonstrated kill); if the saving test is what times mutants out, exclude it from Stryker's test selection (a measurement, not a pin) rather than raising the timeout. Compile checks on Starfire, one job, detached with a log, polled in the foreground: FXC cs_5_0 over every cultmath_iv_* including the new function, both tracked copies; then tools/get-dxc.ps1 (pinned, ruling operator-compiler-downloads) and dxc -T lib_6_3 -HV 2021 on both copies. Carried from r1 and still out of scope, name them in the report: the Unity CultMath.dll is not rebuilt; the Unity HLSL copies stay one blob with packages/cultmath/shaders; public asfloat(uint) stays. Branch from c9bd003b; if main moved by docs only, say so as r1 did."
],
"deletes": [
{
"path": "packages/cultmath/tests/CultMath.Tests/NoiseBoundTests.cs",
"lines": 33,
"note": ":344-376 MarchIntervals, the per-ray adaptive march with its StepMax cap and unconditional probes, replaced by MarchTile on cell ranges."
},
{
"path": "packages/cultmath/tests/CultMath.Tests/NoiseBoundTests.cs",
"lines": 10,
"note": ":401-408 the doc comment naming the r1 fork and :424-425 the Assert.Skip naming it; the fork is answered. A skip survives only on a shortfall in (a) or (c), naming saving-2x-scenarios."
},
{
"path": "packages/cultmath/tests/CultMath.Tests/NoiseBoundTests.cs",
"lines": 12,
"note": ":319-330 Density: the unconditional three-snoise sample is replaced by the envelope-gated sample (coverage first, detail only when density can be nonzero)."
},
{
"path": "packages/cultmath/docs/design.md",
"lines": 15,
"note": ":423-437 the saving paragraph with r1's numbers (0.93x, 1.83x, 0.74x), replaced by the scenario paragraph with r2's printed numbers."
}
],
"keeps_moves": [
"Every iv_* op, both ball bounds, SNOISE_LIPSCHITZ and its provenance tests: untouched. The ruling keeps the bound; only the field model, the march and its test change.",
"SavingField's noise shape (F0 0.35 in the slab, the warped two-phase coverage, the detail octave, period 4) and (d)'s parameters: kept, as the worst case; scenario (d) must reproduce r1's dense count where the detail gating does not bite (print it).",
"MarchDense and Integrate: unchanged except that Density is envelope-gated; the dense grid becomes the scenario's boundary list.",
"IntervalTests, WarpedPointsStayEnclosed, TightnessReport, MeasureLipschitz, LipschitzConstantPinsSampledGradients: unchanged.",
"HlslSourceCompatibilityTests: no change expected; the new function must find its C# twin through the existing mirror."
],
"adds": [
"packages/cultmath/src/CultMath/math.Interval.cs: iv_frustum_ball after iv_fbm_ball, doc comment carrying the derivation and that the caller scales by F0 and adds its translation.",
"packages/cultmath/shaders/CultMath.Interval.hlsl: cultmath_iv_frustum_ball, same text in both tracked copies.",
"tests/CultMath.Tests/NoiseBoundTests.cs: Scenario (grid, wells, camera, ray slopes, units), SavingField with the envelope (PowerPulse wells, fade, floor), EnvelopeBound (the composed interval, test-side; iv_* ops only), MarchTile, the per-tile Run; TileBallEnclosesEveryRaySegment; EnvelopeBoundEnclosesDensity; IntervalSkipHalvesEvaluations prints the scenario x N x warp grid and asserts (a) and (c) at N = 8.",
"packages/cultmath/docs/design.md, 'Intervals': the frustum-ball paragraph after :401; the envelope-then-noise paragraph (how a consumer composes the bound, the short-circuit, where the warp lives); the rewritten saving paragraph with the four scenarios and the headline.",
"packages/cultmath/README.md :38-43: iv_frustum_ball named; one sentence that the bounds compose with an analytic envelope."
],
"file_changes": [
{
"location": {
"path": "packages/cultmath/src/CultMath/math.Interval.cs",
"line": 152
},
"change": "after iv_fbm_ball: public static float4 iv_frustum_ball(float2 centreSlope, float z0, float z1, float footprintPerDepth, float warp)."
},
{
"location": {
"path": "packages/cultmath/shaders/CultMath.Interval.hlsl",
"line": 91
},
"change": "after cultmath_iv_fbm_ball: float4 cultmath_iv_frustum_ball(...); both tracked copies."
},
{
"location": {
"path": "packages/cultmath/tests/CultMath.Tests/NoiseBoundTests.cs",
"line": 294
},
"change": "SavingField becomes envelope-then-noise over a Scenario; Density is envelope-gated; MarchTile replaces MarchIntervals; Run draws tiles and N^2 rays each, counting snoise and envelope evaluations separately."
},
{
"location": {
"path": "packages/cultmath/tests/CultMath.Tests/NoiseBoundTests.cs",
"line": 409
},
"change": "IntervalSkipHalvesEvaluations: scenarios (a)-(d) x N in {1, 4, 8, 16} x warp in {0, D}, 64 tiles each; headline (c) at N 8, 200 tiles; assert >= 2x combined cost for (a) and (c) at N = 8, else Skip naming saving-2x-scenarios."
},
{
"location": {
"path": "packages/cultmath/tests/CultMath.Tests/NoiseBoundTests.cs",
"line": 89
},
"change": "TileBallEnclosesEveryRaySegment and EnvelopeBoundEnclosesDensity beside WarpedPointsStayEnclosed."
},
{
"location": {
"path": "packages/cultmath/docs/design.md",
"line": 401
},
"change": "the frustum-ball and envelope-composition paragraphs; :423-437 the saving paragraph rewritten with r2's printed numbers only."
},
{
"location": {
"path": "packages/cultmath/README.md",
"line": 38
},
"change": "iv_frustum_ball named; the envelope composition in one sentence."
}
],
"authority_map": {
"owner": "CultMath owns the interval ops, the noise bounds and now the frustum ball; a consumer (the test's SavingField, the site's pre-pass, Aetheria's march) owns its envelope and composes the bound from iv_* ops. The saving test owns the proving-ground numbers per scenario; the site's browser measurement owns the shipped gain. The bound (SNOISE_LIPSCHITZ, iv_snoise_ball) is unchanged.",
"inputs": [
"the r1 interval surface, unchanged",
"a tile's geometry (centre slope, depth segment, footprint per depth, warp bound) and a scenario's grid, wells, camera and units; Aetheria's shapes and settings as read from its checkout, cited by file:line in the test"
],
"outputs": [
"iv_frustum_ball / cultmath_iv_frustum_ball: a ball enclosing every point of every ray of the tile over the segment, warped",
"printed numbers per scenario: dense and tile snoise and envelope counts per ray, combined cost ratio, probe-free ceiling, at N in {1, 4, 8, 16}"
],
"derived_state": [
"the tile ball and the envelope box are derived from the tile's geometry; no radius is read from a texture, a uniform or a tuned constant",
"whether a noise probe is attempted is derived from L, the radius and the cutoff (item 2), never from a mode flag",
"the probe-free ceiling is derived from the same run; it states what no amortization can beat"
],
"forbidden_writers": [
"no change to SNOISE_LIPSCHITZ, iv_snoise_ball, the noise shape or e; the scenarios may not be tuned toward 2x",
"no per-ray probe in the tile march; no early-out of the pre-pass on any ray's transmittance",
"no warp on the envelope's argument; the warp enlarges the noise ball only",
"no new CultMath function for the envelope: PowerPulse, the box and the composition are consumer-side, built from iv_* ops",
"no tape, prune, affine or choice token in the interval files; no number in design.md or a comment that the committed test did not print in this run"
],
"shared_paths": [
"the dense march and the tile march integrate the same cells of the same grid with the same Integrate; the transmittance check is the proof that nothing provably empty was there",
"the one composed bound (EnvelopeBound) serves the enclosure test and the march; no second composition",
"the existing mirror test covers the new HLSL function; the Unity copy stays one blob"
],
"deletion_line": "r1's per-ray march, its unconditional probes, its growth cap and the unconditional three-snoise dense sample go first; the tile march on cell ranges and the gated sample are written in their place. Nothing in src is deleted. The liability added is one geometry function and two tests plus the scenario model; it buys the tile-march soundness invariant in three runtimes and a saving number measured where there is empty space to find."
},
"verification": {
"builds": [
"Yggdrasil: dotnet test packages/cultmath/tests/CultMath.Tests green (MeasureLipschitz/MeasureMonotonicity excluded by trait); paste counts and every IV-REPORT line",
"Yggdrasil: dotnet stryker --since:c9bd003b -m math.Interval.cs from a plain clone; 0 survivors; timeouts listed separately from kills",
"Starfire: FXC cs_5_0 kernel over every cultmath_iv_* including cultmath_iv_frustum_ball, both HLSL copies",
"Starfire: tools/get-dxc.ps1 then dxc -T lib_6_3 -HV 2021 on both HLSL copies, exit 0"
],
"tests": [
{
"name": "NoiseBoundTests.TileBallEnclosesEveryRaySegment",
"pins": "every sample point of every ray of an N x N tile over a depth segment, sub-pixel jitter and warp included, lies inside iv_frustum_ball."
},
{
"name": "NoiseBoundTests.EnvelopeBoundEnclosesDensity",
"pins": "the composed bound (envelope box, fade, noise ball enlarged by the warp) encloses density at every warped point of 2,000 slices, no tolerance; intervals-enclose holds for the composition the site and Aetheria will use."
},
{
"name": "NoiseBoundTests.IntervalSkipHalvesEvaluations",
"pins": "scenarios (a) and (c) at N = 8 cost at most half the dense combined cost with transmittance within 1e-3; prints every scenario, N and warp with both counts and the ceiling; a shortfall skips naming saving-2x-scenarios."
},
{
"name": "IntervalTests.EveryOpEnclosesItsPoints (existing)",
"pins": "r1's enclosure for every op still holds."
},
{
"name": "HlslSourceCompatibilityTests.EveryMirrorFunctionMatchesCSharpMath (existing)",
"pins": "cultmath_iv_frustum_ball matches iv_frustum_ball bit for bit."
}
],
"negative": [
{
"pattern": "tape|prune|affine|choice",
"scope": "math.Interval.cs and CultMath.Interval.hlsl: 0 matches (case-insensitive)."
},
{
"pattern": "snoise_grad|fbm_grad",
"scope": "math.Interval.cs and CultMath.Interval.hlsl: 0 matches."
},
{
"pattern": "PowerPulse|pulse|envelope|Scenario",
"scope": "math.Interval.cs and CultMath.Interval.hlsl: 0 matches; the envelope is consumer-side."
},
{
"pattern": "Assert\\.Skip",
"scope": "NoiseBoundTests.cs: at most one, in IntervalSkipHalvesEvaluations, naming saving-2x-scenarios; 0 if (a) and (c) meet 2x."
},
{
"pattern": "10\\.099261|0\\.35f",
"scope": "git diff c9bd003b..HEAD -- packages/cultmath/src: the constant is not touched; the slab's F0 stays in (d)."
},
{
"pattern": "git diff --name-only c9bd003b..HEAD",
"scope": "only packages/cultmath/**; both tracked CultMath.Interval.hlsl copies change together."
}
],
"operator": []
},
"estimate": {
"lines_added": 420,
"lines_removed": 80,
"dependencies_added": [],
"dependencies_removed": [],
"formats_added": [],
"formats_removed": [],
"targets_added": [],
"targets_removed": []
},
"rulings": [
"cultmath-tapes:ruling:operator-tile-amortized-march",
"cultmath-tapes:ruling:operator-compiler-downloads",
"cultmath-tapes:ruling:operator-campaign-home"
],
"questions": []
}

View file

@ -1,8 +1,12 @@
# CultMath intervals and the gamecult.org ground: cut map
Status: Imagination pass 2, 2026-10-02. Pass 1's Q1–Q3 are answered (below);
the Self opens the `cultmath-tapes` campaign from this revision. Where this
map and the Body disagree, the Body wins and this map is stale.
Status: Imagination pass 3, 2026-10-02. Pass 1's Q1–Q3 are answered (below);
the Self opened the `cultmath-tapes` campaign from pass 2; pass 3 (section
"Pass 3" near the end) revises the two CultLib cuts after their first Hands
reports and the operator's challenge to the march, and changes the site field
to envelope-then-noise. Where pass 3 and an earlier section disagree, pass 3
wins. Where this map and the Body disagree, the Body wins and this map is
stale.
Pinned HEADs (every `file:line` below is against these):
@ -153,6 +157,10 @@ cuts cite `site-masthead:ruling:operator-chromeless-column` and depend on
### Density field (site-owned, `cloud.frag.glsl`)
Pass 3 wraps this field in an envelope (a nebula floor with authored wells and
clear sky above it); `coverage` below becomes the noise that displaces the
floor, as in Aetheria. The flow, the phases and the hue stand.
```text
camera at the origin, looking +z, static; the slab z in [Z0, Z1] fills the view
flow(uv) = A · (snoise(q·Ff + o1), snoise(q·Ff + o2), 0) q = slab mid-plane point; quarter-res texture, once per frame
@ -173,6 +181,11 @@ below exact.
### Interval culling along the ray, with the warp
Pass 3 supersedes this section's march: the probe is per screen tile in a
pre-pass, the field has an analytic envelope the probe tests first, and the
loop below becomes the pre-pass's loop over cell ranges. The ball and the warp
enlargement stand.
`cultmath_iv_snoise_ball(c, r)` returns `[n(c) − L r, n(c) + L r] ∩ [−1, 1]`
with `L` the committed Lipschitz constant. Over a segment `[t, t+Δ]` the
unwarped ball is centred at `t + Δ/2` with radius `Δ/2`; the warp displaces
@ -318,7 +331,8 @@ TFLOP available; estimates, replaced by measurement):
| Pass | Work | ms |
| --- | --- | --- |
| Flow texture, quarter res | 160 k texels × 2 snoise | 0.05 |
| March at `S = 8` | 2.6 M × 8 snoise | 3.1 |
| Tile pre-pass (pass 3), 1/64 res, `N = 8` | 40 k tiles × ≤ 144 probes, mostly envelope-only; ≈ 0.4–2 snoise-equivalents per pixel, measured | 0.2–0.8 |
| March at `S = 8` | 2.6 M × 8 snoise, placed in unmasked cells only | 3.1 |
| TAA resolve + DoF gather | ~19 texel reads per pixel | 1.0 |
| Stardust layer, half res, 4,096 particles | fill-bound, small | 0.2 |
| 8×8 downsample + PBO readback at 2 Hz | tiny | 0.05 |
@ -355,10 +369,13 @@ from the mobile preset, the phone's GPU time from the operator's phone.
enclosure tests are the defence.
4. **Tightness is a number**: mean interval width over true range, printed
by a committed test, for a later affine cut to beat.
5. **The saving is a number**: a C# test marches 1,000 rays through a
representative warped coverage field with and without interval skipping
and asserts ≥ 2× fewer evaluations at equal transmittance; the shipped
field's evals per pixel and unfinished fraction come from the browser.
5. **The saving is a number** (amended in pass 3): a C# test marches tiles of
rays through four scenarios, an envelope-then-noise field with authored
empty space among them, with the dense march and with the tile-amortized
interval march, and asserts ≥ 2× lower combined cost at equal
transmittance on the authored scenarios; the uniform slab is printed as
the worst case. The shipped field's evals per pixel and unfinished
fraction come from the browser.
6. **Three runtimes agree**: the GLSL golden fixture passes on WebGL2, with
tolerance class and device named in the report.
@ -384,7 +401,10 @@ site consumes a commit.
Specs: `docs/cultmath-interval-ground-cut-glsl-lowering.spec.json`,
`docs/cultmath-interval-ground-cut-interval-ops.spec.json`,
`docs/cultmath-interval-ground-cut-site-ground.spec.json`,
`docs/cultmath-interval-ground-cut-site-stardust.spec.json`.
`docs/cultmath-interval-ground-cut-site-stardust.spec.json`. Revision 2 of
the two CultLib cuts (pass 3):
`docs/cultmath-interval-ground-cut-interval-ops.r2.spec.json`,
`docs/cultmath-interval-ground-cut-glsl-lowering.r2.spec.json`.
## Standing design decisions (means; Self may overrule)
@ -411,8 +431,164 @@ Specs: `docs/cultmath-interval-ground-cut-glsl-lowering.spec.json`,
- D10. Stardust and DoF land after the ground has been measured alone, so the
report can say what each costs.
## Pass 3: the envelope, the scenarios, the tile pre-pass, the split Phacelle
Pinned: CultLib `main` `d0ea37f8`; `hands/cultmath-interval-ops` at
`c9bd003b` (report `cut-interval-ops.h1`); `hands/cultmath-glsl-lowering` at
`5ae207e8` (report `cut-glsl-lowering.h1`, branched from interval-ops).
Rulings: `operator-tile-amortized-march`, `operator-split-phacelle`,
`operator-compiler-downloads`, `self-exp-measure-in-site`.
### The operator's words
On the march: "I'm skeptical about the interval march, both in implementation
and measurement. Are we testing it in scenarios where we're actually giving
it empty space to skip? Surely if the pre distortion SDF represents a clear
sky with the camera above the fog and aimed up, the interval math can quickly
tell us not to bother marching very far, no? In Aetheria the levels are
authored to place deep gravity wells for the player to gaze across, with
dynamic action spaced around the well, and finding the empty space there is
the whole point of using intervals, no?"
He is right. r1's saving test marched one `snoise` octave against a cutoff
through a slab with coverage everywhere: the worst case for intervals, and
0.93x said nothing about real scenes.
### What r1 measured, and what was wrong in the march
| Finding (`NoiseBoundTests.cs:344-376` at `c9bd003b`) | Effect | r2 |
| --- | --- | --- |
| Every probe charged one `snoise` per ray | a failed probe cost a third of a dense sample | one probe per tile (the ruling) |
| Coarse probes attempted where the ball could never prove emptiness: `hi = n(c) + L r` clamps to 1, so `L r F0 ≥ 1 + cutoff` is a guaranteed failure; at `StepMax = 2`, `L r F0 = 3.7` | every coarse probe wasted; the 0.74x row | a noise probe is attempted only when `L r F0` leaves room to prove `hi < cutoff`; otherwise the level is "maybe" for free |
| Growth capped at `StepMax = 8 Step0` | clear space cost `Depth / StepMax` probes | ranges double to the grid's end |
| The dense sample evaluated the detail octave even at zero occupancy | the baseline was dearer than a sane shader | coverage first, detail only when density can be nonzero (Aetheria's `if (dist < _SafetyDistance)`) |
| The finest-level probe before each dense cell | fine; amortized it is `1/N²` for a chance at 3 snoise | kept |
| No field structure | nothing to skip | envelope-then-noise, below |
### The field, from Aetheria
`Volumetric.cginc:165-221` with `:91-102`, `Zone.cs:369-397`
(`GetHeight`), `Zone.cs:426` (`PowerPulse`), `Settings.asset`
(`DefaultEnvironment`, `PlanetSettings`):
```text
s(p) = p.y + h(p.xz) the pre-distortion SDF; h ≥ 0 the well map
h(xz) = PowerPulse(|xz| / 2R, 2) · 64 + Σ_b PowerPulse(|xz − c_b| / r_b, 16) · d_b
PowerPulse(x, e) = (1 − 4x²)^e on [0, ½], 0 beyond; r_b = 500 M^0.25, d_b = 30 M^0.175
fade(s) = 1 − smoothstep(0.75 S, S, s) S = SafetyDistance 30: no noise above it
s'(p) = s + A · fade(s) · n(warp(p)) n = the two-phase warped coverage + ½ detail
density = max(0, (F − s') / B) F = FloorOffset −20, B = FloorBlend 10
```
Above the safety distance the density is exactly 0 and the noise is never
evaluated, by the dense march too. The warp applies to the noise argument
only; the height is read at the unwarped `xz` (Aetheria reads
`_NebulaSurfaceHeight` at `pos.xz` and warps inside `triNoise3d`).
### The composed bound (invariant `intervals-enclose`, kept)
Over a tile's frustum slice `[z0, z1]`, every step an existing `iv_*` op:
- Envelope over the slice's exact box, not the ball: `y = iv_mul([z0, z1],
m_y)`, `x = iv_mul([z0, z1], m_x)` with the tile's slope intervals;
`iv_h` = Σ over bowls of `[pulse(far), pulse(near)]`, `near`/`far` the
least and greatest distance from the box to the bowl centre (monotone, so
exact per bowl). `iv_s = iv_add(y, iv_h)`; `iv_fade` its ordered endpoints.
- Noise over the ball: `iv_n = iv_snoise_ball(ball.xyz · F0, ball.w · F0)`,
`ball = iv_frustum_ball(m_c, z0, z1, N / (√2 f), D)`: centre on the tile's
central ray at mid-depth, radius `(Δ/2)·|(m_c, 1)| + z1·N/(√2 f) + D`.
The warp enlarges only this radius; the envelope's box takes none.
- `iv_s' = iv_add(iv_s, iv_scale(iv_mul(iv_fade, iv_n), A))`; the slice is
provably empty iff `iv_s'.lo ≥ F`. When `iv_fade.hi = 0` the noise term is
identically zero and `iv_snoise_ball` is not called: the envelope proves
the slice empty at no `snoise` cost. This is why tiles compound: one
envelope probe per tile can retire a whole ray length.
Two tests pin it: `TileBallEnclosesEveryRaySegment` (every ray's points,
sub-pixel jitter and warp included, inside the ball) and
`EnvelopeBoundEnclosesDensity` (the composed bound encloses `density` at
warped points of 2,000 slices, no tolerance).
### Scenarios and predictions (combined cost, `e = 0.2` snoise per envelope evaluation, `N = 8`)
| Scenario | Setup | Prediction |
| --- | --- | --- |
| (a) height fog | `h = 0`, camera at `y = S + 30`, rays level and up | > 50x: dense pays 256 envelope evaluations per ray; the tile about ten probes shared by 64 rays; 0 snoise either way |
| (b) inside the fog | `h = 0`, camera at `y = F − 10`, rays level | 0.8–1.0x: every ray saturates in a few cells; the pre-pass still probes the whole grid |
| (c) Aetheria-like | zone bowl `R = 2000` + four wells (masses 100, 1000, 10000, 1000), camera above the fog in the bowl, rays gazing across (`m_y ∈ [−0.25, 0.1]`), Aetheria's units and 256-sample quadratic grid | 2–4x: the clear cells before the fog surface collapse to a few probes per tile; the fog cells cost the same on both sides. The noise ball proves nothing at Aetheria's scale (`L r F0 ≥ 1.6` at the finest cell with `D = 60`), so it is not evaluated; the saving is the envelope's |
| (d) uniform slab | r1's field, kept as the worst case | 1.3–1.5x: the ceiling at `L = 10.099261` with the cheaper baseline |
The 2x contract is asserted on (a) and (c) at `N = 8`; (b) and (d) are
printed. A shortfall skips the assertion naming `saving-2x-scenarios`; the
fields, grids and `e` are not tuned. The honest summary: at a Lipschitz bound
near 10 the noise ball is weak everywhere; what intervals buy on these fields
is the envelope's empty space, found exactly, and the tiles make finding it
nearly free.
### The site ground gets the envelope
The site's field becomes Aetheria's structure at site scale: a nebula floor
`y = −h(x, z)` below the camera (camera at `y = 0`, looking `+z`), `h` three
authored `PowerPulse` wells seeded in the slab plus a shallow bowl, clear sky
above the safety distance, the coverage noise displacing the floor with the
flow warp as before, the hue from the flow texture as before. The screen's
upper region is clear space (the stardust shows through; the DoF blurs it by
the clouds' opacity where there are clouds); the lower region is the floor,
dipping into the wells the viewer gazes across. Which look the operator wants
is Q6.
The march: a **low-resolution pre-pass** at `W/8 × H/8` runs the tile loop
over the slab's cell ranges (the envelope first, the noise ball only when the
envelope leaves `fade.hi > 0` and `L r F0` leaves room) and writes a per-tile
skip mask of the dense cells not proven empty into an `RGBA32UI` texel (128
bits; 96 cells at the slab's fine step). The main pass reads one texel
(`texelFetch`) and places its `S` samples in unmasked cells only, stratified
over the unmasked length; masked cells contribute exactly zero, so this is
exact importance sampling, not an approximation. The fragment cannot share a
probe across pixels (derivatives reach a 2×2 quad at best), so the pre-pass
is the cheaper and the only sound option in WebGL2. Cost at `N = 8` is in the
budget table: ≤ 144 probes per tile worst case (nothing skipped, hierarchy
descended everywhere), mostly envelope-only, ≈ 0.4–2 snoise-equivalents per
pixel; `N = 16` quarters it but makes the ball's lateral term equal the fine
half-length at the slab's far plane. **Predicted `N = 8`.** The site's own
ratio is measured in the browser against a dense reference with the same
envelope gate (the proving-ground contract's second half).
### The split Phacelle and the compile checks (glsl-lowering r2)
- Transformation step 1 gains one licence-keyed entry: an include under its
own licence is lowered into its own file. `CultMath.glsl` (MIT: `CultMath.hlsl`
+ `CultMath.Interval.hlsl`) and `CultMath.Phacelle.glsl` (MPL-2.0,
concatenated after it only by consumers that call `cultmath_phacelle`; the
site does not). Both pinned; `THIRD-PARTY-NOTICES.md` lists the MPL files
and no longer calls `CultMath.glsl` MPL.
- `iv_exp` joins the fixture with a platform tolerance (1 ulp, the generating
OS named), because `self-exp-measure-in-site` has the site measure WebGL2
`exp` against it. The fixture is regenerated for `cultmath_iv_frustum_ball`.
- glslang 16.6.0 and dxc are downloaded pinned and run: the MIT-only wrapper,
the two-file wrapper, a `lerp(` negative control, and a `cultmath_phacelle`
call against the MIT-only wrapper that must fail (proves the split).
- The glsl branch rebases onto interval-ops r2's head; its first commit is the
regeneration alone.
Carried, still out of scope: the Unity `CultMath.dll` rebuild (no release in
the sequence); the fixture's ulp bounds on WebGL2 (site cut); Stryker's
timeouts are reported as timeouts, not kills.
## Questions
**Q6. The site ground's envelope look.** The envelope gives the shader empty
space to prove; it also changes what the page looks like.
- (a) A nebula floor below the camera with authored wells and clear sky
above: Aetheria's gaze-across-the-well composition; the upper screen is
stardust over the base colour, the lower screen clouds dipping into wells.
**Recommended**: the real shape, the culling pays, and it is the video the
operator described.
- (b) A cloud wall facing the camera with authored clear pockets (holes the
envelope carves): closer to pass 2's everywhere-clouds look; culling pays
only inside the pockets.
- (c) Both, blended by page: the home page (a), the ritual essays (b).
**Q4. Where the shader's palette input comes from.**
- (a) Four new input tokens in `custom.scss` (`--gamecult-nebula-0/1/2`,
`--gamecult-nebula-base`), seeded with today's wash colours.