Уроки з OpenGL з сайту OGLDev - Урок 13 - Простір камери
Урок 13 - Простір камери
В останній серії уроків ми бачили два види перетворень. Перший тип змінював позицію (рух), орієнтацію (обертання) чи розміри (масштабування) об'єкта. Ці перетворення дозволяли нам помістити об'єкт у будь-яку точку 3D простору. Другий тип - це проектування перспективи, яке приймало позицію вершини в 3D просторі та проектувало її в 2D (тобто на площину). Коли координати переведені в 2D, дуже легко перевести координати в простір екрана. Ці координати використовувалися для розтеризації примітивів, з яких і складається об'єкт (точки, лінії або трикутники).
Останній елемент мозаїки – це позиція камери. У всіх попередніх уроках ми вважали, що камера розташована на початку координат. Насправді нам, звичайно, хотілося б свободи переміщення камери в будь-яку точку, і проекція вершин має відбуватися на якусь площину перед камерою. Це задасть правильний зв'язок між камерою та об'єктами на екрані.
На наступному зображенні бачимо камеру, розташовану задом до нас. Перед нею є віртуальна 2D площина і куля проектується на неї. Якщо камера нахиляється, то площина нахиляється так само. Оскільки огляд камери обмежений її кутом огляду, то видима частина (нескінченної) площини - це прямокутник. Все, що поза ним, обрізається. Наша мета – перенести прямокутник на екран.

Теоретично, можливо генерувати перетворення, яке братиме об'єкт з 3D простору і проектувати його на 2D площину, що знаходиться прямо перед камерою, розташованою в довільній позиції. Хоча такі математичні обчислення набагато складніше, що ми бачили раніше. Набагато простіше коли камера знаходиться на початку координат і спрямована всторону зменшення осі Z. Наприклад, об'єкт знаходиться в (0,0,5), а камера (0,0,1) і спрямована протилежно осі Z (тобто прямо на об'єкт). Якщо ми посунемо камеру і об'єкт на одну відстань, тоді відносна відстань і орієнтація (у значенні напрямку камери) залишаться тими самими, як і в старому положенні. Переміщення всіх об'єктів на сцені в одному напрямку дозволить нам правильно рендерувати сцену, при цьому будуть використовуватися вже вивчені методи.
Попередній приклад занадто простий, оскільки камера вже спрямована правильно. Але що станеться, якщо камера спрямована ще кудись? Давайте подивимося на таке зображення. Для ясності тут зображено 2D систему координат, і ми дивимося на камеру зверху.

Камера була направлена у бік зменшення осі Z, а потім повернена на 45 градусів за годинниковою стрілкою. Як ви бачите, камера визначає свою власну систему координат, яка може збігатися зі світовою (верхнє зображення) або відрізнятися (нижчою). Тому ми маємо 2 системи координат одночасно. Перша "світова система координат", в якій розташовуються об'єкти, і друга це система координат камери, зі своїми осями координат. Ці дві системи називають 'світовий простір' і 'простір камери'.
Зелена куля розташована у (0,y,z) у світових координатах. А щодо камери він десь у лівій верхній чверті координатної системи (тобто у нього негативний X та позитивний Z). Нам потрібно вирахувати його координати щодо СК камери. Тоді ми зможемо забути про світовий простір і використовувати лише камерний. У просторі камери сама камера розташована на початку координат і спрямована в бік, зворотний осі Z. Об'єкти співвідносяться з камерою і можуть рендеруватись використовуючи вже вивчені інструменти.
Повороткамери на 45 градусів за годинниковою стрілкою однаково, як і поворот об'єкта на 45 градусів проти годинникової. Рух об'єктів завжди протилежний руху камери. Тому загалом нам потрібно додати 2 нових перетворення та включити їх у конвеєр, який у нас вже є. Ми будемо пересувати об'єкти так, щоб відстань від них до камери була б такою самою, якби камера розташовувалася на початку координат, і ми повертатимемо об'єкти в напрямку, протилежному до обертання камери.
Рух камери влаштований дуже просто. Якщо координати камери (x, y, z), то перетворення позиції (-x, -y, -z). Причина тому проста - камера розташована у світовій СК з використанням переміщення, заснованого на векторі (x,y,z) тому, щоб помістити її назад, необхідне перетворення, зворотне даному. Ось як виглядає матриця перетворення:

Наступний крок - це поворот камери деякі значення, зазначені у світових координатах. Нам потрібно знайти положення вершин у новій СК, яка встановлюється камерою. Тому логічним є питання: як перейти з однієї системи координат до іншої?
Ще раз подивимось на зображення вище. Ми можемо сказати, що світова система задана трьома лінійно незалежними одиничними векторами (1,0,0), (0,1,0) та (0,0,1). Лінійна незалежність означає, що ми не можемо знайти не рівні 0 x,y і z такі, що x(1,0,0) + y(0,1,0) + z(0,0,1) = (0,0 0). Якщо говорити більш математично, то з будь-якої пари векторів з цієї трійки можна отримати площину, для якої 3-й вектор буде перпендикулярним (площина XY перпендикулярна осі Z, і т.д). Легко помітити, що система координат камери задано векторами (1,0,-1), (0,1,0), (1,0,1). Після нормування вектори дорівнюватимуть (0.7071,0,-0.7071), (0,1,0) і (0.7071,0,0.7071).
Наступнезображення показує як вектор вказується у 2 незалежних системах координат:

Ми знаємо, як отримати одиничні вектори, які позначать осі камери у світовому просторі і ми знаємо позицію вектора в ньому (x, y, z). Нас цікавить вектор (x',y', z'). Тепер скористаємося значенням скалярного твору, відомого як скалярна проекція. Скалярна проекція - це результат скалярного добутку між довільним вектором A та одиничного вектора B, і результат це величина A у напрямку B. Іншими словами, проекція вектора A на вектор B. У прикладі вище, якщо ми скалярно помножимо вектор (x,y,z ) і одиничний вектор, що представляє X у камери, ми отримаємо x'. Аналогічно ми можемо отримати y' та z'. (x',y',z') і є координати (x,y,z) у просторі камери.
Давайте розглянемо, як зібрати з цих висновків єдине рішення для положення камери. Рішення називається 'UVN камера' і це одна з багатьох систем, що характеризують камеру. Ідея в тому, що камера визначається такими векторами:
- N - вектор від камери до її мети. Також відомий як вектор 'look at' у деякій літературі про 3D. Цей вектор відповідає осі Z.
- V – Якщо стояти прямо, то цей вектор виходитиме з голови в небо. Якщо ви пишіть симулятор польотів, і один з них перевернуть, вектор буде вказувати на землю. Цей вектор відповідає осі Y.
- U - Цей вектор виходить із камери праворуч. Відповідає осі X.
Отже, для переведення координат зі світової системи до системи камери, визначеної векторами UVN, нам необхідно знайти скалярний твір між вектором позиції з векторами UVN. Матриця найкраще покаже, як це відбувається:

У вихідному коді до цього уроку ви помітите, що змінна 'gWorld'тепер називається 'gWVP'. Ця зміна відбиває серії перетворень, відомих у багатьох книгах. WVP розшифровується як вид світової проекції (World-View-Projection).
Прямо до коду!
У цьому уроці я вирішив зробити невеликі зміни у структурі проекту та перемістив низькорівневі взаємодії з матрицями з класу конвеєра до їхнього власного класу. Тепер конвеєр може ініціалізувати матрицю декількома способами та збирає матриці для створення підсумкового перетворення.
Клас конвеєра має кілька нових параметрів для зберігання даних матриці. Зауважимо, що у нас відсутній вектор праворуч. Він може бути підрахований на ходу, використовуючи векторний твір інших векторів. Крім того, з'явилася нова функція SetCamera для цих значень.
Vector3f придбав новий метод для векторного множення між двома векторами. Така операція повертає вектор, перпендикулярний площині, що визначається вихідними векторами. Стане набагато легше у розумінні, коли ви згадаєте, що вектор має напрямок та значення, але не має позиції. Всі вектори, у яких збігаються напрямки та значення – еквівалентні, незалежно від того, з якої точки вони виходять. Тому ви можете уявити, що всі вектори виходять із початку координат. Це означає, що можна створити трикутник, одна з вершин якого початок координат, а 2 інші на кінці векторів. Трикутник належить площині, а результат векторного добутку дає вектор перпендикулярний цій площині. Докладніше про векторний твір на Вікіпедії.
Ця функція створює перетворення камери, які пізніше будуть використані конвеєром. Вектори U,V і N вираховуються та заносяться до рядів матриці. Так як вектор позиції буде множитися праворуч (у вигляді стовпця),ми отримаємо скалярне твір між цим вектором і векторами U,V і N. Це обчислить значення 3 скалярних проекцій, які стануть XYZ значеннями позиції просторі екрана.
Функція отримує вектор напрямки та верхній вектор. Вектор праворуч обчислюється як їхній векторний твір. Зауважимо, що ми хочемо нормувати вектори у будь-якому разі, навіть якщо вони вже є одиничною довжиною. Після генерації вектор перераховується вгору як векторний добуток між векторами напрямку і вектором вправо. Причина стане зрозумілою пізніше, коли ми почнемо рухати камеру. Простіше оновити тільки вектор напряму, але тоді кут між напрямком і вектором вгору не дорівнюватиме 90 градусам, що порушить лінійність системи координат. Після підрахунку вектора праворуч і потім векторно помноживши його на вектор напрямку, ми отримаємо назад вектор вгору, тим самим ми отримуємо систему координат, у якої кут між будь-якими двома осями дорівнює 90 градусів.
Давайте оновимо функцію, що генерує підсумкову матрицю перетворень об'єктів. Вона стане трохи складніше з двома новими матрицями, що характеризують участь камери. Після завершення світових перетворень (комбінація масштабування, обертання та переміщення об'єкта), ми починаємо перетворення камери 'рухом' її назад на початок координат. Це робиться усуненням на зворотний вектор позиції камери. Тому, якщо камера знаходиться в точці (1,2,3), ми рухаємо об'єкти на (-1,-2,-3). Після цього ми генеруємо обертання камери, ґрунтуючись на напрямку камери та її векторі вгору. На цьому участь камери завершено. Наприкінці ми проектуємо координати.
Ми використовуємо новий функціонал у головному циклі рендеру. Для розміщення камери ми рухаємось назад, уздовж негативного Z, потім зрушуємо праворуч і встаємо прямо. Камера дивиться вздовжзростання осі Z і трохи правіше щодо початку координат. Вектор для простоти позитивний Y. Ми призначаємо це в клас конвеєра, а про решту він подбає сам.