T02. Лес: резкая кромка по маске 10 м, без тёмного ореола, живой тон с высоты

Цель: края лесных массивов — чёткие (как на аэрофото), без «клякс» и тёмного ореола; с 2 км лес — не тёмный ковёр.

Контекст: docs/guide/terrain.md («Карта поверхности», «Раскраска», «Лес с высоты»), scripts/terrain/terrain.gdshader, terrain_common.gdshaderinc, surface_layer.gd, tools/terrain/fetch_landcover.py, итоги T01 (metrics.json). Владения: scripts/terrain/{terrain.gdshader, terrain_common.gdshaderinc, terrain_renderer.gd, surface_layer.gd, terrain.gd}, tools/terrain/fetch_landcover.py, data/terrain/*/ (новые маски), configs/world.json (terrain_look, surface), tests/terrain/test_surface.gd, docs/guide/terrain.md.

Шаги

  1. Причина: класс «лес» берётся с сетки 25 м и билинейно размазан + canopy_edge_dark/DSM-ступенька красят луг у опушки.
  2. fetch_landcover.py: для детального слоя — маска «деталь 10 м» RG8 (R — доля леса из WorldCover 10 м без моды 3×3, G — зарезервировано под воду T03), <слой>_detail10.png; бюджет ≤ 15 МБ на локацию сохранить. Перегенерировать 4 локации.
  3. Шейдер: лес из R маски 10 м с порогом 0,5 и узким шумом (переход ≤ 1,5 клетки); затемнение опушки — только внутри леса; DSM-ступенька не даёт тень/скалы на лугу (уклон на опушке брать по маске, а не по высоте).
  4. Дальний тон: средний тон полога (когда крона < пикселя) — по фото (a): отношение лес/луг и RMS-контраст внутри леса.
  5. API для группы 5: Terrain.forest_at(x, z) -> float и get_forest_mask() -> [Image, origin, cell_m]; surface_at на опушке — по маске 10 м (VR-4, VR-21 как триггер). Рантайм-точка (FR-17) — прежний путь 25 м. В terrain.gd после создания деревьев: if trees.has_method("set_forest_mask") (и у impostors) — передать маску (метод делает V02).
  6. VR-21 «препятствие»: проверить, что height_at над лесом включает кроны (DSM) → посадка в лес = удар; тест.

Критерий приёмки (стенд T01)

  • S3: профиль через опушку — переход 10→90 % яркости ≤ 15 м по земле; луг в 0–30 м от кромки темнее луга в 100 м ≤ 8 %.
  • S5: отношение яркости лес/луг и RMS-контраст внутри леса — в ±20 % от фото (a); монтаж S5|a в отчёте.
  • Тест: forest_at меняется 0,1→0,9 на ≤ 20 м на 20 точках кромки; surface_at = лес ≥ 95 % там, где R ≥ 0,5.
  • GPU «рельеф» в S1–S5 — не выше базы T01 + 0,1 мс; тесты tests/terrain зелёные; загрузка Онгудая ≤ 0,5 с.

Зависимости: T01. Модель: opus. Размер: M.