diff options
Diffstat (limited to 'docs/reference/render-frame.md')
| -rw-r--r-- | docs/reference/render-frame.md | 162 |
1 files changed, 121 insertions, 41 deletions
diff --git a/docs/reference/render-frame.md b/docs/reference/render-frame.md index 10de209..19ccf3e 100644 --- a/docs/reference/render-frame.md +++ b/docs/reference/render-frame.md @@ -24,22 +24,91 @@ затенения, вызывается завершение рендерера, снимается `in_render`, затем рассылается `end-of-render`. -При retry кадра после пересоздания swapchain сохраняется прежний pending -sample, включая уже вычисленные позы и состояния FX и материалов. Для нового -drawable обновляются aspect, проекция и геометрия, зависящая от viewport; pose -берётся из pending camera. Новые события управления камерой остаются в -input controller и применяются следующим sample. Экранные billboards погоды -повторно проецируются из закэшированных мировых головы и хвоста, без второго -шага симуляции окружения и повторной отправки звуковых событий. Пауза нулевого -drawable не добавляет время. - -Pending frame хранит пакет MAT0 для мира и, при наличии GPU-окружения, отдельный -пакет неба. Пакеты содержат выбранные селектор и категорию конвейера, фазовые -коэффициенты материала, четыре UV-преобразования и кортеж фаз дополнительных -текстур. Повторное применение пакета при retry или нескольких resize не выбирает -фазу заново и не продвигает RNG. Перед отрисовкой сначала применяются материалы -мира и их порядок прозрачности, затем MAT0 неба, после чего viewport-зависимый -проход неба записывает свои переопределения. +В интерактивном preview часы материалов и окружения прибавляют полное время +между обновлениями. Ограничение шага в 100 мс остаётся у движения свободной +камеры и не обрезает эти часы. В режиме replay окружение получает разницу +последних применённых значений общего control clock. + +Если swapchain просит повторить ещё не представленный кадр, preview сохраняет +его общий clock, уже вычисленные позы, FX/RNG и фазы материалов. При изменении +drawable обновляются камера, aspect и viewport, после чего sky, environment +geometry и projected shadows пересобираются для нового размера. +Положение и ориентация камеры берутся из сохранённого состояния +ожидающего кадра; новый ввод мыши применяется при следующем сэмпле. Сэмпл +погоды остаётся прежним: экранные billboards дождя и снега повторно +проецируются из закэшированных мировых головы и хвоста, без второго шага +симуляции окружения или повторной отправки звуковых событий. Следующий такт +выбирается после успешного представления кадра; пауза нулевого drawable не +добавляет время. + +## Прозрачность порталов MSH + +GOG `Terrain.dll` (SHA-256 +`af87d1b2e728a0be73c52be3b44cc196ab46da7799f25a15d40f8c9b0b425ead`), +RVA `0x1B0F0`, заменяет diffuse alpha пакета без type 16 с batch bit `0x0008`. +При `0x0010` аргумент кривой — `sqrt(distance)`, границы — `2.6 / fov` и +`7.7999997 / fov`, наклон — `fov / (7.7999997 - 2.6)`. Без этого бита +`0x0040` задаёт ноль; иначе границы — `75 * 1.3 / fov`, `95 * 1.3 / fov`, +наклон — `fov / (95 * 1.3 - 75 * 1.3)`. До ближней границы alpha равна нулю, +после дальней — единице. Внутри перехода alpha равна произведению смещения от +ближней границы на наклон. Это замена alpha MAT0, а не дополнительный множитель. + +Метод расстояния `0x246A0` через `0x247B0` читает первую индексированную +вершину пакета, преобразует её в мир и вычисляет квадрат расстояния до камеры. +Текущий Ngi32 dispatch `FastProc+0x2C` указывает на RVA `0x1D6D0`: +`RSQRTSS` возвращает приближение обратного корня, которое умножается на квадрат +расстояния. Получается приближённое евклидово расстояние; дополнительный `FSQRT` +ветви `0x0010` выполняется уже над ним. Хеш Ngi32 — +`bab9840d94f4e4e74ffc26677724fa896cf4823845504d09a9e025f80016edf5`. +Скаляр камеры в шестом float getter `0x4F5F0` и поле CShade `+0xBB4` +проверены в работающем оригинале: оба содержат текущий горизонтальный FOV. + +`legacy_msh_portal_alpha` хранит кривую в `fparkan-render`. Приложение пока +берёт первую индексированную вершину своего диапазона и обычный `sqrt`, +поэтому выбор вершины после native CPU clipping и точность `RSQRTSS` не +воспроизводятся. Счётчик `render_coverage portal_fade_packet_selection_approx` +показывает число таких диапазонов. Поток type 16 сохраняет прежний отдельный +путь и ещё не декодирован. Это ограничивает приёмку на частично отсечённых +порталах и не позволяет считать интерьер принятой эталонной сценой. + +## Дистанционная кривая тумана + +Программный `CShade::ShadeIndexedStrided` в GOG Terrain.dll сравнивает квадрат +дистанции с квадратами ближней и дальней границ. По RVA `0x2EEED–0x2EF45` +видимость равна `1 - (distance² - near²) / (far² - near²)` между границами, +единице перед ближней и нулю за дальней. Такой же закон использует отдельный +путь цвета пакета `0x2F230`. Линейная интерполяция по самой дистанции давала +слишком плотный туман в середине диапазона. + +В работающем оригинале границы 0 и `0x442389E6`, квадрат дистанции +`0x47707B04` дали видимость `0x3F5B2B94`. Rust-тест проверяет передаваемые GPU +квадраты границ по этому наблюдению. GLSL вычисляет квадрат расстояния вершины, +после чего растеризатор интерполирует коэффициент по треугольнику. +`native_fog.effective.sink_rgb` совпадает с `sky.packed[1]`, нижней границей +освещения CShade. Обычный GPU fog color подтверждён отдельно: `CSky::Render` +по RVA `0x48341` задаёт renderer default из цвета горизонта. В живом замере +`0xFF404064` дошёл до `IDirect3DDevice7::SetRenderState(34)` в том же игровом +тике. В другом ракурсе палитра `[0xFFA8A5CE, 0xFFABA9D5, 0xFFBAA9D5, +0xFFABA9D5]` дала default `0xFFA9A7D1`. Это не покрывает временные изменения +цвета отдельных проходов: например, native render path устанавливает чёрный +цвет и затем восстанавливает сохранённый. +Выбор проходов по native shade-state пока приближён общим туманом мира и +считается как `render_coverage fog_pass_eligibility_approx`. + +## Радиус Control Op1 + +GOG `Effect.dll` (SHA-256 +`386a3e8db1ce23f61957825d633c7dae1ed40c25cab0cf10a534385fd7cee789`), +RVA `0xF6E0`, преобразует направление текущей матрицей, сохраняет его длину +перед нормализацией и умножает на неё интерполированный локальный радиус. +Умножение видно по RVA `0xFB13`; перед LightManager `+0x10` неположительный +результат заменяется на `0.01`. В живом «Конструкторе» для синего источника +завода локальный радиус 7 и длина `0x409B1F91` дали радиус `0x4207BB9F`. +Это же значение проверяется Rust-тестом общего Op1 evaluator. + +Случайные добавки цвета и радиуса ещё не воспроизводятся; приложение сообщает +число таких Control-команд как `control_op1_jitter_unsupported` и использует +их авторские значения. У синего источника в проверенном захвате добавки нулевые. ## Элемент отрисовки @@ -69,21 +138,17 @@ Pending frame хранит пакет MAT0 для мира и, при налич глубину. За фиксированными диапазонами окружения сохраняются их явные состояния. Для MSH без потока цветов type 16 диапазон, цвет которого вычисляет CPU, при -raw-флаге `Batch20` `0x0008` получает переопределение alpha только в одном -подтверждённом случае. В GOG `Terrain.dll` с SHA-256 -`af87d1b2e728a0be73c52be3b44cc196ab46da7799f25a15d40f8c9b0b425ead` проверка -по RVA `0x1B0F0` задаёт alpha `0`, когда установлен bit `0x0040` и сброшен -`0x0010`; поэтому для `0x0148` alpha равна нулю. Для `0x0108` и `0x0118` -превью сохраняет alpha MAT0; для `0x0058` действует приоритет bit `0x0010`, -также оставляя alpha MAT0. При наличии type 16 или отсутствии `0x0008` -переопределения нет. Оно относится к одному draw range и не меняет общий +raw-флаге `Batch20` `0x0008` получает переопределение alpha по +[дистанционной кривой портала](#прозрачность-порталов-msh). Bit `0x0010` +имеет приоритет; иначе bit `0x0040` задаёт нулевую alpha, а оставшаяся +ветвь использует границы 75/95 с поправкой на FOV. При наличии type 16 или +отсутствии `0x0008` переопределения нет. Оно относится к одному draw range и не меняет общий материал; alpha `0` временно задаёт число индексов `0`, а при очистке переопределения исходное число индексов восстанавливается. -Общий fade по расстоянию пока не реализован. Открыты точная метрика расстояния, -выбор первого CPU-принятого треугольника диапазона и эквивалентность frustum- и -culling-отбору оригинала. Поэтому состояния `0x0108` и `0x0118` оставляют alpha -MAT0, пока эти входные данные не установлены. +Fade обновляется с камерой каждый кадр. Открыт выбор первой принятой вершины +после native CPU clipping: приложение пока берёт первую индексированную +вершину диапазона и считает это приближение в `render_coverage`. В превью с фиксированной камерой диапазоны туманности и звёзд, купола и облаков явно помечаются как прозрачный фон. Непрозрачные диапазоны мира остаются на @@ -117,6 +182,10 @@ RVA `0x13D90`. Эти данные подтверждают только порÑ путь превью принимает входные данные с матрицей `selector0_words`, полученные через [процедуру захвата нативного кадра](../../tools/native-frame-capture/README.md). +Формат захвата сохраняет локальную матрицу `selector0_words` и, когда она +доступна, записывает `effective_world_matrix_words` с маркером пространства +`world`. + Процедура захвата записывает наблюдавшийся путь миссии и значение `atmosphere_seconds`, если проверка потока, поколения и часов прошла успешно. В превью с фиксированной камерой эта абсолютная фаза расписания используется, @@ -163,14 +232,26 @@ RVA `0x4EBE0` использует соглашение о вызовах `__std события, слушателю задаются захваченные позиция, направление вперёд и верхняя ось; начальная сортировка диапазонов мира использует ту же позицию. +Если в JSON присутствует пара `effective_world_matrix_space: "world"` и +`effective_world_matrix_words`, превью использует мировую матрицу. Матрица +`selector0_words` остаётся локальной и используется как запасной вариант только +при отсутствии мировых полей, если `parented_camera` не равно `true`. Для +родительской камеры без эффективной мировой матрицы вход отклоняется. + Захват может содержать наблюдавшийся путь миссии и проверенное значение `atmosphere_seconds`; последнее используется только при отсутствии явного параметра командной строки. Это значение фиксирует фазу расписания атмосферы -при сравнении с неподвижной камерой, тогда как FX продолжает получать реальное +при сравнении с неподвижной камерой, тогда как FX по умолчанию получает реальное время кадра. `simulation_time_seconds`, погода, RNG и полное состояние миссии остаются неизвестными, поэтому совпадение камеры и фазы не подтверждает визуального совпадения с нативным кадром. +Параметр `--control-clock-ms` воспроизводит единые часы MSH/FX от нуля до +выбранного значения в миллисекундах за указанное число кадров. Без этого +параметра часы идут в реальном времени. Для `--readback-sequence-out` этот +управляемый режим обязателен. Он не восстанавливает +`simulation_time_seconds`, погоду, RNG или полное состояние миссии. + Для неба превью вычисляет направление как `atan2(forward.x, forward.y)`. Оно совпадает со вторым выходным значением, вычисляемым в `Terrain.dll` по RVA `0x4F9C0`; при выборе сектора экранного градиента `CSky` добавляет π по RVA @@ -183,15 +264,9 @@ RVA `0x4EBE0` использует соглашение о вызовах `__std `1.3` рад — параметры, наблюдавшиеся в проверенном контексте GOG Ngi32 ([формула native projection](../tomes/05-render.md#камера-и-размещение-в-оригинале)). Для отношения ширины к высоте `a` превью вычисляет вертикальный угол -```rust -fn vertical_fov(self, aspect: f32) -> f32 { - 2.0 * ((self.horizontal_fov * 0.5).tan() / aspect).atan() -} -``` - -Код заменяет невалидный `aspect` на отношение 16:9; горизонтальный угол -остаётся `1.3` рад после изменения размера окна, а новый viewport и матрица -камеры применяются при следующей отрисовке. +`2 * atan(tan(horizontal_fov / 2) / a)`. Поэтому горизонтальный угол остаётся +`1.3` рад после изменения размера окна; новый viewport и матрица камеры +применяются при следующей отрисовке. Проекция направления не сравнивает косинус угла с ближней дистанцией: она отбрасывает только лучи за камерой и почти параллельные плоскости изображения @@ -254,6 +329,11 @@ D3D7 в `fparkan-render`, а затем один раз меняет знак в - переход на запасной набор возможностей устройства; - квантование времени симуляции. -Для отладки требуется детерминированный захват кадра: состояние камеры, -идентификаторы видимых объектов, список элементов отрисовки, ключи конвейера, -матрицы и хэши промежуточных буферов. +Параметр `--readback-sequence-out` сохраняет необработанные байты каждого +отправленного кадра; в имя файла записываются индекс кадра, значение общих +управляемых часов и размер изображения. Байты соответствуют выбранному формату +изображения swapchain Vulkan и не гарантируют порядок каналов R, G, B, A. Числовое +формат выводится как `readback_format`; расширение `.rgba` само по себе порядок +каналов не определяет. Последовательность не содержит структурированных ID +видимых объектов, списка элементов отрисовки, ключей конвейера, матриц или +хэшей промежуточных буферов. |
