Как управлять 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();
}

Это мой первый пост о переполнении стека, поэтому, пожалуйста, дайте мне знать, если потребуется дополнительная информация. Пожалуйста, помогите, если вы обнаружите какие-либо ошибки в моем коде и подходе. Спасибо!

0 ответов

Другие вопросы по тегам