PostProcessStage

В рамках рендеринга CesiumJS постобработка реализуется через систему постпроцессинговых стадий, объединённых в конвейер визуальных эффектов, применяемых к уже сформированному кадру сцены. Механизм PostProcessStage служит базовым строительным блоком для добавления экранных эффектов: цветокоррекции, размытия, выделения объектов, контуров, тепловых карт и произвольных шейдерных преобразований.

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

Ключевые элементы системы:

  • PostProcessStage — одиночная стадия постобработки с кастомным шейдером
  • PostProcessStageComposite — объединение нескольких стадий в одну логическую цепочку
  • PostProcessStageLibrary — библиотека готовых эффектов
  • PostProcessStageCollection — контейнер стадий в Viewer или Scene

Каждая стадия представляет собой полноэкранный квад, на который накладывается фрагментный шейдер, обрабатывающий текстуру текущего кадра.

PostProcessStage как базовая единица эффекта

PostProcessStage инкапсулирует GLSL-шейдер, набор uniforms и параметры управления включением/выключением. Стадия не влияет на геометрию сцены, а работает исключительно с пиксельными данными.

Основные характеристики:

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

Создание пользовательской постобработки

Базовая структура PostProcessStage включает фрагментный шейдер, который получает входное изображение через uniform sampler2D.

const stage = new Cesium.PostProcessStage({
    fragmentShader: `
        uniform sampler2D colorTexture;
        varying vec2 v_textureCoordinates;

        void main() {
            vec4 color = texture2D(colorTexture, v_textureCoordinates);
            gl_FragColor = vec4(1.0 - color.rgb, color.a);
        }
    `
});

viewer.scene.postProcessStages.add(stage);

В данном примере реализуется инверсия цвета. colorTexture содержит текущий кадр сцены, а v_textureCoordinates — нормализованные координаты пикселя.

Uniform-параметры и динамическое управление

PostProcessStage поддерживает передачу uniform-переменных, что позволяет изменять поведение шейдера во время выполнения.

const stage = new Cesium.PostProcessStage({
    fragmentShader: `
        uniform sampler2D colorTexture;
        uniform float intensity;
        varying vec2 v_textureCoordinates;

        void main() {
            vec4 color = texture2D(colorTexture, v_textureCoordinates);
            gl_FragColor = vec4(color.rgb * intensity, color.a);
        }
    `,
    uniforms: {
        intensity: 1.5
    }
});

Uniform-значения могут быть функциями, что позволяет динамически пересчитывать параметры на каждом кадре:

uniforms: {
    intensity: () => Math.sin(Date.now() * 0.001) * 0.5 + 1.0
}

Управление включением и производительностью

Каждая стадия содержит состояние активности:

  • enabled — включает или отключает выполнение
  • clear — определяет очистку буфера
  • forcePowerOfTwo — управление размером текстур
  • sampleMode — режим выборки текстуры

Отключение стадии не удаляет её из коллекции, но исключает из конвейера рендеринга.

stage.enabled = false;
stage.enabled = true;

Порядок выполнения стадий

PostProcessStageCollection выполняет стадии строго последовательно. Порядок добавления определяет порядок применения эффектов.

viewer.scene.postProcessStages.add(stage1);
viewer.scene.postProcessStages.add(stage2);

В этом случае stage1 будет применён к исходному кадру, а stage2 — к результату stage1.

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

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

CesiumJS включает библиотеку готовых стадий через PostProcessStageLibrary:

  • FXAA сглаживание
  • размытие (blur)
  • обнаружение контуров
  • ночной режим
  • выделение объектов

Пример применения FXAA:

const fxaa = Cesium.PostProcessStageLibrary.createFXAAStage();
viewer.scene.postProcessStages.add(fxaa);

Встроенные стадии оптимизированы под WebGL и используют упрощённые шейдерные модели.

Эффект размытия и многопроходная обработка

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

const blurStage = new Cesium.PostProcessStage({
    fragmentShader: `
        uniform sampler2D colorTexture;
        varying vec2 v_textureCoordinates;

        void main() {
            float offset = 1.0 / 512.0;

            vec4 color =
                texture2D(colorTexture, v_textureCoordinates) * 0.4 +
                texture2D(colorTexture, v_textureCoordinates + vec2(offset, 0.0)) * 0.15 +
                texture2D(colorTexture, v_textureCoordinates - vec2(offset, 0.0)) * 0.15 +
                texture2D(colorTexture, v_textureCoordinates + vec2(0.0, offset)) * 0.15 +
                texture2D(colorTexture, v_textureCoordinates - vec2(0.0, offset)) * 0.15;

            gl_FragColor = color;
        }
    `
});

Размер offset критически влияет на силу размытия и зависит от разрешения экрана.

Работа с глубиной и ограничениями модели

PostProcessStage по умолчанию оперирует только цветовым буфером. Доступ к глубине требует дополнительных техник:

  • использование depthTexture
  • реконструкция позиции из глубины
  • комбинированные стадии

Пример получения глубины:

uniform sampler2D depthTexture;

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

  • атмосферная перспектива
  • выборочное затемнение
  • выделение объектов по расстоянию

Условная обработка пикселей

Шейдеры PostProcessStage позволяют применять эффекты выборочно.

if (color.r > 0.5) {
    color.rgb *= 2.0;
}

Однако условные ветвления в GLSL могут снижать производительность на GPU, поэтому предпочтительнее использовать математические функции:

color.rgb *= step(0.5, color.r) * 2.0;

Комбинирование стадий в сложные эффекты

Для построения сложных визуальных эффектов используется цепочка стадий:

  • предварительная фильтрация
  • основная обработка
  • финальная цветокоррекция

Пример композиции:

viewer.scene.postProcessStages.add(blurStage);
viewer.scene.postProcessStages.add(colorCorrectionStage);
viewer.scene.postProcessStages.add(vignetteStage);

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

Производительность и ограничения WebGL

PostProcessStage чувствителен к производительности GPU. Основные факторы нагрузки:

  • количество стадий в коллекции
  • сложность фрагментного шейдера
  • разрешение рендеринга
  • количество текстурных выборок

Оптимизация достигается через:

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

Использование масок и селективной обработки

Маскирование позволяет ограничить область действия эффекта.

uniform float mask;

void main() {
    vec4 color = texture2D(colorTexture, v_textureCoordinates);
    gl_FragColor = mix(color, vec4(1.0, 0.0, 0.0, 1.0), mask);
}

Маски часто комбинируются с геометрическими условиями или глубиной.

Взаимодействие с рендерингом сцены

PostProcessStage выполняется после завершения всех следующих этапов:

  1. отрисовка 3D-геометрии
  2. применение материалов
  3. тени и освещение
  4. постобработка

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

Расширенные сценарии применения

На уровне архитектуры приложения PostProcessStage используется для:

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

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

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

Шейдеры PostProcessStage могут генерироваться на лету:

function createTintStage(r, g, b) {
    return new Cesium.PostProcessStage({
        fragmentShader: `
            uniform sampler2D colorTexture;
            varying vec2 v_textureCoordinates;

            void main() {
                vec4 color = texture2D(colorTexture, v_textureCoordinates);
                gl_FragColor = vec4(color.rgb * vec3(${r}, ${g}, ${b}), color.a);
            }
        `
    });
}

Такой подход позволяет строить параметризованные визуальные эффекты.

Особенности отладки

Отладка PostProcessStage требует анализа:

  • промежуточных текстур
  • порядка выполнения стадий
  • корректности координат v_textureCoordinates
  • значений uniforms в реальном времени

Ошибки в шейдере часто проявляются как полностью чёрный кадр или отсутствие эффекта, что связано с компиляцией GLSL на стороне GPU.