Glm угол между векторами

Glm угол между векторами

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

Содержание
  1. Содержание
  2. Класс CTransform3D
  3. Понимание матрицы поворотов
  4. Повороты (без кватернионов)
  5. Понимание кватернионов 3D-графике
  6. Переход к кватернионам и обратно
  7. Углы Эйлера и кватернионы
  8. Авиационно-космическое представление ориентации
  9. Пересечение луча и объектов
  10. Декомпозиция матрицы 4×4 на составляющие
  11. Линейная интерполяция чисел, векторов и кватернионов
  12. learnopengl. Урок 1.7 — Трансформации
  13. Вектора
  14. Скалярные векторные операции
  15. Обратный вектор
  16. Сложение и вычитание
  17. Длина
  18. Умножение вектора на вектор
  19. Скалярное произведение
  20. Векторное произведение
  21. Матрицы
  22. Сложение и вычитание
  23. Умножение матрицы на скаляр
  24. Умножение матриц
  25. Умножение матрицы на вектор
  26. Единичная матрица
  27. Матрица масштабирования
  28. Матрица сдвига
  29. Матрица вращения
  30. Комбинирование матриц
  31. На практике
  32. Урок №7. Трансформации в OpenGL
  33. Векторы
  34. Скалярные векторные операции
  35. Противоположный вектор
  36. Сложение и вычитание векторов
  37. Длина вектора
  38. Умножение двух векторов
  39. Скалярное умножение
  40. Векторное умножение
  41. Матрицы
  42. Сложение и вычитание матриц
  43. Умножение матрицы на скаляр
  44. Умножение матриц
  45. Матрично-векторное умножение
  46. Единичная матрица
  47. Масштабирование
  48. Трансляция вектора
  49. Поворот/Вращение
  50. Комбинирование матриц
  51. На практике
  52. Библиотека GLM

Видео:Как находить угол между векторамиСкачать

Как находить угол между векторами

Содержание

Видео:Угол между векторами | МатематикаСкачать

Угол между векторами | Математика

Класс CTransform3D

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

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

Видео:Угол между векторами. 9 класс.Скачать

Угол между векторами. 9 класс.

Понимание матрицы поворотов

Формулы вычисления произвольной матрицы поворота из угла достаточно сложны. Гораздо проще понимать матрицу поворота как объединение трёх векторов направлений: up (вверх), right (вправо) и forward (вперёд):

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

Подобное преобразование производит glm::lookAt, однако, lookAt для установки преобразования в систему координат камеры устанавливает не только вращение, но и перенос.

Видео:Угол между векторами | Геометрия 7-9 класс #100 | ИнфоурокСкачать

Угол между векторами | Геометрия 7-9 класс #100 | Инфоурок

Повороты (без кватернионов)

  • поворачивает 2D вектор на заданный угол:
  • поворачивает 3D вектор на заданный угол вокруг заданной оси (представленной орт-вектором):
  • поворачивают 3D вектор на заданный угол вокруг осей Ox, Oy, Oz соответственно:

Видео:11 класс, 5 урок, Угол между векторамиСкачать

11 класс, 5 урок, Угол между векторами

Понимание кватернионов 3D-графике

Произвольную ориентацию трёхмерной поверхности можно задать с помощью одного поворота поверхности вокруг некоторой оси вращения (англ. rotation axis) на некоторый угол вращения (англ. rotation angle):

Glm угол между векторами

Кватернионом называется четвёрка чисел, вычисляемых из угла поворота и оси вращения по следующим формулам:

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

Приведём таблицу с примерами значений кватернионов:

wxyzВращение
1000нет вращения
0100180° вокруг оси X
√0.5√0.50090° вокруг оси X
√0.5-√0.500-90° вокруг оси X

Видео:Нахождение угла между векторами через координаты. 9 класс.Скачать

Нахождение угла между векторами  через координаты. 9 класс.

Переход к кватернионам и обратно

  • функция создаёт кватернион на основе оси вращения и угла поворота:
  • с помощью оператора умножения можно применить к вектору вращение, хранимое в кватернионе:
  • функция превращает кватернион в эквивалентную матрицу поворота:

Видео:Урок 3. Произведение векторов и загадочный угол между векторами. Высшая математика | TutorOnlineСкачать

Урок 3. Произведение векторов и загадочный угол между векторами. Высшая математика | TutorOnline

Углы Эйлера и кватернионы

В трёхмерном пространстве любую трансформацию поворота можно представить в виде трёх углов Эйлера. Это возможно благодаря теореме Эйлера, согласно которой любой поворот вокруг произвольной оси можно представить как комбинацию трёх углов, последовательно поворачивающих тело вокруг базовых осей Ox, Oy, Oz.

Тройка углов Эйлера и кватернион являются двумя эквивалентными представлениями некоторой трансформации поворота. Этот факт отражён в GLM:

  • конструктор quat, принимающий vec3, формирует кватернион
  • функция glm::eulerAngles проводит обратную операцию — получение углов Эйлера из заданного кватерниона

Видео:Математика без Ху!ни. Угол между векторами, применение скалярного произведения.Скачать

Математика без Ху!ни. Угол между векторами, применение скалярного произведения.

Авиационно-космическое представление ориентации

Ориентация в авиационных терминах задаётся тремя углами:

Glm угол между векторами

Glm угол между векторами

Glm угол между векторами

Авиационные углы имеют огромное преимущество — исходные оси Ox, Oy, Oz фиксированы в мировых координатах (или в координатах диспетчерской станции аэропорта), и ориентация камеры задаётся лишь последовательным применением авиационных углов для получения вектора курса полёта (англ. heading):

  • сначала считаем курс равным орту оси Ox (т.е. вектору )
  • поворачиваем курс на угол рысканья (yaw, ψ) вокруг оси Oy
  • вычисляем нормаль к плоскости, образованной вектором курса и вектором Oy (назовём эту нормаль “right”)
  • поворачиваем курс на угол тангажа (pitch, θ) вокруг “right”
  • вычисляем нормаль к плоскости, образованной вектором курса и вектором “right” (назовём эту нормаль “up”)
  • поворачиваем вектор “up” вокруг вектора курса на угол крена (roll, ϕ)
  • теперь у нас есть вектор курса (heading, или front) и вектор направления вверх (up), и мы можем использовать уже привычную операцию lookAt для получения матрицы поворота

Данный метод проиллюстрирован на изображении:

Glm угол между векторами

Также в GLM есть функции для преобразования между yaw, pitch, roll и другими представлениями:

  • функция из заголовочного файла преобразует углы yaw, pitch, roll в матрицу поворота:
  • функция из заголовочного файла преобразует матрицу поворота в кватернион; соединив её с предыдущей функцией, получим функцию для преобразования yaw, pitch, roll в кватернион:

Видео:105. Угол между векторамиСкачать

105. Угол между векторами

Пересечение луча и объектов

В библиотеке GLM есть заголовок , который предоставляет средства для поиска пересечения между лучом и геометрическими телами:

  • функция intersectRayPlane определяет пересечение луча и плоскости
  • функция intersectRaySphere определяет пересечение луча и сферы
  • функция intersectRayTriangle определяет пересечение луча и треугольника
  • все функции имеют входные параметры, используемые для расчёта, и выходные параметры, такие как дистанция пересечения (от начала луча)
  • параметр rayDirection должен быть нормализованным вектором

Видео:100 тренировочных задач #135 Угол между векторамиСкачать

100 тренировочных задач #135 Угол между векторами

Декомпозиция матрицы 4×4 на составляющие

В GLM есть расширение, позволяющее провести разделение матрицы на базовые аффинные и неаффинные преобразования. Подробнее об этом рассказано:

Расширение подключается заголовком #include . Использовать его можно следующим образом:

В некоторых версиях GLM в выходной параметр rotation записывается сопряжённый кватернион ориентации тела вместо ожидаемого значения. Исправить эту проблему можно получением кватерниона, сопряжённого сопряжённому, что даст нам исходный кватернион.

Видео:18+ Математика без Ху!ни. Скалярное произведение векторов. Угол между векторами.Скачать

18+ Математика без Ху!ни. Скалярное произведение векторов. Угол между векторами.

Линейная интерполяция чисел, векторов и кватернионов

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

Представьте, как зелёная точка движется между красной и синей; математически это движение описывается изменением весового коэффициента от 0 до 1:

Glm угол между векторами

Функция glm::lerp выполняет линейную интерполяцию. Она перегружена для скаляров, для векторов из 2-4 значений и для кватернионов.

Видео:9 класс, 17 урок, Угол между векторамиСкачать

9 класс, 17 урок, Угол между векторами

learnopengl. Урок 1.7 — Трансформации

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

Часть 2. Базовое освещение

Часть 3. Загрузка 3D-моделей

Часть 4. Продвинутые возможности OpenGL

Часть 5. Продвинутое освещение

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

Как бы то ни было, для полного понимания трансформаций мы, во первых, должны разобраться с векторами. Основная задача этой главы — дать вам основные математические знания, которые нам понадобятся позже.

Видео:№1039. Диагонали квадрата ABCD пересекаются в точке О. Найдите угол между векторами: а) АВ и АССкачать

№1039. Диагонали квадрата ABCD пересекаются в точке О. Найдите угол между векторами: а) АВ и АС

Вектора

В самом простом определении, вектора — это не более чем направления. У вектора может быть направление и магнитуда (также иногда называется модулем или длиной). Вы можете представлять себе вектора в качестве направлений на карте сокровищ: “Сделайте 10 шагов налево, теперь 3 шага на север и теперь 5 шагов направо”. В данном примере “налево” — это направление, а “10 шагов” — это длина вектора. Направления на этой карте сокровищ составляются из 3 векторов. Вектора могут иметь любую размерность, но чаще всего используются двухкомпонентные и четырехкомпонентные вектора. Если вектор двухкомпонентный, то он описывает направление на плоскости (или на 2D графике), если вектор трехкомпонентный, то он описывает направление в трехмерном мире.

Ниже вы можете видеть 3 вектора, каждый из которых представлен в виде (x, y) в качестве стрелок на 2D графике. Поскольку более интуитивно представлять вектора в 2D (чем в 3D), то вы можете думать о 2D векторах, как о 3D векторах, но с нулевой z координатой. До тех пор, пока вектор описывает направление — позиция вектора не меняет его значения. На графике можно увидеть, что вектора v и w одинаковы, хотя из позиции отличаются:

Glm угол между векторами

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

Glm угол между векторами

Поскольку вектора зачастую описывают направление — то иногда их тяжело представить в виде позиции. Обычно мы визуализируем вектор следующим образом: мы устанавливаем центр в (0, 0, 0), а затем указываем направление, описанное точкой. Таким образом получается позиционный вектор (также мы можем взять за центр другую точку, а потом сказать “Этот вектор указывает на точку в пространстве из этой точки”). Позиционный вектор (3, 5) будет указывать на точку (3, 5) на графе с основанием (0, 0). С помощью векторов мы можем описывать как направления так и позиции в двухмерном и трехмерном пространствах.

Также мы можем производить над векторами некоторые математические действия.

Скалярные векторные операции

Скаляр — это одно число (или однокомпонентный вектор, если вы хотите продолжать работать с векторами). Во время сложения/вычитания/умножения или деления вектора на скаляр мы просто складываем/вычитаем/умножаем или делим каждый элемент вектора на этот скаляр. Пример:

Glm угол между векторами

Где вместо сложения может быть вычитание, умножение или деление.

Обратный вектор

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

Glm угол между векторами

Сложение и вычитание

Сложение двух векторов производится покомпонентно. Пример:

Glm угол между векторами

Визуально сумма векторов v=(4,2) и k=(1,2) выглядит так:

Glm угол между векторами

Также как и с обычным сложением и вычитанием, вычитание векторов — это тоже сложение, но с обратным вторым вектором:

Glm угол между векторами

Вычитание векторов друг из друга порождают вектор, который является разницей в позициях операндов:

Glm угол между векторами

Длина

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

Glm угол между векторами

Поскольку длина сторон (x, y) известна, и мы хотим узнать длину гипотенузы — то мы делаем это следующим образом:

Glm угол между векторами

Где ||v|| — это длина вектора v. Такая формула легко расширяется в 3D добалением z^2. Пример расчета длины:

Glm угол между векторами

Вычисленное значение: 4.47

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

Glm угол между векторами

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

Видео:Задание 3 ЕГЭ профиль #121Скачать

Задание 3 ЕГЭ профиль #121

Умножение вектора на вектор

Умножение двух векторов выполняется довольно странно. Нормальное умножение не применимо, поскольку оно не имеет визуального смысла, но у нас есть 2 специфических подхода, из которых можно выбирать во время умножения: первый — скалярное произведение, которое изображается как точка, а второе — векторное произведение, которое изображается как крест.

Скалярное произведение

Скалярное произведение двух векторов эквивалентно скалярному произведению длин этих векторов, умноженное на косинус угла между ними. Если это предложение сбило вас с толку, то посмотрите на формулу:

Glm угол между векторами

Где угол между векторами описан как тета. Почему это может быть интересно? Что же, представим если вектора v и k являются единичными векторами. Соответственно формула сокращается до:

Glm угол между векторами

Теперь скалярное произведение определяет только угол между двумя векторами. Вы возможно помните, что функция cos становится 0, с углом в 90 градусов ну и 1 с углом 0. Это позволяет легко проверять ортогональны ли вектора или параллельны друг другу (ортогональность означает, что вектора прямоугольны). Если хотите узнать больше про sin или cosine, то рекомендую видео Khan Academy про базовую тригонометрию.

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

Так как же считать скалярное произведение? Скалярное произведение — это умножение компонентов векторов и последующее сложение результатов. Пример:

Glm угол между векторами

Для вычисления угла между векторами нам потребуется обратить функцию косинуса (cos^-1) в данном случае — это 143.1 градуса. Таким образом мы эффективно вычислили угол между этими двумя векторами. Скалярное произведение очень полезно во время работы со светом.

Векторное произведение

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

Glm угол между векторами

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

Glm угол между векторами

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

Видео:Угол между векторамиСкачать

Угол между векторами

Матрицы

Теперь, после того как мы обсудили почти все на счет векторов, настало время углубиться в матрицы. Матрица, обычно, это четырехугольних из набора чисел, символов и/или выражений. Вот пример матрицы 2х3:

Glm угол между векторами

Доступ к элементам матрицы осуществляется с помощью (i,j), где i — это строка, а j — это столбец. Вот почему матрица выше называется 2х3 (3 столбца и 2 строки). Такая система — обратна той, что используется в 2D графах (x, y). Для получения значения 4 из матрицы выше, мы должны указать индекс (2, 1) (вторая строка, первый столбец).

Матрицы, по факту, ничего более чем четырехугольные массивы математических выражений. Они также обладают очень приятным набором математических свойств и, также как и вектора, имеют несколько операций — сложение, вычитание и умножение.

Сложение и вычитание

Сложение матрицы со скаляром выполняется следующим образом:

Glm угол между векторами

Скаляр просто прибавляется во всем элементам матрицы. Тоже самое происходит и при вычитании:

Glm угол между векторами

Сложение и вычитание между двумя матрицами выполняется поэлементно. Таким образом операции сложения и вычитания могут быть применены только к матрицам одинакового размера. Пример:

Glm угол между векторами

Тоже самое, только с вычитанием:

Glm угол между векторами

Умножение матрицы на скаляр

Также как сложение и вычитание, умножение матрицы на скаляр производится умножением каждого элемента матрицы на скаляр. Пример:

Glm угол между векторами

Видео:Угол между векторамиСкачать

Угол между векторами

Умножение матриц

Умножение матриц не очень сложное, но и не такое простое. Умножение имеет несколько ограничений:

  1. Вы можете умножать только матрицы, где число столбцов первой совпадает с числом строк второй матрицы.
  2. Умножение матриц не коммутативно. A * B != B * A.

Вот пример умножения двух матриц 2х2:

Glm угол между векторами

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

Glm угол между векторами

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

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

В результате получается матрица размером (n, m), где n — количество строк в левой матрице, а m — количество столбцов в правой матрице.

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

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

Glm угол между векторами

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

Умножение матрицы на вектор

Мы уже использовали вектора в прошлых уроках. Мы использовали их, чтобы представлять позиции, цвета и текстурные координаты. Теперь давайте немного углубимся в кроличью нору и расскажем, что вектор — это на самом деле просто Nx1 матрица, где N — это количество компонентов вектора. Если вы чуть подумаете об этом — это имеет смысл. Вектора, прямо как матрицы — массив чисел, но только с 1 колонкой. И как же нам поможет эта информация? Что же, если у нас есть матрица MxN мы сможем ее умножить на Nx1 вектор, так как количество столбцов матрицы равно количеству строк вектора.

Но зачем нам вообще уметь умножать матрицу на вектор? Довольно много различных 3D/2D трансформаций можно выполнить, умножая матрицу на вектор, получая измененный вектор. Если вы все еще не уверены в том, что полностью понимаете текст выше, то вот немного примеров:

Видео:Геометрия 9 класс (Урок№18 - Угол между векторами. Скалярное произведение векторов.)Скачать

Геометрия 9 класс (Урок№18 - Угол между векторами. Скалярное произведение векторов.)

Единичная матрица

В OpenGL обычно работают с матрицами трансформации размерами 4х4 по той причине, что большинство векторов имеет 4 компонента. Самая простая матрица трансформации которую можно обсудить — это единичная матрица. Единичная матрица — это NxN матрица, заполненная нулями, но с 1 по диагонали. Как мы можете заметить эта матрица совершенно не изменяет вектор:

Glm угол между векторами

Вектор выглядит нетронутым. Это становится очевидно из правил умножения: первый результирующий элемент — это каждый элемент первой строки матрицы, умноженные на каждый элемент вектора. Поскольку каждый элемент строки равен 0, кроме первого — то мы получаем 1 * 1 + 0 * 2 + 0 * 3 + 0 * 4 = 1. Тоже самое применяется и к остальным 3 элементам вектора.

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

Видео:найти угол между единичными векторамиСкачать

найти угол между единичными векторами

Матрица масштабирования

Когда мы масштабируем вектор — мы увеличиваем длину стрелки на величину масштабирования, сохраняя направление. Пока мы работаем в 2 или 3 размерностях мы можем определить масштабирование вектором из 2 или 3 величин, каждая из которых масштабирует одну из осей (x, y или z).

Давайте попробуем масштабировать вектор v = (3,2). Мы будем масштабировать вектор по оси x на 0.5, что сделает его в 2 раза уже; и масштабируем вектор по оси y на 2, что увеличит высоту в 2 раза. Давайте посмотрим как будет выглядеть если мы масштабируем вектор на (0.5, 2). Запишем результат в виде s.

Glm угол между векторами

Помните, что OpenGL зачастую работает в 3D пространстве, соответственно для 2D можно оставить Z координату, равную 1. Операция масштабирования, которую мы только что выполнили, является неоднородной, поскольку величина масштабирования для каждой оси различается. Если бы величина масштабирования была бы одинаковой — то такое преобразование называется однородным.

Давайте построим матрицу трансформации которая выполнит для нас масштабирование. Мы уже увидели на единичной матрице, что диагональный элемент будет умножен на соответствующий элемент вектора. Что если мы заменим единицы в единичной матрице на тройки? В таком случае мы умножим все элементы вектора на это значение. Соответственно если мы представим величины масштабирования как (S1, S2, S3) то мы сможем определить матрицу масштабирования для любого вектора (x, y, z):

Glm угол между векторами

Заметьте, что 4 элемент вектора равняется 1. Этот компонент обозначается как w и будет потом использован для других задач.

Матрица сдвига

Сдвиг — это процесс добавления одного вектора к другому для получения нового вектора с другой позицией, то-есть сдвиг вектора на основании вектора сдвига. Мы уже обсуждали сложение векторов, поэтому для вас это не будет чем-то новым.
Также как и с матрицей масштабирования в матрице 4х4 есть несколько позиций для выполнения требуемых операций, для сдвига — это верхние 3 элемента четвертой колонки. Если мы представим вектор сдвига как (Tx, Ty, Tz) — то мы можем определить матрицу сдвига следующим образом:

Glm угол между векторами

Это работает, потому что все значения вектора умножаются на w компонент вектора и складываются с начальным значениями. Это было бы невозможно при использовании матриц 3х3.

Гомогенные координаты
Компонента вектора w также называется гомогенной координатой. Для получения 3D вектора из гомогенной координаты мы делим x, y и z координаты на w. Обычно этого не замечают, так как w большую часть времени равна 1.0. Использование гомогенных координат имеет несколько преимуществ: они позволяют нам выполнять сдвиги на 3D векторах (без w компоненты это было бы невозможно) и в следующей главе мы используем значение w для создания 3D визуализаций.
Также когда гомогенная координата равна 0 — то вектор считается вектором направления, так как вектор с компонентой w равной 0 не может быть сдвинут.

С матрицей сдвига мы можем двигать объекты по всем 3 направлениям (x, y, z), что делает эту матрицу крайне полезной для наших задач.

Матрица вращения

Последние пару трансформаций были довольно просты для понимания и представления в 2D или 3D пространстве, но вращения немного более заковыристые. Если вы хотите узнать как же именно эти матрицы формируются — то я рекомендую видео Khan Academy про линейную алгебру.

Для начала давайте определим что вообще такое — вращение вектора. Вращение в 2D и 3D определяется углом. Угол может выражаться в углах или в радианах, в которых полный оборот — это 360 градусов или 2Pi соответственно. Я предпочитаю работать с градусами, поскольку они более логичны для меня.

Большинство вращательных функций требует угол в радианах, но благо преобразование из одной системы в другую выполнить довольно просто:
Градусы = радианы * (180.0f / PI)
Радианы = градусы * (PI / 180.0f)
Где PI примерно 3.14159265359

Вращение на половину круга — требует от нас вращения на 360/2 = 180 градусов. Вращение на 1/5 направо требует от нас вращение на 360/5 = 72 градуса направо. Вот пример обычного 2D вектора, где v повернут на 72 градуса направо от k.

Glm угол между векторами

Вращение в 3D описывается углом и осью вращения. Угол определяет то насколько вектор будет повернут относительно данной оси. При вращении 2D векторов в 3D мире, к примеру, мы установим ось вращения — Z.

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

Матрица вращения определена для каждой оси в 3D пространстве, где угол показан как тета.
Матрица вращения вокруг оси X:

Glm угол между векторами

Матрица вращения вокруг оси Y:

Glm угол между векторами

Матрица вращения вокруг оси Z:

Glm угол между векторами

С помощью матриц вращения мы можем вращать наши вектора по одной из трех осей. Также можно совмещать их, например в начале повернуть по X оси, а потом по Y. Правда такой подход быстро приведет к проблеме, называемый проблемой шарнирного замка (Gimbal Lock). Мы не будем вдаваться в детали, но лучше использовать вращение по конкретной оси, например (0.662, 0.2, 0.722) (заметьте, что это единичный вектор), вместо того, чтобы совмещать вращения по конкретным осям. Матрица для таких преобразований существует и выглядит она следующим образом, где (Rx, Ry, Rz) — это ось вращения:

Glm угол между векторами

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

Комбинирование матриц

Для того, чтобы достичь максимальной полезности использования матриц для трансформаций мы должны комбинировать матрицы трансформации в одну матрицу. Давайте посмотрим, сможем ли мы сгенерировать матрицу трансформации, которая будет в себя включать несколько трансформаций. Например у нас есть вектор (x, y, z) и мы хотим масштабировать его в 2 раза и сдвинуть на (1, 2, 3). Для этого нам потребуются матрицы масштабирования и смещения. В результате мы получим что-то вроде:

Glm угол между векторами

Заметьте, что во время умножения матриц мы в начале выполняем сдвиг, а потом масштабирование. Умножение матриц не коммутативно, что означает, что порядок умножения важен. Во время умножения матриц правая матрица умножается на вектор, поэтому вам надо читать умножения справа налево. Рекомендуется в начале масштабировать, затем вращать и в конце сдвигать, во время объединения матриц, в ином случае они могут отрицать друг-друга. Например если вы в начале выполните сдвиг, а затем масштабирование, то матрица сдвига тоже будет масштабировать!

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

Glm угол между векторами

Отлично, вектор масштабирован в 2 раза и смещен на (1, 2, 3).

Видео:Угол между векторами. Уроки 11. Геометрия 9 классСкачать

Угол между векторами. Уроки 11. Геометрия 9 класс

На практике

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

GLM это аббревиатура от OpenGL Mathematics. Эта библиотека является заголовочной, что означает, что нам достаточно подключить требуемые заголовочные файлы. Не нужно заморачиваться ни с линковкой ни с компиляцией. GLM можно скачать с официального сайта. Скопируйте корневую директорию с заголовочными файлами в вашу папку includes и можно начинать.

Большая часть функциональности GLM можно найти в 3 заголовочных файлах:

Давайте посмотрим, сможем ли мы применить наши знания в преобразованиях для сдвига вектора (1, 0, 0) на (1, 1, 0) (заметьте, что мы обозначили из как glm::vec4 с гомогенной координатой равной 1.0):

В начале мы создали вектор названный vec с помощью встроенного в GLM векторного класса. Затем мы определяем mat4, которая является единичной матрицей 4х4. Затем мы создаем матрицу трансформации, передавая нашу единичную матрицу в функцию glm::translate, вместе с вектором сдвига.
Затем мы умножаем наш вектор на матрицу трансформации и выводим результат. Если вы все еще помните как работает матрица сдвига — то вы понимаете, что результирующий вектор должен быть (1+1, 0+1, 0+0), который равен (2, 1, 0). Код выше выводит 210, что означает, что матрица сдвига сделала свою работу.

Давайте попробуем сделать нечто более интересное и попробуем масштабировать, а затем повернуть объект из прошлого урока. В начале мы повернем контейнер на 90 градусов против часовой стрелки. Затем масштабируем его на 0.5 для того, чтобы уменьшить его в 2 раза. Давайте построим матрицу трансформации для этого.

В начале мы уменьшаем контейнер на 0.5, по каждой оси, а затем поворачиваем контейнер на 90 градусов по Z координате. Заметьте, что текстура также повернулась. Поскольку мы передаем матрицу в каждую из GLM функций, GLM автоматически перемножает матрицы, в результате получая матрицу трансформации.

Некоторые версии GLM принимают углы в радианах, а не в градусах. Если у вас такая версия — то преобразуйте градусы в радианы с помощью glm::radians(90.0f).

Следующий большой вопрос — это как передать матрицу трансформации в шейдер? Ранее мы уже говорили, что GLSL имеет тип mat4. Так что нам осталось принять mat4 в качестве uniform переменной и умножить вектор позиции на эту матрицу.

В GLSL также имеются типы mat2 и mat3, которые предоставляют такие же операции, что и вектора. Все затронутые в этой статье операции доступны в матричных типах.

Мы добавили uniform и умножили позиционный вектор на трансформационную матрицу перед тем как передать ее в gl_Position. Наш контейнер теперь должен стать меньше в 2 раза и повернуться на 90 градусов. Но нам все еще надо передать трансформационную матрицу в шейдер?

В начале мы получаем позицию uniform переменной и затем отправляем в нее данные матрицы с помощью функции glUniform с постфиксом Matrix4fv. Первый аргумент должен быть позицией переменной. Второй аргумент сообщает OpenGL сколько матриц мы собираемся отправлять, в нашем случае 1. Третий аргумент говорит требуется ли транспонировать матрицу. OpenGL разработчики часто используют внутренних матричный формат, называемый column-major ordering, который используется в GLM по умолчанию, поэтому нам не требуется транспонировать матрицы, мы можем оставить GL_FALSE. Последний параметр — это, собственно, данные, но GLM не хранит данные точно так как OpenGL хочет их видеть, поэтому мы преобразовываем их с помощью value_ptr.

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

Glm угол между векторами

Отлично! Наш контейнер действительно повернут налево и стал в 2 раза меньше, так что трансформация прошла успешно. А теперь давайте заставим вращаться наш контейнер в реальном времени, а также передвинем его в нижний правый угол. Для того, чтобы это сделать придется производить вычисления при каждой итерации основного цикла. Мы используем функцию GLFW для получения времени, чтобы менять угол со временем:

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

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

Если вы все сделали правильно — то вы получите что-то вроде этого:

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

Если вам не удалось получить правильный результат или вы где-то застряли — то взгляните на исходный код вместе с вершинным и фрагментным шейдерами.

В следующем уроке мы обсудим как использовать матрицы для определения различных координатных пространств для наших вершин. Это будет новым шагом в мир 3D графики в реальном времени!

Видео:Найти угол между векторами и площадь параллелограмма, построенного на этих векторахСкачать

Найти угол между векторами и площадь параллелограмма, построенного на этих векторах

Урок №7. Трансформации в OpenGL

Обновл. 9 Сен 2021 |

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

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

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

Векторы

Векторы — это объекты, задающие направления. Основными характеристиками вектора являются направление и его длина. Вы можете представить себе векторы, как направления на карте сокровищ: «идите влево 10 шагов, теперь идите на север 3 шага и затем направо 5 шагов»; здесь «влево» — это направление, а «10 шагов» — это длина вектора. Таким образом, направления для карты сокровищ содержат 3 вектора. Векторы могут иметь любую размерность, но мы обычно имеем дело с размерностями от 2 до 4. Если вектор имеет 2 измерения, то он представляет собой направление на плоскости (вспомните школьные графики), а когда он имеет 3 измерения, то уже может задавать любое направление в трехмерном пространстве.

Ниже изображены 3 вектора, каждый из которых представлен парой чисел вида (x,y) и изображен на 2D-графике в виде стрелки. Поскольку для простоты восприятия векторов их изображают на плоскости (в 2D-пространстве, а не в 3D-пространстве), то мы можем рассматривать 2D-векторы как 3D-векторы, у которых третья координата z равна 0 . Еще одна особенность заключается в том, что перенос вектора в другую точку не изменяет его величины, так как векторы представляют собой направления. На следующем графике мы видим, что векторы v и w равны, хотя точки, откуда они берут свое начало, различны:

Glm угол между векторами

При описании векторов математики обычно предпочитают обозначать их буквой с небольшой горизонтальной чёрточкой сверху, например, v . А также, при использовании векторов в формулах, их обычно записывают в следующем виде:

Glm угол между векторами

Поскольку векторы задаются как направления на точки (или другие объекты) в пространстве, иногда бывает трудно наглядно изобразить местоположение данных точек. В таком случае можно поступить следующим образом: просто представьте себе вектор с началом в точке (0,0,0) , указывающий направление на некоторую заданную точку. Такой вектор принято называть радиус-вектором (или «вектором позиции»). При этом никто нам не мешает, например, взять другой вектор, начало которого расположено в точке с координатами, отличными от (0,0,0) , перенести его (не меняя направления) в точку (0,0,0) и затем сказать: «Этот вектор, например, (3,5) с началом в точке (0,0) указывает на точку на графике с координатами (3,5) «. Таким образом, используя векторы, мы можем описывать направления и положения в 2D- и 3D-пространстве.

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

Скалярные векторные операции

Скаляр (от лат. «scalaris») — это самое обычное математическое число, которое имеет только свое численное значение. При сложении/вычитании/умножении или делении вектора на скаляр мы просто складываем/вычитаем/умножаем или делим каждую координату вектора на этот скаляр. Для сложения это будет выглядеть следующим образом:

Glm угол между векторами

Противоположный вектор

Умножение вектора на -1 даст нам противоположно направленный вектор. Например, вектор, указывающий на северо-восток, после своего умножения на -1 , будет указывать на юго-запад. Выполнить данную операцию очень легко, достаточно каждую координату вектора умножить на -1 :

Glm угол между векторами

Сложение и вычитание векторов

Сложение двух векторов определяется как покоординатное сложение, то есть каждая координата одного вектора добавляется к соответствующей координате другого вектора (первая — к первой, вторая — ко второй, третья — к третьей):

Glm угол между векторами

Наглядно можно это себе представить следующим образом (правило треугольника): пусть у нас даны вектор v = (4,2) и вектор k = (1,2) , мы прикладываем начало вектора k к концу вектора v . А дальше просто соединяем начало вектора v с концом вектора k . В результате этого мы получаем третий вектор v + k с координатами (5,4) , который является суммой векторов v и k :

Glm угол между векторамиТак же, как и со стандартным сложением и вычитанием, векторное вычитание — это то же самое, что и сложение с противоположным вторым вектором:

Glm угол между векторами

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

Длина вектора

Чтобы получить длину/величину вектора, необходимо воспользоваться теоремой Пифагора, которую вы, возможно, помните из школьных уроков математики. Ведь если изобразить вектор на координатной плоскости, то можно заметить прямоугольный треугольник, у которого длина одной стороны равна значению x -компоненты, а длина другой стороны — y -компоненты вектора:

Glm угол между векторами

А поскольку длина двух сторон — (x,y) , и мы хотим знать длину наклоненной стороны, то можно вычислить её, используя теорему Пифагора:

Glm угол между векторами

Где символом || v || обозначается длина вектора v . Это легко трансформировать в 3D, просто добавив z 2 в уравнение.

Итак, в нашем случае, длина вектора с координатами (4,2) равна:

Glm угол между векторами

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

Glm угол между векторами

Описываемый процесс называется нормализацией вектора. Единичные векторы обозначаются буквой с «домиком на голове», и с ними, как правило, легче работать, особенно в те моменты, когда нас интересует только их направление (замечу, что направление вектора не меняется, если мы изменяем длину вектора).

Умножение двух векторов

А вот с умножением двух векторов дела обстоят несколько хуже, т.к. для обычного умножения векторов не существует наглядного представления. Но при этом есть два конкретных случая, которые мы могли бы использовать для операции умножения, заданной для векторов: первый случай — это скалярное (точечное) произведение векторов, обозначаемое как v · k , а второй — это векторное произведение векторов, обозначаемое как v × k .

Скалярное умножение

Скалярное произведение двух векторов равно произведению их длин, умноженному на косинус угла между ними. Если это звучит запутанно, то взгляните на его формулу:

Glm угол между векторами

Где θ (тета) — это угол между векторами. Почему это произведение так важно? Ну, представьте себе, что если v и k — это единичные векторы, то их длина будет равна 1 . А значит, исходная формула сокращается до вида:

Glm угол между векторами

И теперь скалярное произведение зависит только от угла между векторами. Благодаря этому легко определить, являются ли два вектора параллельными (находятся под углом 0 или 180 градусов) или ортогональными (находятся под углом 90 градусов) друг к другу. Возможно, вы помните, что функция косинус (или cos ) равна 0 , когда угол θ равен 90 градусам, и равна 1 или -1 , когда угол θ равен 0 или 180 градусам, соответственно.

На случай, если вы хотите узнать больше о функциях sin или cos , я бы предложил вам посмотреть видеоролики Академии Хана об основах тригонометрии.

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

Итак, как же вычисляется скалярное произведение векторов? Скалярное произведение векторов — это покоординатное умножение, при котором мы складываем результаты умножения. Рассмотрим пример с двумя единичными векторами (вы сами можете проверить, что их длины равны 1 ):

Glm угол между векторами

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

Векторное умножение

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

Glm угол между векторами

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

Glm угол между векторами

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

Матрицы

Теперь, когда мы обсудили почти всё, что касается векторов, пришло время войти в матрицу! Матрица — это прямоугольный массив чисел, символов и/или математических выражений. Каждое отдельно взятое число в матрице называется элементом матрицы. Пример матрицы 2×3 показан ниже:

Glm угол между векторами

Обозначение матрицы очень похоже на обозначение вектора. В общем случае оно имеет следующий вид: (i, j) , где индексы i — это количество строк в матрице, а j — количество столбцов. Поэтому вышеприведенная матрица называется матрицей размера 2×3 (2 строки и 3 столбца). Заметьте, что это является противоположностью тому, к чему мы привыкли, обозначая на координатной плоскости точки записью (x,y) , где x — это координата вдоль горизонтальной оси (можно сказать, вдоль строки), а y — координата вдоль вертикальной оси (можно сказать, вдоль столбца).

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

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

Сложение и вычитание матриц

Сложение и вычитание между двумя матрицами производится поэлементно. Таким образом, применяются те же самые общие правила, которые мы знаем для обычных чисел, но выполненные на элементах обеих матриц, одинаковой размерности. Это означает, что матрица размера 3×2 и матрица размера 2×3 (или матрица 3×3 и матрица 4×4) не могут суммироваться или вычитаться друг из друга. Давайте посмотрим, как работает сложение матриц на двух квадратных матрицах размера 2×2:

Glm угол между векторами

Те же правила применяются и для вычитания матриц:

Glm угол между векторами

Умножение матрицы на скаляр

Матрично-скалярное произведение выполняется путем умножения каждого элемента матрицы на заданный скаляр. Следующий пример иллюстрирует данную операцию:

Glm угол между векторами

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

Умножение матриц

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

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

Матричное умножение не является коммутативным, то есть A·B≠B·A .

Давайте начнем с примера умножения двух квадратных матриц размера 2×2:

Glm угол между векторами

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

Glm угол между векторами

Сначала мы берем верхнюю строку левой матрицы, а затем берем столбец из правой матрицы. Выбранные нами строка и столбец определяют, значение какого элемента результирующей матрицы 2×2 мы вычисляем. Если мы возьмем первую строку левой матрицы, то вычисляемое значение окажется в первой строке результирующей матрицы, затем мы выбираем столбец, и если это первый столбец, то вычисляемое значение окажется в первом столбце результирующей матрицы. Именно так и обстоят дела с объектами в красной обводке. Для вычисления нижнего правого элемента мы берем нижнюю строку первой матрицы и самый правый столбец второй матрицы.

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

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

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

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

Glm угол между векторами

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

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

Матрично-векторное умножение

До сих пор в примерах по программированию мы, по большей части, имели дело с векторами. Использовали их для представления позиций, цветов и даже текстурных координат. Давайте пройдем немного дальше по кроличьей норе, и я скажу вам, что вектор — это матрица размера N×1 , где N — это число компонентов вектора (также его еще называют «N-мерный вектор»). Если вдуматься, то в этом понятии кроется большой смысл. Векторы — это массивы чисел, но только с 1 столбцом. Итак, каким образом нам может помочь данная информация? Ну, если у нас есть матрица размера M×N , то мы можем умножить эту матрицу на наш вектор размера N×1 , так как столбцы матрицы равны числу строк вектора. Таким образом, операция умножения матрицы является допустимой.

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

Единичная матрица

В OpenGL мы работаем с матрицами преобразований размером 4×4 по нескольким причинам, и одна из этих причин заключается в том, что большинство векторов имеют размер равный 4. Самая простая матрица преобразования, которую мы можем придумать — это единичная матрица. Единичная матрица — это квадратная матрица размера N×N , у которой элементы, стоящие на диагонали от верхнего-левого угла до нижнего-правого, равны 1 (такую диагональ принято называть главной диагональю), а остальные — равны 0 . Как мы видим ниже, эта матрица преобразования оставляет вектор совершенно нетронутым:

Glm угол между векторами

Это становится очевидным из правил умножения: первый элемент результирующей матрицы получается путем суммирования произведения каждого отдельно взятого элемента первой строки матрицы на каждый отдельно взятый элемент вектора. Поскольку каждый из элементов строки равен 0 , кроме первого, то мы получаем: 1 ⋅1+ 0 ⋅2+ 0 ⋅3+ 0 ⋅4=1 , и то же самое относится к остальным трем элементам вектора.

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

Масштабирование

При масштабировании вектора мы увеличиваем длину стрелки на некоторую заданную величину, сохраняя неизменным её направление. Поскольку мы работаем либо в двух, либо в трех измерениях, то можем определить масштабирование в виде вектора из 2 или 3 переменных масштабирования, каждая из которых масштабирует (уменьшает или увеличивает) отдельно взятую координату оси ( x , y или z ).

Попробуем масштабировать вектор v = (3,2) . Мы будем масштабировать вектор вдоль оси x на 0,5 , таким образом, делая его координату по оси x в два раза меньше; и мы будем масштабировать вектор на 2 вдоль оси y , делая его координату по оси y вдвое больше. Давайте посмотрим, как выглядит результат масштабирования (вектор s ) вектора v на (0.5, 2) :

Glm угол между векторами

Имейте в виду, что OpenGL обычно работает в трехмерном пространстве, поэтому для этого 2D-случая мы могли бы установить масштаб оси z равным 1 , оставив его нетронутым. Операция масштабирования, которую мы только что выполнили, представляет собой пример неоднородного (англ. «non-uniform») масштаба, поскольку коэффициент масштабирования для каждой из осей был разным. Если бы по всем осям он был один и тот же, то его можно было бы назвать однородным (англ. «uniform») масштабом.

Давайте построим матрицу преобразования, которая выполнит для нас операцию масштабирования. Из раздела с описанием единичной матрицы мы узнали, что каждый из диагональных элементов умножается на соответствующую ему координату вектора. А что, если мы изменим число 1 в единичной матрице на число 3 ? В этом случае, мы будем умножать каждую из координат вектора на число 3 и, таким образом, однородно масштабируем вектор на 3 . Если мы представим масштабирующие переменные как набор ( S1 , S2 , S3 ) , то можно определить матрицу масштабирования для любого вектора (x, y, z) следующим образом:

Glm угол между векторами

Обратите внимание, что мы сохраняем 4-й элемент масштабирования равным 1 . Координата w , как мы увидим позже, используется для других целей.

Трансляция вектора

Трансляция (от лат. «translatio») вектора выполняется очень просто: мы берем первый вектор (который собираемся переносить), к его концу добавляем вектор перемещения (вдоль которого будем выполнять перемещение) и получаем третий вектор, но с другой позицией. Мы уже обсуждали векторное сложение, так что это не должно быть чем-то новым для вас.

Как и в случае с масштабированием, в матрице 4×4 есть несколько элементов, местоположения которых мы можем использовать для выполнения определенных операций и для трансляции — это 3 верхних значения 4-го столбца. Если мы представим вектор перемещения как ( Tx , Ty , Tz ) , то сможем определить матрицу перемещения следующим образом:

Glm угол между векторами

Данный пример работает потому, что все транслируемые значения умножаются на w -столбец вектора перемещения и добавляются к значениям исходного вектора (помните правила умножения матриц). Это было бы невозможно сделать с матрицей 3×3.

Примечание об однородных координатах: Координата w выбранного нами вектора также именуется однородной координатой. Чтобы получить трехмерный вектор из однородного вектора, мы делим координаты x , y и z на его w -координату. Обычно мы этого не замечаем, так как координата w большую часть времени равна 1.0 . Использование однородных координат имеет ряд преимуществ: они позволяют нам выполнять матричные трансляции на 3D-векторах (без координаты w мы не можем транслировать векторы), и на следующем уроке мы будем использовать значение w для создания 3D-перспективы. Кроме того, вектор, у которого однородная координата равна 0 , называется вектором направления, так как вектор с координатой w=0 не может быть транслирован.

С помощью матрицы перемещения мы можем переносить объекты в любом из трех осевых направлений ( x , y , z ), что делает её очень полезной матрицей преобразования для нашего инструментария преобразования.

Поворот/Вращение

Последние несколько преобразований относительно легко можно было понять и представить в 2D- или 3D-пространстве, но вращения в этом плане выглядят несколько сложнее. Если вы хотите точно знать, как строятся матрицы вращения, то я рекомендую вам посмотреть видео по линейной алгебре в Академии Хана.

Сначала давайте определим, чем фактически является вращение вектора. Поворот в 2D- или 3D-пространстве представлен углом поворота. Угол может задаваться как в градусах, так и в радианах, при этом вся окружность составляет 360 градусов или 2*Pi радиан. Я предпочитаю объяснять вращения, используя градусную меру, поскольку для нас она более привычная.

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

угол в градусах = угол в радианах * (180/Pi)
угол в радианах = угол в градусах * (Pi/180)

Где число Pi равно (округлено) 3.14159265359 .

Выполняя оборот на половину окружности, мы поворачиваемся на 360/2 = 180 градусов, а поворачиваясь на 1/5 вправо, мы выполняем поворот на 360/5 = 72 градуса вправо. Ниже показан пример для базисного 2D-вектора, где вектор v повернут на 72 градуса вправо (или по часовой стрелке от вектора k ):

Glm угол между векторами

Вращения в 3D-пространстве задаются с помощью угла поворота и оси вращения. Задаваемый угол будет вращать объект вдоль заданной оси вращения. Попробуйте визуализировать это, поворачивая на несколько градусов голову, постоянно смотря вдоль одной из осей вращения. Например, при вращении 2D-векторов в трехмерном мире мы устанавливаем ось вращения на ось z (попробуйте наглядно это представить и изобразить).

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

Ниже описаны матрицы вращения для каждой из единичных осей в трехмерном пространстве, где символ θ (тета) — это угол поворота:

Вращение вокруг оси x :

Glm угол между векторами

Вращение вокруг оси y :

Glm угол между векторами

Вращение вокруг оси z :

Glm угол между векторами

Используя матрицы поворота, мы можем преобразовывать наши радиус-векторы вокруг одной из трех единичных осей. Вращение вокруг произвольной 3D-оси представляет собой объединение трех отдельных поворотов: сначала вращаясь вокруг оси x , затем y и затем z , например. Однако так мы быстро попадем на проблему, называемую проблемой складывания рамок. Мы не будем вдаваться в её детали, но лучшим решением избежать этого будет выполнение вращения вокруг произвольной единичной оси, например, (0.662, 0.2, 0.722) (обратите внимание, что это единичный вектор), вместо сочетания матриц вращения. Такая (довольно большая и странная) матрица, где ( Rx , Ry , Rz ) — это произвольная ось вращения, показана ниже:

Glm угол между векторами

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

Комбинирование матриц

Истинная сила использования матриц преобразований заключается в том, что, благодаря умножению матрицы на матрицу, мы можем объединить несколько преобразований в одной матрице. Давайте посмотрим, сможем ли мы создать матрицу, объединяющую несколько преобразований. Предположим, что у нас есть вектор ( x , y , z ), и мы хотим сначала масштабировать его на 2, а затем транслировать его на ( 1 , 2 , 3 ). Для выполнения этих шагов нам потребуются две матрицы: перемещения и масштабирования. Результирующая матрица преобразования будет выглядеть следующим образом:

Glm угол между векторами

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

Применение последней матрицы преобразования к нашему вектору приводит к следующему вектору:

Glm угол между векторами

Отлично! Вектор сначала масштабируется на два, а затем транслируется на ( 1 , 2 , 3 ).

На практике

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

Библиотека GLM

GLM (сокр. от англ. «OpenGL Mathematics») является библиотекой, состоящей только из заголовочных файлов. Всё, что нам нужно сделать — это подключить соответствующие заголовочные файлы, и… всё; никакой возни с настройками линкера и компилятора не требуется. GLM можно скачать с официального сайта. Скопируйте корневой каталог заголовочных файлов в свою папку includes и приступайте к работе.

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

Поделиться или сохранить к себе: