Как управлять 3D-камерой, используя одно кватернионное представление для ее ориентации?
Я создаю 3D-камеру для своего игрового движка и хочу представить камеру одним кватернионом для ее ориентации. Местная верхняя, передняя и боковая оси камеры генерируются путем преобразования этого кватерниона в матрицу вращения. Первая строка этой матрицы дает мне ось X камеры (боковой вектор), 2-я строка дает мне вектор UP, а 3-я строка дает мне вектор FRONT. Чтобы переместить камеру, моя функция движется в направлении движения, а количество и позиция обновляются, а матрица вида () обновляется. Чтобы повернуть камеру, моя функция заставляет ось вращаться вокруг и угол. Из этой оси и угла создается временный кватернион, который применяется к текущей ориентации моей камеры. Я поворачиваюсь вокруг мировой оси Y для рыскания и вокруг бокового вектора камеры, чтобы наклонить. Этот шаг не работает должным образом, когда оси моей камеры не выровнены относительно мировой оси. Если я вместо этого расскажу о глобальной оси X, тогда она будет работать нормально, но я знаю, что это не то, чего я хочу. Также, когда ось моей камеры не выровнена относительно мировой оси, движение также запутывает. Я использую основное движение "Wasd". Мои функции перемещения и тангажа используют локальную ось, и, следовательно, у меня есть догадка, что способ извлечения локальной оси из кватерниона неправильный, но я не могу понять, что происходит не так.
Класс камеры:
#include "stdafx.h"
Camera * Camera::m_currentCamera = NULL;
Camera::Camera(vec3 position , double distToNear , double distToFar , double fovy)
{
m_position = position;
m_distToNear = distToNear;
m_distToFar = distToFar;
m_fovy = fovy;
// generate the view and projection matrices
updateViewMatrix();
projection = glm::perspective(m_fovy, 4.0/3.0, m_distToNear, m_distToFar);
// in radians
m_deltaAngle = 10.0 * PIOVER180;
if(m_currentCamera != NULL)
{
delete m_currentCamera;
}
m_currentCamera = this;
}
Camera::Camera()
{
}
Camera::~Camera()
{
// empty for now
}
Camera* Camera::getCurrentCamera()
{
return m_currentCamera;
}
/** set and get camera position */
vec3 Camera::getPosition()
{
return m_position;
}
void Camera::setPosition(vec3 pos)
{
m_position = pos;
}
/** set and get near plane distance */
double Camera::getNearPlaneDist()
{
return m_distToNear;
}
void Camera::setNearPlaneDist(double dist)
{
m_distToNear = dist;
}
/** set and get far plane distance */
double Camera::getFarPlaneDist()
{
return m_distToFar;
}
void Camera::setFarPlaneDist(double dist)
{
m_distToFar = dist;
}
/** set and get camera field of view */
double Camera::getFovy()
{
return m_fovy;
}
void Camera::setFovy(double angle)
{
m_fovy = angle;
}
mat4 Camera::getViewMatrix()
{
return view;
}
mat4 Camera::getProjectionMatrix()
{
return projection;
}
Quaternion Camera::getOrientation()
{
return m_rotation;
}
void Camera::setOrientation(Quaternion *q)
{
m_rotation.setQuaternion(q);
}
/** this function will be called whenever the camera's position, up vector or lookat vector changes */
void Camera::updateViewMatrix()
{
mat4 orientation = m_rotation.toMatrix();
mat4 translation = translate(mat4(1.0f),vec3(-m_position.x,-m_position.y,-m_position.z));
view = orientation * translation;
}
/** Control Functions */
void Camera::rollCamera(float theta)
{
Quaternion tempRotation;
tempRotation.quaternionFromAxis(getFront(), theta);
tempRotation.normalise();
m_rotation = m_rotation * &tempRotation;
m_rotation.normalise();
updateViewMatrix();
}
void Camera::pitchCamera(float theta)
{
Quaternion tempRotation;
// INSTEAD OF LOCAL AXIS IF (1.0,0.0,0.0) GLOBAL X AXIS IS USED THIS WORKS FINE!!!
tempRotation.quaternionFromAxis(getSide(), theta);
tempRotation.normalise();
m_rotation = m_rotation * &tempRotation;
m_rotation.normalise();
updateViewMatrix();
}
void Camera::yawCamera(float theta)
{
Quaternion tempRotation;
tempRotation.quaternionFromAxis(vec3(0.0,1.0,0.0), theta);
tempRotation.normalise();
m_rotation = m_rotation * &tempRotation;
m_rotation.normalise();
updateViewMatrix();
}
void Camera::moveCamera(vec3 dir, float amt)
{
normalize(dir);
m_position += dir * amt;
updateViewMatrix();
}
vec3 Camera::getFront()
{
m_rotation.normalise();
mat4 orientation = m_rotation.toMatrix();
vec3 front(orientation[2][0], orientation[2][1], orientation[2][2]);
normalize(front);
cout << "FRONT " << front.x << " " << front.y << " " << front.z << endl;
return front;
}
vec3 Camera::getSide()
{
m_rotation.normalise();
mat4 orientation = m_rotation.toMatrix();
vec3 side(orientation[0][0], orientation[0][1], orientation[0][2]);
normalize(side);
cout << "SIDE " << side.x << " " << side.y << " " << side.z << endl;
return side;
}
vec3 Camera::getUp()
{
m_rotation.normalise();
mat4 orientation = m_rotation.toMatrix();
vec3 up(orientation[1][0], orientation[1][1], orientation[1][2]);
normalize(up);
cout << "UP " << up.x << " " << up.y << " " << up.z << endl;
return up;
}
Функции для перемещения и вращения камеры:
void specialKeyboard(int key, int x, int y)
{
switch(key)
{
case GLUT_KEY_LEFT:
Camera::getCurrentCamera()->yawCamera(1.0 * PIOVER180);
break;
case GLUT_KEY_RIGHT:
Camera::getCurrentCamera()->yawCamera(-1.0 * PIOVER180);
break;
case GLUT_KEY_UP:
Camera::getCurrentCamera()->pitchCamera(1.0 * PIOVER180);
break;
case GLUT_KEY_DOWN:
Camera::getCurrentCamera()->pitchCamera(-1.0 * PIOVER180);
break;
}
glutPostRedisplay();
}
void keyboard(unsigned char key, int x, int y)
{
switch(key)
{
case 'w':
Camera::getCurrentCamera()->moveCamera(Camera::getCurrentCamera()->getFront(), -0.1f);
break;
case 's':
Camera::getCurrentCamera()->moveCamera(Camera::getCurrentCamera()->getFront(), 0.1f);
break;
case 'a':
Camera::getCurrentCamera()->moveCamera(Camera::getCurrentCamera()->getSide(), -0.1f);
break;
case 'd':
Camera::getCurrentCamera()->moveCamera(Camera::getCurrentCamera()->getSide(), 0.1f);
break;
}
glutPostRedisplay();
}
Это мой первый пост о переполнении стека, поэтому, пожалуйста, дайте мне знать, если потребуется дополнительная информация. Пожалуйста, помогите, если вы обнаружите какие-либо ошибки в моем коде и подходе. Спасибо!