Прозрачность и блендинг
Стекло, дым и вода частично пропускают то, что за ними — это достигается смешиванием цветов по альфа-каналу.
Блендинг (alpha blending) — смешивание цвета нового фрагмента с уже лежащим в пикселе по формуле, где вес задаёт альфа-канал.
Зачем это знать
Непрозрачные объекты просто перекрывают фон. Прозрачные должны его просвечивать, и здесь возникает целый класс тонкостей: формула смешивания, порядок рисования, конфликт с буфером глубины. Неправильный блендинг — это либо «чёрные края», либо «стекло», через которое ничего не видно.
Формула смешивания
Классическая формула «over»: итоговый цвет = новый_цвет×alpha + старый_цвет×(1-alpha). При alpha=1 объект полностью непрозрачен, при alpha=0 — невидим, при 0.5 — наполовину просвечивает.
def blend_over(src, dst, alpha):
return tuple(round(s*alpha + d*(1-alpha), 2) for s, d in zip(src, dst))
red = (1.0, 0.0, 0.0) # новый фрагмент (стекло)
blue = (0.0, 0.0, 1.0) # фон
for a in [0.0, 0.25, 0.5, 0.75, 1.0]:
print(f"alpha={a}: {blend_over(red, blue, a)}")
Вывод:
alpha=0.0: (0.0, 0.0, 1.0) alpha=0.25: (0.25, 0.0, 0.75) alpha=0.5: (0.5, 0.0, 0.5) alpha=0.75: (0.75, 0.0, 0.25) alpha=1.0: (1.0, 0.0, 0.0)
При alpha=0 виден только фон (синий), при alpha=1 — только новый цвет (красный), между — плавная смесь. Это и есть «стекло».
Порядок имеет значение
Смешивание зависит от того, что уже лежит в пикселе, поэтому для прозрачных объектов порядок критичен. Их рисуют от дальних к ближним (back-to-front), иначе смешивание получится с неправильным «задником».
def blend_over(src, dst, alpha):
return tuple(round(s*alpha + d*(1-alpha), 2) for s, d in zip(src, dst))
# два полупрозрачных слоя над фоном, разный порядок
bg = (0.0, 0.0, 0.0)
A = ((1.0, 0.0, 0.0), 0.5) # красный
B = ((0.0, 1.0, 0.0), 0.5) # зелёный
# порядок A потом B
r1 = blend_over(A[0], bg, A[1]); r1 = blend_over(B[0], r1, B[1])
# порядок B потом A
r2 = blend_over(B[0], bg, B[1]); r2 = blend_over(A[0], r2, A[1])
print("A потом B:", r1)
print("B потом A:", r2)
Вывод:
A потом B: (0.25, 0.5, 0.0) B потом A: (0.5, 0.25, 0.0)
Те же два полупрозрачных слоя дают разный цвет при разном порядке — вот почему прозрачность сортируют.
Конфликт с z-buffer
Если прозрачный фрагмент запишет свою глубину в z-buffer, он перекроет всё, что за ним, и просвечивания не выйдет. Поэтому прозрачные объекты рисуют после непрозрачных, обычно с тестом глубины, но без записи в неё (depth write off).
Как работает под капотом
Есть разные режимы блендинга: «over» (стекло), аддитивный (свет, огонь — цвета складываются и светятся), мультипликативный (затемнение). Аддитивный блендинг (src + dst) приятен тем, что не требует строгой сортировки — сложение коммутативно. Premultiplied alpha (заранее домноженный цвет на альфу) избавляет от тёмной каймы на краях.
Частые ошибки
- Рисовать прозрачное с записью глубины — задние объекты исчезнут.
- Не сортировать back-to-front при «over»-блендинге — неверные цвета наложения.
- Забыть про premultiplied alpha — тёмные ореолы вокруг полупрозрачных краёв.
Итоги
- Alpha blending: цвет = src·alpha + dst·(1-alpha).
- Прозрачные объекты рисуют после непрозрачных, сортируя от дальних к ближним.
- Для прозрачных отключают запись в z-buffer (но тест оставляют).
- Аддитивный блендинг (свет/огонь) коммутативен и не требует сортировки.