OpenGL ReadPixels (Скриншот) Альфа
Я использую настройку Tile Rendering (используя glReadPixels), чтобы делать скриншоты сцены. Мой вывод выглядит правильно:
Но, глядя на альфа-канал, пиксели все еще остаются прозрачными, даже если прозрачная текстура отображается поверх непрозрачной текстуры.
В частности, "внутренняя часть" автомобиля должна быть полностью непрозрачной, где мы видим потолок, но частично прозрачной при прохождении через задние окна.
Есть ли способ тестирования альфа-компонента каждой текстуры в пикселе, а не только в ближайшем?
2 ответа
Я думаю, что вы получили несколько полезных указаний из комментариев и другого ответа, но не решение в полном объеме. Вместо того, чтобы просто дать результат, позвольте мне пройтись по нему, чтобы вы знали, как разобраться в этом сами в следующий раз.
Я предполагаю, что вы рисуете ваши полупрозрачные объекты спереди, используя GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA
смешанная функция. Вы прямо не упоминаете об этом, но это довольно стандартно и согласуется с тем, что вы видите. Для правильного решения также потребуется альфа-компонент в кадровом буфере, но он уже есть, иначе вы ничего не получите при чтении альфы.
Чтобы проиллюстрировать весь процесс, я собираюсь использовать два примера по пути. Я только перечислю (R, A)
компоненты для цветов, G
а также B
будет вести себя так же, как R
,
- Нарисуйте слой с цветом
(R1, 1.0)
затем слой с(R2, 0.4)
на нем. - Нарисуйте слой с цветом
(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 позволяет увидеть эффекты различных режимов смешивания.