Peer-to-Peer врывается в App Store Ronnie O’Sullivan’s Snooker
Июн 27

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

Я помню, как первый раз включил в код смешивание. Алгоритм писал не сам, но результат все равно потряс. Это было на SGL Indigo II с графической платой GR3-Elan (те, кому доводилось видеть SGI Indigo или прочие графические станции 90-х, знают, какого размера были на них графические платы!). Вычислительные мощности системы того времени составляли буквально доли от Мака, на котором я работаю сейчас, но тогда это было по-настоящему впечатляюще!

Момент был особенно волнующим, еще и потому, что в то время никто из нас не сталкивался со смешиванием в реальном времени на объектах с наложенными текстурами, перемещениями в сцене и эффектами освещения. Само собой, все это встречалось в LightWave и другом ПО с имитацией эффектов света, но о реальном времени речь не шла. Да… В то время программированием занимались, чтобы выдать нечто новое и необычное, а не чтобы через пару дней выпустить игру и подзаработать…

Что ж, будем переходить к изучению смешивания. Смешивание (blending) — это процесс комбинирования двух изображений, при котором находящееся ближе к зрителю кажется полупрозрачным. Представьте, что вы смотрите на предметы через кусок красного плексигласа в руке. Мир перед глазами становится красным, поскольку цвет плексигласа меняет окраску других объектов.

Чтобы претворить блендинг в реальность, нам понадобится частично прозрачный объект на переднем плане (в RGBA определении цвета значение альфа должно быть менее 1.0) плюс объект сзади, с которым он и будет “смешиваться”.

Важнейший момент для смешивания: обязательное наличие двух объектов, передний из которых является попупрозрачным. Запомните, что блендинг — это не прозрачность. С прозрачным объектом он работать не будет — только с полупрозрачным, смешивающимся с тем, что за ним.

Меня всегда учили, что вместо термина “смешивание” корректнее употреблять выражение “альфа-сопряжение” (alpha compositing). Я его не применяю, но и такое обозначение иногда встречается. С момента моего обучения компьютерная графика шагнула далеко вперед. Мне представляется, что смешивание — лишь одна из форм альфа-сопряжения, а само альфа-сопряжение — метод рисования единичных или комбинируемых объектов с альфа-каналами.

Начинаем

Загружаем проект из урока №8

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

Смешивание в OpenGL

Чтобы смешивание работало, нужно активировать соответствующее состояние OpenGL. Уверен, что все помнят, как это делается.

1
glEnable(GL_BLEND);

Эту строку вставляем в метод “drawView[]” — прямо перед рисованием частично прозрачных объектов, а затем отключаем посредством “glDisable()“.

Итак, решим смешивания активирован (не забудьте позже отключить его за ненадобностью). Одно из несомненных достоинств OpenGL в том, что выполнение смешивания разными способами дает разные эффекты. Речь идет о так называемых функциях смешивания. Предлагаю подключить одну из них и подробно проанализировать все детали.

Переходим к методу “drawView[]“. Мы будем описывать прямоугольник, который наложим на экран перед двумя объектами.

1
2
3
4
5
6
const GLfloat blendRectangle[] = {
1.0, 1.0, -2.0,
-1.0, 1.0, -2.0,
-1.0, -1.0, -2.0,
1.0, -1.0, -2.0
};

Таких нам нужно визуализировать два. Поэтому для независимого их перемещения в коде рисования используем пару “glPushMatrix()” и “glPopMatrix()“.

Перейдите вниз по методу “drawView[]” — вам нужен фрагмент сразу за кодом рисования пирамиды и куба. Для корректной визуализации эффектов смешивания первыми рисуем непрозрачные объекты, потом прозрачные (по аналогии со старым алгоритмом графических программ, если с таковыми приходилось работать).

Теперь вспоминаем текущее состояние OpenGL. Сейчас у нас активировано наложение текстуры (ну конечно же). Нужна ли нам эта текстура на прямоугольниках? Не в этом примере — отключаем наложение текстур приведенной ниже строкой:

1
glDisableClientState(GL_TEXTURE_COORD_ARRAY);

И вот сейчас подключаем смешивание:

1
2
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE);

Функция “glEnable()” активирует режим смешивания. Но этого недостаточно — нужно сообщить OpenGL, как оно будет работать, иначе нас ждет состояние по умолчанию, не сулящее ничего интересного. Вот здесь на сцену и выходит “glBlendFunc()“. Не волнуйтесь — ниже мы подробно разберем функцию, а пока займемся экспериментами.

Итак, мы отключили наложение текстур, активировали смешивание и сообщили OpenGL о способе блендинга вызовом функции “glBlendFunc()“. Пора рисовать два прямоугольника:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
glPushMatrix();
{
glTranslatef(0.0, 1.0, -4.0);
glVertexPointer(3, GL_FLOAT, 0, blendRectangle);
glEnableClientState(GL_VERTEX_ARRAY);
glColor4f(1.0, 0.0, 0.0, 0.4);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
}
glPopMatrix();

glPushMatrix();
{
glTranslatef(0.0, -1.0, -4.0);
glColor4f(1.0, 1.0, 0.0, 0.4);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
}
glPopMatrix();
glDisable(GL_BLEND);

Ничего нового за исключением вызова “glDisable()” в конце кода. Взяв определение “blendRectangle“, мы вывели его на экран, раскрасили и нарисовали. Теперь нужно отключить смешивание, иначе OpenGL выполнит блендинг для пирамиды и куба при следующем вызове функции (при желании можно будет поэкспериментировать с этим моментом позже).

Вот оно! После щелчка на кнопке “Build and Go” получаем следующий результат:

Обратили внимание, как меняются цвета пирамиды и куба при наложении на них прямоугольников? Это и есть смешивание. Поскольку находящиеся на переднем плане прямоугольники на 40% прозрачны, их комбинирование — смешивание — с цветами объектов дает новые оттенки.

Желтый прямоугольник делает цвета несколько ярче по сравнению с красным (чуть повышая насыщенность цвета).

Если на экране оказались только красный и желтый прямоугольники без объектов, значит, код рисования вставлен не туда: прозрачные объекты рисуются после непрозрачных. Еще одна возможная причина — неверно активированный режим смешивания.

Мы рассмотрели самый простой вариант смешивания — ничего особенного, но начало положено.

Краткое резюме перед продолжением

Итак, чтобы работать со смешиванием, необходим полупрозрачный объект на переднем плане сцены. Первыми всегда рисуем непрозрачные объекты. После этого активируем смешивание вызовом “glEnable()“, настраиваем фактор блендинга с помощью “glBlendFunc()” и, наконец, смещаясь вперед, рисуем полупрозрачные объекты.

Теперь рассмотрим действительно сложный момент смешивания — настройку факторов блендинга.

Факторы блендинга

Я только что удалил две страницы собственноручно набранного текста. Речь в нем шла о расчете итогового цвета пикселя на фоне математики матриц (хотя этих слов я и не употреблял). В конце концов, я решил, что это неверный подход, посколmre с тем же успехом можно просто дать ссылку на руководство

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

Как уже говорилось выше, чтобы выполнить смешивание, нужен, во-первых, полупрозрачный объект, во-вторых — предмет на заднем плане, с которым он будет смешиваться. На самом деле, для блендинга можно ограничиться и фоном, поскольку при каждой перерисовке сцены в уроках мы вызываем “glClear()“.

О каждом пикселе на экране OpenGL располагает определенной информацией:

1.    Есть исходный пиксель — новый пиксель, рисующийся из полупрозрачного объекта (в нашем случае красного и желтого прямоугольников).
2.    Пиксель назначения (адресат) — пиксель, в текущий момент находящийся в буфере экрана. Поверх него рисуется наш частично прозрачный объект. В приведенном выше примере это либо объект (куб или пирамида), либо фон.
3.    Спецификатор фактора блендинга для исходного пикселя. Первый параметр, передаваемый функции “glBlendFunc()“. Сообщает OpenGL о способе обработки нового пикселя.
4.    Спецификатор фактора блендинга для пикселя назначения. Второй параметр, передаваемый “glBlendFunc()“. Сообщает OpenGL о способе обработки пикселя назначения.

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

Главное здесь — два параметра, передаваемые “glBlendFunc()“.

В нашем примере для настройки функции блендинга используется следующий код:

1
glBlendFunc(GL_ONE, GL_ONE);

Оба параметра одинаковы — “GL_ONE“. Первый влияет на исходный, т.е. входящий пиксель, второй — на значение пикселя назначения.

Есть масса различных спецификаторов блендинга, передаваемых функции “glBlendFunc()“. Но я хочу, чтобы вы поняли главное: эффект смешивания определяет не значение каждого из них конкретно, а итоговая  комбинация.

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

Чтобы добиваться максимального эффекта от блендинга, запомните: итоговый эффект определяется взаимодействием окрашенного в новый цвет полупрозрачного объекта (в данном случае прямоугольников) с предварительно нарисованными непрозрачными (куб, пирамида или фон) объектами, т.е. комбинация двух факторов блендинга.

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

Вариант OpenGL по умолчанию при активировании режима смешивания с помощью функции “glEnable()“:

1
glBlendFunc(GL_ONE, GL_ZERO);

Второй параметр поменялся на “GL_ZERO“. Если помните, он отвечает не за полупрозрачные, а за предварительно заданные “непрозрачные” объекты в буфере. Результат будет следующим:

Ну и где объекты на заднем плане? Не стоит особого труда догадаться, что ключ к происходящему — в “GL_ZERO“.

Дело не отсутствии блендинга: просто пиксель назначения перемножается на цвет RGBA (0, 0, 0, 0), в результате чего не происходит визуализации. Источник же (два прямоугольника на переднем плане) “GL_ONE” умножает на RGBA One (1, 1, 1, 1). Понятно, что это несколько упрощенное объяснение, но в целом дело обстоит именно так.

Результат будет тем же, что и при отсутствующем вызове “glBlendFunc()“, поскольку при активации смешивания это режим по умолчанию.

Меняем параметры местами:

1
glBlendFunc(GL_ZERO, GL_ONE);

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

С “GL_ZERO” и “GL_ONE” мы разобрались: они действуют именно так, как предполагает их название. Настало время полного списка возможных параметров для “glBlendFunc()“.

GL_ZERO
GL_ONE
GL_SRC_COLOR
GL_ONE_MINUS_SRC_COLOR
GL_DST_COLOR
GL_ONE_MINUS_DST_COLOR
GL_SRC_ALPHA
GL_ONE_MINUS_SRC_ALPHA
GL_DST_ALPHA
GL_ONE_MINUS_DST_ALPHA
GL_SRC_ALPHA_SATURATE

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

Примеры смешивания

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

1
glBlendFunc(GL_SRC_ALPHA, GL_ONE);

Заметили, что прямоугольники на переднем плане стали прозрачнее? По сравнению с исходным результатом смешивания объекты “кажутся” ярче, а прямоугольники — темнее. Все это не более чем оптические иллюзии.

1
glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE);

Прямоугольники исчезли! Вообще-то, не совсем. Их альфа-значение факторизовано как One Minus Destination Alpha. У пикселя назначения величина альфа равна 1.0, поэтому 1.0 - 1.0 = 0.0.

Обратите внимание: цвет фона перемножается на цвет, заданный “glClearColor()“, у которой показатель альфа тоже составляет 1.0. Вернитесь к методу “setupView[]” и поменяйте вызов функции “glClearColor()” на:

1
glClearColor(0.0, 0.0, 0.0, 0.0);

Редактируем всего одну строку и получаем вот такой результат:

Догадались, что произошло? Хотя кажется, что прямоугольники отодвинуты назад, на самом деле снова имеем дело с блендингом. В предыдущем примере при визуализации с пикселем (в частности, наших объектов), альфа-значение которого равно 1.0, прямоугольники становились полностью прозрачными. Теперь, когда у фона значение альфа составляет 0.0, уровень альфа для прямоугольников 1.0 - 0.0 (визуализируемый цвет).

Рассмотрим это случай подробнее.

Цвет красного прямоугольника (1.0, 0.0, 0.0, 0.4). Поэтому на черном фоне с нулевым альфа значением визуализируемый или смешиваемый цвет будет выглядеть как (1.0, 0.0, 0.0, 0.4). Поскольку фон черный, ситуация упрощенная. Давайте изменим прозрачный цвет на синий. Перейдите в “setupView[]” и замените вызов “glClearColor()” на:

1
glClearColor(0.0, 0.0, 1.0, 0.0);

Получили интенсивно-синий фон. Щелчок на кнопке “Build & Go” дает следующую картину:

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

Рассмотрим последний пример, после чего перейдем к расчетам и вместе выясним цвет пикселя.

1
glBlendFunc(GL_SRC_ALPHA, GL_DST_ALPHA);

Последняя комбинация смешивания…

Проанализируем самую типовую пару параметров:

1
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

Для нашего тестового проекта результат будет следующим:

Если и есть универсальный способ верно подобрать параметры для “glBlendFunc()” — он перед нами. Стандартный пример смешивания и, наверное, самый востребованный. Если сомневаетесь, начинайте с него!

Добавляем дополнительный блендинг

Наши возможности не ограничены смешиванием пикселя с одним объектом — вполне допустимо накладывать друг на друга множество полупрозрачных объектов. Добавьте в метод “drawView[]” приведенный ниже код (после отрисованного второго прямоугольника):

1
2
3
4
5
6
7
8
glPushMatrix();
{
glTranslatef(0.0, 0.0, -3.0);
glScalef(1.0, 0.3, 1.0);
glColor4f(1.0, 1.0, 1.0, 0.6);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
}
glPopMatrix();

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

Само собой, я не преминул добавить нечто новенькое — “glScalef()“.

Масштабирование, о котором еще не говорили!

Масштабирование — это всего лишь изменение размеров объекта (как в графических программах, только там оно двухмерное). Функция принимает три параметра — “xscale“, “yscale” и “zscale“.

В приведенном выше примере фактор масштабирования для “xscale” и “zscale” составляет 1.0, т.е. исходный размер. Значение выше 1.0 увеличивает объекты, делая их больше, меньше 1.0 — соответственно, ужимает. Поскольку значение “yscale” равно 0.3, высота прямоугольника сократилась до 30% от исходной величины. Я сделал это, чтобы лишь частично захватить куб и пирамиду, получив зону “двойного смешивания” с максимальным эффектом.

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

Заключение

По этому уроку пока все. Нужно еще многое охватить, в частности, рассмотрев подробнее комбинирование различных видов смешивания. Но это оставим на следующий раз. Сейчас главное — научиться активировать смешивание и освоить базовые принципы работы функции “glBlendFunc()“.

Исходный код скачать можно [здесь]

Текст оригинальной статьи на английском языке [здесь]

Уважаемые читатели, данный материал был переведен и подготовлен к публикации проектом LookApp.ru, при публикации на другом сайте ссылка на LookApp.ru обязательна.

1 звезда2 звезд3 звезд4 звезд5 звезд (Оцените приложение)
Загрузка ... Загрузка ...

One Ping to “Уроки iPhone SDK: (Часть 10) OpenGL ES: Смешивание. Без блендера. (Часть 1)”

  1. Уроки iPhone SDK: (Часть 11) OpenGL ES: одна текстура, разные представления; вдохновение в математике | LookApp.ru - обзоры программ и игр для iPhone Says:

    [...] вещь. Сколько ни пытался, у меня никак не получилось довести до ума урок по смешиванию (Часть 1). Поэтому я просто опубликовал его, решив вернуться к [...]


2 Responses to “Уроки iPhone SDK: (Часть 10) OpenGL ES: Смешивание. Без блендера. (Часть 1)”

  1. 1. Batu Says:

    Вот это уже посерьезней…

  2. 2. Zippo Says:

    День добрый, скажите пожалуйста, можно ли импортировать в приложение объекты из программы под названием Blender?

Оставьте комментарий