Issues With Quaternion Based Camera

Started by
5 comments, last by c_olin 13 years, 3 months ago
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?
Advertisement
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();
}

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.
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.
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.
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.)
I really appreciate the comments. I will implement them correctly.

This topic is closed to new replies.

Advertisement