diff options
Diffstat (limited to 'docs/reference/atmosphere.md')
| -rw-r--r-- | docs/reference/atmosphere.md | 65 |
1 files changed, 57 insertions, 8 deletions
diff --git a/docs/reference/atmosphere.md b/docs/reference/atmosphere.md index 65ed176..dba20c2 100644 --- a/docs/reference/atmosphere.md +++ b/docs/reference/atmosphere.md @@ -50,7 +50,25 @@ key_time = floor(duration * (key_hour * 60 + key_minute) * 60 / 86400) theta = (clamp((now - birth_start) / lifetime, 0, 1) * 1.2 - 0.1) * pi ``` -Затем к нему применяется матрица рождения. У солнца и луны независимые +Затем к нему применяется матрица рождения как к вектору-столбцу. Если `A` и +`B` — углы рождения, а `x = cos(theta)` и `z = -sin(theta)`, результат равен + +```text +[x*cos(A) + z*sin(A)*sin(B), + x*sin(A) - z*cos(A)*sin(B), + z*cos(B)] +``` + +Порядок важен для всего освещения: транспонированное умножение переносит +солнце на другую сторону мира и меняет освещённые склоны, блики и тени. +В GOG `Terrain.dll` (SHA-256 +`af87d1b2e728a0be73c52be3b44cc196ab46da7799f25a15d40f8c9b0b425ead`) +матрицу строит RVA `0x4A550`, а вектор преобразует `CSun::Update`, RVA +`0x4B570`. Живой замер после 282184 мс при длительности 525000 мс и углах +`[90, 30]` дал направление `[-0.4950135, -0.1408745, -0.8573891]`; +Rust-тест проверяет этот результат с допуском `3e-6`. + +У солнца и луны независимые интервалы и независимые направления: при отсутствии активной луны нельзя подменять её направление противоположностью солнца. `SunFrame::color` — это единственный primary directional RGB. Второй источник получает направление @@ -131,6 +149,31 @@ gradient и clouds row 2. Для nebula/stars/clouds используются UV directional RGB из `sky.packed[0]`. Это четыре фиксированных pass, а не три независимых цветных слоя. +Все три текстурных слоя движутся. Обновление GOG `CSky` +(`Terrain.dll`, RVA `0x48B90`, запись UV `0x4942A–0x495B7`; +[сборка и SHA-256](original-binaries.md)) берёт разность текущего `u32` clock +и сохранённого поля объекта `+8` с переполнением. Это отдельные миллисекунды +жизни неба, а не абсолютная фаза суток из SKE. + +| Слой | Период | Добавка к исходным UV | +|---|---:|---| +| Фон | 300 000 мс | одинаковый сдвиг U и V | +| Звёзды | 100 000 мс | одинаковый сдвиг U и V | +| Облака | 50 000 мс | сдвиг только U | + +Добавка равна остатку от деления elapsed clock на период, умноженному на +нативный коэффициент `f32` (`3.3333333e-6`, `1e-5`, `2e-5`). Вершинные UV +каждый раз вычисляются от позиции купола заново; накопление приращений +создавало бы дрейф. `SkyMesh::update_uvs` получает текущие миллисекунды кадра +и сохраняет геометрию купола. В работающем оригинале проверены UV всех трёх +слоёв, включая разность clock с переполнением. + +Захват сохраняет `sky_uv_clock` на входе в рендер неба, проверяя его по +фактическим UV вершины зенита и clock того же кадра. Replay удерживает эту +фазу вместе с фазой суток. Без такого поля текстуры движутся по elapsed clock +приложения; начальная фаза оригинала неизвестна и явно считается как +`captured_sky_phase_unverified`. + Цвет первого screen-gradient и последнего пояса вычисляет `SkyFrame::screen_gradient(yaw_radians, glare_rgb_delta)`. Native `CSky` сначала округляет `(yaw + pi) * 180 / pi` режимом nearest-even, выбирает сектор @@ -167,21 +210,27 @@ RGB palette 12 с минимумом `80/255` на канал и alpha `150/255` ## Туман и свет -Нативное обновление `CSky` записывает две дальности и один packed RGB. +Нативное обновление `CSky` записывает две дальности и нижнюю границу освещения. `FogFrame` повторяет этот контракт: ```text fog.start = sky.values[0] * 700 fog.end = sky.values[1] * 700 -fog.color = RGB(sky.packed[1]) / 255 +lighting_floor = RGB(sky.packed[1]) / 255 ``` Поля не скрывают порядок исходных параметров: значения сохраняются в -`SkySample`, а преобразование выполняется только в `fog_frame`. Vulkan path -передаёт цвет и дальности в frame uniforms; vertex shader вычисляет линейный -коэффициент по расстоянию до камеры, а fragment shader смешивает RGB материала -с fog color в ветвях combiner, которые используют туман. Конкретный draw state -может обходить это смешивание. +`SkySample`, а преобразование выполняется только в `fog_frame`. Нижняя граница +освещения ограничивает результат расчёта света снизу; она не задаёт цвет тумана. +Обычный цвет тумана выбирается из `sky.colors[0..4]` по направлению камеры, +как цвет горизонта. Это подтверждено в `CSky::Render` и живым совпадением +его значения с `D3DRENDERSTATE_FOGCOLOR` в том же игровом тике. + +Vulkan path передаёт эти параметры раздельно. Vertex shader вычисляет +коэффициент по квадратам расстояния и границ, а fragment shader смешивает +RGB материала с цветом тумана. Конкретный native draw state может обходить +смешивание или временно менять его цвет, например на чёрный для отдельного +прохода. Полный выбор таких проходов ещё не воспроизведён. Цвет primary света солнца — RGB palette 12, нормированный делением на 255 и умноженный на trailing parameter 2. `SunFrame::direction` — нормированный |
