Смешивание iPhone OpenGL ES 2.0 с Cocos2D дает неожиданные результаты

У меня очень простой CCScene с ТОЛЬКО 1 CCLayer, содержащим:

  1. CCSprite для фона со стандартным режимом наложения
  2. CCRenderTexture для рисования кистей со спрайтом, прикрепленным к корневому CCLayer над фоновым спрайтом:
_bgSprite = [CCSprite spriteWithFile:backgroundPath];
_renderTexture = [CCRenderTexture renderTextureWithWidth:self.contentSize.width height:self.contentSize.height];
[_renderTexture.sprite setBlendFunc:(ccBlendFunc){GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA}];
[self addChild:_bgSprite z:-100];
[self addChild:_renderTexture];

Код обработки кисти:

[_renderTexture begin];
glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ONE); // 1.
// calculate vertices code,etc...
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)count);
[_renderTexture end];

Когда пользователь использует кисть первого цвета, она сливается с фоном должным образом. Но если продолжить рисование другим цветом поверх предыдущей кисти, все идет не так (мягкие альфа-края теряют непрозрачность, когда 2 кисти накладываются друг на друга):

введите описание изображения здесь

Я перепробовала много вариантов смешивания, но почему-то не могу найти правильный.

Есть ли в CCRenderTexture что-то особенное, что не совпадает с самим собой (с ранее нарисованным содержимым), как ожидалось?

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

void main()
{
    gl_FragColor = texture2D(u_texture, v_texCoord);
    gl_FragColor.a = v_fragmentColor.a;
}

ОБНОВЛЕНИЕ - ПОЧТИ ИДЕАЛЬНОЕ РЕШЕНИЕ: от jozxyqk

glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
                        GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

в коде рендеринга (вместо // 1. и

[_renderTexture.sprite setBlendFunc:(ccBlendFunc){GL_ONE, GL_ONE_MINUS_SRC_ALPHA}];

ЭТО РАБОТАЕТ ОТЛИЧНО И ДАЕТ МЕНЯ ТО, ЧТО Я ХОЧУ ...

введите описание изображения здесь

... НО ТОЛЬКО КОГДА _rederTexture полностью непрозрачен.

Когда непрозрачность _rendertexture.sprite уменьшается, кисти становятся светлее, а не блекнут, как можно было бы ожидать:

введите описание изображения здесь

Почему альфа-каналы кистей правильно смешиваются с фоном, когда родительская текстура имеет полную непрозрачность, но становится бананом при понижении непрозрачности? Как сделать так, чтобы кисти правильно сочетались с фоном?


person Community    schedule 11.09.2013    source источник


Ответы (1)


ИЗМЕНИТЬ

Кисть для наложения -> слой -> фон

Хорошо, происходит то, что glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) работает для смешивания мазков кисти с текстурой кисти, но результирующие альфа-значения в текстуре неверны. Каждый добавленный фрагмент должен 1. добавить его альфа к окончательному значению альфа - он должен удалить ровно столько света для взаимодействия и 2. масштабировать предыдущую альфу на остаток - предыдущие поверхности уменьшают свет на предыдущее значение, но поскольку добавляется новая поверхность, меньше света для их уменьшения. Я не уверен, что это имело смысл, но это приводит к следующему ...

glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
                    GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

Теперь цветовой канал текстуры кисти содержит общий цвет, который должен быть смешан с фоном (предварительно умноженный на альфа), а альфа-канал дает вес (или степень, в которой цвет закрывает фон). Поскольку цвет предварительно умножается на альфа, смешивание RenderTexture по умолчанию GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA масштабируется с альфа снова и, следовательно, затемняет общий цвет. Теперь вам нужно смешать текстуру кисти с фоном, используя следующую функцию, которая, как я понимаю, должна быть установлена ​​в Cocos2D:

glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

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

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

Это сработало для меня:

glDisable(GL_DEPTH_TEST);
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);

fbo.bind();
glClear(GL_COLOR_BUFFER_BIT);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
                    GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
drawTexture(brush1);
drawTexture(brush2);
fbo.unbind();

drawTexture(grassTex); //tex alpha is 1.0, so blending doesn't affect background
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
drawTexture(fbo.getColour(0)); //blend in the brush layer

небольшой тест в GL

Непрозрачность слоя кисти

Использование GL_ONE, GL_ONE_MINUS_SRC_ALPHA вызывает проблемы с реализацией прозрачности в библиотеке при наложении слоев, поскольку предполагается, что цвет умножается на альфа-канал. При уменьшении значения opacity альфа слоя кисти уменьшается во время смешивания. GL_ONE_MINUS_SRC_ALPHA затем увеличивает количество цвета фона, однако GL_ONE суммирует 100% слоя кисти и перенасыщает изображение.

Самое простое решение - это найти способ самостоятельно уменьшить цвет на глобальную непрозрачность слоя и продолжать использовать GL_ONE, GL_ONE_MINUS_SRC_ALPHA.

  • На самом деле использование GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_ALPHA могло бы быть ответом, если бы библиотека поддерживала его, но, по-видимому, это не так.
  • Вы можете использовать фиксированный рендеринг конвейера для масштабирования цвета: glColor4f(opacity, opacity, opacity, opacity), но для этого потребуется вторая цель рендеринга и выполнение смешивания вручную, как и в приведенном выше коде, где вы рисуете полноэкранный четырехугольник один раз для фона и еще раз для слоя кисти. .
  • Если вы выполняете смешивание вручную, было бы более надежным использовать фрагментный шейдер вместо метода glColor. Это даст гораздо больший контроль, если вы когда-нибудь захотите поиграть с более сложными функциями смешивания, особенно когда речь идет о делениях и временных изменениях за пределами диапазона от 0 до 1: gl_FragColour = texture(brushTexture, coord) * layerOpacity;

КОНЕЦ РЕДАКТИРОВАНИЯ


Стандартная функция альфа-смешивания - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);, а не "начальная" функция / функция по умолчанию GL.

Суммирование альфа-значений, как в glBlendFuncSeparate, приведет к перенасыщению альфа-канала, и нижний цвет будет полностью заменен. Смешивание насыщенности может дать достойные результаты: glBlendFunc(GL_SRC_ALPHA_SATURATE, GL_ONE). Возможно, стоит поэкспериментировать с glBlendEquationSeparate и смешиванием MAX, если оно поддерживается. Преимущество игры с MAX будет заключаться в уменьшении перекрывающихся артефактов (жестких треугольных битов) из кода рисования линии - например, замена цвета, но только до тех пор, пока не будет достигнуто общее альфа-значение X. РЕДАКТИРОВАТЬ: в обоих случаях потребуется наложение и очистка после каждого штриха.

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

С одной стороны, и в значительной степени не имеющей отношения к этому, есть также "Under Blending", где вы сохраняете значение коэффициента пропускания вместо alpha / opacity (из здесь):

glBlendEquation(GL_FUNC_ADD); 
glBlendFuncSeparate(GL_DST_ALPHA, GL_ONE, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA); 
person jozxyqk    schedule 11.09.2013
comment
glBlendFunc (GL_SRC_ALPHA_SATURATE, GL_ONE) не работает - см. обновленную часть вопроса (внизу). - person Lukasz; 11.09.2013
comment
Я предполагаю, что вы имеете в виду затемнение по краям. нормализация (деление цвета на альфа-значение) при смешивании текстуры исправит это, но я предполагаю, что вы не контролируете это. Также было бы неплохо, если бы вы могли смешать и сохранить текстуру кисти с фоном - разрешив альфа-смешение между штрихами, но насыщенность внутри штриха. Почему не работает стандартное альфа-смешивание? - person jozxyqk; 11.09.2013
comment
Да, я имел в виду затемнение по краям. Почему альфа-смешение не работает? Что ж, если бы я знал это, я бы не стал задавать этот вопрос. Я новичок в Cocos2D и OpenGL и пробовал использовать стандартные режимы наложения с этой очень простой сценой. У меня нет идей, меняет ли Cocos2D что-то за кулисами или я делаю что-то ужасно неправильно. - person Lukasz; 11.09.2013
comment
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) звучит так, как будто это то, что вы хотите. Единственная причина, по которой я это упомянул, заключается в том, что в вашем коде есть glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA), которая перенасыщает цвет - person jozxyqk; 11.09.2013
comment
@Lukasz см. Правку. Легко ли изменить функцию наложения для текстуры рендеринга кисти (например, cocos2d-iphone.org/forums/topic/) - person jozxyqk; 12.09.2013
comment
см. мое III-е обновление. Ваши последние настройки смешивания отлично работают при полной непрозрачности текстуры (100%). Но когда я опускаю его, кисти безумно светлеют (вместо того, чтобы блекнуть, как ожидалось). Вы можете объяснить, почему это происходит? - person Lukasz; 13.09.2013
comment
Поскольку текстура кисти содержит общий цвет и используется GL_ONE, уменьшение масштаба не выполняется. Уменьшение значения альфа без уменьшения цвета приводит к тому, что фон не вычитается, а кисть полностью добавляется, следовательно, становится ярче. Если вы нашли способ вернуть цвет к той же непрозрачности, что и текстура рендеринга, это сработает. К сожалению, не похоже, что есть простой способ сделать это: cocos2d-iphone.org/forums/topic/. - person jozxyqk; 13.09.2013
comment
Некоторые исправления могут быть следующими: 1. Поместите фон в текстуру кисти и не беспокойтесь о смешивании слоев; 2. Найдите другой способ масштабировать цвет по непрозрачности слоя или разделить цвет на альфа-канал и использовать смешивание по умолчанию, когда библиотека смешивает слои (может потребоваться вторая / промежуточная цель рендеринга и шейдер). 3. Найдите способ визуализировать кисть таким образом, чтобы поддерживать значение по умолчанию GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA - person jozxyqk; 13.09.2013
comment
Я ценю ваш опыт, но это звучит просто невероятно. Окончательный рендеринг кисти - это просто текстура (CCRenderTexture имеет даже свойство CCSprite) и поэтому должна смешиваться с фоном, как другие CCSprites. Означает ли это, что нельзя изменить непрозрачность CCSprites, которые используют динамически визуализируемые текстуры (CCRenderTexture). Но разве это не одна из основных целей класса CCRenderTexture? Я не нашел ни одного предложения в документации Cocos2D, в котором говорилось бы: CCRenderTexture великолепен ... вы можете использовать его для создания отличных процедурных объектов CCSprite, но, пожалуйста, НЕ СМЕЙТЕ их постепенно проявлять / затухать ;-)! - person Lukasz; 13.09.2013
comment
Это связано с функцией непрозрачности, не ожидающей GL_ONE, GL_ONE_MINUS_SRC_ALPHA. Настоящая трудность здесь заключается в создании текстуры кисти с правильной альфой. Это объясняет ту же проблему (надеюсь, лучше, чем у меня) ... stackoverflow.com/questions/2171085/. К сожалению, он также не предлагает ответа для вас, кроме как также предлагать хранить все в одной цели рендеринга. - person jozxyqk; 13.09.2013
comment
Если я сохраню все в одном CCRenderTexture, смогу ли я использовать функцию «стирать кисть»? Не стирается ли фон вместе с кистью? - person Lukasz; 13.09.2013
comment
Вы можете заставить кисть стирания снова рисовать фоновую текстуру. Если вы серьезно относитесь к реализации слоев, я бы посоветовал использовать собственный шейдер для смешивания слоев, поскольку вы получите гораздо больший контроль (вы можете реализовать свою собственную непрозрачность слоя и соответственно масштабируйте цвет в шейдере). - person jozxyqk; 13.09.2013
comment
позвольте нам продолжить обсуждение в чате - person Lukasz; 14.09.2013