Searchlights with real spot lights and volumetric beams (#60); replay director: several angles per event, slow-motion kills, /airdef replay film and export to MP4 (#58) (fork patch 32)
build / mod (push) Successful in 3m32s

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-30 09:28:13 +03:00
parent 8a12225ba1
commit 76f680eba6
40 changed files with 3827 additions and 52 deletions
+18 -1
View File
@@ -32,7 +32,7 @@ Horizon действует правило clean-room. Исключение — P
| Материалы (классы спрайтов: листва, растения, металл, полированное), ветер, мокрые поверхности | `gfx/hd/materials.rs`; `terrain.wgsl` (`terrain_vertex`/`wind_offset`, `material_params`), `entity.wgsl`, `gfx/hd/light.wgsl` (`shade_surface`) |
| Пост-проход мода (взрывы, дым, вспышки, свечение) и его прелюдия | `airdef-mod/shaders/effects.wgsl`, прелюдия в `horizon/.../gfx/mod_post.rs` |
| Источники света мода (пламя, ракеты, взрывы, прожекторы) | `airdef-mod/src/lib.rs`, `fn lights` |
| API мода: `render.set-lights`, `render.set-lighting` (уровень API 10) | `horizon/crates/horizon-mod-sdk/wit/horizon.wit`, `horizon/crates/horizon-mods/src/runtime.rs` |
| API мода: `render.set-lights`, `render.set-lighting` (уровень API 10), `render.set-spot-lights` (16) | `horizon/crates/horizon-mod-sdk/wit/horizon.wit`, `horizon/crates/horizon-mods/src/runtime.rs` |
Форк ведётся в `horizon/` (ветка `airdef`), в репозиторий мода попадает **патчами**:
@@ -339,3 +339,20 @@ HORIZON_HD_TAA=0 --out run-home-bench/out/taa-off` — A/B; `--baseline run-home
- **Малые виды** (мониторы, `gfx/mod_views.rs`) остаются экономными: небо, твёрдая земля, сущности и вторым проходом пост-эффект мода с камерой этого вида (`ModPost::write_to/run_view`, режим `params[0].w = 0`: пост сам освещает землю и рисует дым). Бюджет по площади картинки (`post_budget`: <4096 px без пост-прохода, до 150k px — 4 взрыва/12 огней, до 600k — 12/32, больше — всё). HD-освещения, облаков и TAA у малых видов нет.
- Мод в повторе берёт взрывы из записи (`record::replay_blasts`, чистая функция времени), время идёт по часам повтора (`params[255].x` — доля частичного тика, которую эффекты не используют при паузе/замедлении).
- #51: на перехвате слои звука (`cinema.rs`), свет-«удар» (белая вспышка в HD-освещении, радиус до 256), пик экспозиции/bloom (`set-lighting`), в `effects.wgsl` широкое гало, подсветка неба/облаков и яркое кольцо ударной волны на экране.
## Прожекторы: направленный свет и объёмный луч (#60)
- **API 16** `render.set-spot-lights(list<spot-light>) -> bool`: апекс, направление, цвет, интенсивность, `range` (до 1024), внутренний/внешний полуугол (рад), `beam` (видимость луча 0..1). До 32 от мода, рисуются ближайшие 16 (`MAX_SPOTS`). Возвращает, рисует ли HD (`HORIZON_HD_SPOTS=0` выключает), иначе мод держит свой запасной луч. Игра интерполирует по id между двумя вызовами (`mod_models.rs: mod_spots`) и переводит в `HdSpot` (`gfx/hd/frame.rs`): координаты камеры, косинусы углов.
- **Данные в шейдере**: `HdFrame.spots` (4 vec4 на источник: апекс+range, ось+cos внешнего, цвет+интенсивность, cos внутреннего+beam), число в `vfog.w`. Новых привязок нет.
- **Тайлы**: заголовок тайла 5 слов (`TILE_WORDS`), пятое: маска конусов, бит j стоит, если выпуклая оболочка конуса (апекс и квадрат вокруг дальнего круга, `tiles.rs: spot_hull`, `cover_points`) на экране накрывает тайл. Маска служит и поверхностям, и лучу. Индекс тайла в WGSL: `* 5u`.
- **Поверхности и цели**: `spot_light` (`spot.wgsl`, вставляется маркером `//SPOT` в `assemble`): конус с мягкой кромкой (smoothstep по косинусам), `I/(d²+1)`, окно в последние 3 блока до `range`, угол падения как у точечных; в `shade_surface` (земля, стены, дроны: они в G-буфере). Теней нет: свет обрывается по `range`, который мод ставит по броску оси на землю (`ground.world.probe`, раз в 4 тика).
- **Луч**: `spot_beams` (`spot.wgsl`): пересечение луча взгляда с конусом (`spot_span`: квадратное уравнение конуса, обрезка по оси и range); внутри 8 шагов с джиттером IGN; яркое ядро, мягкая периферия; затухание `1/(1+d/70)`; плотность воздуха `max(spot_air, 0.0009)` (в чистом воздухе слабый луч, в дымке/тумане ярче и короче); фаза Henyey-Greenstein вперёд (на лампу ярче). Складывается (прозрачный), ограничен глубиной сцены: за стеной не продолжается; камера внутри конуса работает. **Туман вкл**: считается в `fs_fog` (`spot_air = fog_sigma`, длина до 1100 блоков, в небо тоже, не 192 как у тумана); **туман выкл или нет атмосферы**: тот же код в `fs_light` с постоянной плотностью `ambient.z`.
- **Мод**: `sim/lights.rs`: голова прожектора шлёт один `SpotLight` (`Sam::spot_beam`, `lib.rs: spots`; конус 0.03/0.075 рад, range = свободная длина оси + 3.5). Цель «освещена» (`lit`) только в угле 0.05 от оси и если она не дальше свободной длины (за горой бонуса нет). Старые `airdef:beam` рисуются, только когда HD не рисует споты и эффекты включены (тоньше), при эффектах off луча нет. Точечный «шар» на цели остаётся лишь без спотов. В реплеях споты не шлются (реплей не пишет голову прожектора; #56/#58 отдельно).
- **Ограничения**: тень рельефа вне оси (боковая часть конуса за гребнем светит), запасной луч непрозрачен (текстура модели без альфы), GPU-цена на массовой сцене не снята.
## Кинорежиссёр и экспорт MP4 (#58, API 17)
- **Качество фильма** (`render.set-film-quality`, включается и на время захвата): мир HD в полном размере окна (`hd::scale_override` → 1.0), шаги облаков ×2 (шаг 7 блоков, 16–52 шага), шаги тумана ×2; флаг идёт в `HdFrame.surface.z`. Смена `set-main-view` id (склейка) сбрасывает историю TAA и облаков (`Renderer::cut`).
- **Захват** (`render.capture-start/stop/status`, `game/film.rs`, `Renderer::capture_*`): часы игры идут ровно `1/fps` секунды за кадр (тики, интерполяция, `tick` мода), кадр копируется с GPU через очередь из ≤3 буферов и уходит в поток-писатель → `ffmpeg` (libx264, иначе h264_videotoolbox; иначе PNG-последовательность). Звук: микшер OpenAL-подобный (`audio.rs`) работает офлайн (`film_begin/film_mix`), слышны только звуки мода, ориентация — по камере; дорожка вмуксивается AAC. После склейки рисуются `warmup` кадров без записи, чтобы сошлись TAA и объёмы. Размер: offscreen-игра ресайзится, окно просится (`request_inner_size`).
- Мод-HUD (`set-hud`) теперь рисуется и при `set-main-view` (титры, леттербокс фильма); ванильный HUD по-прежнему скрыт.
- Режиссура: `airdef-mod/src/film.rs` (план по записи, варианты ракурсов, проверка рельефа по высотам `Ground::land_state`, ускорение/замедление времени).