threejs-ambient-contact-shading ยท diff
git:20260713.f0e8d5c to git:20260716.2ce13bc
145 added, 1 removed. Audit C to A.
- ../../threejs-ambient-contact-shading/SKILL.md
+ ---
+ name: threejs-ambient-contact-shading
+ description: Ground indirect lighting with ambient visibility in Three.js r185 WebGPU/TSL. Use when choosing authored material AO, dynamic GTAO, forward-lighting placement, reduced-resolution reconstruction, temporal AO, or bent normals.
+ ---
+
+ # Ambient Contact Shading
+
+ AO is visibility of indirect illumination. It may attenuate indirect diffuse and
+ environment/specular response. Direct light, emission, UI, and the tone-mapped
+ frame remain invariant.
+
+ `$threejs-choose-skills` is an optional multi-system coordinator. Use
+ `$threejs-image-pipeline` when AO shares depth, normals, velocity, history, or
+ final-output ownership.
+
+ ## 1. Choose the ambient-visibility branch
+
+ Use the first branch whose gate passes:
+
+ | Gate | Branch | Consequence |
+ | --- | --- | --- |
+ | Required occlusion is static/local and can be authored in assets | material `aoMap` / `aoNode` | One forward render; no dynamic inter-object contact. |
+ | Dynamic screen contact is required and the renderer exposes indirect lighting separately | apply GTAO visibility to that term | One geometry pass can remain possible. |
+ | Stock forward `NodeMaterial` needs dynamic contact and the complete marginal cost passes | depth/normal pass -> GTAO -> optional reconstruction -> second lit pass with `builtinAOContext()` | Correct placement costs two scene traversals. |
+ | No previous branch meets its quality and cost gates | omit screen AO | Preserve materials, direct shadows, and silhouette readability. |
+
+ r185 `GTAONode` needs current depth/normal before AO exists, while
+ `builtinAOContext()` must be present during material lighting. Treat the first
+ pass as a full material/deformation/alpha-tested scene pass unless a parity-
+ proven depth/normal-only pass replaces it.
+
+ **Complete when:** the chosen branch names the indirect-light owner and either
+ charges every added pass/attachment or records screen AO as omitted.
+
+ ## 2. Fix the input contract
+
+ - Initialize the renderer and require a WebGPU backend before graph creation.
+ Stock r185 GTAO is gated to standard depth; a custom reversed-depth adapter
+ must prove sky classification, reconstruction, and occluder ordering.
+ - Define opaque occluders, opaque receivers, alpha coverage, and one transparent
+ policy: no screen AO, authored material AO, or a validated custom lighting
+ model. Stock `builtinAOContext()` skips transparent materials.
+ - Bind AO to the active view's `screenUV`, drawing-buffer dimensions, and
+ projection. Keep width and height independent for non-square/asymmetric views.
+ - Choose depth-reconstructed normals for reduced raw AO only when edge fixtures
+ and target timing pass. Choose an MRT normal when it is shared, reconstruction
+ is materialized, smooth/thin geometry fails, or its measured attachment delta
+ is cheaper.
+ - Express physical contact radii in world units:
+ `radiusRender = radiusMeters * renderUnitsPerMeter`, with the same conversion
+ for dimensioned thickness and bias. An authored-look branch instead declares
+ scene-unit-only controls and revalidates them after asset/world scaling.
+
+ When another system supplies scale, motion, or resources, bind units, coordinate
+ frame, current/previous presentation times, authority, version, resource
+ generation, validity, and reset conditions before using those inputs.
+
+ **Complete when:** depth convention, screen coordinates, normal source,
+ transparency, scale meaning, and every external producer are explicit and
+ dimensionally compatible.
+
+ ## 3. Materialize scalar visibility
+
+ Build the selected screen-space branch in this order:
+
+ ```text
+ shared-or-AO-owned depth + optional normal/velocity
+ -> GTAO scalar visibility
+ -> optional materialized edge-aware reconstruction
+ -> indirect-light application
+ -> optional temporal resolve
+ -> one tone-map/output-transform owner
+ ```
+
+ - Reuse a shared scene pass; do not create a second G-buffer for AO.
+ - Raw reduced-resolution AO receives ordinary texture filtering, not bilateral
+ reconstruction. When edges fail, evaluate `rtt(denoise(...))` once, then
+ sample the materialized texture with `screenUV`.
+ - Inside a mesh material graph, sample both raw and reconstructed visibility
+ explicitly with `screenUV`; implicit texture coordinates resolve to mesh UVs.
+ - Keep scalar visibility single-channel. Replace the output graph and mark the
+ `RenderPipeline` dirty when AO is disabled so inactive work is unreachable.
+
+ When implementing GTAO or choosing reconstruction, read
+ [the r185 GTAO pipeline](references/gtao-bent-normal-pipeline.md#r185-graph-and-api-gates)
+ and [its reconstruction tradeoff](references/gtao-bent-normal-pipeline.md#reconstruction-and-cost).
+
+ **Complete when:** the active graph has one depth/normal owner, visibility is
+ sampled in screen space, AO reaches only indirect lighting, and AO-off removes
+ every AO pass and dependency.
+
+ ## 4. Admit temporal filtering only with valid history
+
+ `GTAONode.useTemporalFiltering` rotates samples; it does not create or reproject
+ history. Enable it only with a live TRAA/custom resolve, valid camera and object
+ motion (including deformation/instancing/alpha coverage), matching beauty/depth/
+ velocity dimensions, rejection, and reset behavior.
+
+ Reset or reseed on camera/projection cuts, uncompensated rebases, geometry or
+ coverage discontinuities, AO parameter/scale/resolution changes, and quality
+ migration. r185 `TRAANode` has no public camera-cut reset: rebuild and dispose
+ the node, replace the output graph, and mark the pipeline dirty.
+
+ When temporal AO is selected, read the
+ [temporal contract](references/gtao-bent-normal-pipeline.md#temporal-contract)
+ before constructing history.
+
+ **Complete when:** moving-occluder, disocclusion, camera-cut, resize, and AO-
+ parameter-change fixtures either pass with explicit rejection/reset or temporal
+ AO is disabled.
+
+ ## 5. Add bent normals only after scalar AO passes
+
+ A bent normal is the visibility-weighted mean unoccluded direction. Add this
+ branch only when scalar AO already passes, directional environment response is
+ visible and required, and the one-wall fixture proves the direction points away
+ from the blocked hemisphere.
+
+ When bent normals are selected, read the
+ [bent-normal contract](references/gtao-bent-normal-pipeline.md#bent-normal-extension)
+ for basis, filtering, normalization, storage, and sign checks.
+
+ **Complete when:** scalar visibility remains independently available, the
+ direction is transformed exactly once, and the one-wall fixture passes; otherwise
+ directional use stays disabled.
+
+ ## 6. Verify the finished graph
+
+ Capture raw depth, normals, raw/reconstructed AO, indirect contribution,
+ direct/emissive residuals, velocity/history rejection when present, and AO off.
+ Exercise UV-seam meshes, thin silhouettes, transparent crossings, smooth curves,
+ screen edges, asymmetric projections, motion, resize, and disposal/recreation.
+
+ **Complete when:** direct light and emission are invariant; no UV-following,
+ cross-edge halo, seam, crawl, or trail remains in the accepted branch; AO-off
+ shows zero AO work; target-device marginal time and resource use pass; and
+ recreation returns resource counters to baseline.
+
+ ## Ownership
+
+ This skill owns scalar GTAO, reconstruction choice, indirect-light placement,
+ temporal eligibility, bent normals, and AO diagnostics. The image-pipeline owner
+ owns shared MRTs, global pass order, history infrastructure, tone mapping, output
+ conversion, and adaptive resolution.
+