Files
open-wc/docs/RENDER_BASELINE.md
T
sindoring aea7787b9b test(M00): diagnose checkpoint camera occluders
Work-Package: M00-QAR-CAMERA-OCCLUDERS-001
Agent: sindo-main-codex
Tests: five-point camera occluder probe, coordination and documentation gates
Fidelity: no camera containment found; manual target and FOV mismatch recorded
2026-07-11 22:23:33 +04:00

16 KiB
Raw Blame History

Renderer baseline M00

Этот документ описывает воспроизводимый baseline renderer перед архитектурной декомпозицией. Он не является заявлением о визуальной совместимости 1:1 с WoW 3.3.5a.

Единый запуск

Из корня репозитория:

.\tools\run_render_baseline.ps1

Скрипт последовательно проверяет headless-загрузку проекта, renderer materials, M2 unique-id dedupe, regression manifest и затем снимает checkpoint-ы. Для CI или быстрой проверки контракта без PNG:

.\tools\run_render_baseline.ps1 -DryRun -WaitSeconds 0.1 -MeasureSeconds 0.1

Если godot отсутствует в PATH, скрипт использует локальный executable, указанный в RENDER.md. Другой executable задаётся через -GodotPath.

Результат по умолчанию находится в user://render_baseline:

  • report.json — Godot, OS, CPU, GPU/backend, viewport, revision, cache inventory, cache versions, параметры checkpoint-ов и результаты;
  • <checkpoint>__cold_process.png — первый визит в текущем процессе;
  • <checkpoint>__warm_revisit.png — повторный визит в том же процессе после обхода контрольных точек.

Парное визуальное сравнение

Одобренные снимки оригинального клиента хранятся вне Git. Если каталог с ними доступен локально, единый запуск может сразу сравнить PNG с одинаковыми именами:

.\tools\run_render_baseline.ps1 `
  -ReferenceCheckpointDirectory 'D:\private-fixtures\wow-3.3.5a-checkpoints'

Результат записывается в user://render_baseline/visual_comparison.json. Другой путь задаётся через -VisualComparisonReport. -DryRun несовместим с визуальным сравнением, потому что не создаёт PNG. Runner сохраняет обычный performance capture в нормативном 1280×900, затем отдельно снимает visual candidates в 2560×1440; разрешение можно переопределить через -VisualComparisonWidth и -VisualComparisonHeight, не меняя performance baseline.

Отдельный запуск сравнения:

godot --headless --path . `
  --script res://src/tools/compare_render_checkpoints.gd -- `
  --reference 'D:\private-fixtures\wow-3.3.5a-checkpoints' `
  --candidate "$env:APPDATA\Godot\app_userdata\OpenWC\render_baseline" `
  --output user://render_baseline/visual_comparison.json

Reference-каталог принимает JPG/JPEG/PNG оригинального клиента с именем checkpoint, например goldshire_dense_m2.jpg. Диагностические кадры с префиксом diagnostic_ игнорируются. Один reference автоматически сопоставляется с <checkpoint>__cold_process.png и <checkpoint>__warm_revisit.png OpenWC.

Сравнение переводит sRGB в linear color space, вычисляет взвешенную ошибку яркости/цвета для каждого пикселя, среднюю ошибку кадра и долю пикселей выше локального порога. Нормативные defaults находятся в comparison_budgets manifest: mean error 0.015, changed-pixel ratio 0.01, pixel error threshold 0.04. Любое превышение, несовпадение размеров, отсутствие пары или пустой reference-каталог возвращает ненулевой exit code.

Автоматический pass не является доказательством parity: animation phase, weather, camera alignment и intentional gaps требуют human approval. Для проверки алгоритма без proprietary данных используется:

godot --headless --path . --script res://src/tools/compare_render_checkpoints.gd -- --self-test
flowchart LR
    R[Private approved reference PNGs] --> C[Checkpoint comparator]
    N[New baseline PNGs] --> C
    M[Manifest tolerance defaults] --> C
    C --> J[JSON metrics and pass/fail]
    J --> H[Human fidelity approval]

cold_process не означает очищенный Windows filesystem cache. Поле cache_state и полный inventory обязаны интерпретироваться вместе с результатом. Удаление или принудительная пересборка cache не входит в baseline-команду. Для отдельного запуска после контролируемой очистки безопасного локального cache следует передать осмысленную метку, например -CacheState rebuilt-clean; proprietary source assets команда не изменяет.

Контрольные точки и детерминизм

Нормативный manifest: res://src/tools/render_baseline_manifest.json. Он не содержит proprietary assets и фиксирует семь обязательных классов:

  • Elwynn terrain overview;
  • граница ADT около world center;
  • dense M2 в Goldshire;
  • Goldshire Inn как large WMO;
  • Elwynn waterfall как liquid;
  • синтетический native-animation probe GryphonRoost01.m2;
  • тот же Elwynn overview в 19:00 для sky transition.

Зафиксированы camera/target/player position, viewport, FOV, quality preset и время мира. Для temporal shader/animation кадров SHA-256 служит идентификатором артефакта, но не самостоятельным pass-критерием: автоматическая визуальная проверка использует perceptual diff и согласованный tolerance. Синтетический animation probe существует только внутри capture tool и не меняет runtime streaming scene.

Метрики и budget

Каждая точка записывает load/wait time, frame time p50/p95/p99, максимальный frame hitch, static/video memory, draw calls, rendered objects и снимок streaming queues. При сравнении следующего renderer с этим baseline p95, p99, max hitch, load time и memory не должны ухудшаться более чем на 10% без отдельного принятого объяснения и обновления baseline.

Измерение считается неполным, если snapshot показывает незавершённые очереди: это состояние остаётся в report.json и не должно скрываться. Значения сравниваются только на одинаковых Godot, backend, hardware/profile, cache state, viewport и manifest revision.

Cache contract

Manifest фиксирует проверяемые версии и причины invalidation:

Cache Version Invalidate when
baked terrain 5 изменились baked geometry или placement payload
streaming terrain 2 изменились streaming geometry или M2 unique_id payload
terrain splat 1 изменился splat resource payload
terrain control splat 3 изменились control atlas, layer map или texture array
WMO streaming 2 изменилась WMO render geometry
WMO builder 2 изменились scene transform или builder payload
M2 material 2 изменились material или custom vertex payload

verify_render_baseline_manifest.gd сравнивает таблицу с runtime constants. Shader-only refresh versions, не изменяющие serialized payload, обновляются по правилам RENDER.md; изменение payload требует version bump и rebake.

Fidelity gaps относительно 3.3.5a

Baseline пока не имеет approved парных кадров оригинального клиента build 12340. До их появления нельзя утверждать parity. Известны следующие классы расхождений:

  • неполные M2 animation/skinning, particles и ribbons;
  • приближённые M2/WMO material combiners и pass ordering;
  • отсутствие настоящего WMO portal/room culling;
  • упрощённые liquid depth/shore rules и texture selection;
  • неполная проверка skybox/lighting transition по зонам;
  • возможная смена D3D12 на Vulkan при ошибке descriptor heap — фактический backend всегда берётся из report.json.

Парное сравнение с клиентом 3.3.5a должно использовать те же map position, local time и weather. Каждый gap регистрируется как terrain/material, placement, animation, lighting, liquid или culling; baseline сам по себе gap не закрывает.

Локальная сессия build 12340 от 2026-07-11 откалибровала три непригодные исходные позиции: dense-M2 camera была полностью закрыта кронами, large-WMO camera находилась у дымохода кузни, liquid camera попадала под terrain оригинального клиента. Manifest теперь содержит проверенные replacement camera positions. Пять approved локальных reference JPG хранятся вне Git; synthetic animated probe и dusk checkpoint всё ещё требуют отдельной процедуры.

Первый paired run после калибровки создал десять сравнений (пять reference × cold/warm), без missing pairs. Все пары ожидаемо превысили строгий tolerance: mean perceptual error 0.0707..0.1746, changed-pixel ratio 0.5504..0.8187. Human inspection показал, что это пока не чистая material/lighting ошибка: при тех же WoW-derived координатах OpenWC terrain-overview camera находится под terrain/placements, WMO camera — внутри таверны, liquid camera — внутри скал; ADT и dense-M2 композиции также существенно смещены. До исправления coordinate/placement mismatch эти значения являются gap evidence, а не основанием расширять tolerance.

Capture tool строит camera basis явно из target и world-up. Это исключает неоднозначный roll look_at при автоматической съёмке. ViewportTexture.get_image() сохраняется без дополнительного vertical flip для Godot 4.6.1.

Coordinate calibration

Пять принятых build 12340 viewpoints записаны в manifest как reference_wow_camera. Они содержат только числовые world coordinates и не включают proprietary данные. Headless probe:

godot --headless --path . --script res://src/tools/verify_render_coordinate_calibration.gd
flowchart LR
    O[Observed build 12340 WoW XYZ] --> W[WoW to Godot formula]
    W --> G[Manifest Godot camera XYZ]
    G --> R[Godot to WoW round-trip]
    R --> E[Maximum numeric error]

На пяти точках maximum mapping/round-trip error равен 0.000015. Это исключает текущую формулу gx = center - wy, gy = wz, gz = center - wx как источник крупного paired mismatch. Результат не доказывает renderer parity: следующая диагностика должна отдельно проверить terrain height, placement transforms и фактическое camera direction/FOV. Production CoordinateMapper остаётся задачей M01; M00 probe не создаёт второй публичный coordinate contract.

Terrain height diagnostic

Rendered terrain проверяется без нового runtime API: offline probe использует уже загруженный tile mesh, строит TriangleMesh и выполняет вертикальный ray в tile-local space.

godot --headless --path . --script res://src/tools/probe_render_terrain_height.gd -- --wait 2
flowchart LR
    C[Calibrated camera XZ] --> S[Streaming tile state]
    S --> M[Active terrain mesh]
    M --> T[Tile-local TriangleMesh ray]
    T --> H[Terrain height and camera clearance]

Измеренный clearance: terrain overview 89.044, ADT boundary 44.788, dense M2 90.178, large WMO 12.034, waterfall примерно 76.128 Godot units. Следовательно, все пять камер находятся над rendered terrain; visual obstruction принадлежит placements/WMO/composition, а не terrain height.

Waterfall XZ сначала давал no_intersection, хотя tile 30_49 был available, полностью загружен, имел control_splat_cache quality mesh и LOD0 mesh, а probe находился внутри mesh AABB. Ray со смещением 2.0 units пересёк тот же mesh на высоте 113.872; точная XZ попала на triangle seam/edge numerical miss. Probe теперь сообщает sampled_nearby, distance и source tile вместо ложного streaming ownership gap. --require-all остаётся строгим режимом для действительно неснятых точек.

Camera occluder diagnostic

Scene-tree placement composition проверяется transformed AABB без изменения renderer:

godot --headless --path . --script res://src/tools/probe_render_camera_occluders.gd -- --wait 3
flowchart LR
    C[Calibrated camera] --> A[Published Mesh/MultiMesh AABBs]
    T[Manifest target] --> S[Camera-to-target segment]
    A --> P[Camera containment test]
    A --> I[Segment intersection test]
    S --> I
    P --> J[JSON occluder report]
    I --> J

Ни одна из пяти камер не находится внутри опубликованной scene-tree geometry. Terrain-overview segment пересекает четыре Stormwind WMO groups, large-WMO segment — три Goldshire Inn groups, waterfall segment — liquid surface; ADT boundary и dense-M2 segments не пересекают placement AABB. Поэтому прежнее визуальное впечатление «камера внутри WMO/placements» не подтверждается. Основной paired gap сейчас — неточно воспроизведённые manual look direction/target/FOV reference-кадров: например, автоматический Goldshire target направляет луч через фасад внутрь WMO, тогда как reference был вручную скадрирован на весь фасад. Probe охватывает только scene-tree MeshInstance/MultiMesh; RID-only instances не имеют доступного semantic path и явно исключены из coverage.