OpenGL ReadPixels (Скриншот) Альфа

Я использую настройку Tile Rendering (используя glReadPixels), чтобы делать скриншоты сцены. Мой вывод выглядит правильно:

Полное изображение

Но, глядя на альфа-канал, пиксели все еще остаются прозрачными, даже если прозрачная текстура отображается поверх непрозрачной текстуры.

Альфа маска

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

Есть ли способ тестирования альфа-компонента каждой текстуры в пикселе, а не только в ближайшем?

2 ответа

Решение

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

Я предполагаю, что вы рисуете ваши полупрозрачные объекты спереди, используя GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA смешанная функция. Вы прямо не упоминаете об этом, но это довольно стандартно и согласуется с тем, что вы видите. Для правильного решения также потребуется альфа-компонент в кадровом буфере, но он уже есть, иначе вы ничего не получите при чтении альфы.

Чтобы проиллюстрировать весь процесс, я собираюсь использовать два примера по пути. Я только перечислю (R, A) компоненты для цветов, G а также B будет вести себя так же, как R,

  1. Нарисуйте слой с цветом (R1, 1.0)затем слой с (R2, 0.4) на нем.
  2. Нарисуйте слой с цветом (R1, 0.5)затем слой с (R2, 0.4) на нем.

Цвет фона (Rb, 0.0)Вы всегда хотите очистить со значением альфа 0,0 для этого вида смешивания.

Во-первых, давайте рассчитаем результат, который мы хотим достичь для цветов:

  • Для случая 1 рисование первого слоя полностью покрывает фон, так как он имеет альфа = 1,0. Затем мы накладываем второй слой поверх него. Поскольку он имеет альфа = 0,4, мы сохраняем 60% первого слоя и добавляем 40% второго слоя. Итак, цвет, который мы хотим, это

    0.6 * R1 + 0.4 * R2

  • Для случая 1, рисование первого слоя сохраняет 50% фона, так как он имеет альфа = 0,5. Так что цвет пока

    0.5 * Rb + 0.5 * R1

    Затем мы накладываем второй слой поверх него. Мы снова сохраняем 60% предыдущего цвета и добавляем 40% второго слоя. Итак, цвет, который мы хотим, это

    0.6 * (0.5 * Rb + 0.5 * R1) + 0.4 * R2 = 0.3 * Rb + 0.3 * R1 + 0.4 * R2

Теперь давайте выясним, какой результат мы хотим получить для альфы:

  • Для случая 1 наш первый слой был полностью непрозрачным. Один из способов оценки непрозрачности заключается в измерении доли света, поглощаемой объектом. Когда у нас есть слой, который поглощает весь свет, все, что мы делаем, не изменит этого. Наша общая альфа должна быть

    1.0

  • Для случая 2 у нас есть один слой, который поглощает 50% света, и один, который поглощает 40% оставшегося света. Поскольку 40% из 50% составляют 20%, в общей сложности 70% поглощается. Наша общая альфа должна быть

    0.7

С помощью GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA для смешивания дает желаемый результат для цвета. Но, как вы заметили, не для альфы. Делаем расчет на примере:

  • Случай 1: рисование слоя 1, SRC_ALPHA 1,0, исходное значение S = (R1, 1.0) и значение назначения D = (Rb, 0.0), Таким образом, функция смешивания оценивается как

    SRC_ALPHA * S + ONE_MINUS_SRC_ALPHA * D = 1.0 * (R1, 1.0) + 0.0 * (Rb, 0.0) = (R1, 1.0)

    Это записывается в кадровый буфер и становится целевым значением для слоя рисования 2. Источником для слоя 2 является (R2, 0.4), Оценка с 0,4 для SRC_ALPHA дает

    SRC_ALPHA * S + ONE_MINUS_SRC_ALPHA * D = 0.4 * (R2, 0.4) + 0.6 * (R1, 1.0) = (0.4 * R2 + 0.6 * R1, 0.76)

  • Случай 2: рисование слоя 1, SRC_ALPHA 0,5, исходное значение S = (R1, 0.5) и значение назначения D = (Rb, 0.0), Таким образом, функция смешивания оценивается как

    SRC_ALPHA * S + ONE_MINUS_SRC_ALPHA * D = 0.5 * (R1, 0.5) + 0.5 * (Rb, 0.0) = (0.5 * R1 + 0.5 * Rb, 0.25),

    Это записывается в кадровый буфер и становится целевым значением для слоя рисования 2. Источником для слоя 2 является (R2, 0.4), Оценка с 0,4 для SRC_ALPHA дает

    SRC_ALPHA * S + ONE_MINUS_SRC_ALPHA * D = 0.4 * (R2, 0.4) + 0.6 * (0.5 * R1 + 0.5 * Rb, 0.25) = (0.4 * R2 + 0.3 * R1 + 0.3 * Rb, 0.31),

Итак, мы подтвердили то, что вы уже знали: мы получаем желаемые цвета, но не те альфы. Как мы это исправим? Нам нужна другая функция смешивания для альфа. К счастью, OpenGL имеет glBlendFuncSeparate(), что позволяет нам делать именно это. Все, что нам нужно, это выяснить, какую функцию смешивания использовать для альфа. Вот мыслительный процесс:

Допустим, мы уже визуализировали некоторые полупрозрачные объекты с общей альфа A1, который хранится в кадровом буфере. То, что мы сделали до сих пор, поглощает часть A1 от общего света, и позволяет дроби 1.0 - A1 пройти через. Мы рендерим еще один слой с альфа A2 на нем. Этот слой поглощает фракцию A2 света, который прошел раньше, поэтому он поглощает дополнительный (1.0 - A1) * A2 всего света. Мы должны добавить это к количеству уже поглощенного света, так что в общей сложности (1.0 - A1) * A2 + A1 сейчас поглощается.

Все, что осталось сделать, это перевести это в уравнение смешивания OpenGL. A2 это значение источника S, а также A1 целевое значение D, Таким образом, наш желаемый альфа-результат становится

(1.0 - A1) * A2 + A1 = (1.0 - A1) * S + 1.0 * D

Что я назвал A1 является альфа-значением в кадровом буфере, который упоминается как DST_ALPHA в спецификации функции смешивания. Итак, мы используем ONE_MINUS_DST_ALPHA чтобы соответствовать нашему исходному множителю 1.0 - A1, Мы используем GL_ONE соответствовать целевому множителю 1.0,

Таким образом, параметры функции смешивания для альфа (GL_ONE_MINUS_DST_ALPHA, GL_ONE)и полный вызов функции смешивания:

glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
                    GL_ONE_MINUS_DST_ALPHA, GL_ONE);

Мы можем дважды проверить математику для альфы с примерами еще раз:

  • Случай 1: рисование слоя 1, DST_ALPHA 0,0, исходное значение S = (.., 1.0) и значение назначения D = (.., 0.0), Таким образом, функция смешивания оценивается как

    ONE_MINUS_DST_ALPHA * S + ONE * D = 1.0 * (.., 1.0) + 1.0 * (.., 0.0) = (.., 1.0)

    Это записывается в кадровый буфер и становится целевым значением для слоя рисования 2. Источником для слоя 2 является (.., 0.4), а также DST_ALPHA сейчас 1,0. Оценка уравнения смешивания для слоя 2 дает

    ONE_MINUS_DST_ALPHA * S + ONE * D = 0.0 * (.., 0.4) + 1.0 * (.., 1.0) = (.., 1.0)

    Мы получили желаемое значение альфа 1.0!

  • Случай 2: рисование слоя 1, DST_ALPHA 0,0, исходное значение S = (.., 0.5) и значение назначения D = (.., 0.0), Таким образом, функция смешивания оценивается как

    ONE_MINUS_DST_ALPHA * S + ONE * D = 1.0 * (.., 0.5) + 1.0 * (.., 0.0) = (.., 0.5)

    Это записывается в кадровый буфер и становится целевым значением для слоя рисования 2. Источником для слоя 2 является (.., 0.4), а также DST_ALPHA сейчас 0,5. Оценка уравнения смешивания для слоя 2 дает

    ONE_MINUS_DST_ALPHA * S + ONE * D = 0.5 * (.., 0.4) + 1.0 * (.., 0.5) = (.., 0.7)

    Мы получили желаемое значение альфа 0.7!

Есть ли способ тестирования альфа-компонента каждой текстуры в пикселе, а не только в ближайшем?

Нет. OpenGL хранит одно значение RGBA для каждого пикселя; нет способа получить предыдущие значения (так как для этого потребуется тонна ОЗУ).

То, какие альфа-значения записываются в кадровый буфер, зависит от вашего уравнения альфа-смешивания, которое вы можете установить с помощью glBlendFunc или же glBlendFuncSeparate, См. Страницу смешивания в вики OpenGL для получения дополнительной информации, и это приложение JavaScript позволяет увидеть эффекты различных режимов смешивания.

Другие вопросы по тегам