Effect классы

Effect-классы в deck.gl представляют собой механизм расширения визуального рендеринга на уровне сцены, позволяя применять глобальные или слой-специфичные визуальные преобразования без модификации исходных геометрических данных. Архитектурно система эффектов отделена от слоёв (layers) и работает на этапе финального рендеринга через композицию шейдеров и постобработки.

Рендеринг в deck.gl строится вокруг последовательности этапов: подготовка данных слоя, рендеринг в framebuffer и последующая композиция результата. Effect-классы подключаются после того, как слои сформировали изображение в буфере кадра.

Основная идея заключается в том, что эффект:

  • не изменяет геометрию
  • не влияет на данные слоя
  • работает с изображением или освещением сцены
  • может быть глобальным (для всей сцены)

Механизм эффектов интегрирован через EffectManager, который управляет жизненным циклом эффектов и их применением при каждом кадре.

Базовый класс Effect

Корневой абстрактный класс Effect определяет интерфейс, который обязаны реализовать все визуальные эффекты.

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

  • id — уникальный идентификатор эффекта
  • enabled — флаг активности
  • apply(...args) — основная функция применения эффекта
  • cleanup() — освобождение ресурсов WebGL
  • params — конфигурационные параметры эффекта

Базовая логика выглядит концептуально так:

class CustomEffect {
  constructor() {
    this.id = 'custom-effect';
    this.enabled = true;
  }

  apply({gl, framebuffer, inputBuffer, outputBuffer}) {
    // логика обработки кадра
  }

  cleanup() {
    // удаление шейдеров, буферов
  }
}

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

EffectManager и управление эффектами

EffectManager является центральным координатором. Он:

  • хранит список активных эффектов
  • отслеживает их изменения
  • организует последовательность применения
  • управляет framebuffer chaining (цепочкой буферов)

При каждом кадре выполняется следующий порядок:

  1. Рендер слоёв в исходный framebuffer
  2. Последовательное применение эффектов
  3. Запись результата в финальный буфер вывода

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

Типы эффектов в deck.gl

Постобработочные эффекты (Post-processing effects)

Постобработка выполняется после рендеринга всей сцены и работает с изображением как с текстурой.

Характерные задачи:

  • размытие (blur)
  • цветокоррекция
  • виньетирование
  • антиалиасинг
  • HDR-компрессия

Принцип работы основан на fullscreen quad shader:

apply({gl, inputBuffer, outputBuffer}) {
  this.program.setUniforms({
    texture: inputBuffer
  });

  drawFullScreenQuad(gl, this.program, outputBuffer);
}

Ключевая особенность — отсутствие привязки к геометрии сцены.


LightingEffect

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

LightingEffect передаёт в GPU:

  • источники света
  • параметры отражения
  • глобальное освещение

Типы источников света:

  • AmbientLight — фоновое освещение
  • DirectionalLight — направленный источник
  • PointLight — точечный источник

Пример конфигурации:

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

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

LightingEffect передаёт uniform-переменные в шейдеры слоёв, где выполняется расчёт освещения на пиксельном уровне.


PostProcessEffect и эффектные цепочки

PostProcessEffect представляет собой класс-обёртку над фрагментными шейдерами, применяемыми ко всему кадру.

Он используется как базовый строительный блок для эффектов:

  • bloom
  • grayscale
  • edge detection
  • tone mapping

Пример логики шейдера:

const fs = `
  precision highp float;
  varying vec2 vTexCoord;
  uniform sampler2D texture;

  void main() {
    vec4 color = texture2D(texture, vTexCoord);
    gl_FragColor = vec4(color.rgb * 1.2, color.a);
  }
`;

PostProcessEffect позволяет подключать произвольную GLSL-логику без изменения слоя.

Взаимодействие эффектов с слоями

Слои deck.gl могут поддерживать эффекты на двух уровнях:

  • глобальные эффекты сцены
  • эффекты конкретного слоя (через параметры материала или шейдерные хелперы)

LightingEffect, например, влияет на все слои, поддерживающие освещение.

Внутренне слой получает массив эффектов:

layer.draw({effects});

И затем каждый эффект может:

  • модифицировать uniforms
  • влиять на shader modules
  • изменять расчет цвета

Порядок применения эффектов

Последовательность критична:

  1. Рендер слоя в framebuffer A
  2. Effect 1: A → B
  3. Effect 2: B → C
  4. Финальный buffer C

Некоторые эффекты требуют знания предыдущего состояния сцены, например bloom или motion blur, поэтому используются ping-pong buffers.

Производительность и GPU-аспекты

Effect-система напрямую работает с WebGL и оказывает влияние на:

  • количество проходов рендеринга
  • использование framebuffer memory
  • bandwidth текстур

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

  • минимизация количества post-process passes
  • reuse framebuffer objects
  • использование half-float текстур вместо full float
  • избегание лишних копирований буферов

Особенно дорогими являются эффекты с несколькими проходами (multi-pass shaders).

Кастомные эффекты

Создание собственного эффекта требует реализации логики на уровне WebGL.

Структура кастомного эффекта:

class InvertEffect {
  constructor() {
    this.id = 'invert-effect';
  }

  setup(gl) {
    this.program = new ShaderProgram(gl, {
      vs,
      fs
    });
  }

  apply({inputBuffer, outputBuffer, gl}) {
    this.program.setUniforms({
      texture: inputBuffer
    });

    drawFullScreenQuad(gl, this.program, outputBuffer);
  }
}

Ключевые аспекты:

  • обязательная работа через framebuffer
  • управление shader program lifecycle
  • корректная очистка ресурсов

Интеграция с shader modules

deck.gl использует модульную систему шейдеров. Effect может внедряться через:

  • inject defines
  • modify fragment shader
  • extend lighting calculations

Пример расширения:

const shaderModule = {
  name: 'custom-light',
  fs: `
    vec3 applyCustomLight(vec3 color) {
      return color * 1.1;
    }
  `
};

Ограничения системы эффектов

Несмотря на гибкость, система имеет ряд ограничений:

  • отсутствие доступа к геометрии внутри post-process эффектов
  • зависимость от возможностей WebGL контекста
  • ограничения по количеству framebuffer операций
  • высокая стоимость сложных эффектов на слабых GPU

Использование эффектов в сложных сценах

В сценах с большим количеством слоёв эффекты становятся инструментом унификации визуального стиля:

  • единая цветокоррекция для всех слоёв
  • согласованное освещение
  • синхронизированные постобработки

Комбинация нескольких эффектов формирует эффектный pipeline:

  • LightingEffect для физически корректного освещения
  • Tone mapping для HDR
  • PostProcessEffect для стилизации

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