Небольшая ошибка камеры Quaternion? (слегка вращается вокруг оси Z)

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

Я использую Eigen для кватернионов и векторов, поэтому проблема должна подпадать под код, который я опубликовал.

Камера:

#include <Eigen/Dense>
#include <cstring>
#include <math.h>

typedef Eigen::Vector3f vec3;
typedef Eigen::Quaternionf Quaternion;

namespace vec
{
    //
    // Constants
    //
    const vec3 i(1, 0, 0);
    const vec3 j(0, 1, 0);
    const vec3 k(0, 0, 1);
    const vec3 zero(0, 0, 0);
    const vec3 ones(1, 1, 1);
}

class Camera 
{
void init();

void moveX(float dist);
void moveY(float dist);
void moveZ(float dist);

void rotateX(float radians); //yaw
void rotateY(float radians); //pitch
void rotateZ(float radians); //roll

void moveLeft(float dist);
void moveForward(float dist);
void moveUp(float dist);

void rotateLeft(float radians); //yaw
void rotateUp(float radians); //pitch
void rollLeft(float radians); //roll

void setPosition(const vec3 &pos);
void setDirection(const vec3 &dir);
void lookAt(const vec3 &pos); //changes direction

// Camera Vectors
vec3 left() const;
vec3 right() const;
vec3 up() const;
vec3 down() const;
vec3 forward() const;
vec3 backward() const;
const vec3 &pos() const;

protected:
    Quaternion mRotation;
    vec3 mPos;

    void rotateL(float radians, const vec3 &axis);
    void rotateR(float radians, const vec3 &axis);
};

Реализация:

void Camera::init()
{
    mPos.setZero();
    mRotation.setIdentity();
}

inline void Camera::rotateL(float radians, const vec3 &axis)
{
    Quaternion q(Eigen::AngleAxis<float>(radians, axis));
    mRotation = (q * mRotation).normalized();
}

inline void Camera::rotateR(float radians, const vec3 &axis)
{
    Quaternion q(Eigen::AngleAxis<float>(radians, axis));
    mRotation = (mRotation * q).normalized();
}

void Camera::moveX(float dist)
{
    mPos.x() += dist;
}

void Camera::moveY(float dist)
{
    mPos.y() += dist;
}

void Camera::moveZ(float dist)
{
    mPos.z() += dist;
}

void Camera::rotateX(float radians)
{
    rotateL(radians, vec::i);
}

void Camera::rotateY(float radians)
{
    rotateL(radians, vec::j);
}

void Camera::rotateZ(float radians)
{
    rotateL(radians, vec::k);
}

void Camera::moveLeft(float dist)
{
    mPos += dist * left();
}

void Camera::moveUp(float dist)
{
    mPos += dist * up();
}

void Camera::moveForward(float dist)
{
    mPos += dist * forward();
}

void Camera::rotateLeft(float radians)
{
    rotateL(radians, up());
}

void Camera::rotateUp(float radians)
{
    rotateL(radians, left());
}

void Camera::rollLeft(float radians)
{
    rotateL(radians, forward());
}

void Camera::setPosition(const vec3 &pos) 
{
    mPos = pos;
}

void Camera::setDirection(const vec3 &dir)
{
    mRotation.setFromTwoVectors(vec::k, dir);
    mRotation.normalize();
}

void Camera::lookAt(const vec3 &pos)
{
    setDirection(mPos - pos);
}

// Camera Vectors
vec3 Camera::left() const
{
    return mRotation._transformVector(vec::i);
}

vec3 Camera::right() const
{
    return -left();
}

vec3 Camera::up() const
{
    return mRotation._transformVector(vec::j);
}

vec3 Camera::down() const
{
    return -up();
}

vec3 Camera::forward() const
{
    return mRotation._transformVector(vec::k);
}

vec3 Camera::backward() const
{
    return -forward();
}

const vec3 &Camera::pos() const
{
    return mPos;
}

Функции обновлений:

void App::onMouseMotion(int x, int y, int dx, int dy)
{
    cam.rotateUp(dy * ROTATE_SCALE); 
    cam.rotateLeft(-dx * ROTATE_SCALE);
}


void App::update()
{
    cam.moveLeft(((int)Keyboard::isKeyDown('a')) * MOVE_SCALE * mDt);
    cam.moveLeft(((int)Keyboard::isKeyDown('d')) * -MOVE_SCALE * mDt);
    cam.moveForward(((int)Keyboard::isKeyDown('w')) * MOVE_SCALE * mDt);
    cam.moveForward(((int)Keyboard::isKeyDown('s')) * -MOVE_SCALE * mDt);

    cam.rollLeft(((int)Keyboard::isKeyDown('q')) * -ROLL_SCALE * mDt);
    cam.rollLeft(((int)Keyboard::isKeyDown('e')) * ROLL_SCALE * mDt);
}

Я читал, что некоторые проблемы возникают из-за объединения кватернионов с неправильной стороны... например, QR вместо RQ. Переключение оси X, как предложено здесь точно не поможет.

Я также был бы признателен за любые предложения с моей реализацией. Я рассматривал возможность перехода на матрицы для поворотов, но их нормализация и объединение обходятся дороже.


person ffhighwind    schedule 01.06.2013    source источник


Ответы (1)


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

  1. Держите руку перед собой. «Вперед» — это ваши пальцы. «Вверх» — это тыльная сторона ладони.
  2. Наклоните его вверх примерно на 30.
  3. Из новой ориентации наклоните его влево примерно на 30.
  4. Из новой ориентации наклоните его примерно на 30° вниз.
  5. От его новой ориентации наклоните его вправо примерно на 30.

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

Людям это может показаться странным, поскольку мы привыкли к фиксированному направлению вверх.

person Markus Jarderot    schedule 01.06.2013
comment
Я полагаю, что у меня нет большого опыта в играх, где доступна бесплатная ротация. Было бы невозможно определить, была ли это проблема в симуляторах полета, поскольку вы всегда будете двигаться. Я думаю, что то, как я представлял себе, что это слева / вверх, эквивалентно X / Y ... но в моем случае они относительные. - person ffhighwind; 02.06.2013
comment
Даже после замены на rotateX и rotateY у него все еще есть проблема... так что я думаю, что это что-то другое. Попытаюсь использовать один плавающий рыскание/тангаж/крен или просто использовать Эйлера с минимальным/максимальным шагом. - person ffhighwind; 02.06.2013