feat(render3d): a bole that does not quake, leaves out of the lens, a light with a reach

layer_flutter's mask was the vertex's height alone, so on a ten metre aspen every part of
the trunk above 1.2 m trembled with the leaves and the bole read as cloth. What separates
a leaf from a bole is distance from the model's centre line, not height; the mask is
radial now, and small plants keep the old one because a flower is all leaf.

NEAR_FADE dithers tree foliage out inside arm's length of the camera, in the depth
prepass, so the lit pass never sees those pixels and the equal-depth optimisation is
untouched. gl_Position.w is the view depth, so it costs one varying and no uniform.
Blades, cards and flowers are excluded - they live at the player's feet - and the shadow
pass keeps every leaf.

daylight_hand takes a reach in metres. The falloff was an inverse square windowed between
26 and 6 with both numbers hard-coded: two per cent of its own near field at ten metres,
so a torch lit your boots. It is a gentle power out to the reach now.

Measured in Maroon Lake, twice per side: the crown mask moves 1.7% of an aspen frame and
nothing at all in the meadow under it; the near fade moves 7.1% of a first-person frame
at a conifer and 0% where there is no foliage in the lens; the torch's reach lifts the lit
ground 20 to 45% and leaves the sky bit-identical.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-20 00:05:22 +03:00
parent a88aecb993
commit 18d23ca20d
8 changed files with 97 additions and 8 deletions

View file

@ -90,7 +90,8 @@ Characters and moving objects
61. Static collision: ground circles in a cell grid with push-out and a segment test — `collide.ludic`
62. Cut-out and emissive actors (a flame's cards) — `actor.ludic`, `shaders/model.frag` `u_emissive`
63. Per-instance skin clones (a herd posed individually), per-part tints and hidden parts on actors, uniform-location caching with frustum and distance culling — `skin.ludic` `skin_clone`, `actor.ludic`
64. A second point / cone light (a torch or flashlight in the hand) — `daylight.ludic` `daylight_hand`, `shaders/lighting.glsl` `handLight`
64. A second point / cone light (a torch or flashlight in the hand) with a reach in metres and a gentle falloff to it — `daylight.ludic` `daylight_hand`, `shaders/lighting.glsl` `handLight`
64a. Tree foliage dithered out inside arm's length of the camera, in the depth prepass — `shaders/model.vert` `v_near`, `shaders/depth.frag`, `#define NEAR_FADE`
65. Auto-exposure adapted on the GPU: a 1x1 pass eases last frame's value toward key / mean and the tonemapper samples it, so nothing is read back (a pixel-buffer read still synchronised on Apple's GL) — `post.ludic` `post_measure`, `shaders/adapt.frag`
66. A PPM reader with box-filtered downscale (photo thumbnails) and word-wrapped overlay text — `texture.ludic` `tex_load_ppm`, `overlay.ludic` `ov_text_wrap`
67. The overlay batches a whole frame into one upload and draws per texture range with optional scissor clips (per-change uploads stalled the driver: 40 ms -> 25 ms), plus nine-slice, rotated, line, disc and arc primitives — `overlay.ludic` `ov_flush`, `ov_clip`, `ov_nine`, `ov_sub_rot`, `ov_line`, `ov_disc`, `ov_arc`