Builtin Library
README.md
aoShared
The parts every ambient-occlusion technique has in common.
local Shared = require("@builtin::systems.ambientOcclusion.aoShared")
return Shared.feature("ssao", KERNEL) -- a technique's whole renderFeature
Shared.setScale(0.5) -- the next feature computes occlusion at half resolution
Shared.scale() -- the fraction in force
A technique differs from its siblings in one thing only: how it estimates, for one pixel, what fraction of the hemisphere above the surface is visible. Everything either side of that estimate — reconstructing the surface from depth and normal, sizing the screen-space reach of a world-space radius, filtering the result across the noise the estimate carries, and multiplying it into the frame — is the same work whichever estimate produced it. So it lives here once, and a technique file is its kernel and nothing else.
What a kernel is given
Shared.HELPERS is compiled ahead of the kernel, so it can call:
ao_config() | The settings, as AoConfig — radius, intensity, power, thickness, slices, steps, bias. |
ao_surface(coord, dims, inv_vp, cam, radius) | The shaded point as AoSurface — world position, normal, view vector, and the pixel span the world radius covers at that depth. valid is false for a sky pixel. |
ao_world_at(coord, dims, inv_vp) | World position of any pixel, from its depth. |
ao_hash12(p) | The per-pixel jitter that turns a sampling pattern into noise rather than banding. |
ao_frame_coord(px, out_dims, dims) | The frame pixel an output texel stands for, when the occlusion grid is coarser than the frame. |
ao_store(px, visibility, cfg) | Write the answer, with power and intensity applied. |
ao_unoccluded(px) | Write "nothing occludes this" — the identity, 1. |
and read the bindings scene_depth, scene_normal, frame_cam and
ao_params, writing ao_out.
What the feature draws
Three passes at afterLighting: the kernel writes raw visibility, a depth-aware
filter smooths it and multiplies it into the frame, and a blit puts the result
on screen. The raw target is the only one that can run below the frame's
resolution — the filter lifts it back, weighting each of its texels by the depth
where that texel sits, so an upsample carries silhouettes instead of smearing
across them. The filter also writes the visibility alone, blitted on the
ambientOcclusion capture view's channel — invisible on the live viewport,
drawn for capture pass=ambientOcclusion.
The render targets are screen-sized and track the target being drawn, so the result is correct in offscreen captures and RTT cameras at their own resolutions.