learnopengl. Урок 1.9 — Камера

Автор: admin от 30-04-2017, 00:35, посмотрело: 251

learnopengl. Урок 1.9 — КамераВ предыдущем уроке обсуждалась матрица вида, и то, как её можно использовать для перемещения по сцене (мы немного отодвинули назад точку зрения наблюдателя). В OpenGL отсутствует концепция камеры, но можно попытаться её сымитировать, перемещая все объекты сцены в направлении противоположном движению наблюдателя, и тем самым создать иллюзию, что движемся мы сами.


В этом уроке мы рассмотрим, как можно создать камеру в OpenGL. Мы обсудим камеру типа FPS (First Person Shooter), которая позволит вам свободно перемещаться в трехмерной сцене. Кроме того, мы поговорим о вводе с клавиатуры и мыши, а закончим созданием собственного C++ класса камеры.






исходным кодом, а шейдеры с текстами вершинного и фрагментного шейдеров.


уроке 1.3 мы создали функцию обратного вызова key_callback, необходимую для получения от GLFW ввода c клавиатуры, а теперь давайте добавим несколько новых проверок на нажатия определенных кнопок:


void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
    ...
    GLfloat cameraSpeed = 0.05f;
    if(key == GLFW_KEY_W)
        cameraPos += cameraSpeed * cameraFront;
    if(key == GLFW_KEY_S)
        cameraPos -= cameraSpeed * cameraFront;
    if(key == GLFW_KEY_A)
        cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
    if(key == GLFW_KEY_D)
        cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;  
}

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


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


Если вы дополните функцию key_callback этим фрагментом кода, то сможете перемещаться по сцене, двигаясь вперед/назад или в сторону.



Поразвлекавшись с этой простой системой управления камерой, вы вероятно заметили, что не можете двигаться в двух направлениях одновременно (совершать диагональное перемещение), а когда вы удерживаете одну из клавиш, то она сначала срабатывает один раз, и только потом, после небольшой задержки, начинается непрерывное движение. Это происходит из-за того, что большинство систем ввода имеют событийно-ориентированную архитектуру (event-driven architecture, EDA) способную одновременно обрабатывать только одно нажатие клавиши, которое вызывает соответствующий обработчик. Это неплохо работает во многих системах с графическим интерфейсом, но не очень подходит для плавного движения камеры. Я покажу небольшой трюк, позволяющий решить эту проблему.


Хитрость заключается в том, чтобы в функции обратного вызова key_callback отслеживать, какие клавиши были нажаты или отпущены. Затем, в игровом цикле мы считаем эти значения, проверим, какие клавиши активны и, исходя из их состояния, изменим соответствующим образом значение переменной cameraPos. Таким образом в функции-обработчике, мы просто сохраняем информацию о том, какие клавиши были нажаты или отпущены, а реагируем на их состояния уже в игровом цикле. Сперва давайте создадим массив переменных логического типа, представляющий нажатые или отпущенные состояния клавиш:


bool keys[1024];

После этого в функции key_callback мы должны установить нажатые клавиши в true, а отпущенные в false:


if(action == GLFW_PRESS)
  keys[key] = true;
else if(action == GLFW_RELEASE)
  keys[key] = false;

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


void do_movement()
{
  // Camera controls
  GLfloat cameraSpeed = 0.01f;
  if(keys[GLFW_KEY_W])
  	cameraPos += cameraSpeed * cameraFront;
  if(keys[GLFW_KEY_S])
  	cameraPos -= cameraSpeed * cameraFront;
  if(keys[GLFW_KEY_A])
  	cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
  if(keys[GLFW_KEY_D])
  	cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
}

Код из предыдущего раздела теперь перенесен в функцию do_movement. Поскольку GLFW-перечисления идентификаторов клавиш являются просто целыми числами, то мы можем использовать их для индексации массива.


И последнее, но не менее важное, нам нужно добавить вызов новой функции в тело игрового цикла:


while(!glfwWindowShouldClose(window))
{
  // Проверка поступивших событий и вызов их обработчиков
  glfwPollEvents();
  do_movement();  
  
  // Визуализация
  ...
}

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


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


исходный код.


От переводчика: не совсем понятно, зачем нужна проверка с переменной firstMouse, когда можно было бы воспользоваться функцией glfwSetCursorPos или наоборот — инициализировать переменные lastX и lastY функцией glfwGetCursorPos.


исходным кодом.


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


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


Разработанная нами FPS-подобная система управления камерой подходит для большинства целей и хорошо работает с углами Эйлера, но будьте осторожны при создании камер другого типа, например камеры для симуляции полёта. Каждый тип камеры имеет свои собственные сложности и причуды, поэтому обязательно почитайте о них подробнее. Например, наша FPS-камера не допускает значений тангажа больше 90 градусов, а константный вектор указывающий направление вверх (0,1,0) перестанет работать, если к математической модели добавить угол крена.


Обновленную версию исходного кода использующего новый объекта Camera можно найти здесь.


решение.
  • Попробуйте создать свою собственную функцию LookAt, в которой вы вручную создадите матрицу вида, как было описано в начале этого урока. Замените функцию glm::LookAt своей собственной реализацией и посмотрите, правильно ли она работает: решение.



  • Источник: Хабрахабр

    Категория: Программирование, Веб-разработка

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

    Добавление комментария

    Имя:*
    E-Mail:
    Комментарий:
    Полужирный Наклонный текст Подчеркнутый текст Зачеркнутый текст | Выравнивание по левому краю По центру Выравнивание по правому краю | Вставка смайликов Выбор цвета | Скрытый текст Вставка цитаты Преобразовать выбранный текст из транслитерации в кириллицу Вставка спойлера
    Введите два слова, показанных на изображении: *