Jump to content

  • Log In with Google      Sign In   
  • Create Account


Issues With Quaternion Based Camera


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
6 replies to this topic

#1 colinhect   Members   -  Reputation: 193

Like
0Likes
Like

Posted 09 January 2011 - 12:33 PM

I'm attempting the implement a quaternion based camera but I am having issues with the rotation.

My camera has an origin and an orientation (quaternion). The origin and quaternion are lerped and slerped between logical updates. The camera vector components (direction, origin, up, left) are computed every frame for the renderer (in my case it goes directly into a gluLookAt()):


void Camera::update() {
    direction = (orientation * Vector<3>(0, 0, 1)).normalize();
    up = (Vector<3>(0, 1, 0)).normalize();

    left = Vector<3>::cross(up, direction);
}
void Camera::translate(const Vector<3>& amount) {
    origin += amount;
}

void Camera::rotate(const Vector<3>& axis, const Angle& angle) {
    orientation *= Quaternion<>::fromAxisAngle(axis, angle);
    orientation.normalize();
}

This all seems to work fine, but when I rotate the camera by the mouse then I get strange results. Here is the camera rotation code (Lua):


self.camera : rotate(self.camera.left, Angle(self.rotateVelocity.y))
self.camera : rotate(self.camera.up, Angle(self.rotateVelocity.x))

self.camera : translate(self.velocity)

(self.rotateVelocity is the change in mouse coordinates)

At first the rotation appears to be correct, but as I turn the camera around the y-axis rotation inverts (moving mouse up makes camera rotate down) and if I face left the camera does not rotate at all vertically. It seems as though my camera's left vector is incorrect, but I know that it is correct (camera strafing works).

I am also sure that my quaternion rotation is correct, I believe I am just using them incorrectly. Is there anything obviously wrong with my approach?

Sponsor:

#2 apatriarca   Crossbones+   -  Reputation: 1593

Like
1Likes
Like

Posted 09 January 2011 - 01:06 PM

You code to perform a rotation using a quaternion is not correct. If q is a unit quaternion and v is a vector you want to rotate, then the correct rotated vector is


u = q v q-1 = q v q*,


where q* is the quaternion conjugate of q. The correct code should be something like:

void Camera::rotate(const Vector<3>& axis, const Angle& angle) {
    Quaternion<> q = Quaternion<>::fromAxisAngle(axis, angle);
    orientation = q * orientation * q.inverse();
    orientation.normalize();
}


#3 colinhect   Members   -  Reputation: 193

Like
0Likes
Like

Posted 09 January 2011 - 02:04 PM

You code to perform a rotation using a quaternion is not correct. If q is a unit quaternion and v is a vector you want to rotate, then the correct rotated vector is


u = q v q-1 = q v q*,


where q* is the quaternion conjugate of q. The correct code should be something like:

void Camera::rotate(const Vector<3>& axis, const Angle& angle) {
    Quaternion<> q = Quaternion<>::fromAxisAngle(axis, angle);
    orientation = q * orientation * q.inverse();
    orientation.normalize();
}



I don't know if this affects your answer or not... but when I multiply a quaternion by a vector I believe it is calculating the rotation. I honestly didn't attempt to understand the math behind this when I implemented it, I was just looking for a quick solution:


template <class T>
Vector<3, T> Quaternion<T>::operator*(const Vector<3, T>& vector) const {
    Vector<3> uv, uuv;
    uv = Vector<3, T>::cross(v, vector);
    uuv = Vector<3, T>::cross(v, uv);
    uv = uv * (2.0 * w);
    uuv = uuv * 2.0;

    return vector + uv + uuv;
}

Does that code not rotate the vector by the given quaternion?

edit: I have implemented what you have suggested and I still am not seeing correct results:


template <class T>
Quaternion<T> Quaternion<T>::inverse() const {
    return Quaternion(w, -v);
}

//...


void Camera::rotate(const Vector<3>& axis, const Angle& angle) {
    Quaternion<> q = Quaternion<>::fromAxisAngle(axis, angle);

    orientation = q * orientation * q.inverse();
    orientation.normalize();
}

edit again: More specifically, when I add the inverse to the multiply no rotation ever occurs.

#4 colinhect   Members   -  Reputation: 193

Like
0Likes
Like

Posted 09 January 2011 - 02:46 PM

I believe I have found the answer. I had to re-implement my quaternion/vector multiply and I appear to be seeing correct results:


template <class T>
Vector<3, T> Quaternion<T>::operator*(const Vector<3, T>& vector) const {
    Quaternion<T> q(0, vector);
    q *= this->inverse();

    return (*this * q).v;
}

Thank you for the help. I always find a way to confuse myself when using quaternions.

#5 apatriarca   Crossbones+   -  Reputation: 1593

Like
1Likes
Like

Posted 09 January 2011 - 03:50 PM

Your inverse isn't correct. Conjugation and inversion are equal only when working on unit quaternions (i.e. quaternions such that the norm is 1). More generally, the inverse quaternion is given by

q-1 = q*/(qq*).


The correct implementation of inverse and conjugate are therefore something like

template <class T>
Quaternion<T> Quaternion<T>::conjugate() const {
    return Quaternion(w, -v);
}

template <class T>
T Quaternion<T>::norm() const {
    return w*w + v.lengthSquared();
}

template <class T>
Quaternion<T> Quaternion<T>::inverse() const {
    T n = this->norm();
    return Quaternion(w / n, - v / n);
}

I find quite confusing to use the product to do rotation when working with quaternions. In mathematics the product between a quaternion and a 3D vector is usually interpreted as a quaternion product.

#6 scgames   Members   -  Reputation: 1969

Like
1Likes
Like

Posted 09 January 2011 - 04:46 PM

Just to reinforce what apatriarca said, you definitely don't want to implement the * operator as rotation of a vector by a quaternion (IMO), as it's counterintuitive, potentially confusing, and mathematically incorrect.

(Some math libraries do use the * operator for this, so obviously not everyone agrees with the above, but nevertheless I strongly recommend avoiding ambiguous overloading of this sort.)

#7 colinhect   Members   -  Reputation: 193

Like
0Likes
Like

Posted 09 January 2011 - 07:17 PM

I really appreciate the comments. I will implement them correctly.




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS