Skip to content

shadowSDF2D(surfaceWorldPos, lightWorldPos, sdfTexture, worldSize, worldOffset, options?): Node <"float">

Defined in: packages/nodes/src/lighting/shadows.ts:271

Sphere-trace a 2D hard shadow ray through an SDF texture.

Walks along the line from the shaded surface point toward the light, sampling the SDF at each step to advance by the guaranteed-clear distance. Binary result: 0 if the ray hits an occluder along the way, 1 if it reaches the light cleanly. Soft shadow edges come from (a) the separable gaussian blur pass applied in SDFGenerator and (b) the IQ soft-shadow accumulation along the trace — primarily, not from texture filtering. The SDF sample filter is selectable via shadowFilter (auto|nearest|linear): nearest for crisp / pixel- snapped shadows (avoids the eps-threshold halo), linear for smoother non-snapped edges. auto picks nearest when shadow pixel-snap is on, linear otherwise. Either way the blur + penumbra math carry the bulk of the softness.

The classic IQ penumbra term min(k · h / t) was removed because it accumulates at every step of the walk, which in closed 2D scenes (dungeons, corridors — casters scattered everywhere) produces a uniform global darkening: every ray walks near something, so h/t always drops below 1 somewhere, shadow always ends below 1, and shadowStrength linearly amplifies that scene-wide. The softness option is retained for API stability (now ignored); a future PCSS-style two-phase trace could reintroduce real distance-aware penumbra widening without the false-proximity issue.

The SDF texture is assumed to be produced by SDFGenerator and encodes SIGNED world-space distance on the .r channel — negative inside occluders, positive outside. Signed distance lets the trace detect “stepped into an occluder” mid-walk without needing the hardcoded caster-escape offset the unsigned variant required. World- space distances keep the sphere-trace isotropic on non-square viewports. worldSize / worldOffset are still consumed here to transform the fragment/ray world position into the SDF’s UV space for sampling.

Node <"vec2">

World-space position of the shaded fragment.

Node <"vec2">

World-space position of the light.

Texture 

SDF texture captured at build time. Must come from SDFGenerator (UV-space distances in .r).

Node <"vec2">

Camera frustum size (Node — uniform, updated each frame from the camera bounds).

Node <"vec2">

Camera frustum offset (Node — uniform).

FloatInput

World-space hit threshold. Default 0.5.

Node <"bool">

When provided, gates the nearCaster escape path: the ray only skips past an occluder it’s sitting on if THIS fragment is itself a shadow caster. Without this gate, a floor fragment that happens to lie under a sprite’s rasterized silhouette (seeded in the SDF) would incorrectly escape and render as lit — leaving a bright alpha-blended halo wherever a sprite’s anti-aliased edge overlaps the floor. Pass readCastShadowFlag() here from the caller’s light shader.

FloatInput

Maximum world-space distance from the receiver at which shadow still applies. A ray that hits an occluder at t = t_hit has its shadow scaled by 1 - t_hit / maxShadowDistance, clamped to [0, 1]. Default 0 means no distance falloff (shadow is binary at every distance). Set >0 to hide point-light cone-fan artifacts far from the caster — close-range shadows stay solid, long-range shadows fade to lit.

FloatInput

Retained for API stability; currently ignored. Soft edges come from SDF blur + linear sampling, not per-ray integration. Will re-enable once a PCSS-style trace lands.

FloatInput

World-space distance to push the ray origin forward when the fragment itself sits inside a caster silhouette (signed SDF < 0). Must clear the caster’s radius — too small and the first samples land inside the caster (self-shadow) or in the Voronoi-seam zone near the silhouette (shadow-edge ringing). Default 40 world units matches the old unsigned-SDF escape calibration; scenes with smaller/larger casters should tune accordingly.

number

Compile-time loop count. Default 32.

Node <"float">

Node<‘float’> in [0, 1]. 0 = fully shadowed, 1 = fully lit.