opengl - смешивание с предыдущим содержимым фреймбуфера - PullRequest
17 голосов
/ 31 января 2010

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

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

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

Используемый мной режим смешивания - GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA. (Я считаю, что это, как правило, стандартный blendmode)

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

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

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

Ответы [ 3 ]

27 голосов
/ 01 февраля 2010

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

17 голосов
/ 29 августа 2013

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


В сторону:

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

    glBlendFuncSeparate(GL_ONE_MINUS_DST_ALPHA, GL_ONE);

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

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

4 голосов
/ 02 ноября 2010

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

...