Урок №10. Цвета в OpenGL
Обновл. 30 Апр 2021 |
На предыдущих уроках мы никогда должным образом не определяли цвет объектов, а только лишь бегло касались взаимодействия с этим атрибутом объекта. Поэтому в данной статье мы обсудим, что такое цвета в OpenGL, и начнем строить сцену для последующих уроков, в которых будет обсуждаться тема освещения.
Цвета в мире графики
В реальном мире существует бесконечное множество различных цветов и их оттенков. Но вычислительная техника не может похвастаться такими безграничными возможностями, поэтому не все реальные цвета могут быть представлены в цифровом виде. Следовательно, возникает задача: «Как описать (или закодировать) бесконечное количество различных вариантов реальных цветов с помощью ограниченного набора цифровых значений?». Ответом на данный вопрос является применение особой системы, в которой реальные цвета представляются в цифровом виде с помощью красного, зеленого и синего компонентов (сокр. «RGB» от англ. «Red, Green, Blue»). Используя различные комбинации только этих 3-х значений, определенных в диапазоне [0,1] , мы можем закодировать практически любой цвет, который только существует. Например, чтобы получить коралловый цвет, необходимо определить вектор цвета следующим образом:
Цвет объекта, который мы видим в реальной жизни, — это не тот цвет, который он действительно имеет. На самом деле, мы видим луч конкретного цвета, отраженный от объекта. Лучи различных цветов, которые не поглотились объектом, создадут именно тот цвет объекта, который мы и увидим. Например, свет солнца воспринимается нами как белый свет, но на самом деле он является совокупностью многих лучей разных цветов. Если бы мы посветили этим белым светом на синюю игрушку, то она поглотила бы все лучи, составляющие белый свет, кроме синего. А поскольку луч синего цвета игрушка не поглотила, то он отражается от нее. Отраженный луч попадает в наш глаз и в результате мы воспринимаем нашу игрушку в виде предмета синего цвета. На следующем рисунке этот процесс показан для игрушки кораллового цвета; она отражает несколько лучей различных цветов с различной интенсивностью:
Вы можете видеть, что белый солнечный свет — это совокупность лучей всех видимых цветов, и при этом объект поглощает большую часть из них. Оставшиеся же лучи отражаются от него. Попадая к нам в глаз, их сочетание и придает объекту тот цвет, каким мы его воспринимаем (в данном случае, коралловым).
Данные правила отражения цвета напрямую применяются в мире графики. Предположим, что мы захотели определить в OpenGL некий источник света и задать для него какой-нибудь цвет. В предыдущем абзаце использовался белый цвет, выберем его и в этот раз. Далее, умножая цвет источника света на значение цвета объекта, мы получим цвет, отраженный от объекта. Этот цвет мы и будем воспринимать, как цвет объекта. Давайте теперь вернемся к нашей игрушке (с коралловым цветом) и посмотрим, как мы будем вычислять её воспринимаемый цвет в мире графики. Чтобы получить результирующий вектор цвета, необходимо выполнить покомпонентное умножение между вектором падающего на объект света и вектором собственного цвета объекта:
Источник
Таблица цветов для opengl
Посмотрим на соответствующую цветовую таблицу. В ней сейчас 256 цветов, но отнюдь не все из них используются в изображении. Удалим некоторые цвета из цветовой таблицы, которые заведомо не используются в изображении, вручную. Тем самым мы уменьшим размер файла и постараемся сохранить в изображении нужные цвета. Выбираем инструмент Пипетка в диалоговой панели команды SAVE FOR WEB и щелкаем по тому цвету, который нужно сохранить. Вот этот цвет в цветовой таблице. Он обведен дополнительной рамочкой. Он сейчас выделен. Щелкнем левой кнопкой мыши по пиктограмме, изображающей замочек, внизу цветовой таблицы, предохраняя, таким образом, этот цвет от последующего удаления. В правом нижнем углу квадратика с эти цветом в цветовой таблице появился маленький белый кружок, признак блокировки этого цвета от последующего удаления.
Щелкаем по оранжевому цвету и блокируем его. По чёрному. Блокируем. Видите: у заблокированных цветов появляется маленький белый кружок, подчеркиваю, внизу квадрата с этим цветом в цветовой таблице. На кружок в центре квадрата с цветом сейчас не обращаем внимания.
Он к нашему процессу не имеет никакого отношения. Это признак так называемого безопасного цвета для веб. Аналогичным образом я заблокировала и некоторые другие плашечные цвета, встречающиеся в этом изображении. С цветами на глазах поступим так, для ускорения процесса выбора нужных цветов. Щёлкнем в этом месте глаза (был выбран зеленый цвет), затем нажмем на клавиатуре клавишу Shift, и пощелкаем вдоль всего глаза. Посмотрите: выделяются другие цвета. Клавишу Shift нужно удерживать во время всего процесса выделения цветов. Затем отпускаем клавишу Shift и блокируем сразу всю группу выделенных цветов, нажимая на пиктограмму с замочком.
А что там у нас с раскладкой ноздрей? Нужно сохранить цвета и этой раскладки. Повторное нажатие на пиктограмму с замочком снимает блокировку выделенных цветов, поэтому сначала снимем выделение с только что заблокированных цветов. Нажимаем на кнопку вызова меню цветовой таблицы и выбираем там команду Deselect All Colors (Снять выделение со всех цветов). Снова выбираем инструмент Пипетка и опять пройдемся, но уже по носу, так же, как мы только что прошлись по глазу. Посмотрите снова на цветовую таблицу. Все цвета, которые имеют в правом нижнем углу белый кружочек, заведомо используются в изображении. Я надеюсь, что таким способом я выбрала все цвета, которые присутствуют в изображении, и поэтому остальные сейчас удалю.
Вот этот цвет у меня заблокирован. Его удалять не надо. Выбираю ближайший к нему в цветовой таблице. Следующий используемый в картинке цвет — вот этот. Всё, что между ними, можно удалить. Нажимаю на клавиатуре клавишу Shift и, удерживая ее, щелкаю по вот этому цвету. Отпускаю клавишу Shift. Таким образом, я выделила в цветовой таблице целый непрерывный промежуток цветов, который можно удалить. Удаляю их, нажав на пиктограмму мусорной корзины в правом нижнем углу цветовой таблицы.
Картинка, похоже, не изменилась. Видимо, я и вправду не удалила ни одного нужного цвета. Вообще, должна заметить, что работа в диалоговом окне команды Сохранить для веб требует повышенного внимания, потому как отменить неправильно произведенные действия невозможно. По крайней мере, я этой возможности здесь не нашла. Повторяем процесс удаления ненужных цветов. Щелчок.
Нажимаем клавишу Shift. Щелчок. Затем щелчок по пиктограмме мусорной корзины. Обратите внимание: количество цветов уменьшается, их уже 222, а вместе с этим количеством цветов уменьшается и размер файла. Изображение же при этом визуально не меняется. И вот что мы имеем в итоге. Цветов всего 24. Размер файла чуть больше. чуть больше 15 килобайт, глаза раскрашены так, как я хотела, и нос не пострадал.
Посмотрим, как этот бык будет выглядеть на веб-странице. Вполне прилично. И посмотрите, какая большая картинка, и какой небольшой размер файла: всего 15 килобайт. Подобный способ визуального представления цветов, отсутствующих в цветовой таблице, требует довольно кропотливой работы, и хуже всего то, что, ошибившись, приходится начинать всё сначала. Существует и другой способ представления цветов, отсутствующих в цветовой таблице. Это так называемый Дизаринг.
Поиск
TeachPro WEB-дизайн
- Обучение в пошаговом режиме
- Контрольный режим
- Тестовый режим
- Область демонстрации урока
- Панель управления системы теаеирго
- Дополнительные функциональные клавиши teachpro — подсказка
- Закладки
- Устранение возможных проблем с программой
- Проблемы при инсталляции программы
- Затруднительные ситуации при проигрывании урока
- То необходимо знать web-дизайнеру
- Раздел 1. основы дизайна — история русского дизайна
- История русского дизайна. русский дизайн с xix в. до наших дней
- Формообразование
- Основные правила композиции
- Цвет в дизайне
- Раздел 2. компьютерная графика — что это такое? — немного из истории
- Основные виды компьютерной графики
- Растровая графика
- Векторная графика
- Трехмерная графика
- Фрактальная графика
- Цветовые модели и палитры
- Вспомним а0оьв photoshop
- Размеры изображения, кадрирование. цветокоррекция
- Раздел 3. общие сведения о web-дизайне — основы интернет
- Worldwideweb. www, браузер, доменные имена
- Worldwideweb-2. сайты и страницы, гипертекст и гиперссылки
- Свойства документа
- Особенности изображений для web
- Приемы оптимизации графики
- О программе macromedia dreamweaver
- Форматирование текста
- Синтаксис текста
- Разметка текста
- Вставка изображения и редактирование его свойств
- Создание простого web-сайта
- Раздел 4. технологии web-дизайна — html — то такое html
- То собой представляют html-файлы
- Синтаксис html и структура html-документа
- Тело документа. элементы body, div, span
- Разметка текста. phrase elements, font-style-elements
- Разметка текста-2
- Технический уровень — списки
- Ссылки
- Таблицы
- Изображения
- Формы
- Фреймы
- Изменение цветов фона. горизонтальная линия
- Стили шрифтов в html
- Меню выбора в html
- Создание формы отправки файлов в html
- Карта изображения в html
- Раздел 5. программы для работы с web-сайтами — векторная графика: macromedia flash — первое знакомство. работа с панелью инструментов — обзор интерфейса программы
- Установка основных параметров фильма, настройка интерфейса редактора
- З. инструмент «.линия»
- Инструменты «карандаш», «овал», «прямоугольник»
- Инструмент «заливка»
- Инструмент «преобразование заливки»
- Инструменты «кисть», «ернильница», «пипетка», «ластик»
- Инструмент «стрелка»
- Инструмент «свободная трансформация»
- Инструмент «.лассо»
- Инструменты «перо», «астичное выделение»
- Основы анимации во flash, символы и экземпляры — создание покадровой анимации, отображение кадров
- Раскадровка формы (морфинг)
- З. раскадровка движения
- Символы и экземпляры, изменение свойств экземпляров символов
- Графические символы и символы видеоклипов
- Символы кнопок и их особенности, создание пульсирующей кнопки
- Принципы работы со специальными слоями.применение сцен — з.1. общие принципы работы со слоями, опорные слои
- I.3.3. примеры создания трехмерных эффектов с помощью слоев траекторий
- З.4. принципы работы с маскирующими слоями. примеры их использования
- З.5. примеры применения текста при маскировании (ii уровень)
- Трехмерная графика. создание рекламного ролика в 3d max 6
- Создание изображений для web в программе adobe photoshop cs — форматы графических файлов для web
- Команда save for web. инструменты и режимы просмотра команды
- Сохранение для web в формате jpg
- Цветовые модели изображения в формате jpg и web
- Изображения в формате jpg
- Формат gif
- Цветовая таблица (color table)
- Дизеринг. поэтапный вывод изображения на экран
- Изображения в формате gif и прозрачность
- Надпись с тенью и объемная надпись
- Анимации в формате gif
- Разработка электронных документов в программе adobe illustrator cs — создание web страницы
- Создание web страницы с вырезками и гиперссылками
- Создание анимационного клипа. движение объекта
- Создание анимационного клипа. преобразование объектов и их появление
- Обработка исходного документа.
- Ввод гиперссылок и закладок. вставка объектов интернета. формирование изображения для web
- Порядок формирования изображения для web
- Создание документов в формате html
- Создание документов в формате pdf
- Вставка мультимедиа при помощи программы frontpage 2003 — вставка фильма в формате flash
- Изменение свойств фильма в формате flash
- Вставка видеозаписи
- Настройки автоэскизов
- Создание коллекции фотографий на основе шаблона
- Создание коллекции фотографий как web-компонента
- Вставка фотографии в коллекцию фотографий с помощью сканера
- Изменение рисунка в коллекции фотографий
- Макеты коллекции фотографий
- Вставка меняющейся (интерактивной) кнопки
- Изменение свойств меняющейся кнопки
- Раздел 6. web-cайты. типы и разработка — типы сайтов, назначение, цель, целевая аудитория
- Кодировка русского языка. мировая паутина. браузеры
- Обозреватели интернет — знакомство с браузерами — з.1.1. то такое обозреватели?
- З.1.2. навигация в обозревателе
- З.1.4. автономный режим работы
- Интерфейс internet explorer 6.0 — з.2.1. панели браузера
- Панели инструментов
- Некоторые приемы работы
- Кнопки поиск, избранное
- Кнопки медиа, журнал
- Кнопки почта, печать, показать в виде html
- Главное меню. меню файл
- З.2.8. главное меню. меню правка
- Главное меню. меню сервис
- Путешествие в web — путешествие по миру авто
- Энциклопедия и web
- Литература в web
- Музеи в web
- Отправление открытки
- Покупка книги
- Бронирование билетов
- Отличия от поли графического дизайна на примере публикаций quarkxpress 6.0 — создание простых web-проектов
- Палитра web tools
- Диалоговое окно preferences. вкладка export выбор браузера. палитры, ориентированные на web
- Преобразование печатных макетов в web формат
- Стилизованный текст
- Создание гиперссылок
- Создание карты изображения
- Установка фона страницы. использование изображения в качестве фона страницы
- Анкеры
- Редактирование гиперссылок. создание мета-дескрипторов
- Xml
- Общие сведения о программировании на java — введение
- Среда программирования — установка языка java
- Компиляция и выполнение простой программы
- Текстовый редактор textpad
- Апплеты
- Основные структуры языка 6.7.з.1. простая программа на языке java
- Простая программа на языке java. комментарии
- Типы данных. переменные. целые числа
- -ричные, 8-ричные и действительные числа
- Символьный и булевый типы данных
- Операторы. арифметические действия
- Битовые операции. математические функции
- Операторы
- Строки: создание, слияние, выделение подстроки, длина
- Ввод данных с диалоговым окном и операции с ними
- Форматирование данных для отображения на консольном окне
- Основные структуры языка (продолжение) — оператор if. синтаксис
- Оператор if. составной оператор. вложение операторов if
- Операторы цикла do
- Операторы цикла for
- Оператор switch
- Прерывание потока управления. оператор break
- Большие числа. арифметические операции
- Большие числа. сравнение. вычисление с большой точностью
- Одномерные массивы. создание. инициализация.
- Создание простого апплета
- Преобразование приложения в апплет
- Архивы jar
- Упаковка ресурсов в архивы jar
- Всплывающие окна в апплетах создание кнопки
- Всплывающие окна в апплетах. обработчик событий
- Создание передаваемых параметров на html странице
- Тение параметров в апплете ици теперь, внутри этого метода init,
- Передача параметров апплетам. построение гистограммы на их основе
- Регистрация сайта и загрузка файла на сайт
- Редактирование опубликованного сайта
- Доступ к файлам по ftp
- Е-издательство «мультимедиа технологии и дистанционное обучение»
- Мультимедийные самоучители на cd-rom серия «teachpro™»
- Мультимедийные самоучители серии «teachpro™» по программным продуктам microsoft.
Програмирование с использованием OpenGL
Последние публикации
Организация рабочего пространства – проблема, актуальная для любого офиса или конторы.
Цифровая техника настолько плотно прижилась в доме каждого из нас, что порой с трудом вспоминаются те времена, когда обходились без видеокамер, фотоаппаратов, мобильных телефонов или планшетов
Если каких-то десять лет назад посмотреть любимый фильм можно было только по телевизору или в кинотеатре, то сегодня индустрия досуга развита куда больше и уже нет необходимости следить за программой телепередач, достаточно иметь интернет
Кто такие бизнесмены, и где их найти
Советы о том, как бороться с вирусами
Источник
Полный список
— передаем цвет для вершин
— используем varying переменную
На прошлом уроке мы научились рисовать графические примитивы. Теперь научимся использовать при этом различные цвета.
Напомню, что цвет мы задавали следующим образом:
Где uColorLocation – это переменная, которая знает, где находится во фрагментном шейдере переменная u_Color, которая отвечает за цвет (см. Урок 169).
Вызов метода glUniform4f можно вынести из метода bindData и поместить его в onDrawFrame
Открываем проект, в нем модуль этого урока — lesson171_colors. Смотрим класс OpenGLRenderer. Он похож на этот же класс из примера прошлого урока. Т.е. он нарисует те же 4 треугольника, 2 линии и три точки. Но теперь он сделает это разными цветами.
Вершины всех примитивов заданы в массиве
А в методе onDrawFrame есть изменения:
Перед каждым вызовом glDrawArrays идет вызов glUniform4f, который задает цвет. Т.е. треугольники будут синими, линии – зелеными, а точки – красными.
Но что, если мы хотим например одну из линий нарисовать желтым цветом. Тогда мы просто разделим вызов glDrawArrays, который рисует две линии, на два вызова, каждый из которых будет рисовать одну линию. И перед каждым вызовом будем ставить нужный цвет.
Перепишем onDrawFrame:
Мы разбили один вызов рисования линий на два и перед каждым из них задаем нужный цвет.
Обратите внимание на параметры этих двух новых вызовов метода glDrawArrays. Первая линия использует две вершины, начиная с индекса 12. А вторая также 2 вершины, но начиная с индекса 14. А до разделения этот метод брал 4 вершины, начиная с индекса 12.
Этот способ задания цвета достаточно прост. Существует более интересный способ. Мы можем задать цвет для каждой вершины примитива. И в процессе рисования система сама выполнит интерполяцию цветов вершин на всю поверхность графического примитива.
Т.е., например, мы рисуем линию, используя две вершины. Для первой вершины мы указали зеленый цвет, а для второй – красный. Нарисованная линия будет градиентной, т.е. иметь зеленый цвет со стороны первой вершины, а по мере приближения ко второй вершине зеленый цвет будет сменяться красным. Т.е. система сама рассчитает цвета всех промежуточных пикселей между вершинами (это и называется умным словом Интерполяция).
Перепишем шейдеры, сначала вершинный vertex_shader.glsl:
Мы добавили атрибут a_Color. В него мы будем передавать значение цвета для каждой вершины, аналогично тому как мы передаем координаты вершины в a_Position.
Также, мы добавили переменную v_Color. Обратите внимание на слово varying. Мы уже знаем, что существуют attribute переменные, в которые мы передаем отдельные данные для каждой вершины в вершинном шейдере. Есть uniform переменные, в которые мы передаем одно значение для всех вершин в вершинном шейдере и всех точек в фрагментном шейдере. Теперь мы добрались до третьего (и последнего) вида переменных в шейдерах — varying. Такие переменные используются для обмена данными между вершинным и фрагментным шейдером. Переменную varying мы сами заполняем в вершинном шейдере, далее система интерполирует эти значения и возвращает результат нам в фрагментный шейдер.
В нашем примере в вершинном шейдере мы помещаем в v_Color значение a_Color. Т.е. если рассматривать пример зелено-красной линии, то первая вершина поместит в v_Color значение зеленого цвета, а вторая – красного. Далее выполняется фрагментный шейдер для каждой точки между этими вершинами, и в этом шейдере мы будем получать интерполированное значение v_Color. Оно будет соответствовать зеленому цвету для точек около первой вершины и постепенно меняться на красный цвет по мере отрисовки точек по пути ко второй вершине. Это даст нам возможность нарисовать зелено-красную линию.
Все расчеты значений varying переменных выполняет система. Нам нужно только задать значения в вершинном шейдере и считать их в фрагментном.
Перепишем фрагментный шейдер fragment_shader.glsl:
Мы добавляем varying переменную v_Color. Значение в ней уже рассчитано системой на основании данных из вершинного шейдера. Нам остается только записать его в gl_FragColor.
Теперь необходимо поменять код приложения, чтобы в вершинный шейдер передавать не только координаты вершин, но и цвет.
Меняем OpenGLRenderer.java:
и вместо нее добавляем
В prepareData задаем вершины:
Будем рисовать три линии, т.е. задаем 6 вершин. Но теперь для каждой вершины есть не только две координаты XY, но и три RGB-компонента цвета. Всего 2 + 3 = 5 значений для каждой вершины.
Сначала передаем данные по координатам. Тут почти без изменений, только в методе glVertexAttribPointer пятым параметром мы передаем 20. Раньше мы передавали сюда 0.
Этот пятый параметр называется stride. В него необходимо поместить кол-во байт, которое занимают в нашем массиве данные по каждой вершине. У нас для каждой вершины заданы 5 float значений: 2 координаты (XY) и три компонента цвета (RGB). 5 float значений – это 5 * 4 байта = 20 байтов. Именно это значение мы и передаем в stride.
Т.е. если мы рассмотрим две этих строки
то получится примерно такая схема:
1) позиция в массиве vertexData ставится в 0, т.е. на первый элемент
2) система берет 2 float значения (т.е. координаты вершины) из vertexData и передает их в aPositionLocation (что соответствует атрибуту a_Position в вершинном шейдере)
3) позиция перемещается на 20 байтов, т.е. к координатам следующей вершины.
Пункты 2 и 3 выполняются столько раз, сколько вершин необходимо нарисовать. Смещение на 20 байтов каждый раз будет устанавливать позицию в массиве на данные о координатах следующей вершины.
Смотрим дальше. Тут все аналогично. В aColorLocation мы получаем расположение атрибута a_Color и выполняем код
1) позиция в массиве vertexData ставится в 2, т.е. на третий элемент (туда, где начинаются данные о цвете первой вершины)
2) система берет 3 float значения (т.е. RGB-компоненты цвета вершины) из vertexData и передает их в aColorLocation (что соответствует атрибуту a_Color в вершинном шейдере)
3) позиция перемещается на 20 байтов, т.е. к цвету следующей вершины
Пункты 2 и 3 выполняются столько раз, сколько вершин необходимо нарисовать. Смещение на 20 байтов каждый раз будет устанавливать позицию в массиве на данные о цвете следующей вершины.
Осталось переписать метод onDrawFrame:
Как видите, здесь мы просто просим систему нарисовать нам линии используя 6 вершин. И ничего не говорим ни про цвет, ни про координаты. Система будет запускать вершинный шейдер 6 раз, и благодаря методам glVertexAttribPointer (которые мы только что подробно рассмотрели) сможет разобраться, какие данные из массива ей надо будет использовать в качестве координат вершин (она передаст их в a_Position), а какие – в качестве данных о цвете (a_Color).
В результате видим линии, цвет которых меняется от одной вершины к другой. Это результат того, что мы передали данные о цвете в вершинный шейдер и использовали varying переменные.
Используем режим рисования линий, который соединит все вершины между собой.
И напоследок нарисуем треугольник с вершинами разного цвета и посмотрим, как он интерполирует эти цвета на всю свою поверхность.
Перепишем массив vertices в prepareData:
Задаем три вершины, для каждой из которых заполняем две координаты и три компонента цвета.
В методе onDrawFrame просим нарисовать треугольник.
и получаем градиентную заливку
Надеюсь, что после этого урока начала складываться общая картина механизма шейдеров. Ее можно разбить на пункты:
1)Метод glDrawArrays, в котором мы указываем какие фигуры рисовать и сколько для этого использовать вершин. Сколько вершин мы здесь укажем, столько раз и вызовется вершинный шейдер.
2)Вершинный шейдер имеет атрибуты, в которые нам необходимо передавать данные о вершинах. За это отвечает метод glVertexAttribPointer, в котором мы подробно объясняем системе, из какого массива брать данные и по каким правилам (оступ, тип данных, кол-во значений на вершину)
3)Запускается вершинный шейдер, в котором мы пока что просто передаем полученные данные дальше на отрисовку — gl_Position. По этим координатам система будет рисовать вершины примитивов. Также в вершиннном шейдере мы используем varying переменную, чтобы интерполировать цвет и передать его в фрагментный шейдер.
4)Фрагментный шейдер служит для прорисовки содержимого примитива. Т.е. он вызывается для каждой точки примитива. В нашем случае он получает интерполированный цвет и передает его далее в gl_FragColor. Этот цвет мы и увидим на экране.
Советую поиграться с массивом vertices и попробовать задать там свои координаты и цвета, и использовать разные фигуры в методе glDrawArrays. Это поможет лучше понять все эти механизмы.
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме
Источник
learnopengl. Урок 2.1 — Цвета
Перевод очередного урока с сайта learnopengl.com. Недавно обнаружил на русском Уроки по OpenGL с сайта OGLDev, но некоторые из них требуют 4 версию opengl, а примеры кода слишком зависимы от предыдущих уроков и объектно-ориентированы. Поэтому, вниманию всех интересующихся opengl’ем новичков со стареньким железом предлагаю коротенькую статью о цвете, с которой начинается вторая часть обучающего курса от Joey de Vries:
Цвета
В предыдущих уроках встречались краткие упоминания о том, как работать с цветом в OpenGL, но до сих пор мы касались только самой поверхности этого вопроса. Сейчас мы подробно обсудим, что такое из себя представляет цвет, и начнем построение сцены, которую будем использовать в следующих уроках, посвященных освещению.
В реальном мире у каждого объекта есть свой собственный цвет, который может принимать практически любые значения. В цифровом же мире нам необходимо отобразить (бесконечные) цвета реальности посредством (ограниченного диапазона) цифровых значений, поэтому в цифровом виде могут быть воспроизведены не все существующие цвета. Однако мы можем отображать настолько огромное количество цветов, что вы, вероятно, все равно не заметите разницы. В цифровой форме цвета представляют совокупностью трех компонент: красного, зеленого и синего, обычно сокращенно называемой RGB (Red Green Blue). Используя различные комбинации всего лишь этих трех значений, мы можем передать почти любой существующий цвет. Например, чтобы получить коралловый цвет, мы зададим вектор цвета следующим образом:
Цвета, видимые нами в реальной жизни, это цвета не самих объектов, а цвет отраженного ими света; т.е. мы воспринимаем цвета, которые остаются не поглощёнными (отброшенными) объектом. Например, свет солнца воспринимается как белый свет, который состоит из всех цветов спектра (вы можете это видеть на картинке). Таким образом, если мы посветим белым светом на синюю игрушку, то она будет поглощать все составляющие белый свет цвета, кроме синего. Так как игрушка не поглощает синий цвет, он отразится, и этот отраженный свет, воздействуя на наши органы зрения, создаёт впечатление, что игрушка окрашена в синий цвет. Следующий рисунок иллюстрирует этот феномен на примере объекта кораллового цвета, отражающего исходные цвета с разной интенсивностью:
Вы можете видеть, что белый солнечный свет на самом деле представляет собой совокупность всех видимых цветов, и объект поглощает наибольшую часть этого диапазона. Он отражает только те цвета, комбинация которых воспринимается нами как цвет объекта (в данном случае коралловый цвет).
Эти правила отражения света непосредственно применяются в компьютерной графике. Когда в OpenGL мы создаем источник света, то указываем его цвет. В предыдущем абзаце мы говорили о белом солнечном свете, поэтому давайте и нашему источнику света тоже зададим белый цвет. Если затем мы умножим цвет источника света на цвет объекта, то полученное значение будет отраженным цветом объекта (и, следовательно, цветом в каком мы воспринимаем объект). Вернемся к нашей игрушке (на этот раз кораллового цвета) и посмотрим, как графическими средствами вычислить её цвет, воспринимаемый наблюдателем. Для получения нужного нам цвета, произведем покомпонентное умножение двух цветовых векторов:
Игрушка поглощает наибольшую часть белого света, а оставшееся количество красного, зеленого и синего излучения она отражает соответственно цвету своей поверхности. Это демонстрация того, как цвета ведут себя в реальном мире. Таким образом, мы можем задать цвет объекта вектором, характеризующим величины отражения цветовых компонент, поступающих от источника света. А что бы произошло, если бы для освещения мы использовали зеленый свет?
Как мы видим, в источнике света нет красной и синей составляющей, поэтому игрушка не будет их поглощать и/или отражать. Одну половину всего количества зеленого света игрушка поглощает, а вторую отражает. Поэтому цвет игрушки, который мы увидим будет темно-зеленый. Таким образом, если мы используем зеленый источник света, то отражаться и восприниматься будут только зеленые компоненты; никакие красные и синие оттенки не будут видны. В результате объект кораллового цвета неожиданно становится темно-зеленоватым. Давайте проведем еще один эксперимент с темным оливково-зеленым светом:
Перед вами пример получения странной окраски объекта из-за использования разноцветного освещения. Оказывается стать оригинальным колористом совсем не сложно.
Но хватит рассуждать о цветах, давайте лучше приступим к созданию сцены, в которой сможем попрактиковаться.
Освещенная сцена
В следующих уроках, углубленно работая с цветами, мы создадим интересные визуальные эффекты, имитирующие освещение в реальном мире. С этого момента, для имитации освещения мы станем использовать не менее одного источника света, и будем его изображать как видимый объект сцены.
Первое, что нам понадобится — это освещаемый объект, в качестве которого мы будем возьмем уже знакомый нам по предыдущим урокам куб. Нам также будет нужен какой-нибудь яркий объект, для того, чтобы показать, где в 3D-сцене находится источник света. Для простоты источник света мы тоже представим кубом (ведь у нас уже есть координаты вершин, неправда ли?).
Поскольку создание VBO (vertex buffer object), установка указателей атрибутов вершин и выполнение всех остальных мудрёных операций для вас теперь не должно представлять сложности, то мы не будем на этом останавливаться. Если эти темы у вас по-прежнему вызывают трудности, то, прежде чем продолжить, я рекомендую вам ознакомиться с предыдущими уроками и, по возможности, выполнить предлагаемые упражнения.
Итак, давайте начнем с вершинного шейдера для рисования контейнера. Координаты вершин контейнера остаются старыми (хотя на этот раз текстурные координаты нам не понадобятся), поэтому в коде не будет ничего нового. Мы используем «урезанную» версию вершинного шейдера из последних уроков:
Убедитесь в том, что вы обновили данные вершин и указатели атрибутов в соответствии с новым вершинным шейдером (впрочем, если вы хотите, то можете оставить в VAO буфер с текстурными координатами и активный указатель на эти атрибуты; мы просто пока не будем их использовать, но и начать всё «с чистого листа» тоже не такая уж и плохая идея).
Поскольку мы собираемся добавить в сцену куб-лампу, то для неё нам потребуется создать новый VAO. Мы могли бы изобразить лампу тем же самым VAO, трансформировав матрицу модели, но в будущих уроках мы будем довольно часто менять данные вершин и атрибутов объекта-контейнера и при этом не хотим, чтобы изменения затрагивали объект лампы (в нём нас интересует только координаты вершин лампы), поэтому давайте создадим еще один VAO:
Этот код относительно прост. Теперь, когда мы создали и контейнер, и лампу-куб, осталось сделать еще одну вещь — фрагментный шейдер:
Фрагментный шейдер через uniform-переменные получает два параметра: цвета объекта и цвета источника света. Как мы уже обсуждали в начале этого урока, для получения воспринимаемого (т.е. отражаемого объектом) цвета, мы умножаем вектор источника света на вектор цвета объекта. И снова, исходный текст шейдера не должен вызывать вопросов.
Давайте зададим объекту коралловый цвет из предыдущего раздела:
Остается заметить, что если мы начнем редактировать вершинный и фрагментный шейдеры, то куб лампы тоже измениться, а это совсем не то, чего мы хотим. В следующих уроках появятся лишние неудобства, если вычисление освещенности станет влиять на цвет лампы, поэтому мы предпочтём отделить цвет лампы от всего остального. Сделаем так, что бы лампа была константного яркого цвета, не подверженного воздействию других цветовых изменений (от этого куб лампы будет выглядеть как будто он действительно является источником света).
Чтобы этого добиться мы создадим второй набор шейдеров, который будем использовать только для рисования лампы, и тем самым обезопасим себя от любых изменений в шейдерах, вычисляющих освещение сцены. Вершинный шейдер останется без изменений, поэтому вы можете просто скопировать его исходный код в вершинный шейдер лампы. А фрагментный шейдер будет обеспечивать яркость лампы, путем задания константного белого цвета фрагментов:
Когда мы будем рисовать наш куб-контейнер (или, может быть, множество контейнеров), то воспользуемся созданным в этом уроке шейдером освещения, а когда захотим нарисовать лампу, то применим шейдеры лампы. На других уроках мы займемся постепенным улучшением шейдеров освещения, и в конце концов достигнем более реалистичных результатов.
Основное предназначение куба лампы — показать месторасположение источника света. Мы задаем где-то в сцене положение источника света, но это только координата точки, которая не имеет визуального представления. Чтобы действительно показать лампу, мы нарисуем в месте нахождения источника света куб. Рисование лампы осуществляется с помощью шейдера лампы, и это гарантирует нам, что куб лампы всегда останется белым, вне зависимости от условий освещения сцены.
Итак, давайте объявим глобальную переменную vec3, которая будет представлять положение источника света в координатах мирового пространства:
Теперь, прежде чем нарисовать лампу, сдвинем её в позицию источника света, и немного уменьшим размер куба, чтобы убедиться, что лампа не слишком доминирует в сцене:
Получившийся в результате код рисования лампы должен выглядеть примерно так:
Вставка всех приведенных выше фрагментов кода в соответствующие места, даст нам правильно настроенную OpenGL-сцену для дальнейших экспериментов с освещением. При успешной компиляции все должно выглядеть следующим образом:
Да, смотреть пока особо не на что, но обещаю, что в следующих уроках эта сцена станет гораздо более привлекательной.
Если вам сложно разобраться с тем, как все фрагменты кода сочетаются друг с другом и объединяются в готовую программу, тогда тщательно изучите исходный код и внимательно проследите всю последовательность совершаемых в нем действий.
Теперь, когда у нас достаточно знаний о цвете и есть базовая сцена для более интимного знакомства со светом, мы можем перейти к следующему уроку, в котором и начнется настоящее волшебство.
Источник