Освещение и тени

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

В основе системы освещения Deck.gl лежит объект LightingEffect, который агрегирует набор источников света и передаёт их в рендеринговый пайплайн слоёв. Каждый слой, поддерживающий освещение (например, экструдированные полигоны, столбчатые диаграммы, 3D-сетки), вычисляет итоговый цвет пикселя с учётом:

  • нормалей поверхности
  • положения наблюдателя
  • параметров материалов
  • вкладов источников света

Физическая модель упрощена, но близка к Blinn–Phong с возможными расширениями.

Источники света

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

AmbientLight

Рассеянный свет, равномерно освещающий всю сцену без направления. Используется для предотвращения полного провала в тени.

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

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

Особенности:

  • не имеет направления
  • не создаёт теней
  • задаёт базовый уровень освещённости

DirectionalLight

Свет, имитирующий удалённый источник (например, солнце). Все лучи параллельны.

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

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

Ключевые свойства:

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

PointLight

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

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

const pointLight = new PointLight({
  color: [255, 200, 180],
  intensity: 2.0,
  position: [0, 0, 1000]
});

Особенности:

  • затухание с расстоянием
  • полезен для локальных сценических эффектов
  • может имитировать городское освещение или фонари

LightingEffect и композиция света

Все источники объединяются через LightingEffect, который передаётся в Deck как один из эффектов рендера.

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

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

const deckgl = new Deck({
  initialViewState: {
    longitude: 0,
    latitude: 0,
    zoom: 2,
    pitch: 45
  },
  controller: true,
  layers: [],
  effects: [lightingEffect]
});

Порядок и комбинация источников напрямую влияет на визуальную читаемость сцены: чрезмерный directional light делает сцену контрастной, а сильный ambient light «плоской».

Материалы поверхностей

Материал в Deck.gl описывает, как поверхность реагирует на свет. Он задаётся через свойство material у поддерживающих слоёв.

const material = {
  ambient: 0.35,
  diffuse: 0.6,
  shininess: 32,
  specularColor: [255, 255, 255]
};

Компоненты материала

Ambient (окружающее отражение) Определяет, сколько рассеянного света поглощает поверхность.

Diffuse (диффузное отражение) Основной вклад от направленного света. Формирует базовый цвет объекта.

Specular (зеркальные блики) Отвечает за яркие отражения источников света.

Shininess (гладкость) Контролирует размер и резкость блика: чем выше значение, тем «гладче» поверхность.

Освещение в слоях

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

  • PolygonLayer (включая экструзию)
  • ColumnLayer
  • GridCellLayer
  • HexagonLayer
  • ScenegraphLayer

Пример с экструдированными полигонами:

import {PolygonLayer} from '@deck.gl/layers';

const layer = new PolygonLayer({
  id: 'buildings',
  data,
  extruded: true,
  wireframe: false,
  getPolygon: d => d.coordinates,
  getElevation: d => d.height,
  material: {
    ambient: 0.4,
    diffuse: 0.6,
    shininess: 10
  }
});

При включённой экструзии нормали рассчитываются автоматически, что позволяет корректно реагировать на направление света.

Тени и их вычисление

Тени в Deck.gl основаны на принципе shadow mapping: сцена рендерится с точки зрения источника света, и полученная глубина используется для определения затемнённых областей.

Directional Light Shadow

При использовании направленного света возможно включение теней:

const directionalLight = new DirectionalLight({
  color: [255, 255, 255],
  intensity: 1.0,
  direction: [-1, -2, -3],
  shadow: true
});

Тени особенно заметны при работе с:

  • экструдированными зданиями
  • 3D-гистограммами
  • поверхностями с выраженной геометрией

Ограничения теней

  • высокая стоимость по производительности
  • ограниченное разрешение shadow map
  • возможные артефакты (acne, peter-panning)
  • необходимость балансировки между качеством и FPS

Взаимодействие света с WebGL пайплайном

Deck.gl использует WebGL-шейдеры для расчёта освещения. Вершинный шейдер передаёт:

  • нормали
  • позиции
  • координаты текстур

Фрагментный шейдер вычисляет итоговый цвет:

  1. нормализация нормали
  2. расчёт направления к источнику света
  3. вычисление диффузной компоненты
  4. вычисление блика
  5. добавление ambient компоненты

Упрощённая схема:

vec3 normal = normalize(vNormal);
vec3 lightDir = normalize(uLightDirection);

float diff = max(dot(normal, lightDir), 0.0);

vec3 diffuse = diff * uLightColor;
vec3 ambient = uAmbientColor;

gl_FragColor = vec4(diffuse + ambient, 1.0);

Влияние камеры на освещение

Освещение в Deck.gl не фиксировано относительно экрана — оно вычисляется в мировом пространстве. Это означает:

  • при вращении карты свет остаётся стабильным
  • при изменении pitch изменяется восприятие объёма
  • при zoom меняется плотность визуальных деталей, но не физика света

Особенно важен параметр наклона камеры (pitch), который усиливает или ослабляет читаемость рельефа.

Производительность и оптимизация

Освещение и тени существенно влияют на производительность WebGL-приложений.

Основные факторы нагрузки:

  • количество источников света
  • наличие теней
  • сложность геометрии
  • количество пикселей (fill-rate)

Практические оптимизации:

  • ограничение числа PointLight
  • использование одного DirectionalLight
  • отключение теней на низких уровнях zoom
  • упрощение материала (уменьшение specular)
  • снижение детализации геометрии

Цветовые и визуальные артефакты

При неправильной настройке освещения возникают типичные проблемы:

Пересвет (overexposure) Слишком высокая интенсивность источников света.

Плоская сцена Чрезмерный ambient light без directional компоненты.

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

Ломанные тени Недостаточное разрешение shadow map или неправильная настройка bias.

Практика построения сбалансированного освещения

Типичная конфигурация для городской 3D-карты:

  • один directional light как солнечный источник
  • слабый ambient light для заполнения теней
  • умеренный specular для материалов зданий
  • включённые тени только для ключевых слоёв
const lightingEffect = new LightingEffect({
  ambientLight: new AmbientLight({
    color: [255, 255, 255],
    intensity: 0.4
  }),
  directionalLight: new DirectionalLight({
    color: [255, 255, 255],
    intensity: 0.9,
    direction: [-1, -1, -2],
    shadow: true
  })
});

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