1
$\begingroup$

So, I've been writing this raytracer for my own entertainment and recently I discovered Quaternions so I decided to implement a camera that uses them. After a lot of struggle I finally managed to create a clear Quaternion class and a Camera that uses it to represent it's orientation. The camera only has a Vec3 which is it's position and a single Quaternion that represents it's orientation.

Now, I've seen this being done in professional game engines like Unity so I knew it was possible, but after I had written the calculations using my own little fingers and I tried it and it just worked my mind was just blown away and I laughed for at least a minute straight. I did not believe it would work but it just did, the program was calculating the up, forward and right vectors from the single Quaternion on the fly while I was rotating the camera and yaw, pitch and roll all worked beautifully...

I've been looking for a good explanation about this with no luck and I don't know really where else I could ask about this so I'll try my luck here. Sorry if this has been answered here before, I tried to search for quite a while with no luck.

public Quaternion createFromAxisAngle(float x, float y, float z, float theta)
{
    theta = (float) Math.toRadians(theta);

    this.w = (float) Math.cos(theta / 2.0);
    this.x = x * (float) Math.sin(theta / 2.0);
    this.y = y * (float) Math.sin(theta / 2.0);
    this.z = z * (float) Math.sin(theta / 2.0);

    return this;
}

public Quaternion mul(Quaternion q)
{
    Quaternion r = new Quaternion();

    r.w = w * q.w - x * q.x - y * q.y - z * q.z;
    r.x = w * q.x + x * q.w + y * q.z - z * q.y;
    r.y = w * q.y - x * q.z + y * q.w + z * q.x;
    r.z = w * q.z + x * q.y - y * q.x + z * q.w;

    return r;
}

public Quaternion mul(Vec3f v)
{
    Quaternion r = new Quaternion();

    r.w = -x * v.x - y * v.y - z * v.z;
    r.x = w * v.x + y * v.z - z * v.y;
    r.y = w * v.y + z * v.x - x * v.z;
    r.z = w * v.z + x * v.y - y * v.x;

    return r;
}

public Vec3f getForwardVector()
{
    return new Vec3f(0, 0, 1).mul(this);
}

public Vec3f getUpVector()
{
    return new Vec3f(0, 1, 0).mul(this);
}

public Vec3f getRightVector()
{
    return new Vec3f(1, 0, 0).mul(this);
}

Those are the methods I'm using taken straigth out from my Quaternion class, I understand the idea behind createFromAxisAngle but those multiplication operations are something I can't comprehend but I can accept them as facts. The thing I can't believe is that how can those getForwardVector() and so on calculations just work? It's just mindblowing...

This is what I'm doing in there to get the forward vector for example:

forward = Q_cam_eye * vec3(0, 0, 1) * Q_cam_eye_conjugate

And that's it. That gives me the forward direction vector from a chosen Quaternion... It just uses those two multiplication methods I've described above, nothing else really. My wild guess is that the information about the local x, y and z axes of a quaternion is somehow hidden into the imaginary parts of it? I don't really have any idea about that though, which is why I'm asking for a somehow understandable explanation about it here.

Thank you in advance. Sorry about the not-so-mathematical text, I'm more of a programmer than a mathematician.

$\endgroup$
2
$\begingroup$

One short explanation is that your axes don't actually hold as much information as you think they do. At first blush, it looks as though it's impossible for a quaternion (which has four 'parts': its real component and its $i$, $j$ and $k$ components) to hold all the data for a coordinate frame (which is three vectors each with three coordinate components, a total of 9 real values). But in fact, those real values are much more constrained than they look:

  • First of all, all of our axes are unit vectors. This means that given two of the components we can calculate the third (up to a sign, and we're going to ignore signs here because they're not 'full' values); in other words, a unit vector actually holds two pieces of data, not three. This brings us down to $3\times2=6$ real values our quaternion has to hold.
  • And similarly, once we have two of our axes (say, $\vec{x}$ and $\vec{y}$) we can get the third from $\vec{z}=\vec{x}\times\vec{y}$. This means that our frame really only has two vectors in it, not three, and brings us down to $2\times2=4$ real values our quaternion has to hold.

And from here, it looks like we're done. But wait — I said a quaternion has four parts, but that's not really true; the same argument about unit vectors applies to unit quaternions, so really our quaternion is only holding 3 values. What gives? Well, I said that our $\vec{x}$ and $\vec{y}$ axes had two pieces of data each — but they also have one constraint on them, that $\vec{x}\cdot\vec{y}=0$ (in other words, they're orthogonal). This lets you shave one more value out of the puzzle (another way of seeing this is to see that once you have your $\vec{x}$ axis, then all of the possible $\vec{y}$ axes lie in a circle orthogonal to the $\vec{x}$ axis, so you can essentially describe your $\vec{y}$ axis with just an angle) and leaves you with only three real values necessary to extract the $\vec{x}$,$\vec{y}$ and $\vec{z}$ axes of a frame, a perfect match for the amount of data a unit quaternion holds.

I'm skipping over a lot of fine technical details in all of this (in particular, the notion of what a 'part' is - more formally I'd be talking about the dimension of all of the relevant spaces) but if you want to know more about it (including why a quaternion $\mathbf{q}$ and its negation $-\mathbf{q}$ represent the same rotation and how this is related to the 'belt trick' or 'butler trick' and even to why you have to loop a car's sunshade into thirds rather than in half!) then I encourage you to look up the Special Orthogonal Group $SO(3)$ - there's a lot more to this than meets the eye, and more than I can briefly describe here.

$\endgroup$
  • $\begingroup$ That explanation is way better than anything I expected. Thank you kind sir. I would vote your answer up if I had the 15 reputation needed to do that. Also, thanks for those great recommendations, I will probably look a bit more into those in the near future since I'm starting to really like Quaternions now. $\endgroup$ – Harha Feb 2 '15 at 23:20
0
$\begingroup$

Not sure what you're confused about. Quaternions are just a different way of representing a rotation. Multiplying with them in the usual double-sided way performs a rotation. You're just taking the standard frame (forward, up, and right directions in some standard orientation) and rotating them according to the quaternion, so you can figure out what the actual frame vectors are.

People say quaternions represent orientations; that's a simplified way of saying they represent how to rotate one orientation--one set of forward, up, and right vectors--to another. But, you can always take one of those orientations to be some standard reference orientation to begin with.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.