Ambient Contact Shading

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.

Image: Image-pipeline AO integration final presentation (from demo browserEntry). Source lab: integration-image-pipeline-ao.

$threejs-ambient-contact-shading 2 primary targets 2 flagships 1 secondary surface accepted runtime evidence Latest skill update commit 62a9bc5 ↗ SKILL.md on GitHub ↗ raw (for agents) ↗

Evidence reports

Source hashes, claim verdicts, promoted same-lab media, fixed routes, exact tier contracts, and current limitations.

The approach, mathematically

Ambient occlusion is ambient visibility: it attenuates indirect and environment light only, never direct light or emission. Ground-truth AO integrates hemisphere visibility:

$$A(\mathbf p) = \frac{1}{\pi}\int_\Omega V(\mathbf p, \omega)\,(\mathbf n\cdot\omega)\,d\omega$$

GTAO approximates this by scanning screen-space slices: for each direction it finds the maximum horizon angles and integrates the visible arc analytically:

$$A \approx \frac{1}{\pi}\int_0^\pi \Big(\cos\theta_1(\phi) + \cos\theta_2(\phi)\Big)\,\text{arc terms}\;d\phi$$

The pass runs at half resolution; bilateral upsampling rejects samples across depth discontinuities with weights $w = w_{spatial}\cdot\exp\!\big(-|z - z_c|/\sigma_z\big)$, and the result modulates only the indirect diffuse term of the lighting equation — the composition point is the contract.

Accepted primary labs

Only schema-v2 labs with accepted runtime and evidence contracts appear here. Other source directories remain visible through the demo registry without being promoted to runnable proof.

Preview and evidence ledger

Every image identifies what it proves. Page screenshots demonstrate the published presentation only; generated inputs demonstrate asset channels only; rendering acceptance still requires same-lab readback and a schema-v2 bundle.

Accepted runtime evidence available9 published images
Native WebGPU runtime evidence preview

Image Pipeline and Material-context AO Integration

Accepted
visualCorrectness
PASS
mechanismCorrectness
PASS
performanceCompliance
NOT_CLAIMED
gpuAttribution
NOT_CLAIMED
lifecycleStability
PASS
visualError
PASS
  • Evidence pixels are native-WebGPU readbacks from threejs-ambient-contact-shading/examples/integration-image-pipeline-ao/index.html (the live demo), not substitute gallery art.
  • Tracked v2 release bundle: docs/visual-validation/integration-image-pipeline-ao/bundle.
  • hardwarePerformance remains NOT_CLAIMED without named-adapter GPU timestamps.

The full skill

The complete SKILL.md as loaded by agents, rendered verbatim.

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:

shared-or-AO-owned depth + optional normal/velocity
  -> GTAO scalar visibility
  -> optional materialized edge-aware reconstruction
  -> indirect-light application
  -> optional temporal resolve over the matching admitted layers
  -> excluded-layer composition only when those layers were separated
  -> 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 stock transparent/transmission rendering in the non-temporal lit pass; builtinAOContext() already skips transparent materials. Use an opaque-only lit pass plus separate layer composition only for a temporal resolve or an already-owned external compositor. Account for its full cost; charge AO only the marginal delta when that compositor is shared.
  • 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 and its reconstruction tradeoff.

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 and layer membership, rejection, and reset behavior. Resolve only layers represented by those depth and velocity signals. Composite excluded transparent or refractive layers afterward; admit them to the resolve only when their matching depth, motion, coverage, and rejection behavior are proven.

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 before constructing history.

Complete when: beauty, depth, and velocity cover the same admitted layers; any excluded transparent/refractive layers are composed after the resolve; and 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 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

For an active screen-space branch, capture raw depth, the selected normal input, raw/reconstructed AO when admitted, indirect contribution, direct/emissive residuals, velocity/history rejection when temporal filtering is present, and AO off. Exercise UV-invariance, thin silhouettes, transparent crossings, smooth curves, screen edges, asymmetric projections, motion when present, resize, and disposal/recreation.

For authored material AO, validate the authored UV set, asset/world scaling, and indirect-only placement instead. For omitted screen AO, record that no screen-AO pass, attachment, history, or dependency is reachable.

Complete when: direct light and emission are invariant; every fixture for the selected branch passes; an active screen-space branch has no unintended UV-following, cross-edge halo, crawl, or trail; AO-off or omission shows zero screen-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.

Secondary provider surfaces

Preserved concept proxies and generated-asset previews. They are excluded from primary completion counts and link to the canonical lab through the schema-v2 registry.