Смешивание в OpenGL ES не работает так, как я думаю

Я упростил свою задачу до рисования квадрата для иллюстрации. Я хочу нарисовать текстуру, которая изменяет альфа-канал от 0 до 255 и обратно до 0 по вертикали, чтобы она сливалась с фоном. Сейчас он затемняет фон, и я не могу понять почему. Это использует OpenGL ES на iOS 9.

Сначала я включаю смешивание, которое, как мне кажется, мне нужно:

glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

Затем заполняю буфер зеленым цветом:

glClearColor(0, 0.5, 0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);

У меня есть это изображение, которое я использую в качестве текстуры:

Текстура

И я рисую следующие вершины ({x, y, z}, {r, g, b, a}, {tx, ty}) с помощью GL_TRIANGLES:

{{0.4, 0.3, 0}, {1, 0, 0, 1}, {0, 0}},
{{0.6, 0.3, 0}, {1, 0, 0, 1}, {1, 0}},
{{0.4, 0.5, 0}, {1, 0, 0, 1}, {0, 1}},
{{0.6, 0.3, 0}, {1, 0, 0, 1}, {1, 0}},
{{0.4, 0.5, 0}, {1, 0, 0, 1}, {0, 1}},
{{0.6, 0.5, 0}, {1, 0, 0, 1}, {1, 1}},

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

varying lowp vec4 DestinationColor;

varying lowp vec2 TexCoordOut;
uniform sampler2D Texture;

void main(void) {
    gl_FragColor = DestinationColor * texture2D(Texture, TexCoordOut);
}

Это так, но в результате получается не просто красный цвет на зеленом. По краям темнеет, я ожидал, что будет нарисован только зеленый цвет:

Проблема

В ответе на аналогичный вопрос здесь говорится, что я должен использовать glTexEnv (GL_BLEND). Однако я думаю, что это API OpenGL ES 1.

Кто-нибудь знает, как я могу добиться желаемого смешивания? Я хочу, чтобы красный потускнел на зеленом. Верх и низ должны быть зелеными, а не более темными. Не должно быть явных горизонтальных краев.

РЕДАКТИРОВАТЬ: Я добавляю больше изображений, иллюстрирующих мою проблему. Вот две розовые точки в GIMP:

Розовые точки в GIMP

Когда я кладу один на другой (в GIMP), они прекрасно сочетаются. Также они красиво сочетаются с зеленым фоном:

Розовые точки в GIMP, наложенные

Когда я воссоздаю это в OpenGL ES, я получаю затемнение, которое особенно заметно там, где накладываются розовые точки. На этот раз я сделал один очень узким, чтобы вы могли лучше увидеть эффект:

Розовые точки в OpenGL ES

Я попробовал как glBlendFunc, так и glBlendFuncSeparate, что дало тот же эффект. Обратите внимание, что альфа фона равна 1.


person Kevin Wood    schedule 20.10.2015    source источник
comment
Я немного забыл о функциях наложения, но ваш снимок экрана выглядит так, как будто результат похож на умножение в Photoshop. Я думаю, вам нужна другая функция наложения, но сейчас не могу точно ответить, какая из них ...   -  person Nicolas Miari    schedule 20.10.2015
comment
Где-то должен быть шпаргалка с общими функциями смешивания, для справки ...   -  person Nicolas Miari    schedule 20.10.2015
comment
Я пришел к этой функции наложения, потому что она рекомендована как обычно. GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA должно означать, что color_blended = color_src * alpha_src + color_destination * (1 - alpha_src). Вот почему я запутался. Предположим, что альфа равна 0,1, красный - 1, а зеленый - 0,5, эти края должны быть 0,1 (1, 0, 0) + (1-0,1) (0,0,5,0) = (0,1, 0,45, 0), Правильно? Если вы откроете сайт сравнения цветов и сравните # 1A7300 (смешанный пример) с # 008000 (фон), он не такой уж темный.   -  person Kevin Wood    schedule 20.10.2015
comment
Да, это одна из функций, которые вы используете при смешивании растровых изображений (спрайтов), в зависимости от того, имеет ли растровое изображение предварительно умноженную альфа или нет (хотя я не могу вспомнить, какая это). Другой, я думаю, использует GL_ONE в одном из двух параметров (опять же, не помню, какой).   -  person Nicolas Miari    schedule 20.10.2015
comment
Помните, что source - это входящее значение (то, которое вы накладываете), а destination - это значение, уже находящееся в буфере кадра: opengl.org/sdk/docs/man2/xhtml/glBlendFunc.xml   -  person Nicolas Miari    schedule 20.10.2015


Ответы (2)


Ваша проблема, скорее всего, заключается в отображении самого альфа-канала, а не цвета. Поскольку вы используете функцию прямого наложения, она установит для альфа-канала значение ниже 1.0, которое вы затем увидите как предварительно умноженные значения цвета на дисплее: RGB = RGB*A. Вы должны использовать функцию смешивания отдельно, чтобы отдельно обрабатывать альфа-канал:

glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE)

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

person Matic Oblak    schedule 20.10.2015
comment
Это действительно полезная информация, и похоже, что это гораздо лучший способ смешивания. но с сожалением должен сказать, что на меня это не подействовало. - person Kevin Wood; 20.10.2015
comment
Может быть, ваша текстура загружена из изображения, поэтому цвет предварительно умножен на альфа-канал? В основном то же самое происходит, когда вы конвертируете изображение в необработанные данные RGBA, и цвет RGB умножается на альфа-канал, что дает результат с оттенками. Попробуйте проверить какой-нибудь полупрозрачный пиксель, если часть RGB такая же или масштабируется (она должна быть такой же) - person Matic Oblak; 20.10.2015
comment
Гениальная идея! Мне и в голову не приходило, что виновата сама текстура. Я только что посмотрел и рисую текстуру в памяти, используя (постоянную iOS) kCGImageAlphaPremultipliedLast. Думаю, вы почти наверняка нашли корень моей проблемы. Я не получаю никаких данных с помощью kCGImageAlphaLast, поэтому я продолжу работать над этим. Я скоро вернусь, чтобы убедиться, что это действительно решение. - person Kevin Wood; 21.10.2015
comment
Я подтвердил, что проблема действительно в текстуре. Я еще не решил это, но отмечу вас как правильное, потому что теперь я ясно вижу проблему в отладчике. - person Kevin Wood; 21.10.2015
comment
Странно, что нельзя творить с альфа-последней. Возможно, вам стоит добавить этот код с вариантами, которые вы пробовали, объясняя, в чем проблема. - person Matic Oblak; 21.10.2015

Если ваше устройство использует настройки по умолчанию, так что предполагается, что пиксельные данные предварительно умножены, вы можете получить результаты, подобные тем, которые вы видите. Я бы посоветовал преобразовать изображение с альфа-каналом в простые оттенки серого, а затем сохранить его как PNG и убедиться, что это оттенки серого, а не формат BGRA. Затем загрузите эти 8-битные данные на пиксель в текстуру обычным способом, не беспокоясь о каких-либо проблемах с предварительным умножением (оттенки серого не содержат и альфа-канала). Затем выполните шаг предварительного умножения в своем шейдере, прочитав один байт из теста оттенков серого, а затем умножьте 3 компонента существующего DestinationColor на это линейное альфа-значение. Этот шаг предварительного умножения заставил мои шейдеры работать, например, на iOS.

person MoDJ    schedule 10.03.2016