Урок №10. Цвета в OpenGL

  Дмитрий Бушуев  | 

  Обновл. 8 Июн 2020  | 

 948

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

Цвета в мире графики

В реальном мире существует бесконечное множество различных цветов и их оттенков. Но вычислительная техника не может похвастаться такими безграничными возможностями, поэтому не все реальные цвета могут быть представлены в цифровом виде. Следовательно, возникает задача: «Как описать (или закодировать) бесконечное количество различных вариантов реальных цветов с помощью ограниченного набора цифровых значений?». Ответом на данный вопрос является применение особой системы, в которой реальные цвета представляются в цифровом виде с помощью красного, зеленого и синего компонентов (сокр. «RGB» от англ. «Red, Green, Blue»). Используя различные комбинации только этих 3-х значений, определенных в диапазоне [0,1], мы можем закодировать практически любой цвет, который только существует. Например, чтобы получить коралловый цвет, необходимо определить вектор цвета следующим образом:

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

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

Данные правила отражения цвета напрямую применяются в мире графики. Предположим, что мы захотели определить в OpenGL некий источник света и задать для него какой-нибудь цвет. В предыдущем абзаце использовался белый цвет, выберем его и в этот раз. Далее, умножая цвет источника света на значение цвета объекта, мы получим цвет, отраженный от объекта. Этот цвет мы и будем воспринимать, как цвет объекта. Давайте теперь вернемся к нашей игрушке (с коралловым цветом) и посмотрим, как мы будем вычислять её воспринимаемый цвет в мире графики. Чтобы получить результирующий вектор цвета, необходимо выполнить покомпонентное умножение между вектором падающего на объект света и вектором собственного цвета объекта:

Мы видим, что итоговый цвет игрушки поглощает большую часть белого света, но при этом отражает красные, зеленые и синие цвета, и это отражение зависит от собственных значений цвета игрушки. Данный пример даёт нам представление о том, как цвета будут работать в реальной жизни. Таким образом, мы можем определить понятие «цвет объекта» как количество каждого цветового компонента источника света, отраженного от объекта. А что будет, если мы поменяем цвет света у источника на зеленый?

Как мы видим, на игрушку не падают красный и синий компоненты света, а это значит, что она не сможет их поглотить и/или отразить. Игрушка поглощает половину зеленого значения света, но при этом также и отражает половину зеленого значения света. В результате итоговый воспринимаемый нами цвет игрушки будет темно-зеленоватым. Не трудно заметить, что если использовать зеленый свет, то только зеленые компоненты цвета могут быть отражены и, таким образом, восприняты; красные и синие компоненты цвета не будут восприниматься. В результате коралловый объект внезапно становится темно-зеленоватым. Ниже представлен ещё один пример с темным оливково-зеленым светом источника:

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

Но хватит о цветах, давайте начнем строить сцену, где мы сможем поэкспериментировать.

Освещение сцены


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

Первое, что нам нужно, — это объект, который будет отбрасывать свет, и в качестве такого предмета будет задействован уже известный нами куб-контейнер из предыдущих уроков. Нам также понадобится ещё один объект, обозначающий положение источника света в 3D-сцене. Для простоты мы также представим источник света с помощью куба.

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

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

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

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

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

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

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

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

Для этого нам нужно создать второй набор шейдеров, которые мы будем использовать для отображения куба-источника, тем самым избегая влияния любых изменений в шейдерах освещения. Вершинный шейдер точно такой же, как и вершинный шейдер освещения, поэтому вы можете просто скопировать исходный код. Фрагментный шейдер куба-источника будет задавать для нашей «лампочки» постоянный белый цвет, обеспечивая эффект неизменного яркого белого света.

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

Основное назначение куба-источника — это показать, откуда собственно и исходит свет. Обычно, положение источника света определяется где-то в произвольном месте сцены, но при этом подразумевается, что от данной позиции не требуется обеспечивать визуальное восприятие конкретно самого источника света. Чтобы показать, где на самом деле находится источник, мы визуализируем куб в том же месте, где находится источник света. Визуализировать данный куб мы будем с помощью шейдера куба-источника, чтобы убедиться, что куб всегда остаётся белым, независимо от условий освещения сцены.

Итак, давайте объявим глобальную переменную vec3, которая представляет местоположение источника света в координатах мирового пространства:

Затем мы переводим куб-источник в положение источника света и перед визуализацией уменьшаем его масштаб:

Полученный код рендеринга для куба-источника должен выглядеть примерно следующим образом:

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

Не густо, но в следующих уроках будет ещё интереснее.

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

  Google Drive / Исходный код — Урок №10. Цвета в OpenGL

  GitHub / Исходный код — Урок №10. Цвета в OpenGL

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

Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (7 оценок, среднее: 5,00 из 5)
Загрузка...

Добавить комментарий

Ваш E-mail не будет опубликован. Обязательные поля помечены *