# Кадр отрисовки Кадр завершает цикл и не является отдельной операцией рендерера. До вызовов отрисовки собирается ввод, вычисляется такт, применяются отложенные операции, выбирается камера и обновляется слушатель 3D-звука. ## Порядок кадра ```text системные сообщения и ввод -> расчёт симуляции -> отложенные операции над объектами -> анимация и преобразования -> камера и звуковой слушатель -> видимость и очереди отрисовки -> материалы и проходы отрисовки -> завершение рендерера -> обратные вызовы после отрисовки и UI ``` По анализу вызовов установлен общий порядок в `World3D::stdRenderGame`: камера передаётся в Terrain, задаются `viewport` и матрицы, вызываются граничные слоты рендерера, выставляется `in_render`, обходится мир, завершаются проходы мира и затенения, вызывается завершение рендерера, снимается `in_render`, затем рассылается `end-of-render`. ## Элемент отрисовки Подготовленный элемент отрисовки содержит: - мировую матрицу узла; - флаги пакета `batch` и диапазон индексов; - дескриптор материала WEAR; - активную фазу и коэффициенты MAT0; - дескриптор текстуры; - необязательный дескриптор карты освещения; - фазу отрисовки и ключ сортировки; - состояние конвейера устаревшей отрисовки. Подготовленный элемент должен ссылаться на неизменяемые данные кадра. Изменение фазы или кэша текстур посреди прохода не должно менять уже собранную очередь. ## Состояние MAT0 и прозрачные фоны Для диапазонов мира миссии выбранный материал MAT0 задаёт нативную категорию конвейера. Категория определяет смешивание и альфа-тест, сохраняя настройки проверки глубины и отсечения граней. Если селектор материала анимируется, вместе с ним обновляется и категория. Принадлежность к очереди определяется отдельно: прозрачным считается диапазон мира с выбранной прозрачностью меньше единицы или с нативными флагами пакета `batch` `0x108`. Такой диапазон при включённой проверке глубины не записывает глубину. За фиксированными диапазонами окружения сохраняются их явные состояния. В превью с фиксированной камерой диапазоны туманности и звёзд, купола и облаков явно помечаются как прозрачный фон. Непрозрачные диапазоны мира остаются на своих местах; прозрачная очередь мира сортируется от дальних элементов к ближним и вставляется сразу после последнего помеченного фона. Порядок остальных фиксированных диапазонов сохраняется. Поэтому облака могут отрисовываться перед прозрачной листвой, а расположенные позднее солнце, погода и другие передние эффекты сохраняют свой относительный порядок. Это локальное правило очереди, а не перестановка всех проходов неба или эффектов. ## Проверки `cargo test --locked --offline -p fparkan-render-vulkan --lib material_category_and_transparency_preserve_world_state_in_either_order` Проверяет, что категория 1 сохраняет смешивание `source-alpha` и альфа-тест, сохраняет настройки проверки глубины и отсечения граней при любом порядке обновления и отклоняет неподдерживаемые категории. `cargo test --locked --offline -p fparkan-render-vulkan --lib tree_canopy_queue_follows_sky_and_precedes_foreground_effects` Проверяет заданный порядок очереди: градиент экрана, непрозрачная местность и здание, тень, туманность, купол и облака, прозрачная крона от дальних элементов к ближним, затем солнце и погода. ## Свидетельства диспетчеризации камеры GOG Для версии GOG `World3D.dll` с SHA-256 `17e4a3089b2583a8cf2356c9db0390b1aba138356a09130d79b4e7e4791da61e`, экспорт `stdRenderGame` находится по RVA `0x13BD0`. Вначале он вызывает `Terrain::stdSetCurrentCamera2(camera)`, сохраняет указатель камеры на время кадра и очищает его перед возвратом. `sendEndOfRender` — отдельный экспорт по RVA `0x13D90`. Эти данные подтверждают только порядок вызовов в кадре: анализ не восстанавливает ABI камеры, матрицы или значения `viewport`. Реализованный путь превью принимает входные данные с матрицей `selector0_words`, полученные через [процедуру захвата нативного кадра](../../tools/native-frame-capture/README.md). Процедура захвата записывает наблюдавшийся путь миссии и значение `atmosphere_seconds`, если проверка потока, поколения и часов прошла успешно. В превью с фиксированной камерой эта абсолютная фаза расписания используется, если не задан параметр `--atmosphere-seconds`; на всех кадрах чтения сохраняется та же фаза. Явный параметр командной строки имеет приоритет. Эта фаза не равна времени полной симуляции: погода, RNG и другие состояния миссии остаются неизвестны, поэтому она не подтверждает полного совпадения кадра. Анализ принимающей стороны уточняет этот контракт. `stdSetCurrentCamera2` (RVA `0x4FD40` в Terrain с SHA-256 `af87d1b2e728a0be73c52be3b44cc196ab46da7799f25a15d40f8c9b0b425ead`) запрашивает селектор `18` у переданного объекта и вызывает слот `+12` полученного интерфейса. Указатель не сохраняется в `stdGetCurrentCamera2`: этот геттер возвращает глобальное значение Terrain, установленное селектором `8` при инициализации Terrain. `Terrain.dll!LoadCamera` задаёт отдельный контракт фабрики. Точка входа GOG по RVA `0x4EBE0` использует соглашение о вызовах `__stdcall` и принимает четыре машинных слова, выделяет `0x1A4` байта, вызывает конструктор по RVA `0x4EC60` и возвращает интерфейс по смещению объекта `+0x134`. Конструктор передаёт аргументы 3 и 4 в инициализацию базового объекта по `this + 4`, записывает аргумент 3 в `this + 0x138` и вызывает вспомогательную функцию по RVA `0x4CEF0`, передавая ей аргументы 1, 2 и 4 вместе с `this`. Аргумент 1 — строка режима: ненулевое значение по `this + 0x1A0` дают только `REFLECTION` и `REFLECTION_SHIFTED`. Это свидетельство ABI фабрики, а не восстановленный контракт матрицы вида или проекции. ## Предпросмотр по захвату камеры D3D7 `fparkan-game --legacy-camera-capture ` принимает матрицу камеры `selector0_words`, прямоугольник области отображения `viewport` D3D7 `[left, top, right, bottom]`, ближнюю и дальнюю плоскости и угол поля зрения. По этим данным превью строит матрицы вида и проекции D3D7, передаёт полученную матрицу `clip_from_world` камере превью и напрямую использует оси камеры из матрицы вида. Поэтому базис захвата, включая крен, не сводится к рысканию и тангажу. Прямоугольник задаёт физический размер области отрисовки: `right - left` на `bottom - top`. Размер окна превью закреплён: если при запуске или после ненулевого изменения окна физический размер не совпадает с заданным, приложение завершает работу, а не растягивает изображение. Сравнивайте результат с фрагментом исходного PNG по прямоугольнику `[left, top, right, bottom]`. В этом режиме `FreeFlightCamera` не создаётся, но при повторной отрисовке обновляются окружение и проецируемые тени для захваченной камеры. До обновления окружения, которое может создавать звуковые события, слушателю задаются захваченные позиция, направление вперёд и верхняя ось; начальная сортировка диапазонов мира использует ту же позицию. Захват может содержать наблюдавшийся путь миссии и проверенное значение `atmosphere_seconds`; последнее используется только при отсутствии явного параметра командной строки. Это значение фиксирует фазу расписания атмосферы при сравнении с неподвижной камерой, тогда как FX продолжает получать реальное время кадра. `simulation_time_seconds`, погода, RNG и полное состояние миссии остаются неизвестными, поэтому совпадение камеры и фазы не подтверждает визуального совпадения с нативным кадром. Для неба превью вычисляет направление как `atan2(forward.x, forward.y)`. Оно совпадает со вторым выходным значением, вычисляемым в `Terrain.dll` по RVA `0x4F9C0`; при выборе сектора экранного градиента `CSky` добавляет π по RVA `0x481C5`. Это преобразование применяется только на границе неба и не меняет рыскание или управление движением `FreeFlightCamera`. ## Ограничения точного совпадения `VulkanStaticCamera::from_legacy_d3d7` выполняет восстановление вида и проекции D3D7 в `fparkan-render`, а затем один раз меняет знак вертикального члена проекции на границе адаптера Vulkan. В области отображения D3D7 положительная координата NDC Y направлена вверх экрана; Vulkan использует положительную высоту области отображения, поэтому адаптер меняет знак NDC Y, а шейдер сохраняет `gl_Position`. Общий путь `from_row_major_view_projection` не меняется: вызывающие его функции передают матрицы, уже подготовленные для Vulkan, как и камера свободного полёта. - точность и округление x87; - различия scalar/SIMD в `g_FastProc`; - порядок объектов, пакетов и прозрачных примитивов; - переходы состояний проверки глубины, отсечения граней, альфа-теста и смешивания; - пропуск mip-уровней, палитра и координаты Page; - запасной материал и выбор фазы; - последовательность RNG для FX и атмосферы; - переход на запасной набор возможностей устройства; - квантование времени симуляции. Для отладки требуется детерминированный захват кадра: состояние камеры, идентификаторы видимых объектов, список элементов отрисовки, ключи конвейера, матрицы и хэши промежуточных буферов.