OpenGL-Tutorial. Урок 2. Первый треугольник

OpenGL-Tutorial. Урок 2. Первый треугольник

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

Не забывайте тестировать код из статьи на регулярной основе.

Если ваша программу падает сразу после запуска, возможно вы запускаете ее из неправильной директории. Прочтите еще раз раздел про настройке Visual Studio из первого урока.

Не будем сейчас вдаваться в детали. Вам сейчас надо создать Vertex Array Object и установить его как активный:

Выполните это один раз, после создания окна (создания контекста) и перед другими вызовами функций OpenGL.

Если хотите узнать побольше об VAO — то добро пожаловать в английскую википедию под

Экранные коодинаты

Треугольник характеризуется тремя точками. Когда в трехмерной графике идет разговор о «точках» имеются ввиду вершины (вертексы, vertex). Вершина имеет 3 координаты: X, Y и Z. Вы можете представлять эти 3 координаты так:

  • X — направо.
  • Y — вверх.
  • Z — на себя (да, имеено на себя, а не от себя).
  • X — это большой палец.
  • Y — это указательный палец.
  • Z — это средний палец.

Представлять координату Z в таком ключе довольно странно. Почему же это так? Короткий ответ: потому что сотни лет существования Математики Правой Руки, дадут Вам кучу полезных инструментов. И единственным минусом будет неинтуитивный Z.

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

Так что нам понадобятся три 3D точки для того, что бы описать треугольник:

Первая вершина (-1, -1, 0). Это означает, что до тех пор, пока мы ее не трансформировали, она будет отображаться в координатах (-1, -1) на экране. Что же это значит? Ось экрана находится по центру, X — направо, как всегда, Y — вверх. Пример:

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

Отрисовка треугольника Следующим шагом будет передача треугольника в OpenGL. Мы сделаем это с помощью создания буффера:

Это надо сделать только 1 раз. Теперь в нашем основном цикле, где раньше мы ничего не отрисовывали, мы можем отрисовать треугольник:

Если вам повезет — то при запуске вы получите белый треугольник.

Но если у вас все также черный экран — то значит у вас отрисовывается черный треугольник на черном фоне. Что бы это исправить можно вызвать glClearColor и glClear перед каждой отрисовкой. Что изменит цвет фона. Либо задать цвет треугольнику. Чем мы сейчас и займемся.

Шейдеры Компиляция шейдеров

В самой простой конфигурации нам понадобится 2 шейдера: один называется «Вершинным шейдером», а другой «Фрагментным шейдером». Вершинный шейдер вызывается для каждой вершины, в то время, когда Фрагментный шейдер вызывается для каждого сэмпла. У нас используется 4х кратный antialising, а значит у нас по 4 сэмпла на пиксель.

Шейдеры программируются на языке GLSL: Graphics Library Shader Language, который является частью OpenGL. В отличии от C или Java, GLSL компилируется во время исполнения программы, что означает, что вы должны компилировать шейдеры при каждом запуске программы.

Обычно для каждого шейдера отводится отдельный файл. К примеру у нас есть SimpleFragmentShader.fragmentshader и SimpleVertexShader.vertexshader. Расширение может быть любым. Хоть .txt или .glsl.

Вот код. Не обязательно полностью понимать код, поскольку он вызывается лишь один раз в программе, так что комментариев должно быть достаточно. Так как эта функция будет использоваться во всем уроках, она будет помещена в отдельный файл common/loadShader.cpp. Заметьте, что также как и к буфферам, к шейдерам доступ осуществляется по их индексу. Реализация спрятана в драйвере.

Наш вершинный шейдер

Давайте начнем с вершинного шейдера. Первая строка скажет компилятору, что мы используем синтаксис OpenGL 3.

Вторая строка описывает входные данные:

Давайте опишем эту строку по подробнее:

  • «vec3» это трехкомпонентный вектор в GLSL. Он похож (но отличен) на glm::vec3, который мы использовали для описания треугольника. Важно помнить, что если мы используем 3 компонентный вектор — то мы должны использовать 3 компонентный вектор в GLSL.
  • «layout(location = 0)» ссылается на буффер, который будет использоваться для заполнения vertexPosition_modelspace. Каждая вершина имеет множество аттрибутов: позиция, один или несколько цветов, один или несколько текстурных координат (UV) и т.д. OpenGL не знает, что определенный атрибут — это цвет. Он просто видит трехкомпонентный вектор. Поэтому мы должны сообщить какой буффер за что отвечает. Сообщаем мы при помощи ключевого слова layout и указании индекса, который должен быть таким же, как и первый аргумент glVertexAttribPointer. Значение 0 не важно, оно может быть любым. (Но не больше, чем glGetIntegerv(GL_MAX_VERTEX_ATTRIBX, &v) ).
  • «vertexPosition_modelSpace» — название аргумента.
  • «in» — означает, что это входной аргумент. Вскоре мы познакомимся в «out».

Функция, вызываемая для каждой вершины, называется main, прямо как в C:

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

«gl_Position» одна из нескольких встроенных переменных. Вы должны передать ей какое-то значение. Все остальное — не обязательно. (Про «все остальное» мы поговорим в 4 уроке)

Наш фрагментный шейдер

Для нашего первого фрагментного шейдера мы реализуем нечто очень простое: раскраску каждого фрагмента в красный цвет. (Помните, что на каждый пиксель 4 фрагмента, поскольку мы используем 4х AA)

Да, vec3(1, 0, 0) означает красный. Дело в том, что компьютерный экраны представляют цвет, как комбинацию из Красного, Зеленого и Синего (RGB). Так что (1, 0, 0) означает самый яркий красный, нет зеленого и нет синего.

Соединение всего этого

Перед главным циклом вызываем функцию LoadShaders.

Теперь внутри главного цикла в начале очищаем экран. Функция glClearColor(0.0f, 0.0f, 0.4f, 0.0f) установит цвета фона на синий. Для очистки экрана вызывается:

А затем говорим OpenGL, какой шейдер мы используем:

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

📎📎📎📎📎📎📎📎📎📎