Настройка отражений

Отражения в трёхмерной визуализации позволяют создавать более реалистичное восприятие сцены за счёт имитации отражающей поверхности. В экосистеме Deck.gl отражения не являются встроенной функцией высокого уровня, как в полноценных игровых движках, однако могут быть реализованы через возможности WebGL, пользовательские шейдеры, настройку освещения и интеграцию с внешними рендерерами.

Отражения чаще всего используются для следующих объектов:

  • водных поверхностей;
  • стеклянных элементов;
  • зеркальных панелей;
  • полированных городских объектов;
  • архитектурных визуализаций;
  • цифровых двойников городов.

При работе с отражениями необходимо учитывать баланс между качеством изображения и производительностью, поскольку вычисление отражённых сцен требует дополнительных проходов рендеринга.


Взаимодействие отражений и системы освещения

Отражение напрямую зависит от освещения сцены. Чем сложнее система света, тем более реалистичным становится отражённый объект.

В Deck.gl освещение обычно задаётся через объект LightingEffect.

import {AmbientLight, DirectionalLight, LightingEffect} from '@deck.gl/core';

const ambientLight = new AmbientLight({
  color: [255, 255, 255],
  intensity: 1.0
});

const sunLight = new DirectionalLight({
  color: [255, 255, 255],
  intensity: 2.0,
  direction: [-1, -3, -1]
});

const lightingEffect = new LightingEffect({
  ambientLight,
  sunLight
});

Даже если полноценное отражение отсутствует, качественное освещение создаёт визуальную иллюзию отражающих поверхностей благодаря бликам и изменению яркости в зависимости от угла обзора.


Псевдоотражения через материалы

Наиболее простой способ создания эффекта отражения заключается в использовании свойств материала.

Для слоёв, поддерживающих освещение, можно задавать параметры материала:

material: {
  ambient: 0.2,
  diffuse: 0.6,
  shininess: 128,
  specularColor: [255, 255, 255]
}

Описание параметров:

Параметр Назначение
ambient Вклад окружающего освещения
diffuse Интенсивность рассеянного света
shininess Размер и резкость блика
specularColor Цвет зеркального отражения

Большие значения shininess позволяют создавать впечатление полированной поверхности.

Пример для слоя зданий:

new ScenegraphLayer({
  id: 'buildings',
  data,
  scenegraph: '/models/building.glb',
  material: {
    ambient: 0.1,
    diffuse: 0.5,
    shininess: 256,
    specularColor: [255, 255, 255]
  }
});

Такой подход не создаёт настоящих отражений окружающей среды, но обеспечивает характерный зеркальный блеск.


Отражения через Environment Mapping

Наиболее распространённой техникой является Environment Mapping.

Суть метода заключается в использовании кубической текстуры, содержащей изображения окружающего пространства.

Схема работы:

  1. Формируется кубическая карта окружения.
  2. Для каждого пикселя вычисляется отражённый вектор.
  3. По этому вектору выбирается цвет из кубической карты.
  4. Полученный цвет используется как отражение.

Визуально объект начинает отражать не реальную сцену, а заранее подготовленное окружение.


Cube Map

Cube Map состоит из шести изображений:

  • Front;
  • Back;
  • Left;
  • Right;
  • Top;
  • Bottom.

Структура файлов может выглядеть следующим образом:

skybox/
├── px.jpg
├── nx.jpg
├── py.jpg
├── ny.jpg
├── pz.jpg
└── nz.jpg

Каждая сторона представляет одну грань виртуального куба.


Использование Cube Map в пользовательском слое

Deck.gl позволяет расширять базовые слои через механизм шейдеров.

Создание собственного слоя:

import {Layer} from '@deck.gl/core';

class ReflectiveLayer extends Layer {
  getShaders() {
    return {
      vs: vertexShader,
      fs: fragmentShader,
      modules: []
    };
  }
}

Внутри фрагментного шейдера выполняется выборка данных из кубической карты.

uniform samplerCube uCubeMap;

vec3 reflectedDir =
    reflect(viewDirection, normal);

vec4 reflectionColor =
    textureCube(
        uCubeMap,
        reflectedDir
    );

Функция reflect() является встроенной функцией GLSL и вычисляет направление отражённого луча.


Формула отражённого вектора

Отражение вычисляется на основе нормали поверхности и направления взгляда.

Используется выражение:

R=I-2(NI)N

где:

  • R — отражённый вектор;
  • I — входящий луч;
  • N — нормаль поверхности.

Эта формула лежит в основе большинства реализаций отражений в графических движках и WebGL-приложениях.


Dynamic Environment Mapping

Статические карты окружения подходят не всегда.

Если сцена постоянно изменяется, требуется динамическое окружение.

Алгоритм выглядит следующим образом:

  1. Камера размещается в точке отражающего объекта.
  2. Выполняется рендеринг сцены шесть раз.
  3. Полученные изображения записываются в Cube Map.
  4. Cube Map используется для отражения.

Схема обеспечивает актуальное отражение окружающих объектов.

Недостатки:

  • шестикратное увеличение нагрузки на GPU;
  • рост потребления памяти;
  • снижение FPS на больших сценах.

Поэтому метод применяется только для небольшого количества объектов.


Отражения водной поверхности

Одним из самых востребованных сценариев является отражение воды.

Обычно применяется техника Render-to-Texture.

Основные этапы:

  1. Создание дополнительного framebuffer.
  2. Зеркальное расположение камеры.
  3. Рендеринг сцены во вспомогательную текстуру.
  4. Использование текстуры на плоскости воды.

Схема позволяет получить отражение объектов над поверхностью воды.


Создание Framebuffer

Deck.gl использует инфраструктуру библиотеки luma.gl.

Пример создания framebuffer:

const framebuffer = device.createFramebuffer({
  colorAttachments: [texture]
});

Во время дополнительного прохода рендеринга сцена записывается не на экран, а в созданную текстуру.


Зеркальная камера

Для формирования отражения положение камеры отражается относительно плоскости воды.

Исходная камера:

{
  x: 100,
  y: 50,
  z: 200
}

После отражения:

{
  x: 100,
  y: -50,
  z: 200
}

Полученное изображение выглядит так, словно сцена наблюдается из-под поверхности.


Использование текстуры отражения

Во фрагментном шейдере текстура применяется как источник отражённого изображения.

uniform sampler2D reflectionTexture;

vec4 reflection =
    texture(
        reflectionTexture,
        uv
    );

Затем отражение смешивается с базовым цветом поверхности.

vec4 finalColor =
    mix(
        waterColor,
        reflection,
        0.7
    );

Коэффициент смешивания определяет силу отражения.


Эффект Френеля

В реальном мире интенсивность отражения зависит от угла обзора.

Этот эффект называется эффектом Френеля.

При взгляде сверху отражение слабее.

При взгляде под острым углом отражение значительно сильнее.

Типичная реализация:

float fresnel =
    pow(
        1.0 - dot(viewDir, normal),
        5.0
    );

Далее коэффициент используется при смешивании цветов:

vec4 finalColor =
    mix(
        baseColor,
        reflectionColor,
        fresnel
    );

Благодаря этому поверхность выглядит значительно реалистичнее.


Отражения в ScenegraphLayer

ScenegraphLayer особенно часто используется для отображения моделей формата GLTF и GLB.

Сами модели могут содержать материалы PBR.

Современные модели экспортируются из:

  • Blender;
  • Autodesk Maya;
  • 3ds Max.

Материалы PBR включают:

  • metallic;
  • roughness;
  • normal maps;
  • environment maps.

Если модель содержит корректно настроенные параметры металличности и шероховатости, отражения автоматически выглядят гораздо правдоподобнее.


Настройка металличности

Для отражающих объектов важны два параметра.

Metallic

Определяет степень металлического поведения материала.

0.0 = диэлектрик
1.0 = металл

Roughness

Определяет шероховатость поверхности.

0.0 = зеркало
1.0 = матовая поверхность

Примеры:

Материал Metallic Roughness
Зеркало 1.0 0.0
Хром 1.0 0.05
Стекло 0.0 0.02
Алюминий 1.0 0.15
Бетон 0.0 0.95

Screen Space Reflections

Screen Space Reflections (SSR) — техника отражений, использующая уже отрисованное изображение экрана.

Преимущества:

  • отсутствие дополнительного рендеринга сцены;
  • высокая скорость работы;
  • реалистичные локальные отражения.

Недостатки:

  • отражаются только видимые объекты;
  • появляются артефакты по краям экрана;
  • сложная реализация в чистом WebGL.

В стандартный набор Deck.gl SSR не входит, однако может быть реализован через пользовательские post-processing эффекты.


Постобработка отражений

Deck.gl поддерживает систему эффектов постобработки.

Общий подход:

const deck = new Deck({
  effects: [
    lightingEffect,
    customReflectionEffect
  ]
});

Собственный эффект может:

  • считывать глубину сцены;
  • использовать нормали;
  • строить экранные отражения;
  • добавлять размытие отражённых областей.

Такой подход часто применяется в крупных геопространственных визуализациях.


Оптимизация отражений

Отражения являются одной из самых ресурсоёмких частей рендеринга.

Основные методы оптимизации:

Уменьшение разрешения текстуры

Вместо:

2048 × 2048

используется:

1024 × 1024

или

512 × 512

Ограничение числа отражающих объектов

Вместо отражений на каждом объекте сцены отражения применяются только к:

  • воде;
  • стеклянным фасадам;
  • металлическим конструкциям.

Обновление Cube Map по таймеру

Перестраивать карту окружения можно не каждый кадр.

Например:

setInterval(updateCubeMap, 100);

Это уменьшает нагрузку на видеокарту.


Размытие отражений

Сильное размытие позволяет использовать текстуры меньшего разрешения без заметной потери качества.

reflection =
    blur(
        reflectionTexture
    );

Комбинирование отражений и теней

Максимально реалистичный результат достигается при совместном использовании:

  • направленного освещения;
  • теней;
  • environment mapping;
  • эффекта Френеля;
  • PBR-материалов;
  • динамических отражений воды.

Такая комбинация позволяет строить визуально сложные сцены в Deck.gl, включающие городские модели, инфраструктурные объекты, цифровые двойники территорий и высокодетализированные трёхмерные геопространственные приложения.