feat(light): render-quality tiers 3-4 — cones, falloff, soft shadows, gels, normals, day/night (#49)
All checks were successful
bootstrap / cfree-fixpoint (push) Successful in 18s
ci / build-and-test (push) Successful in 1m21s
commit-lint / conventional-commits (push) Successful in 4s
docs / build-and-deploy (push) Successful in 19s

The remaining lighting tiers from the original proposal, all extending the
deterministic accumulation core (light.ludic) — no new ECS plumbing:

- Light.spot: cone / flashlight lights (direction + spread degrees), with a
  self-contained integer atan2-in-degrees and a feathered edge.
- Light.falloff: a brightness-ramp exponent (1 linear, 2 quadratic, …) via
  repeated fixed multiply.
- Light.soft: soft shadows — an area-sampled light so an occluder edge fades
  through a penumbra instead of a hard cut.
- Light.gel + Light.clear_gel: colour cookies — a light gels from its centre
  colour to a rim colour.
- Light.normal + Light.clear_normals + Light.height: a normal G-buffer so
  surfaces shade by facing (N·L), not distance alone (tier 3).
- Light.time_of_day: a day/night ambient ramp from a single 0..1 value.

The engine lighting system (systems_light.ludic) consumes matching optional
Light2D fields — direction/spread/falloff/softness/gel — each defaulting off so
an older five-field Light2D lights exactly as before. Every tier is integer +
Q16.16 fixed, so scenes light identically on every run and headless.

Worked example + regression: examples/library/light_tiers.ludic (1 1 1 1 1 1 1 1 1).
Nine new docs/language/light pages. Full suite 76 passed, self-host C-free
fixpoint intact, no golden drift.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-08-31 16:35:41 +03:00
parent 377b6d1186
commit 382826889f
18 changed files with 7752 additions and 6502 deletions

View file

@ -0,0 +1,33 @@
---
id: light-normal
name: Light.normal
category: light
kind: namespace-method
tokens: Light.normal
sig: Light.normal(x, y, width, height, nx, ny)
tip: Stamp a surface normal over a rectangle so lights shade it by facing (N·L).
order: 10
ns: Light
member: normal
---
Stamps a **surface normal** over a screen rectangle into the light pass's normal buffer. Where a normal is stamped, a light shades the surface by the angle it faces — the Lambert term <code>N·L</code> — not by distance alone: a wall facing a torch is bright, one turned away is dim, giving flat sprites a sense of relief (tier 3). <code>nx</code> and <code>ny</code> are the normal's x/y as a <code>fixed</code> in <code>[-1, 1]</code> (a surface facing straight at the camera is <code>0, 0</code>); the z component is derived. Unstamped pixels are unaffected, and the buffer is allocated only when you stamp — clear it each frame with <a href="light-clear_normals"><code>Light.clear_normals</code></a>. Deterministic (Q16.16 dot product).
Parameters:
- `x`, `y`, `width`, `height` — the screen rectangle to stamp
- `nx`, `ny` — the surface normal's x/y, a `fixed` in `[-1, 1]`
```ludic
program Demo {
property Wall { x: int = 0, y: int = 0 }
model Block { Wall }
handler Render phase Update {
Screen.clear(0)
Light.ambient(Color.rgb(24, 24, 32))
Light.clear_normals()
Light.normal(60, 50, 16, 24, 0.7, 0.0) # this face is tilted rightward
Light.point(120, 62, 90, Color.rgb(255, 240, 200), 1.0)
Screen.show()
}
}
```