Можно ли добиться MAX (As, Ad) смешивания openGL? - PullRequest
17 голосов
/ 27 января 2010

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

Я не могу просто отобразить их в png спрайта, иначе тени будут перекрывать соседние спрайты.

Я также не могу просто наложить тени на нижний слой самостоятельно, потому что, когда они перекрываются, они создадут темные полосы на своем пересечении.

Эти спрайты являются анимированными, поэтому не представляется возможным сделать их массовыми.

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

enter image description here

Я полагаю, что это эквивалентно смешиванию openGL (Rs, Gs, Bs, Max (As, Ds)), где меня не волнуют R, G и B, поскольку оно всегда будет одинаковым цвет в src и dst.

Однако это недопустимый режим смешивания openGL. Есть ли простой способ сделать это, особенно в cocos2d-iphone?

Я бы смог приблизить это, сделав теневые спрайты непрозрачными, затем применив их обоих к родительскому спрайту и сделав 40% непрозрачности родительского спрайта. Однако то, как работает cocos2d, позволяет установить непрозрачность каждого дочернего элемента только на 40%, а не на объединенное изображение спрайта, что приводит к одинаковой полосе.

1 Ответ

18 голосов
/ 27 января 2010

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

Во-первых, есть способ сделать смешивание Max Alpha openGL, и для этого используется glBlendEquations. Вам нужно изменить метод draw для этого (и создать новый атрибут для textureNode для переключения между этими методами) и использовать любой из них:

 glBlendEquationOES( GL_MAX_EXT );
 glBlendEquationSeparateOES( GL_FUNC_ADD_OES, GL_MAX_EXT );

Первый использует max () для RGBA, а второй использует стандартное сложение для RGB и max () для альфы.

Теперь проблема в том, что каждый спрайт и его дочерние элементы в z-порядке отрисовываются на экране . Это означает, что после прорисовки первой тени она становится частью буфера основного экрана. Другими словами, его непрозрачность изменилась, и с смешиванием max () и стандартным непрозрачным фоном, теперь он закрашивается на фон, поэтому, когда вы рисуете следующую тень, она будет делать max (As, 1), который равен 1, потому что фон непрозрачный. Так что я не добиваюсь смешения, которое хочу. Это «сработало бы», если бы мой фон был прозрачным, но тогда я не смог бы применить какой-либо фон «за этим», мы можем только добавить его в буфер.

Я мог бы сначала нарисовать тени, используя смесь max (), а затем сделать {ONE_MINUS_DST_ALPHA, DST_ALPHA} glBlend. Это хороший обходной путь, но у меня есть другие проблемы, которые затрудняют это.

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

Обновление:

ОК, у меня есть рабочее решение.

Как ни странно, мне в конце концов не понадобилось это решение, потому что в моем конкретном случае полосатость была недостаточно заметна, чтобы оправдать усилия, однако это решение, которое я придумала, казалось, делало то, что я хотел: 1024 *

  1. Сначала нарисуйте 0% черный альфа-PNG на весь экран. (Я не знаю, если это необходимо. Я не знаю, какова стандартная альфа экрана).

  2. Нарисуйте тени на экране, используя glEquationSeperateOES( GL_ADD, GL_MAX_ENT) и glBlend( GL_ONE, GL_ONE ). Это объединит все тени вместе, как описано, взяв max () из альф.

    • Поскольку вы создаете композицию для черного, это эквивалентно использованию glBlendEquationOES( GL_MAX_ENT ). (X + 0 == max (X, 0))

    • glBlend( GL_ZERO, GL_ONE ) устранит необходимость шага 1 или, по крайней мере, требование этого слоя быть черным 0%. GL_ZERO по существу заставляет его быть черным 0%.

  3. Нарисуйте пол, который будет над тенями, но используйте glBlend( ONE_MINUS_DST_ALPHA, DST_ALPHA ). Это приводит к точно таким же результатам, как если бы вы добавили тени на пол с помощью glBlend( SRC_ALPHA, ONE_MINUS_SRC_ALPHA ), однако, если бы вы сделали это таким образом, вы не смогли бы смешать тени вместе (см. Почему вверху ответа).

  4. Готово! Хорошая особенность этого метода в том, что теневые спрайты можно анимировать как обычно, без необходимости вызывать «визит» в отдельной рендеринг-текстуре.

Остальное:

Я также изменил CocosNode, чтобы позволить мне добавлять тень к слою, который ссылается на другой CocosNode. Таким образом, тень отображается как дочерний элемент пола (или 0% -ный черный фоновый спрайт для смешивания теней), но связана с другим CocosNode. Когда спрайт перемещается, если у него есть тень, он также обновляет положение теней. Это позволяет мне располагать все тени под всеми объектами на экране, и чтобы тени автоматически следовали за объектом.

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

...