OpenGL фон прозрачного цвета сливается в прозрачную текстуру
Я пишу простой 2D-фреймворк с OpenGL и C++ и теперь столкнулся с проблемой, связанной с прозрачными текстурами и смешиванием. Я свел свою проблему к следующему.
У меня есть две текстуры: напольная плитка и рыбья кость. Последний содержит прозрачные пиксели. Я установил свой прозрачный цвет OpenGL на "прозрачный зеленый" и включил проверку глубины и смешивание следующим образом:
glClearColor(0, 1, 0, 0)
glEnable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
С двумя звонками glDrawElements()
Я рисую рыбу, а затем пол. Рыба имеет более высокое значение Z и поэтому должна быть помещена перед плиткой для пола. Это результат.
Очевидно, я не хочу, чтобы зеленая коробка вокруг рыбы была такой. Я думаю, что происходит то, что пиксели рыб смешиваются с тем, что находится в цветовом буфере кадров фреймбуфера в момент рисования, и это оказывается сплошным зеленым (из-за glClearColor
а также glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
). Для каждого прозрачного пикселя в текстуре рыбы, я ожидал бы, что это смешивание преобразуется в прозрачный (так как я установил прозрачный прозрачный цвет), но, как вы можете видеть, это не то, что происходит.
Если я сначала рисую пол, а потом рыбу, все работает как положено.
Мой фрагментный шейдер очень прост:
varying lowp vec2 TexCoordOut;
uniform sampler2D Texture;
void main(void) {
gl_FragColor = texture2D(Texture, TexCoordOut);
}
Мне действительно нужно управлять порядком рисования вручную (упорядоченным по координате Z) или есть способ решить эту проблему? Разве система буфера глубины OpenGL не предназначена для решения этой проблемы?
Я тестирую свою программу на симуляторе iOS через Xcode.
3 ответа
Я действительно считаю, что сортировка по глубине необходима - так как, несмотря на то, что результирующий пиксель из смеси прозрачен, буфер глубины записан. Таким образом, все, что будет нарисовано позади (более высокая глубина) этих прозрачных пикселей, просто отбрасывается. К сожалению, буфер глубины не учитывает прозрачность.
Если в ваших текстурах нет полупрозрачных пикселей, вы можете использовать альфа-тестирование, при котором пиксель отбрасывается, если он прозрачный, и поэтому не записывает буфер глубины. Это, очевидно, не работает (хорошо) с полупрозрачными пикселями, поскольку они либо становятся полностью непрозрачными, либо отбрасываются в зависимости от вашей реализации / настроек. Вы можете легко реализовать это, изменив свой шейдер на:
varying lowp vec2 TexCoordOut;
uniform sampler2D Texture;
void main(void) {
vec4 tc = texture2D(Texture, TexCoordOut);
if (tc.a < 0.5) //for example, change to any value suitable
discard;
gl_FragColor = tc;
}
Кроме этого, я не знаю способа исправить прозрачность буферизацией глубины, кроме сортировки.
Подробнее о сортировке прозрачности и альфа-тестировании вы можете прочитать здесь: http://www.opengl.org/wiki/Transparency_Sorting
Имел эту точную проблему и пытался исправить ее без каких-либо серьезных результатов, пытаясь работать с большинством предложений, которые были сосредоточены вокруг изменения сделанных расчетов. В конце концов это было довольно просто!
Виновник в моем коде выглядел так:
glClearColor(1, 0, 1, 0);
glClear(GL_COLOR_BUFFER_BIT);
Несмотря на то, что он был прозрачным, когда он смешивался между фоном и цветом объекта, он кровоточил. Что исправило это для меня, так это завернуть этот код в маску шага glClear. Это выглядело так:
glColorMask(false, false, false, true);
glClearColor(1, 0, 1, 0);
glClear(GL_COLOR_BUFFER_BIT);
glColorMask(true, true, true, true);
Это, кажется, сводит на нет проблемы с кровотечением, устраняя все, кроме альфа-канала, из процесса очистки.
Значения Z используются для отбраковки. Фрагмент не рисуется, если уже есть другой фрагмент с меньшим значением z или он находится вне усечения вида. Но все объекты буфера Vertex отрисовываются по порядку. Для 2D вы можете использовать любой алгоритм "по z". Отбрасывание фрагментов будет проблематичным, если позже вы захотите добавить некоторые фильтры, такие как размытие или сглаживание контуров.