- Remove geoN (ddx/ddy) from smooth PS entirely — use smooth interpolated
normal N for all triplanar sampling (albedo, heightmap, normal map).
geoN changes discontinuously at triangle edges, causing per-triangle
faceting in texture weights and normal perturbation.
- Tune consistency-based vertex normal blend to smoothstep(0.70, 0.90):
snaps to face normal at 90° boundaries (seamless blocky join) while
preserving smooth normals on curved terrain.
- Unify all 3 edge axes (X/Y/Z) to same smoothstep formula (was mixed
smoothstep + pow4).
- Remove grass-specific hardcoded shading from both PS (side darkening,
warm shift, ambient boost) — will be data-driven per-material later.
- Remove CPU SmoothMesher code (GPU-only path).
- Document all findings in TROUBLESHOOTING.md with calibration table.
- Load CC0 FreeStylized textures (6 materials: grass, dirt, stone, sand, snow, smoothstone)
as Texture2DArray: t1=albedo+heightmap RGBA, t7=normal maps GL format
- Height-based texture blending: winner-takes-all with sharpness=16, 40% blend zone,
asymmetric bias (coeff 1.6) for resistBleed materials (grass resists sand bleed)
- UDN triplanar normal mapping with 3 critical fixes:
* Use raw normal (NOT abs) in UDN formula — abs inverts lighting on -X/-Y/-Z faces
* sign(normal) correction on tangent X for back-facing UV mirror
* GL green channel flip on Y-projection only (not X/Z where V=worldY is correct)
- Dirt material rendered smooth (FLAG_SMOOTH), ground_02 texture darkened 0.75
- Sun orbit debug mode (F7): 10s cycle with sinusoidal altitude
- Crosshair + face debug HUD (F8): DDA raycast, camera/target/face/normal info
- Screenshot F6 now writes companion .log file with full debug state
- Document UDN pitfalls and logical vs physical coordinates in TROUBLESHOOTING.md
- Add tools/prepare_textures.py for texture pipeline (ZIP → albedo+height RGBA + normal)
Per-frame CreateRaytracingAccelerationStructure calls during F3 animation
caused VRAM explosion (especially toping BLAS at ~23M vertices). Now all
3 BLASes use capacity-based allocation with 25% headroom — only recreated
when vertex count exceeds capacity, otherwise just BuildRaytracingAS with
updated desc.vertex_count. TLAS only recreated when instance count changes.
Also adds deferred toping BLAS position upload via UpdateBuffer in Render()
(topingBLASDirty_ flag), enabling toping shadows to update during animation.
Split CLAUDE.md into CLAUDE.md + TROUBLESHOOTING.md for maintainability.