В рамках рендеринга CesiumJS постобработка реализуется через систему постпроцессинговых стадий, объединённых в конвейер визуальных эффектов, применяемых к уже сформированному кадру сцены. Механизм PostProcessStage служит базовым строительным блоком для добавления экранных эффектов: цветокоррекции, размытия, выделения объектов, контуров, тепловых карт и произвольных шейдерных преобразований.
Постобработка в CesiumJS строится поверх финального изображения сцены. После завершения основного прохода рендеринга результат помещается в буфер, над которым выполняются последовательные стадии.
Ключевые элементы системы:
Каждая стадия представляет собой полноэкранный квад, на который накладывается фрагментный шейдер, обрабатывающий текстуру текущего кадра.
PostProcessStage инкапсулирует GLSL-шейдер, набор uniforms и параметры управления включением/выключением. Стадия не влияет на геометрию сцены, а работает исключительно с пиксельными данными.
Основные характеристики:
Базовая структура 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 — нормализованные координаты
пикселя.
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:
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 по умолчанию оперирует только цветовым буфером. Доступ к глубине требует дополнительных техник:
Пример получения глубины:
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);
Каждая стадия модифицирует результат предыдущей, формируя итоговое изображение.
PostProcessStage чувствителен к производительности GPU. Основные факторы нагрузки:
Оптимизация достигается через:
Маскирование позволяет ограничить область действия эффекта.
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 выполняется после завершения всех следующих этапов:
Это означает, что эффект не влияет на освещение сцены напрямую, но может визуально его модифицировать.
На уровне архитектуры приложения 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 требует анализа:
Ошибки в шейдере часто проявляются как полностью чёрный кадр или отсутствие эффекта, что связано с компиляцией GLSL на стороне GPU.