Найти тему
ZDG

OpenGL #4: Раскрашиваем вершины треугольника

Оглавление

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

Предыдущие части: Наш первый зелёный шейдер, Рисуем треугольник, Приступим, помолясь, OpenGL на пальцах

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

Всё, что нужно доделать, это

  • Сделать ещё один буфер с цветами вершин
  • Сказать вершинному шейдеру, что нужно использовать два входных буфера, а не один
  • Сказать пиксельному шейдеру, чтобы брал те данные цвета, которые передал ему вершинный шейдер.

Основная работа заключается во втором пункте. Нужно связать входные параметры шейдера с разными буферами.

Добавляем буфер

По полной аналогии с вершинами создаём массив цветов:

Каждый цвет это три RGB-компоненты. Первый будет чистый R, второй будет чистый G, и третий будет чистый B.

Затем точно так же по аналогии с вершинами создаём буфер на стороне OpenGL и перекачиваем в него наши данные о цветах:

-2

Назначение входных параметров

Все входные параметры шейдеры берут из VAO – Vertex Array Object, или вершинного массива. Код для создания VAO мы уже писали:

-3

Сюда сейчас добавилась лишь одна строчка. glEnableVertexAttribArray(0) разрешал использование входного параметра с индексом 0. А мы, добавив glEnableVertexAttribArray(1), также разрешили использование входного параметра с индексом 1. Таким образом, теперь входных параметров будет два.

Теперь самая магия, где всё происходит.

-4

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

glVertexAttribPointer() настраивает для текущего буфера индекс параметра (0) и его формат (длина элемента в байтах, тип элемента и т.д.)

Что мы добавили: то же самое, только для буфера цветов color_buffer_handle. И назначили ему индекс не 0, а 1.

Наконец-то OpenGL имеет два указателя на буферы, проиндексированные как 0 и 1, и знает их формат. То есть может скармливать эти данные шейдерам.

Переделываем шейдеры

Вершинный шейдер:

-5

Теперь он принимает на вход два параметра: координаты вершины из буфера вершин (VertexPosition) и цвет из буфера цветов (VertexColor). Цвет просто передаётся дальше в выходной параметр Color для пиксельного шейдера.

Пиксельный шейдер элементарно получает этот цвет и красит им пиксел:

-6

Попробуем запустить программу:

-7

Вот даже что-то получилось, но треугольник неправильный (вершины не там, где надо) и цвета вершин тоже неправильные (есть красный и зелёный, но нет синего).

Очевидно, в параметрах вершинного шейдера что-то напутано и он берёт данные не оттуда, откуда надо.

Ранее мы обозначили вершинные параметры индексом 0, а цветовые параметры индексом 1. С помощью следующих инструкций мы сообщаем шейдеру, какой из них какой:

-8

Перед in-параметром VertexPosition мы пишем layout (location = 0).

layout это значит "расклад", location – "позиция". То есть мы пишем что-то вроде "расклад такой: VertexPosition находится на позиции 0".

Подобный синтаксис кажется мне довольно нелепым, так как проще было бы написать, допустим,

in(0) vec3 VertexPosition

Но вероятно, я о чём-то просто не в курсе. В общем, мы сообщили шейдеру, что параметр с индексом 0 – это VertexPosition.

Аналогично мы поступаем с параметром VertexColor с индексом 1.

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

-9

Работает!

Как меняется цвет?

Мы задали только три цвета, а треугольник состоит из 100500 цветов, плавно переходящих один в другой. Кто создал все эти переходы?

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

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

Такое поведение не всегда требуется, и его можно поменять. Например, заставить OpenGL не интерполировать цвет, а отдавать его фиксированным от какой-то вершины. Эта вершина называется "провоцирующей" (provoking).

Но эти детали пока не нужны, займёмся ими когда-нибудь потом.

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

Текущая версия кода лежит на github в ветке layouts.

Читайте дальше: Оптимизации, VAO и uniform