PostProcessEffect — механизм постобработки изображения в
Deck.gl, позволяющий применять графические эффекты уже после завершения
рендеринга всех слоёв сцены. Вместо изменения отдельных слоёв или
объектов эффект воздействует на итоговый кадр целиком, что делает его
удобным инструментом для реализации визуальных фильтров, повышения
качества изображения и создания специальных эффектов.
Постобработка широко используется в графических движках и системах визуализации данных. После того как все слои нарисованы во внутренний буфер кадра, Deck.gl передаёт результат цепочке эффектов, которые могут модифицировать пиксели перед выводом на экран.
Типичные сценарии применения:
Deck.gl поддерживает специальную систему эффектов, которая
подключается через свойство effects.
Упрощённая схема работы выглядит следующим образом:
Пример подключения эффектов:
const deck = new Deck({
initialViewState,
controller: true,
layers: [...],
effects: [...]
});
Каждый объект внутри массива effects реализует
определённый визуальный эффект.
Класс наследуется от базовой системы эффектов Deck.gl.
Упрощённая структура:
Effect
└── PostProcessEffect
Effect определяет общий интерфейс жизненного цикла, а
PostProcessEffect реализует обработку готового изображения
через шейдеры.
В современных версиях Deck.gl импорт выполняется следующим образом:
import {PostProcessEffect} from '@deck.gl/core';
После импорта можно создавать экземпляры эффектов и передавать их в
массив effects.
Конструктор принимает два основных аргумента:
new PostProcessEffect(module, props)
где:
module — шейдерный модуль обработки;props — параметры эффекта.Пример:
const effect = new PostProcessEffect(shaderModule, {
intensity: 1.0
});
Основой любого PostProcessEffect является shader
module.
Модуль обычно содержит:
const shaderModule = {
name: 'custom-effect',
fs: `
uniform float intensity;
vec4 customEffect_filterColor(
vec4 color,
vec2 texSize,
vec2 texCoord
) {
return vec4(color.rgb * intensity, color.a);
}
`,
uniforms: {
intensity: 1.0
}
};
Deck.gl автоматически интегрирует такой модуль в конвейер постобработки.
Во время обработки каждый пиксель итогового изображения проходит через функцию фильтра.
Типичная сигнатура:
vec4 custom_filterColor(
vec4 color,
vec2 texSize,
vec2 texCoord
)
Аргументы:
| Параметр | Описание |
|---|---|
| color | текущий цвет пикселя |
| texSize | размеры текстуры |
| texCoord | координаты пикселя |
Возвращаемое значение:
return vec4(...);
Именно оно станет новым цветом пикселя.
Увеличение яркости изображения:
const brightnessModule = {
name: 'brightness',
fs: `
uniform float brightness;
vec4 brightness_filterColor(
vec4 color,
vec2 texSize,
vec2 texCoord
) {
return vec4(
color.rgb * brightness,
color.a
);
}
`,
uniforms: {
brightness: 1.5
}
};
const brightnessEffect =
new PostProcessEffect(brightnessModule);
Все цвета становятся ярче в полтора раза.
Пример создания негативного изображения:
const invertModule = {
name: 'invert',
fs: `
vec4 invert_filterColor(
vec4 color,
vec2 texSize,
vec2 texCoord
) {
return vec4(
1.0 - color.rgb,
color.a
);
}
`
};
const invertEffect =
new PostProcessEffect(invertModule);
Результат:
Красный → Голубой
Зелёный → Пурпурный
Синий → Жёлтый
Белый → Чёрный
Часто используется при аналитической визуализации.
const grayscaleModule = {
name: 'grayscale',
fs: `
vec4 grayscale_filterColor(
vec4 color,
vec2 texSize,
vec2 texCoord
) {
float gray =
dot(
color.rgb,
vec3(0.299, 0.587, 0.114)
);
return vec4(
vec3(gray),
color.a
);
}
`
};
Здесь применяется стандартная формула вычисления яркости.
Параметры передаются через объект свойств.
Создание:
const effect =
new PostProcessEffect(module, {
intensity: 2.0
});
Объявление в модуле:
uniform float intensity;
Использование:
return vec4(
color.rgb * intensity,
color.a
);
Это позволяет динамически менять параметры без изменения шейдера.
Например, управление эффектом через интерфейс пользователя.
let intensity = 1.0;
const effect =
new PostProcessEffect(module, {
intensity
});
При изменении значения:
effect.setProps({
intensity: 2.5
});
Deck.gl обновит uniform-переменные на стороне GPU.
Deck.gl поддерживает композицию эффектов.
const deck = new Deck({
layers,
effects: [
grayscaleEffect,
brightnessEffect
]
});
Последовательность имеет значение.
Вариант №1:
[
grayscaleEffect,
brightnessEffect
]
Сначала изображение станет серым, затем повысится яркость.
Вариант №2:
[
brightnessEffect,
grayscaleEffect
]
Сначала увеличится яркость, затем выполнится преобразование в серый цвет.
Результат может существенно отличаться.
Координаты пикселя доступны через:
texCoord
Диапазон:
(0.0, 0.0)
↓
(1.0, 1.0)
Это позволяет создавать пространственные эффекты.
Пример затемнения краёв:
float distanceFromCenter =
distance(
texCoord,
vec2(0.5)
);
float factor =
1.0 - distanceFromCenter;
return vec4(
color.rgb * factor,
color.a
);
Получается эффект виньетирования.
Полный пример:
const vignetteModule = {
name: 'vignette',
fs: `
vec4 vignette_filterColor(
vec4 color,
vec2 texSize,
vec2 texCoord
) {
float d =
distance(
texCoord,
vec2(0.5)
);
float factor =
smoothstep(
0.8,
0.2,
d
);
return vec4(
color.rgb * factor,
color.a
);
}
`
};
Эффект создаёт плавное затемнение по краям изображения.
Размеры изображения доступны через:
texSize
Пример:
float width = texSize.x;
float height = texSize.y;
Это особенно важно для:
Упрощённый вариант blur:
vec2 offset =
vec2(1.0) / texSize;
vec4 sum = vec4(0.0);
sum += texture2D(texture, texCoord);
sum += texture2D(texture, texCoord + offset);
sum += texture2D(texture, texCoord - offset);
return sum / 3.0;
Алгоритм усредняет соседние пиксели.
В реальных приложениях обычно применяются:
Модуль может содержать любое количество uniform-параметров.
Пример:
uniform float intensity;
uniform float radius;
uniform vec3 tint;
Передача значений:
new PostProcessEffect(module, {
intensity: 2,
radius: 5,
tint: [1, 0, 0]
});
В шейдере:
color.rgb *= tint;
Наложение цветового оттенка:
const tintModule = {
name: 'tint',
fs: `
uniform vec3 tint;
vec4 tint_filterColor(
vec4 color,
vec2 texSize,
vec2 texCoord
) {
return vec4(
color.rgb * tint,
color.a
);
}
`
};
Пример:
new PostProcessEffect(
tintModule,
{
tint: [1.0, 0.8, 0.8]
}
);
Изображение получает красноватый оттенок.
Комбинация нескольких операций:
float gray =
dot(
color.rgb,
vec3(
0.299,
0.587,
0.114
)
);
return vec4(
vec3(
gray * 0.1,
gray,
gray * 0.1
),
color.a
);
На выходе изображение становится зелёным, напоминающим экран прибора ночного видения.
Постобработка выполняется на каждом кадре.
Стоимость зависит от:
При разрешении:
1920 × 1080
обрабатывается более двух миллионов пикселей каждый кадр.
Даже небольшое усложнение шейдера может существенно повлиять на FPS.
Рекомендуемые практики:
Плохо:
20 texture2D(...)
Лучше:
5 texture2D(...)
Дорогие операции:
pow()
exp()
log()
Быстрее:
x * x
Вместо:
effectA
effectB
effectC
часто эффективнее:
combinedEffect
с одним проходом обработки.
Следует учитывать несколько особенностей:
После рендеринга остаётся только изображение.
Недоступны:
Layer
Object
Feature
Attribute
Доступны только пиксели итогового кадра.
Нельзя напрямую определить:
Даже если изменяется небольшая область изображения, вычисления всё равно выполняются для всего кадра.
| Возможность | PostProcessEffect | Layer Extension |
|---|---|---|
| Обработка отдельных объектов | Нет | Да |
| Работа после рендеринга | Да | Нет |
| Доступ к пикселям кадра | Да | Нет |
| Изменение геометрии | Нет | Да |
| Полноэкранные фильтры | Да | Ограниченно |
| Эффекты камеры | Да | Нет |
Layer Extensions предназначены для модификации поведения
слоёв, тогда как PostProcessEffect работает с уже
сформированным изображением.
Во время работы Deck.gl проходит несколько этапов:
Понимание этого процесса позволяет проектировать сложные цепочки визуальных эффектов и эффективно использовать возможности GPU для обработки итогового изображения сцены.