Проблема альфа-смешивания с несколькими итерациями рендеринга в одну и ту же текстуру (OpenGL)

Сценарий

Я создаю объект буфера кадра и привязываю текстуру к прикреплению цвета 0. Я не использую буфер глубины. После создания отвязываю.

Спустя какое-то время буфер кадра связывается, ему рендерится треугольная полоса (некоторые части частично прозрачны), а затем он снова отключается. Это повторяется несколько раз с разными треугольными полосками.

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

Проблема

Я обнаружил, что частично прозрачные части полос треугольника, нарисованные в текстуре, которые перекрываются с другими полосами треугольника, не смешиваются должным образом. Кажется, что они смешаны с белым, а не с цветом, который уже присутствует в текстуре. Даже если я заполню текстуру сплошным зеленым цветом (например), цвет, полученный при смешивании, все равно будет белым.

Вот несколько фрагментов кода, который я использую для всего этого:

Инициализация

glGenTextures(1, &texture_id);
glBindTexture(GL_TEXTURE_2D, texture_id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glGenFramebuffers(1, &framebuffer_id);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_id);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_id, 0);
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);

Рендеринг в текстуру (повторяется)

glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_id);
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

// On the first render iteration, do_clear is true
if (do_clear)
{
    glClearColor(r, g, b, a);
    glClear(GL_COLOR_BUFFER_BIT);
}

// ... render the current triangle strip ...

glBindFramebuffer(GL_FRAMEBUFFER, 0);

Отрисовка текстуры в главный буфер кадра

// Set up the texture (making no assumptions about current state)
glEnable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture_id);
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

// Set up the vertex buffer (quad made up of triangle strip)
glBindBuffer(GL_ARRAY_BUFFER, vertices_id);
glVertexAttribPointer(VERTEX_ATTRIB_POSITION_TAG, 3, GL_FLOAT, GL_FALSE, sizeof(MyRenderVertex), BUFFER_OFFSET(0));
glEnableVertexAttribArray(VERTEX_ATTRIB_POSITION_TAG);
glVertexAttribPointer(VERTEX_ATTRIB_TEXCOORD_TAG, 2, GL_FLOAT, GL_FALSE, sizeof(MyRenderVertex), BUFFER_OFFSET(3 * sizeof(GLfloat)));
glEnableVertexAttribArray(VERTEX_ATTRIB_TEXCOORD_TAG);
glVertexAttribPointer(VERTEX_ATTRIB_COLOR_TAG, 4, GL_FLOAT, GL_FALSE, sizeof(MyRenderVertex), BUFFER_OFFSET(5 * sizeof(GLfloat)));
glEnableVertexAttribArray(VERTEX_ATTRIB_COLOR_TAG);
glVertexAttribPointer(VERTEX_ATTRIB_NORMAL_TAG, 3, GL_FLOAT, GL_FALSE, sizeof(MyRenderVertex), BUFFER_OFFSET(9 * sizeof(GLfloat)));
glEnableVertexAttribArray(VERTEX_ATTRIB_NORMAL_TAG);

// Draw the textured geometry
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

// Reset everything
glDisableVertexAttribArray(VERTEX_ATTRIB_POSITION_TAG);
glDisableVertexAttribArray(VERTEX_ATTRIB_TEXCOORD_TAG);
glDisableVertexAttribArray(VERTEX_ATTRIB_COLOR_TAG);
glDisableVertexAttribArray(VERTEX_ATTRIB_NORMAL_TAG);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindTexture(target, 0);
glActiveTexture(0);

Пример того, что я вижу

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

Плохое смешивание

Обновить

С тех пор, как опубликовал это, я сделал несколько открытий:

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

Хорошее смешивание

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

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


person bitjeep    schedule 08.10.2014    source источник
comment
Я не могу полностью объяснить белую часть. Я думаю, вы могли бы получить лучший результат, очистив FBO до черного. Вы используете шейдеры? Генерирует ли фрагментный шейдер правильное альфа-значение? В любом случае, это очень похоже на предыдущий вопрос, на который я подробно отвечал (щедрость!), Может быть, это поможет: stackoverflow.com/questions/24346585/.   -  person Reto Koradi    schedule 09.10.2014
comment
Я безуспешно пытался очистить FBO для всех цветов. И да, я использую вершинные и фрагментные шейдеры (очень похожие на те, что описаны в главе 8 руководства по программированию OpenGL ES 2.0). Я действительно видел этот другой вопрос, и я пробовал кучу различных функций смешивания, но безрезультатно. Смотрите мое обновление выше - я считаю, что проблема с сетками треугольников.   -  person bitjeep    schedule 09.10.2014


Ответы (1)


При рендеринге в текстуру попробуйте

glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);

вместо:

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

Дело в том, что при рендеринге в текстуру альфа-канал также смешивается.

Чтобы прояснить ситуацию, давайте рассмотрим визуализацию полупрозрачного края над непрозрачной полосой треугольника, которая была визуализирована ранее. Пусть альфа-значение края будет 0,5 (исходная альфа), а альфа в буфере рендеринга будет 1,0 (конечное значение). Таким образом, результирующее значение будет:

r = SrcAlpha * SrcAlpha  + DstAlpha * (1.0 - SrcAlpha) = 0.5 * 0.5 + 0.5 * 0.5 = 0.5

Как видите, значение альфа в буфере рендеринга не равно 1.0, как вы ожидали. В результате исходный цвет будет смешан с целевым цветом в главном буфере кадра.

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

См. this и this для получения дополнительных сведений.

Также убедитесь, что вы установили правильное уравнение смешивания:

glBlendEquation(GL_FUNC_ADD);

Дополнительные сведения см. В this.

person Podgorskiy    schedule 08.10.2014
comment
Отличные предложения (я бы поддержал этот ответ, если бы мог)! Я ранее экспериментировал с glBlendFuncSeparate (), но не с этими конкретными параметрами. Я попробовал оба ваших предложения, но все равно не повезло. Похоже, это связано с вершинами, используемыми в сетках треугольников, которые каким-то образом пробивают дыры в моей текстуре. - person bitjeep; 09.10.2014
comment
Не обращайте внимания на последний комментарий. Я забыл, что рендеринг треугольной сетки выполняется в пакетном режиме и что средство пакетного рендеринга применяет свою собственную функцию смешивания. Как только я поместил ваше уравнение смешивания в пакетный рендерер, проблема устранилась! - person bitjeep; 09.10.2014