2010-01-31 3 views
15

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

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

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

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

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

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

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

+2

Смешивание должно работать с тем, что находится в фреймбуфере. (вот что смешивает _does_). Предоставьте больше данных (какой режим смешивания вы используете, что вы называете смешением «не правильно»? – Bahbar

+0

Я обновил дополнительную информацию. Спасибо за ваш быстрый ответ. – staticfloat

ответ

26

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

Помните, что все линии наложения смешаны дважды здесь. Однажды, когда они смешиваются с текстурой 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%.

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

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

теперь вы будете иметь

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

Другими словами, предварительное смешивание линий наложения в 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. В этом случае вы хотите получить правильные результаты, но в других случаях программисты используют его для скорости. (При предварительном умножении он удаляет мультипиксель при выполнении операции смешивания.)

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

+0

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

3

Как сказал Бен Супник, лучший способ сделать это - показать всю сцену с отдельными функциями смешивания для цвета и альфы. Если вы используете классическую функцию 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) не поддерживают разделенные функции смешивания. :(

13

Очистить 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 

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

Written с помутнения (значения альфа), это становится

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) по умолчанию blend equation GL_FUNC_ADD.


Как и в сторону:

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

glBlendFuncSeparate(GL_ONE_MINUS_DST_ALPHA, GL_ONE); 

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

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

+0

Я хотел сказать, что ~ 5 лет спустя я снова искал эту проблему, прочитал ваше сообщение и подумал «Я думаю, кто задал оригинальный вопрос». – staticfloat

+0

@staticfloat Хех, я рад, что это дошло до вас. Я искал это для кого-то другого, пытающегося отобразить вложенные элементы пользовательского интерфейса (я плохо разбираюсь в практическом графическом программировании), и предыдущий ответ подсказывал мне о разных режимах смешивания, но я подумал, что было бы очень странно, если бы точный solutio n было невозможно.Думая об этом снова, это решение является только оптимальным, если вам нужно нарисовать _over_ предыдущий предыдущий буфер. Я добавлю заметку для случаев, когда вы можете выбрать порядок, например, при визуализации элементов управления графическим интерфейсом в буфер, поскольку для людей, которые это делают, вряд ли удастся закончить здесь. – Tamschi

+0

Работает как очарование! Это помогло мне отобразить прозрачный текст с FBO на экран. Что касается понимания того, почему/как это работает, я думаю, что мне нужно еще кое-что прочитать :) – Arahman