Repository navigation
[Materials] Add a shared energy-conserving layered PBR model #134
Copy link
Copy link
Closed
Labels
enhancementNew feature or requestNew feature or request
Description
Parent: #126
Inventory: #11 material/BRDF section
Problem
The current PBR path covers core metallic/roughness rendering, but UE-class look development needs composable, energy-conserving material lobes rather than one-off shader forks. glTF extensions such as clearcoat, sheen, anisotropy, iridescence, specular, and IOR are also common interoperability requirements.
Outcome
A versioned layered-PBR material model shared by deferred/visibility, forward transparent, path-traced, and reference-render validation paths.
Minimum first release:
- base metallic/roughness dielectric/conductor;
- clearcoat with independent normal/roughness;
- sheen;
- tangent-space anisotropy;
- specular/IOR controls;
- iridescence/thin-film lobe;
- emissive energy represented consistently for exposure and GI.
Screen-space subsurface profiles may be added as a separate milestone only after the core lobe ABI is stable.
Material ABI
- Version the GPU material record; declare exact alignment/packing once for Rust and WGSL.
- Use a lobe/feature bitmask plus indices into texture/resource tables.
- Centralize BRDF functions in shared WGSL included by every relevant pipeline.
- Preserve a compact fast path for common base-only materials; do not branch every pixel through all lobes if no lobe is active.
- Translate glTF extensions without losing texture transforms, factors, color spaces, or tangent requirements.
- Invalid/missing tangent data must select a documented fallback or emit an actionable import warning.
Physical correctness rules
- Enforce energy compensation between base specular/diffuse and added lobes.
- Use consistent normal orientation, geometric normal safeguards, and roughness floors.
- Filter normal variance for minification; do not let added lobes reintroduce sparkle/aliasing.
- Match Fresnel/IOR conventions across realtime and reference/path-traced implementations.
- Explicitly define multiple-scattering approximation and reciprocity assumptions.
Implementation sequence
- Write a CPU/reference evaluation and sphere-matrix corpus for the base model.
- Introduce versioned lobe ABI without visual change.
- Land clearcoat + IOR/specular and validate energy.
- Land sheen and anisotropy with correct tangent handling.
- Land iridescence with bounded parameter ranges.
- Wire import, serialization, authoring API, debug views, and path-tracer parity.
Each lobe should be a reviewable commit/PR slice with its own tests even if one agent owns the whole issue.
Acceptance criteria
- A generated parameter matrix compares each lobe against
tools/bloom-referenceor Cycles at multiple view/light angles; thresholds and known model differences are recorded. - White-furnace tests show no unexplained energy gain across roughness, metallic, IOR, and lobe-weight sweeps.
- Deferred/visibility, forward, and path-traced versions agree within the approved model tolerance.
- Canonical glTF extension assets import with correct factors/textures and no silent fallback.
- Anisotropy orientation follows mesh tangents and survives negative scale/mirrored UV cases.
- Normal-map minification does not create temporal sparkle in the motion corpus.
- Base-only material cost does not regress by more than an approved measured budget; attach shader stats and pass timings.
- Public APIs remain simple: useful defaults and one material descriptor, not a required sequence of low-level FFI calls.
Likely files
native/shared/src/renderer/material_pipeline.rs,material_system.rs,shaders/native/shared/shaders/material_abi.wgslnative/shared/src/models_gltf.rstools/bloom-reference/src/tracer.rs- path-tracer shaders under
native/shared/src/renderer/shaders/pt.rs src/models/public material types
Non-goals
- A node-based material editor.
- Strand hair, eye geometry, or a full BSSRDF in the first release.
- Maintaining two mathematically unrelated BRDF implementations.
Dependencies
- [Renderer] Add capability-tiered global material and texture indirection #130 for scalable texture references.
- [Materials] Implement correct glTF alpha blending, transmission, volume, and refraction #133 for lobe use on transmitted/transparent materials.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or request
Activity
proggeramlug commentedon Jul 26, 2026
Implemented the first #134 slice: an image-neutral, versioned base-BRDF
target evaluator with deterministic white-furnace and parameter-matrix
oracles.
This package deliberately does not connect the new equations to any live or
scene-rendering shader yet. An exploratory connection changed broad diffuse
regions, so that migration is deferred until it has an external
Cycles/reference review rather than silently replacing an image model under
the no-regression rule.
Version-1 target contract
Added
tools/bloom-reference/src/layered_pbr.rs, a pure evaluator defining:0.04dielectric F0 and0.04roughness floor;(1 - F(NdotV)) (1 - F(NdotL));BRDF * NdotL, and current mixture-PDF outputs.The evaluator is side-effect-free and has no renderer, texture, scene,
tone-map, graph, or GPU dependency.
Defects exposed before live migration
The current independent CPU/GPU path-tracing copies use
sqrt((NdotV * (1 - alpha²) + alpha²) * NdotV)instead of the correlated-GGX squared-cosine form
sqrt(NdotV² * (1 - alpha²) + alpha²).The old equation reports about 2.06 units for a rough white conductor at
NdotV = 0.1in a unit white furnace. After fixing that in the targetevaluator, the old diffuse split also exposed grazing energy stacking:
specular approached one while diffuse retained nearly full response.
Version 1 therefore uses the squared Smith equation, the roughness-dependent
Burley
1 -> 1/1.51normalization, and symmetric view/light interfacetransmission. These are now pinned independently of any whole-image golden.
Deterministic corpus
Added the companion
bloom-brdf-referencebinary and checked-intools/bloom-reference/reference/layered-pbr-v1.json.The 48 cases span:
0.04,0.25,0.5, and1.0;NdotV0.1,0.5, and1.0.Each row records separate direct diffuse/specular,
BRDF * NdotL, MIS PDF,and deterministic white-furnace directional reflectance. Serialization rounds
to six decimals; tests evaluate full precision. The checked matrix SHA-256 is
9b8b465ecf2349b6c814210ab9c11190e1ea1a9b67f4698b56ac6c252b15b91b.The furnace oracle uses deterministic GGX visible-normal importance sampling
for specular and cosine sampling for diffuse. This avoids false gain from
uniform quadrature aliasing at near-delta roughness. The checked 48-case
matrix's maximum furnace value is
0.917948.Tests
The companion binary has four passing tests:
intermediate metallic, four roughnesses, and four view angles;
The existing
bloom-referencescene binary retains its two passing built-inscene/sky tests. Focused clippy with
-D warningspasses for the new binary.The full legacy tool clippy invocation still reports its five pre-existing
main/tracer lints; none is in the new modules.
Exact image and performance neutrality
No realtime shader, material ABI, renderer path, render graph, draw, image,
allocation, or platform code changed. The existing CPU scene tracer also does
not import the new evaluator yet.
Preserved pre-package and current binaries rendered the built-in PT scene at
384x384, 256 spp, four bounces, fixed camera/seed, in
old / new / new / oldorder:50579c0528ea96ea00c1ffec943737cbc44fe277c7cd0aae5561e659786fe35b;3.705 s;3.331 s(-10.08%);SSIM
1.0, RMSE/OKLab/edge delta0.The favorable timing movement is recorded only as evidence of no regression;
this package claims no scene-tracer optimization because its render code is
semantically unchanged.
Static gates and structure
cargo test --manifest-path tools/bloom-reference/Cargo.toml: passed(
4 + 2tests across the two binaries);-D warnings: passed;cargo fmt --check: passed;git diff --check: passed;layered_pbr.rs: 331 lines,brdf_reference.rs: 154 lines);renderer/legacy overages and no new violation.
Documentation is in
docs/layered-pbr.mdandtools/README.md. Evidence isunder
tools/quality/out/issue-134-base-reference/.Next slice
The next safe package is the versioned GPU material-record/lobe-bit ABI with
the bitmask at zero for every existing material. It must prove byte-identical
base images and unchanged base-only shader/pipeline cost before clearcoat,
specular/IOR, sheen, anisotropy, or iridescence is activated.
proggeramlug commentedon Jul 26, 2026
Implemented #134 sequence step 2: a versioned lobe ABI that is inert for
every existing material and adds no base-path GPU work.
This is intentionally an ABI/compatibility package, not a visible material
change. It gives clearcoat, sheen, anisotropy, iridescence, specular/IOR, and
transmission stable identities before any realtime shader is allowed to
evaluate them.
Exact packing
Added
native/shared/src/renderer/layered_pbr.rsas the Rust packing oracle:1;GpuMaterialRecord.header.y: high 8 bits = version, low 24 bits =lobe mask;
MaterialFactors.foliage_params.zw: exactu32version/maskbit patterns stored through
f32::from_bitsand read with WGSLbitcast;Every established material is version 1 with mask zero. Global version-zero
or unknown-version input is normalized to version 1/mask zero on allocation
and update, so data from the previously unused flags lane cannot silently
activate a future lobe.
No record/layout growth
The two existing representations reuse reserved words:
foliage_params.xy, preserving themetadata words in
zw.material_abi.wgslandmaterial_indirection.wgslnow expose namedversion/mask constants and accessors. No production shader calls those
accessors in this package, so mask zero does not add a shader load or branch.
Runtime observability
Quality telemetry now reports
renderer_paths.layered_pbr:The final PBR-sphere control reports version
1, both default masks0,active count
0, record sizes80/176, and all four added-cost fields0.Exact visual and graph neutrality
A preserved pre-ABI PBR-spheres binary and the current binary were run
old/new/new/old at 512x512, quality preset 3, render scale 1, 120 warmup and
240 measured frames. A final rebuilt current binary was captured once more
after the source-layout cleanup.
All five PNGs are byte-identical:
5e20cb864081a0d179d49bfcc012428485e18f2330a939f787c57c0063da6558Every run also retained:
7ead8d0477965a86;The ABI therefore produces no tolerance-level drift: it preserves the exact
encoded PNG.
Counterbalanced timing evidence
Averages of the old pair versus the new pair:
The GPU-mean movement is below the repository's 1.0 ms same-machine absolute
reproducibility allowance; the other throughput and tail measurements do not
regress. Large CPU-tail variation appeared in alternating old and new runs,
so no optimization is claimed. Structurally the live shader has no new
instruction, read, or branch.
Tests and targets
cargo check --features models3d: clean;0 warnings;
cargo fmt --check: passed;git diff --check: passed.The line policy still reports the same six cumulative legacy/renderer
overages. The new ABI module is 126 lines and introduces no new over-limit
file.
Documentation is in
docs/layered-pbr.mdanddocs/material-texture-indirection.md. Reproducible artifacts are undertools/quality/out/issue-134-lobe-abi/.Next slice
The next package can activate clearcoat plus specular/IOR behind the reserved
bits. It will first extend the CPU/reference matrix and public/import records,
then add a base-only shader specialization so mask-zero materials remain on
the exact established code path. Clearcoat energy compensation, roughness,
normal handling, and white-furnace evidence will be qualified before the lobe
is enabled in a general scene.
proggeramlug commentedon Jul 26, 2026
Implementation package 3 — live clearcoat + specular/IOR qualified
The version-2 clearcoat/specular/IOR contract is now live across the forward renderer without changing the ordinary-material fast path.
What landed
Energy and quality behavior
Qualification
Strict shared-crate clippy is still blocked by two unrelated pre-existing empty-doc-comment warnings in input.rs and renderer/mod.rs. The versioned reference binary clippy gate remains clean.
Next package on this issue: sheen + tangent-space anisotropy, followed by iridescence, public authoring/debug surfaces, and path-tracing parity.
proggeramlug commentedon Jul 27, 2026
Implementation package 4 — sheen + tangent-space anisotropy qualified
Sequence step 4 is now implemented and qualified across the live forward
material paths, the CPU reference oracle, canonical glTF import, and the
native/folded platform layouts.
This package does not claim the issue's final public authoring/debug or
path-tracer transport parity; those remain sequence step 6 after iridescence.
Realtime material model
KHR_materials_sheen:KHR_texture_transform;compensation.
KHR_materials_anisotropy:tangent.w;adding its lobe. Clearcoat remains the top interface over sheen and the
anisotropic base lobe.
temporally stable point/sun highlights.
The specialization is live for retained and cached meshes across opaque,
prepassed, sorted BLEND, TAA-reactive BLEND, weighted OIT, and the combined
physical-transmission/refraction path. Scalar, UV1, double-sided,
native/folded scene-input, and reactive refraction variants parse through
Naga.
The expanded layered layout uses one shared sampler for all nine material
textures. This stays under Metal's sampler limit while preserving independent
texture views and transforms.
Fast paths and lazy cost
The ordinary scene shader and ordinary material layout remain untouched.
Base-only telemetry proves:
scene_specialization_initialized = false;combined_refraction_specialization_initialized = false;sheen_lut_initialized = false;bytes are all
0;7ead8d0477965a86, 24 passes, and zero transientbytes for the PBR-sphere control.
Within the layered specialization, version-2 clearcoat/specular/IOR materials
return before all sheen/anisotropy texture reads, UV transforms, derivatives,
and LUT work when both new factors are zero. Anisotropy's derivative/TBN work
is additionally guarded by positive anisotropy strength, so sheen-only
materials do not pay for it.
CPU/reference contract
bloom-brdf-reference --version 3now records a 30-row matrix spanningsheen color/roughness, anisotropy strength/rotation, clearcoat, metallic,
roughness, and three view angles.
0324c95b489f611888163df8ac879033313af6b9190e05af251db91aaca06bfa;c4433c235610212432e0da83a0106b8289f08beec9c2524fd82b677945235118;clearcoat, and cosine samples;
0.030422to0.834041;rotation periodicity, LUT-vs-full-Charlie agreement, bounded furnace
energy, and exact v1/v2/v3 regeneration;
bloom-brdf-referencepasses with-D warnings.Canonical glTF import
The import-only validator was run against the official Khronos sample assets:
CompareSheen.glb(
70b6ab5ba8006aa71079b567783a81cfe778a11cca8abc44c45e5e0b1f4252cc):2 meshes, 1 active sheen material;
AnisotropyStrengthTest.glb(
306321ff8214727b145188ceb56d96d2882b393d0b596fa089fd20b07e959834):50 meshes, 42 active anisotropy materials, 53,263 authored tangent
vertices retained.
Both staged and plain glTF loaders preserve factors, source texture metadata,
runtime texture indices, texture transforms, requested UV sets, and authored
tangents. Missing UV1/runtime texture data keeps the scalar material and emits
the documented fallback instead of silently dropping the extension.
Real-GPU tangent and composition coverage
Metal tests exercise:
tangent.wwith a visible negative control;rotation parity, and a visible uncompensated control;
The full release GPU suite passes 21 tests, 0 failures, 2 intentionally
ignored hardware-oracle jobs. Existing lit/clustered/TAA, masked coverage,
transmission, transparent-GI, PT progressive, and PT motion goldens remain
unchanged; both PT captures compare exactly (
mean = 0,SSIM = 1).Image and performance neutrality
The package-4 base-only A/B produced eight byte-identical 512x512 captures:
6053ceca7461ed2ac4c57917d58c6876048c73e1be9d8fcdf8869882615ddb64The relevant
main_hdr_passmovement was1.515 ms -> 1.591 ms(
+0.075 ms), below the repository's 1 ms same-machine reproducibilityallowance. CPU mean/p95 and wall mean improved in that counterbalanced set.
The original total-GPU mean was rejected as evidence because unrelated host
contention appeared almost entirely in bloom, not the material pass.
That investigation found eleven persistent bloom parameter buffers being
rewritten every frame plus a per-frame mip-dimension heap allocation. The
buffers are now initialized on construction/resize, only threshold buffer
zero is writable when manual exposure changes, and the mip extent is computed
without allocation.
A separate causal old/new/new/old qualification of the final bloom cleanup
(four runs per binary, 120 warmup + 240 measured frames) remained
byte-identical. Averages:
The sub-0.025 ms CPU/tail movements are noise-level; GPU mean, GPU tail, and
the implicated bloom pass all improve slightly. No optimization is claimed
for the material lobe itself. The structural claim is narrower: base-only
materials retain the ordinary shader and graph, version-2 layered materials
retain an early exit, and the final candidate introduces no measurable
rendering regression.
The final rebuilt source capture is still the exact hash above and reports
zero base layered cost.
Final gates
wasm-pack build --release --target web: passed;0 warnings;
cargo fmt --check: passed;git diff --check: passed.Repository-wide strict Clippy remains advisory and reports 162 cumulative
legacy/roadmap findings outside the package-4 paths. The line ratchet still
reports the same six cumulative over-limit files; every new/split package-4
file is below 2,000 lines (largest:
layered_pbr_scene.rs, 1,556).Documentation and reproduction commands are in
docs/layered-pbr.mdandtools/README.md; artifacts are undertools/quality/out/issue-134-sheen-anisotropy/.Next slice
Sequence step 5 is iridescence/thin-film interference with bounded thickness
and IOR ranges. It will extend the CPU matrix first, then activate a lazy
realtime lobe behind bit 3, with furnace, angular hue-shift, image-neutral
base-path, and cross-target gates before public/debug/transport parity.
proggeramlug commentedon Jul 27, 2026
Implementation package 5 — iridescence + stable HDR transport qualified
Sequence step 5 is implemented on draft PR #147 at commit
570b811. This package activatesKHR_materials_iridescencebehind the existing lazy layered specialization and does not claim sequence-step-6 public authoring/path-tracer closure yet.Reference and physical contract
bloom-brdf-referenceadds bounded thin-film Fresnel for dielectric and conductor bases, clearcoat-over-film, and combined clearcoat/sheen/anisotropy/iridescence cases.tools/bloom-reference/reference/layered-pbr-v4.json, SHA-256cc8b586bb69123be172987b5c974e1f001e9ef5df3f99df2687396a3f7ae7209.Import and realtime paths
KHR_materials_iridescencefactor, IOR, thickness range, factor/thickness textures, channels, UV selection, and texture transforms are preserved through plain/staged GLB import and retained/cached draws.CompareIridescence.glbimports as two meshes with exactly one active iridescent material; factor1.0, IOR1.5, thickness100..400 nm, and the factor texture are retained.Firefly/root-cause fix
The visible colored box/pinhole clusters were traced to high-variance GGX convolution of the 61k-radiance sun texels in the HDR panorama. A material-specific minimum-mip workaround was rejected and removed.
The shared HDR prefilter now:
The final production and prefiltered-environment captures remove the clustered thin-film fireflies without changing authored material roughness. The optimized builder is byte-identical to the initially accepted candidate.
Ordinary-path and performance evidence
An exact same-commit Damaged Helmet A/B, with only the new shared HDR prefilter absent/present, passes the renderer image gate:
0.01653;0.98803;0.00473;0.00176.The candidate preserves texture and silhouette detail while smoothing HDR reflection energy. Base-only PBR telemetry keeps layered specialization inactive, adds no normal-frame graph pass or branch, and reports CPU mean
2.161 ms, CPU p952.577 ms, GPU mean15.665 ms, and GPU p9516.866 mswithin the scene budgets. HDR mip preparation occurs only when an environment is loaded.Gates
260 passed, 0 failed, 1 ignored;18 passed, 0 failed; scene-reference tests:2 passed;cargo fmt --checkandgit diff --check: pass.Remaining sequence step 6
The issue remains open until those items and the cumulative branch gates are complete.
proggeramlug commentedon Jul 28, 2026
Issue #134 path-traced clearcoat-normal evidence
This checkpoint closes the remaining independent-clearcoat-normal gap in
Bloom's layered GPU path tracer. It is qualified at revision
5c0ab20on anApple M1 Max / Metal adapter with ray query and texture arrays enabled.
Transport contract
The path tracer now preserves the imported clearcoat-normal texture, UV set,
KHR_texture_transform, and normal scale at primary and bounce intersections.It reconstructs the committed vertex tangent and handedness only for a
qualified anisotropy or clearcoat-normal record, maps the coat normal without
changing the base normal, and uses the mapped normal for:
The mapped normal is constrained to the base geometric hemisphere. The
normal-length/variance stored by the existing vector-mip upload path widens
the coat roughness for minification. Path tracing deliberately does not use
screen derivatives; realtime shading additionally retains its established
screen-space curvature variance.
On adapters without the complete texture-array feature pair, only the normal
map is ignored. Scalar clearcoat factor and roughness remain active, and no
normal or UV1 sidecar is allocated.
Lazy resource cost
Clearcoat-normal metadata has its own 48-byte-per-instance sidecar:
It uses group 2 binding 8, resource-key bit 7, and pipeline-key bit 9. The
existing 64-byte clearcoat factor/roughness record and 96-byte scalar layered
record do not grow. Base, scalar-clearcoat, and factor/roughness-textured
scenes report:
path_tracing_clearcoat_normal_specialization_initialized = false;path_tracing_clearcoat_normal_sidecar_allocated_bytes = 0.A normal-only clearcoat material allocates no factor/roughness texture
sidecar. There is no added render-graph pass, image, base-material branch, or
base-pipeline binding. The shared sidecar upload helper retains the established
power-of-two capacity, dirty-upload, and bind-group invalidation behavior.
Metal image gates
The release ray-query golden runs the complete texture-array path rather than
the former scalar-fallback path. Its fixture owns real per-face UVs locally,
so unrelated transparent-GI geometry remains unchanged.
The zero-scale flat normal is byte-identical to scalar clearcoat. A directional
normal map produces a bounded response without mean display-energy gain:
Flat and mapped mean display luminance are
84.161719and83.784545respectively. The existing layered transport gate supplies the finite,
visible-response, and bounded-energy checks; its thresholds were not weakened.
The rotation and UV1 controls retain their established minimum mean-RGB
difference of
0.02.Enabling texture arrays exposed a previous golden-harness blind spot: the
device requested ray query but not its supported texture-array feature pair,
so older textured-lobe assertions always exercised the documented fallback.
The harness now requests both supported features. All textured specular,
clearcoat, sheen, iridescence, and anisotropy variants consequently execute on
Metal. The anisotropy fixture was strengthened to full authored strength and a
clear directional texture while retaining its original visibility threshold.
Regression qualification
./scripts/ci-check.sh --quickpassed in 23 seconds:compilation, quality governance, visual fault controls, and canonical
example inventory passed.
The formerly affected
physical_transmission_gi_specializations_run_on_hardware_ray_querycontrolalso passes after localizing the fixture UVs (
20,050affected pixels,RGB delta
[28, 19, 247]).All clearcoat-normal WGSL feature combinations parse through Naga. The
layered PT implementation remains below the repository's 2,000-line file
limit (
layered_pbr_pt.rs: 1,958 lines; shared resource helper: 34 lines;clearcoat-normal module: 328 lines).
Commits
e69b23e— independent clearcoat-normal transport and qualification;8585425— shared typed sidecar resource helper and line-policy split;a44bc9b— real Metal texture-array golden coverage;5c0ab20— fixture-local UVs preserving unrelated test geometry.Machine-readable measurements accompany this note in
docs/evidence/issue-134-clearcoat-normal-pt.json.proggeramlug commentedon Jul 28, 2026
Issue #134 layered-PBR qualification
This checkpoint qualifies Bloom's shared layered material model against a
generated angular oracle, the realtime forward renderer, the hardware
ray-query path tracer, and a motion sequence. It is qualified at revision
21356b8on an Apple M1 Max / Metal adapter.Angular reference corpus
bloom-brdf-reference --angulargenerates the checkedtools/bloom-reference/reference/layered-pbr-angular-v1.jsoncorpus. Its 63rows cover base, specular/IOR, clearcoat, sheen, anisotropy, iridescence, and
combined materials at three view directions by three light directions per
scenario. Every row records the material, directions, individual BRDF lobes,
total
BRDF * NdotL, PDF, and reciprocity error.Regeneration is byte-exact. CPU component and reciprocity tolerances are both
3e-5; the measured maximum reciprocity error is zero. The corpus records fiveintentional comparison boundaries: it excludes environment lighting and
post-processing, realtime finite-highlight compression is assessed in image
space, normal minification belongs to the motion corpus, stochastic path
transport uses converged-radiance tolerances, and iridescence uses Bloom's
bounded Khronos approximation rather than a spectral conductor model.
The checked corpus SHA-256 is
110142a66bc22acaf0e7f6664a86f135b194fc62d203f1a92b685fbf2487f309.Renderer parity
layered_pbr_parityselects the samev1-l2record for every lobe, disablesIBL, SSR, SSGI, SSAO, bloom, automatic exposure, motion blur, shadows, and
environment energy, then compares the 32x32 center region of the forward MRT
renderer and a 64-frame ray-query accumulation. It also compares each
path-traced lobe response with the linear oracle direction.
The gates require mean display RGB MAE at most 24, final RGB cosine at least
0.96, relative display luminance error at most 0.30, and PT/oracle response
cosine at least 0.85 for significant linear responses. Smaller responses are
bounded to a maximum display response of 12. The worst measured values are the
anisotropy row, and all retain useful margin without weakening a threshold.
Clearcoat-normal motion stability
layered_pbr_motionrenders a high-frequency alternating tangent-spaceclearcoat normal through Bloom's vector/variance mip chain while the camera
moves slowly, then subtracts the flat-normal control. The measured camera
motion mean RGB is
2.432037, the filtered response mean is0.205539, themaximum texture-specific adjacent-frame residual mean is
0.532235, and thecoherent outlier-channel fraction is
0.0%.The hard sparkle gates are a maximum residual mean of
1.5and maximumcoherent outlier fraction of
2%. This test covers minification and temporalstability independently from the static clearcoat-normal qualification in
issue-134-clearcoat-normal-pt.md.Regression and cost qualification
./scripts/ci-check.sh --quick --summary target/ci/quick-issue-134-final.jsonpassed at the qualified revision in 25seconds:
governance, visual-fault controls, and 20 canonical examples passed;
bloom-reference.The angular generator and both renderer gates add no shipping render pass,
image, shader branch, GPU allocation, binding, or per-frame work. Their
production runtime and binary shader cost is zero.
Remaining cross-path dependency
Bloom currently uses the forward MRT renderer; the visibility-buffer renderer
tracked by issue #27 is not implemented. Consequently, issue #134's
forward/visibility/path-traced parity item must remain open. When #27 lands,
its material evaluator must consume this same checked corpus and satisfy the
same documented parity contract. The generated-matrix and normal-minification
acceptance items are independently complete.
Commits
eca754e— clearcoat-normal motion stability gate;ad97e77— versioned layered-PBR angular reference corpus;21356b8— forward, path-traced, and oracle parity gate.Machine-readable measurements accompany this note in
docs/evidence/issue-134-layered-pbr-qualification.json.proggeramlug commentedon Aug 8, 2026
Capability-contract checkpoint landed in PR #147 as
65a24b4.The visibility-shading compatibility test exposed that the lazy scene layered-PBR pipeline needs 22 sampled textures per fragment stage, while production device negotiation granted only the 19-texture core contract. The full audit also found the VSM variant needs 24 and combined layered refraction can need up to 30.
The preferred high-end request now asks for the bounded optional budget the adapter can grant (22–30). The featureless/lower-tier fallback remains at 19. Every lazy specialization checks the exact granted limit before pipeline creation and falls back to base PBR or ordinary physical refraction instead of causing a wgpu validation failure. Capability and quality reports expose grant, requirement, availability, and initialization state.
Verified locally on Metal:
89 / 81,920changed channels, max delta1).This removes a crash/fallback hole but does not complete the remaining forward/visibility/path-traced lobe parity criterion.
proggeramlug commentedon Aug 9, 2026
Progress in commit 40ff96e (
fix(models): preserve textured specular glossiness):KHR_materials_pbrSpecularGlossinessRGB specular + alpha glossiness textures instead of collapsing them to scalar metallic/roughness values.MeshData, scene attachment, compatibility uniforms, and the GPU material-indirection record.Validation:
bistrox.gltfplacements and produces a valid warmed frame.proggeramlug commentedon Aug 10, 2026
Final cross-path closure — layered visibility parity
The last acceptance item is complete on Apple M1 Max / Metal.
Commit
2b99693expands the process-isolated forward/visibility fixture from one clearcoat draw to six moving compatibility draws covering specular/IOR, clearcoat, sheen, anisotropy, iridescence, and the combined material. It asserts the authored lobe fixture is active, the layered specialization and sheen LUT initialize, and visibility mode records the compatibility population rather than silently falling back to base PBR.Forward vs visibility results:
All existing sub-LSB/f16 gates pass. The independent 64-frame hardware ray-query lobe-response test also passes unchanged against the checked angular reference corpus, including anisotropy and the combined material.
This matches the implemented architecture: visibility shading owns eligible ordinary geometry, while layered materials retain the authoritative forward compatibility evaluator and final depth ownership. The gate adds no shipping pass, image, allocation, shader branch, binding, or per-frame work.
Evidence is updated in
docs/evidence/issue-134-layered-pbr-qualification.{md,json}ate8d67e7.Validation passed:
cargo test --test visibility_buffer_parity -- --nocapture(2/2);cargo test --test visibility_buffer_shading_runtime -- --nocapture(1/1);cargo test --test golden_render layered_pbr_parity:: -- --nocapture(1/1);The repository quick wrapper currently stops before tests on an unrelated pre-existing file-size ratchet:
renderer/mod.rsis 20 lines above its stored grandfathered baseline. This package does not modify that file.