Урок 10. Прозрачность

Альфа канал

Концепция альфа канала очень проста. Вместо того, чтобы просто записывать знрачения RGB, мы будем записывать RGBA:

// Выходные данные. Теперь это vec4
out vec4 color;

Первые три компонента как были xyz так и остаются ими, а вот четвертый – а:
color.a = 0.3;
Основная непонятность – альфа=непрозрачность. То есть альфа = 1 – предмет польность непрозрачный, а альфа = 0 – полностью прозрачный.
Тут мы просто захардкодили значение альфа канала в шейдере в значение 0.3, но обычно это значение берут из RGBA текстуры (формат TGA поддерживает альфа канал, а библиотека GLFV поддерживает формат TGA).
А вот и результат. Не забудьте выключить отсечение обратных поверхностей(glDisable(GL_CULL_FACE). Так как теперь мы можем смотреть сквозь предмет, и поэтому должны видеть обратную сторону.

Порядок имеет значение!

На предыдущем скриншоте все, вроде бы как неплохо, но это просто потому, что нам повезло.

Проблема

Давайте нарисуем два квадрата с 50% прозрачностью, один зеленый, и один красный. Как вы видите, результирующий цвет зависит от того, какой из квадратов находится сзади.
Эта же проблема есть и у нас, давайте немножко сменим вид:

Как оказывается, это достаточно сложная проблема. Вот почему в играх не часто можно видеть множество прозрачных объектов.

Простое решение

Самым простым решением будет отсортировать все прозрачные треугольники. Да, ВСЕ прозрачные треугольники.
  • Сначала рисуем непрозрачные объекты, чтобы потом Z буфер мог автоматом отсечь все невидимые прозрачные треугольники.
  • Сортируем прозрачные треугольники от самого дальнего до ближнего.
  • Рисуем прозрачные треугольники.
Вы можете сортировать все что угодно с помощью функций qsort в С, или std::sort в С++. Я тут не буду вдаваться в подробности, так как…

Проблема

  • Мы можем напороться на ограничение пропускной способности. Каждый фрагмент будет перезаписываться 10, 20 раз, а может быть и больше. А это уж слишком для несчастной шины памяти. Обычно Z буфер отсекает достаточно далекие фрагменты, но тут мы вручную отсортировали треугольники, поэтому Z буфер уже непригоден.
  • Это придется повторять каждый раз для каждого пикселя(так как мы используем 4xMSAA) если только не применять какие-нибудь хитрые оптимизации.
  • Сортировка занимает время.
  • Если вам необходимо будет сменять текстуры, или еще хуже – шейдер, то будут вообще дикие тормоза. Не делайте этого.

Хорошей практикой будет:
  • Ограничить количество прозрачных полигонов.
  • Использовать один и тот же шейдер и одну и ту же текстуру для всех треугольников.
  • Если они должны выглядеть по-разному, все равно пользуйтесь одной текстурой, можно даже и большой.
  • Если вы можете избежать сортировки, а результат приемлемый, то считайте, что вам повезло.

Порядконезависимая прозрачность


Существуют другие техники которые стоит почитать, если в вашем приложении нужно действительно много прозрачностей:
  • The original 2001 Depth Peeling paper: очень точная, но не быстрая.
  • Dual Depth Peeling : немного улучшенный алгоритм.
  • Несколько работ с сортировкой. Используется массив фрагментов который сортируется прямо в шейдере
  • ATIs Mecha Demo : хороший и быстрый алгоритм, но достаточно сложный для реализации. Требует современного железа. Использует связанный список фрагментов.
  • Cyril Crassin’s variation on the ATI’s  technique : еще более запутанный алгоритм.

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

Функция блендинга

Для того, чтобы предыдущий код заработал нам нужно настроить функцию блендинга.
// Включаем блендинг
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

что означает:
Новый цвет во фреймбуфере =
            Текущий альфа во фреймбуфере * текущий цвет во фреймбуфере+ (1-текущая альфа во фреймбуфере)*результирующий цвет шейдера

Комментариев нет:

Отправить комментарий