Sky: небо, солнце и карта окружения
Полные шейдеры, создание окружения, промежуточные проходы и обновление отражений.
Sky-шейдер описывает цвет неба по направлению взгляда. Его функция sky() используется для видимого фона, карты окружения для освещения/отражений и включённых промежуточных проходов. Настройка частоты обновления карты окружения не отключает отрисовку фона.
Подключите небо к окружению
- Добавьте WorldEnvironment и назначьте ему Environment.
- Выберите BackgroundMode = Sky и создайте ресурс Sky в Environment.Sky.
- В SkyMaterial создайте ShaderMaterial с Shader типа sky.
- Убедитесь, что активная Camera3D смотрит на свободный фон. Непрозрачная геометрия закрывает небо.
Этот полный шейдер показывает, какое направление использовано: зенит красный, горизонт зелёный, нижняя полусфера синяя. Это диагностические цвета, не модель атмосферы.
shader_type sky;
uniform vec3 zenith_color : source_color = vec3(1.0, 0.0, 0.0);
uniform vec3 horizon_color : source_color = vec3(0.0, 1.0, 0.0);
uniform vec3 ground_color : source_color = vec3(0.0, 0.0, 1.0);
void sky() {
float height = clamp(abs(EYEDIR.y) * 2.0, 0.0, 1.0);
vec3 pole = EYEDIR.y >= 0.0 ? zenith_color : ground_color;
COLOR = mix(horizon_color, pole, height);
}Для создания тех же ресурсов из C# сохраните помощник SkySetup.cs:
using Reckfell;
public static class SkySetup
{
public static Reckfell.Environment Create(Shader shader)
{
var material = new ShaderMaterial { Shader = shader };
var sky = new Sky
{
SkyMaterial = material,
ProcessMode = Sky.ProcessModeEnum.Quality,
RadianceSize = Sky.RadianceSizeEnum.Size256,
};
return new Reckfell.Environment
{
BackgroundMode = Reckfell.Environment.BGMode.Sky,
Sky = sky,
TonemapMode = Reckfell.Environment.ToneMapper.Linear,
};
}
}Загрузите шейдер через ResourceLoader.Load<Shader>("asset://DirectionSky.reckfellshader"), вызовите SkySetup.Create(shader) и назначьте результат свойству Environment у WorldEnvironment. Создайте WorldEnvironment внутри того же World3D, который использует камера; в SubViewport с OwnWorld3D нужно собственное окружение. Linear здесь выбран для проверки цвета, а не как универсальная настройка художественного рендера.
Проверка поворачивает камеру вверх/вниз и получает красный/синий фон. Перенос камеры при неизменной ориентации не меняет этот шейдер, поскольку он использует EYEDIR, но не POSITION. Параметры меняются у ShaderMaterial: material.SetShaderParameter("zenith_color", Colors.Blue). Передача default(Variant) возвращает начальное значение uniform.
Разделяйте фон и карту освещения
AT_CUBEMAP_PASS обозначает расчёт radiance-карты окружения. В основном фоновом проходе он false. Можно упростить освещение относительно видимого фона, но тогда отражения и фон намеренно различаются. В проверке одна ветка пишет синий в карту окружения, другая — красный на экран; чтение готовой панорамы подтверждает это различие.
Статическое дорогое вычисление можно перенести в ветку карты и читать её результат при отрисовке фона. Полный минимальный пример:
shader_type sky;
void sky() {
if (AT_CUBEMAP_PASS) {
COLOR = vec3(0.0, 0.0, 1.0);
} else {
COLOR = textureLod(RADIANCE, EYEDIR, 0.0).rgb;
}
}Читайте RADIANCE только вне расчёта самой карты: нельзя использовать незавершённый результат как собственный источник. textureLod с уровнем 0 явно выбирает базовую детализацию. Для панорамы из файла используйте отдельный sampler2D и SKY_COORDS либо PanoramaSkyMaterial; RADIANCE — уже обработанная карта окружения, а не исходный файл изображения.
Кэширование не бесплатное: фон всё равно выполняет выборку каждый кадр. Детализацию ограничивают разрешение карты и фильтрация; мелкие облака или звёзды могут потеряться. Пример доказывает работоспособность пути, а не ускорение любого эффекта.
Выберите способ обновления
| Sky.ProcessMode | Назначение |
|---|---|
| Automatic | Рендерер выбирает режим по зависимостям шейдера и настройкам. Это не обещание постоянного Realtime. |
| Quality | Качественное обновление карты при необходимости, рассчитанное на статичное или редко меняющееся небо. |
| Incremental | Разносит работу фильтрации карты по кадрам; обновление отражений может завершаться позже изменения фона. |
| Realtime | Путь для частых изменений, с ограничениями детализации и качества фильтрации. |
В текущем RD-рендерере Realtime использует размер 256. Сочетание с другим RadianceSize вызывает предупреждение и внутреннее приведение. RadianceSize — enum, а не произвольное число пикселей. Ресурс хранит запрошенное значение, поэтому его getter не служит подтверждением фактического внутреннего размера после приведения. В проверке Realtime используется с Size256, а Quality — также с Size64.
Карта помечается для обновления при изменении материала/параметров, используемого времени, позиции камеры при наличии POSITION, используемых направленных источников и некоторых параметров тумана. При активных промежуточных проходах изменение разрешения требует обновить их буферы. Удалите ненужные зависимости: даже использование TIME в ветке, предназначенной только для фона, учитывается анализом шейдера. Automatic в текущем RD выбирает Realtime для TIME/POSITION при поддерживаемом размере, Incremental для света, рассеяния солнца в тумане или uniform-параметров, иначе Quality; детали политики не следует считать одинаковыми для всех рендереров.
TIME — время рендерера с учётом Engine.TimeScale и периодом rendering/limits/time/time_rollover_secs, по умолчанию 3600 секунд. Пауза дерева его сама по себе не останавливает. Для управляемой анимации передавайте собственное время и обновляйте его только при нужном изменении.
Половинный и четвертной проходы
use_half_res_pass создаёт проход с половиной ширины и высоты, use_quarter_res_pass — с четвертью каждой стороны. Это примерно 1/4 и 1/16 числа пикселей соответствующего полного прохода. Дополнительные проходы всё равно имеют стоимость; не включайте их без вычисления, которое можно перенести на низкое разрешение.
Следующий диагностический шейдер записывает отдельные значения в оба прохода и читает их в полном. ALPHA половинного прохода попадает в красный канал итогового цвета, зелёный и синий подтверждают чтение обеих текстур:
shader_type sky;
render_mode use_half_res_pass, use_quarter_res_pass;
void sky() {
if (AT_QUARTER_RES_PASS) {
COLOR = vec3(0.0, 0.0, 1.0);
ALPHA = 0.25;
} else if (AT_HALF_RES_PASS) {
COLOR = vec3(0.0, 1.0, 0.0);
ALPHA = 0.5;
} else {
COLOR = vec3(HALF_RES_COLOR.a, HALF_RES_COLOR.g, QUARTER_RES_COLOR.b);
}
}Для облаков вместо диагностических цветов вычисляйте цвет и покрытие на низком разрешении, затем самостоятельно смешивайте их с небом через mix. Чтение HALF_RES_COLOR или QUARTER_RES_COLOR не включает автоматическое смешивание. Их фильтрация сглаживает выборку, но не восстанавливает потерянные детали. AT_CUBEMAP_PASS может одновременно быть true с флагом промежуточного прохода: проверяйте флаги в нужном порядке.
ALPHA используется как данные промежуточного прохода. В основном небе ALPHA=0 не показывает прозрачный фон. Для экспорта с прозрачностью настройте отдельный способ композиции viewport; не рассчитывайте получить её выходом sky().
Направленный свет
Встроены четыре слота LIGHT0…LIGHT3. Проверяйте LIGHTX_ENABLED перед чтением остальных полей. В них попадают подходящие DirectionalLight3D, а не PointLight3D; номер слота не является стабильным ID источника. LightOnly исключает источник из неба, SkyOnly оставляет его для неба.
Полный пример формирует световое пятно по направлению источника. Это не физическая атмосфера и не диск с точным угловым диаметром:
shader_type sky;
void sky() {
COLOR = vec3(0.0);
if (LIGHT0_ENABLED) {
float alignment = max(dot(EYEDIR, LIGHT0_DIRECTION), 0.0);
COLOR = LIGHT0_COLOR * LIGHT0_ENERGY * pow(alignment, 64.0);
}
}В проверенном RD-пути LIGHTX_DIRECTION направлен к источнику вдоль мировой +Z его преобразования. Поэтому dot(EYEDIR, LIGHTX_DIRECTION) выделяет область взгляда на источник; не инвертируйте знак по названию «направление света». Поворот DirectionalLight3D на π вокруг Y делает пятно видимым для камеры, смотрящей вдоль −Z. LIGHTX_COLOR приходит в линейном пространстве, ENERGY учитывает настройки энергии и применимые физические единицы/экспозицию. LIGHTX_SIZE — угловой диаметр в радианах, не радиус: для диска нужен косинус половины диаметра.
Режимы и встроенные значения
| Режим | Действие |
|---|---|
| use_half_res_pass | Включает проход половинного разрешения. |
| use_quarter_res_pass | Включает проход четвертного разрешения. |
| disable_fog | Отключает влияние тумана, включая пользовательский FOG в проверенном RD-пути. |
| use_debanding | Добавляет шум для уменьшения полос на плавных градиентах; это не дополнительная детализация. |
| Тип и имя | Значение |
|---|---|
in float TIME | Время рендерера; влияние на обновления описано выше. |
in float PI / TAU / E | Математические константы. |
in vec3 POSITION | Мировая позиция камеры; не положение пикселя неба. |
samplerCube RADIANCE | Выборка готовой карты окружения по направлению вне её собственного прохода. |
in bool AT_CUBEMAP_PASS | Расчёт карты окружения, включая её промежуточные проходы. |
in bool AT_HALF_RES_PASS / AT_QUARTER_RES_PASS | Текущий проход половинного / четвертного разрешения. |
in bool LIGHTX_ENABLED | Доступность слота X от 0 до 3. |
in vec3 LIGHTX_DIRECTION / LIGHTX_COLOR | Направление к источнику / линейный цвет. |
in float LIGHTX_ENERGY / LIGHTX_SIZE | Энергия / угловой диаметр в радианах. |
in vec3 EYEDIR | Нормализованное направление текущего луча. |
in vec2 SCREEN_UV | Координаты текущего прохода; не UV сферической панорамы. |
in vec4 FRAGCOORD | Координаты фрагмента текущего целевого буфера. |
in vec2 SKY_COORDS | Сферические UV для панорамной текстуры. |
in vec4 HALF_RES_COLOR / QUARTER_RES_COLOR | Выборки соответствующих включённых проходов; цвет и записанная ALPHA. |
out vec3 COLOR | Выходной линейный RGB; затем применяется экспозиция/тональная обработка. |
out float ALPHA | Данные прозрачности промежуточного прохода; не прозрачность основного фона. |
out vec4 FOG | Пользовательский цвет тумана в RGB и вес смешивания в A. |
in — только чтение, out — записываемый результат, sampler — источник выборки. Флаги прохода, POSITION, RADIANCE, LIGHTX и TIME доступны глобально; EYEDIR, SCREEN_UV, SKY_COORDS, FRAGCOORD и выходы относятся к sky(). FOG смешивается с результатом: RGB задаёт цвет, A — вес; это не плотность FogVolume. disable_fog в текущем RD отключает и этот вклад.
Диагностика
| Симптом | Что проверить |
|---|---|
| Небо чёрное | BackgroundMode, Environment.Sky, SkyMaterial, shader_type sky, активную камеру и общий World3D. |
| Фон меняется, отражения запаздывают | ProcessMode и завершение обновления/фильтрации radiance-карты. |
| Небо слишком яркое | Значения HDR в шейдере, энергию окружения, экспозицию камеры и тональную обработку. |
| ALPHA=0 не скрывает небо | Основной проход не смешивается по ALPHA; настройте композицию отдельно. |
| Неверный цвет или мелкие детали после кэширования | Различие веток фона/карты, разрешение, LOD и фильтрацию RADIANCE. |
| Световое пятно с другой стороны | Знак LIGHTX_DIRECTION, поворот источника и выбранный слот. |
| Солнце не появляется | LIGHTX_ENABLED, DirectionalLight3D.SkyMode, видимость и предел четырёх источников. |
| Полосы градиента | Диапазон и тональную обработку; при необходимости use_debanding. |
| Предупреждение о размере Realtime | Используйте Size256 в текущем RD или выберите другой режим обновления. |
Проверены фактические пиксели Windows Forward+/Vulkan: направления, uniform, фон/radiance, чтение карты, промежуточные проходы, alpha, FOG и слоты направленного света. Настройки Quality/Incremental/Realtime проверены на статическом результате; это не замер производительности или задержки фильтрации. Политика обновления, прочие встроенные значения, вращение/FOV окружения и ограничения других конфигураций сверены по исходникам. Новые снимки редактора, художественная атмосфера, физическая корректность, HDR/XR и все платформы/экспорты здесь не проверялись.