Постобработка: эффекты на весь экран
Готовый кадр можно обработать ещё раз целиком — как фотофильтр поверх всего изображения.
Постобработка — этап, на котором уже отрисованный кадр обрабатывается фрагментным шейдером как одна большая текстура на весь экран.
Зачем это знать
Блюр, свечение, виньетка, цветокоррекция, motion blur, размытие глубины — всё это не привязано к отдельным объектам, а накладывается на финальную картинку. Понимание постобработки объясняет, как из «плоского» рендера получают кинематографичную картинку.
Идея: рендер в текстуру
Сцену рисуют не сразу на экран, а в текстуру (offscreen framebuffer). Затем рисуют один прямоугольник на весь экран (full-screen quad) и его фрагментный шейдер читает эту текстуру, преобразуя каждый пиксель.
Конвейер постобработки:
[сцена] --> рендер в текстуру --> [шейдер пост-эффекта] --> экран
блюр / bloom / цвет
Блюр: усреднение соседей
Размытие — это усреднение пикселя с соседями. Простейший box-blur берёт окно вокруг пикселя и считает среднее. Смоделируем на 1D-строке яркостей.
def box_blur_1d(pixels, radius=1):
out = []
n = len(pixels)
for i in range(n):
lo = max(0, i - radius)
hi = min(n, i + radius + 1)
window = pixels[lo:hi]
out.append(round(sum(window) / len(window), 1))
return out
row = [0, 0, 100, 0, 0, 100, 0]
print("Исходная:", row)
print("После блюра:", box_blur_1d(row, radius=1))
Вывод:
Исходная: [0, 0, 100, 0, 0, 100, 0] После блюра: [0.0, 33.3, 33.3, 33.3, 33.3, 33.3, 50.0]
Резкие пики «растеклись» на соседей — это и есть размытие. В 2D окно квадратное, а гауссов блюр даёт более мягкий, естественный результат.
Свечение (bloom)
Bloom имитирует переэкспонированный яркий свет, расплывающийся вокруг источника. Алгоритм: выделить очень яркие пиксели (порог), размыть их и сложить с исходным кадром.
def extract_bright(pixels, threshold=200):
# оставляем только яркие пиксели, остальные гасим
return [p if p >= threshold else 0 for p in pixels]
frame = [50, 255, 80, 240, 30, 100]
bright = extract_bright(frame, 200)
print("Кадр: ", frame)
print("Яркие части: ", bright)
print("Их размоют и добавят обратно -> свечение")
Вывод:
Кадр: [50, 255, 80, 240, 30, 100] Яркие части: [0, 255, 0, 240, 0, 0] Их размоют и добавят обратно -> свечение
Как работает под капотом
Тяжёлые эффекты вроде гауссова блюра разбивают на два прохода (сначала по горизонтали, потом по вертикали) — это дешевле, чем большое 2D-ядро. Bloom обычно делают на уменьшенной копии кадра, чтобы сэкономить. Тонмаппинг переводит расчёты в широком динамическом диапазоне (HDR) в отображаемые [0,1], сохраняя детали в светах и тенях.
Частые ошибки
- Делать блюр одним 2D-проходом большого радиуса — дорого; правильнее два 1D-прохода.
- Bloom без порога яркости — «замыливается» весь кадр, а не только источники света.
- Забыть тонмаппинг при HDR — пересветы превращаются в плоские белые пятна.
Итоги
- Постобработка применяет шейдер к готовому кадру как к текстуре на весь экран.
- Блюр — усреднение пикселя с соседями; гауссов разбивают на два 1D-прохода.
- Bloom = порог ярких пикселей → размытие → сложение с кадром.
- Тонмаппинг сжимает HDR в отображаемый диапазон без потери деталей.