Opengl - смешивание с предыдущим содержимым фреймбуфера

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

Есть ли способ правильно соединить содержимое текстуры с новыми данными?

РЕДАКТИРОВАТЬ: более подробная информация, я попытаюсь объяснить более четко;

Я использую blendmode: GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA. (Я считаю, что это обычно стандартный blendmode)

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

Все это работает отлично, за исключением того, что когда я рисую фигуры с альфами меньше 1 на текстуру, она не смешивается должным образом с предыдущим содержимым текстуры. Скажем, у меня есть черные линии с альфа =.6, нарисованные на текстуре. Через пару циклов цикла я рисую черный круг с альфа =.4 над этими строками. Линии "под" круга полностью перезаписаны. Хотя круг не плоский черный (он хорошо сочетается с белым фоном), нет никаких "темных линий" под кругом, как и следовало ожидать.

Если я рисую линии и круг в том же кадре, они смешатся правильно. Я предполагаю, что текстура просто не сочетается с предыдущим содержимым. Это похоже на смешивание с glclearcolor. (Который в этом случае равен 1.0, 1.0f, 1.0f, 1.0f > )

Ответы

Ответ 1

Я думаю, что здесь есть две возможные проблемы.

Помните, что все перекрывающиеся линии смешиваются здесь дважды. Однажды, когда они смешиваются с текстурой FBO и снова, когда текстура FBO смешивается над сценой.

Итак, первая возможность заключается в том, что у вас нет смешивания, если вы рисуете одну линию над другой в наложении FBO. Когда вы рисуете на поверхности RGBA с смещением, текущая альфа просто записывается непосредственно в альфа-канал наложения FBO. Затем, когда вы смешаете всю тексту FBO над сценой, эта альфа делает ваши линии полупрозрачными. Поэтому, если вы смешиваете "мир", но не между элементами наложения, возможно, что смешение не происходит.

Другая связанная проблема: когда вы смешаете одну линию над другой в "стандартном" режиме смешивания (src alpha, 1 - src alpha) в FBO, альфа-канал "смешанной" части будет содержать смесь альфах двух элементов наложения. Вероятно, это не то, что вы хотите.

Например, если вы нарисуете две 50% альфа-линии друг над другом в оверлее, чтобы получить эквивалентный эффект, когда вы blit FBO, вам нужно, чтобы FBO-альфа... 75%. (То есть 1 - (1 -.5) * (1-0.5), что и произойдет, если вы просто нарисовали две 50% альфа-линии над вашей сценой. Но когда вы рисуете две 50% линии, вы будете получить 50% альфа в FBO (смесь 50% с... 50%.

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

blend (смесь (смесь (цвет фона, модель), первая строка), вторая строка);

теперь у вас будет

blend (смесь (первая строка, вторая строка), смесь (цвет фона, модель)).

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

Во-первых, простой способ обойти это: не использовать FBO. Я понимаю, что это ответ на вопрос "переработайте свое приложение", но использование FBO - не самая дешевая вещь, и современные карты GL очень хороши в рисовании линий. Таким образом, одним из вариантов было бы: вместо смешивания строк в FBO, написать геометрию линии в объект буфера вершин (VBO). Просто растягивайте VBO каждый раз. Если вы рисуете меньше, скажем, 40 000 строк за раз, это почти наверняка будет так же быстро, как вы делали раньше.

(Один совет, если вы идете по этому маршруту: используйте glBufferSubData для записи строк, а не glMapBuffer - сопоставление может быть дорогостоящим и не работает на поддиапазонах на многих драйверах... лучше просто позволить драйверу скопировать несколько новых вершин.)

Если это не вариант (например, если вы рисуете сочетание типов фигур или используете сочетание состояния GL, так что "запоминание" того, что вы сделали, намного сложнее, чем просто накопление вершин), тогда вы может хотеть изменить, как вы рисуете в VBO.

В основном то, что вам нужно сделать, это включить отдельное смешивание; инициализируйте наложение до черного + 0% альфа (0,0,0,0) и смешайте "стандартное смешивание" RGB, но добавьте смесь альфа-каналов. Это все еще не совсем корректно для альфа-канала, но, как правило, оно намного ближе - без этого чрезмерно вытянутые области будут слишком прозрачными.

Затем, при рисовании FBO, используйте "предварительно умноженный" альфа, то есть (один, один минус-src-alph).

Вот почему нужен последний шаг: когда вы рисуете в FBO, вы уже умножали каждый призыв на призыв по его альфа-каналу (если смешение включено). Поскольку вы чернели черным, зеленая (0,1,0,0,5) линия теперь темно-зеленая (0,0,5,0,0,5). Если альфа включена, и вы снова смешиваетесь снова, альфа повторно применяется, и у вас будет 0,0.25,0,0,5.). Просто используя цвет FBO как есть, вы избегаете второго альфа-умножения.

Это иногда называют "предварительно умноженной" альфа, потому что альфа уже умножена на цвет RGB. В этом случае вы хотите получить правильные результаты, но в других случаях программисты используют его для скорости. (При предварительном умножении он удаляет мульти на пиксель, когда выполняется операция blend op.)

Надеюсь, что это поможет! Получение смешивания прямо, когда слои не смешиваются в порядке, становится очень сложным, и отдельная смесь недоступна на старом оборудовании, поэтому простое рисование строк каждый раз может быть путем наименьшего страдания.

Ответ 2

Очистите FBO прозрачным черным (0, 0, 0, 0), нарисуйте его в обратном направлении с помощью

glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

и нарисуйте FBO с помощью

glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

чтобы получить точный результат.

Как писал Бен Супник, FBO содержит цвет, уже умноженный на альфа-канал, поэтому вместо того, чтобы делать это снова с GL_SRC_ALPHA, он нарисован с помощью GL_ONE. Цвет назначения аттенуируется обычно с помощью GL_ONE_MINUS_SRC_ALPHA.

Причина смешивания альфа-канала в буфере таким образом отличается:

Формула объединения прозрачности

resultTr = sTr * dTr

(Я использую s и d из-за параллелизма с исходным кодом и пунктом назначения OpenGL, но, как вы видите, порядок не имеет значения.)

Написанный с непрозрачностью (значения альфа), это становится

    1 - rA = (1 - sA) * (1 - dA)
<=> rA = 1 - (1 - sA) * (1 - dA)
       = 1 - 1 + sA + dA - sA * dA
       =         sA + (1 - sA) * dA

который является таким же, как функция смешивания (исходные и целевые факторы) (GL_ONE, GL_ONE_MINUS_SRC_ALPHA) с уравнением смешивания по умолчанию GL_FUNC_ADD.


В стороне:

Приведенное выше отвечает на конкретную проблему из вопроса, но если вы можете легко выбрать порядок рисования, теоретически может быть лучше рисовать цвет premultiplied в буфер вперед-назад с помощью

    glBlendFuncSeparate(GL_ONE_MINUS_DST_ALPHA, GL_ONE);

и в противном случае использовать тот же метод.

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

Ответ 3

Как сказал Бен Супник, лучший способ сделать это - показать всю сцену с отдельными функциями blend для цвета и альфы. Если вы используете классическую функцию non premultiplied blend, попробуйте glBlendFuncSeparateOES (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE), чтобы сделать вашу сцену FBO. и glBlendFuncSeparateOES (GL_ONE, GL_ONE_MINUS_SRC_ALPHA), чтобы отобразить FBO.

Это не на 100% точно, но в большинстве случаев это не создаст неожиданной прозрачности.

Имейте в виду, что старые аппаратные средства и некоторые мобильные устройства (в основном, устройства OpenGL ES 1.x, такие как оригинальный iPhone и 3G) не поддерживают разделенные функции смешивания.:(