Home » Community » Forums » Math and Physics » Quaternions
  Intel sponsors gamedev.net search:   
[Control Panel] [Register] [Bookmarks] [Who's Online] [Active Topics] [Stats] [FAQ] [Search]

Add Forum to Favorites |  Send Topic To a Friend | View Forum FAQ | Track this topic


 Last Thread Next Thread 
 Quaternions
Post New Topic  Post Reply 
Hello, I'm trying to implement the Quaternion based camera presented here, but I am not getting the results I'd expect. I've read the top few links on Google about quaternions, but that's about as far as my understanding goes.

What happens is I load up the scene, move my mouse, and nothing happens. I checked my view vector throughout the program and all I'm gettings are some very small numbers for the components.

here's a copy of what I believe to be all of the relevant source code.

void Camera::mouseMove()
{
	POINT mousePos;
	int middleX = screenWidth  >> 1; // divide the width by two
	int middleY = screenWidth >> 1;  // divide the height by two
	
	float angleX = 0.0f;	// This is the direction for looking up or down
	float angleY = 0.0f;	// This will be the value we need to rotate around the Y axis (Left and Right)
	
	// Get the mouse's current X,Y position
	GetCursorPos(&mousePos);						
	
	// curser didn't move, quit wasting my time!
	if( (mousePos.x == middleX) && (mousePos.y == middleY) ) 
		return;

	// move the mouse back to the center of the screen
	SetCursorPos(middleX, middleY);							

	angleX = (float)( (middleX - mousePos.x) ) / mouseSensivity;		
	angleY = (float)( (middleY - mousePos.y) ) / mouseSensivity;

	Vector axis = Vector::cross(Vector(c_view.getX() - c_position.getX(),
							           c_view.getY() - c_position.getY(),
									   c_view.getZ() - c_position.getZ()), c_up);
	axis = Vector::normalize(axis);

	// Rotate around the y axis
    rotateCamera(angleY, axis.getX(), axis.getY(), axis.getZ());
    // Rotate around the x axis
    rotateCamera(angleX, 0, 1, 0);
	
	updateCamera();
}

void Camera::rotateCamera(float theta, float x, float y, float z)
{
  Quaternion temp;
  Quaternion qview;
  Quaternion final;

  float sintheta = sin(theta / 2);
  temp.setX(x * sintheta);
  temp.setY(y * sintheta);
  temp.setZ(z * sintheta);
  temp.setW(    cos(theta/2));

  qview.setX(c_view.getX());
  qview.setY(c_view.getY());
  qview.setZ(c_view.getZ());
  qview.setW(0);

  final = Quaternion::multiply(Quaternion::multiply(temp, qview), Quaternion::conjugate(temp));

  c_view.setX(final.getX());
  c_view.setY(final.getY());
  c_view.setZ(final.getZ());
}

void Camera::updateCamera()
{
	gluLookAt(c_position.getX(), c_position.getY(), c_position.getZ(),
			  c_view.getX()    , c_view.getY()    , c_view.getZ(),
			  c_up.getX()      , c_up.getY()      , c_up.getZ());
}




and here is the quaternion multiplication
Quaternion Quaternion::normalize(Quaternion quat)
{
	float length = sqrt( (quat.getX() * quat.getX()) + (quat.getY() * quat.getY())
		               + (quat.getZ() * quat.getZ()) + (quat.getW() * quat.getW()) );

	quat.setX(quat.getX() / length);
	quat.setY(quat.getY() / length);
	quat.setZ(quat.getZ() / length);
	quat.setW(quat.getW() / length);
		
	return quat;
}

Quaternion Quaternion::conjugate(Quaternion quat)
{
	quat.setX( -quat.getX() );
	quat.setY( -quat.getY() );
	quat.setZ( -quat.getZ() );

	return quat;
}

Quaternion Quaternion::multiply(Quaternion one, Quaternion two)
{
	Quaternion three = Quaternion();

	three.setX(one.getW() * two.getX() + 
			   one.getX() * two.getW() + 
			   one.getY() * two.getZ() -
			   one.getZ() * two.getY());

	three.setY(one.getW() * two.getY() - 
		       one.getX() * two.getZ() + 
			   one.getY() * two.getW() + 
			   one.getZ() * two.getX());

	three.setZ(one.getW() * two.getZ() + 
		       one.getX() * two.getY() - 
			   one.getY() * two.getX() + 
			   one.getZ() * two.getW());

	three.setW(one.getW() * two.getW() -
		       one.getX() * two.getX() -
			   one.getY() * two.getY() - 
			   one.getZ() * two.getZ());

	return three;
}





Not the best code I've written (obviously, since it doesn't work :) ), but hopefully you guys should be able to see what I'm doing wrong. Thanks for your help.

EDIT: if it matters, mouseSensivity is 2.0f;
Thanks

 User Rating: 1123   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

I think someone else was having trouble with this recently. Just glancing over that tutorial, it doesn't look right to me. It would make sense to rotate a view *vector* using a rotation quaternion, but the tutorial (and your code) appears to be rotating a point, i.e. a view *target*.

I'd have to dig into it a little more to be sure of my answer, but you might try the following:

Comment out this:

Vector axis = Vector::cross(Vector(c_view.getX() - c_position.getX(),
c_view.getY() - c_position.getY(),
c_view.getZ() - c_position.getZ()), c_up);

And replace it with:

Vector axis = Vector::cross(c_view, c_up);

Then comment out:

gluLookAt(c_position.getX(), c_position.getY(), c_position.getZ(),
c_view.getX() , c_view.getY() , c_view.getZ(),
c_up.getX() , c_up.getY() , c_up.getZ());

And replace it with:

Vector target = c_position + c_view;
gluLookAt(c_position.getX(), c_position.getY(), c_position.getZ(),
target.getX(), target.getY(), target.getZ(),
c_up.getX(), c_up.getY() , c_up.getZ());

(This is all assuming that your quat mult function is correct, which I didn't check.)

Anyway, I'd try those changes and see what happens.

[Edit: My code is changing your view target to a view vector. One thing I forget is that your view vector initialization will probably have to be changed. Setting it to [0 0 -1] might work.]

 User Rating: 1986   |  Rate This User  Send Private MessageView ProfileView Journal Report this Post to a Moderator | Link

So, I tried doing what you suggested and I am still having the same results as before (move mouse, nothing happens). However, when I print the target vector to a file and look at the values, it looks like it's doing something right. I don't know exactly what to expect, but the fact that all of them are unit vectors is certainly encouraging.

I don't really have much (read "any") experience with the gluLookAt function, so I'm guessing my problem now lies in some other silly mistake that I'm making elseware in my code. Thanks for the help, what you said has given me a better understanding of quaternions.

 User Rating: 1123   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Why not just use standard Euclidean Vector algebra i.e. arbitrary axis rotations?
Quaternions are just an overrated buzz-word IMHO.

[Edited by - iMalc on February 6, 2005 2:56:31 AM]

 User Rating: 1714   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Aside from the complexity of understanding the Quaternion rotations, this was a very simple program to write. Even then, this is not the most difficult piece of math I've digested; certainly plently of people will have no problems getting a working quaternion camera.

The question then becomes why do something that looks like (after just glancing at the link, correct me if I'm wrong) it's code would be drawn out and ugly when you could write something as quick and concise as quaternions. Once again I'm baseing this off a quick glance at your link, but I would also guess that a quaternion camera would be more efficient at run-time.

A little complexity will never stop a programmer from writing more efficient code =)

Update: I found the bug in my program, the camera works perfectly now.
Thanks for your help.

[Edited by - Fibonacci One on February 5, 2005 12:28:44 PM]

 User Rating: 1123   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Quote:
Update: I found the bug in my program, the camera works perfectly now.

What was the bug?

Quote:
Quaternions are just an overrated buzz-word IMHO.

Well, they are certainly overkill for some applications, but 'overrated buzz-word'? I don't know about that. (Although to be fair, you aren't the only one who feels that way - there have been some rather fiery discussions about this around the net.)

However, consider:

1. It's much easier to interpolate quaternions than matrices, which has all kinds of useful applications.

2. Quats take up less memory, which can be useful for, say, keyframe animations or console platforms.

3. Quats can be concatentated faster than matrices, which again can make a difference in an animation system.

4. Quats can be renormalized faster than matrices.

I will say that quats are probably not necessary for a standard 'FPS' camera, i.e. one where the only rotational DOFs are yaw and pitch. A 6DOF 'Descent-style' camera can still be done with matrices only (e.g. Descent :-), but a quat-based implementation is a good alternative and has some advantages.

Anyway, glad you got it working.

 User Rating: 1986   |  Rate This User  Send Private MessageView ProfileView Journal Report this Post to a Moderator | Link

Quote:

What was the bug?


In addition to what you helped me with, I had set my program to call gluLookAt after I finished rendering... Doh!

The bugs that are easiest to fix are often the most embarassing to admit to ever having in the first place =).

 User Rating: 1123   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Quote:
Original post by jyk
Quote:
Update: I found the bug in my program, the camera works perfectly now.

What was the bug?

Quote:
Quaternions are just an overrated buzz-word IMHO.

Well, they are certainly overkill for some applications, but 'overrated buzz-word'? I don't know about that. (Although to be fair, you aren't the only one who feels that way - there have been some rather fiery discussions about this around the net.)

However, consider:

1. It's much easier to interpolate quaternions than matrices, which has all kinds of useful applications.

2. Quats take up less memory, which can be useful for, say, keyframe animations or console platforms.

3. Quats can be concatentated faster than matrices, which again can make a difference in an animation system.

4. Quats can be renormalized faster than matrices.

I will say that quats are probably not necessary for a standard 'FPS' camera, i.e. one where the only rotational DOFs are yaw and pitch. A 6DOF 'Descent-style' camera can still be done with matrices only (e.g. Descent :-), but a quat-based implementation is a good alternative and has some advantages.

Anyway, glad you got it working.
Okay, in all fairness, perhaps I have used quaternions as little as you have arbitrary axis rotations. Though I do understand how to use both. Unfortunately what you've posted is simply myths about arbitrary axis rotations. (Sorry I said matricies earlier, I meant axes)

1. It's only easier for someone who's used it a lot more than the alternative. for a quat you have to interpolate 4 values right? For the arbitrary rotation you only have to interpolate the angle.

2. The information necessary to describe an arbitrary rotation is the same size as that of a quaternion.

3. Quats have to be converted back into a matrix at some point. The equivalent arbitrary rotation matrix is directly generated from an axis/angle with the same amount of effort.

4. This applies equally in both cases. You only have to normalise a quat if the arbitrary vector you were to rotate around was not unit length, I think.

I'm very sorry if I digress. I don't know why I'm bothering, but the truth is, the two methods are equivalent and neither has any real advantage over the other.
Quats really have only become more popular because of their cool name and funky imaginary mathematics. Read the rest of the article and you'll see.

 User Rating: 1714   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Quote:

3. Quats have to be converted back into a matrix at some point. The equivalent arbitrary rotation matrix is directly generated from an axis/angle with the same amount of effort.

To convert axis-angle to matrix, you need sin and cos. It is MUCH more expensive operation. Also, have you tried to combine axis and angle rotations together?
If you would really work with either thing, you would know that axis and angle is essentially converted to quaternion prior to doing something, e.g. conversion to matrix. Quaternion stores axis and sine and cosine of half-angle. To do something with axis and angle, you need to get sine and cosine of angle. What's the point in storing angle itself if we rarely need it?

Another thing. Quaternion is easily converted to axis and angle and back. You might have quaternion class with get_axisangle , set_axisangle. How it could possibly be more complex than axis and angle? Quaternions is , indeed, equivalent to axis and angle, but offer many computational benefits such as fast combination of several rotations.

Let anybody who think axis and angle is any simpler than quaternions will right now write combination of rotations. (You'll see that essentially, axis and angle is converted to quaternion, multiplied, and converted back. So in program where you use axis and angle, you'll do tons of sin and cos and asin and acos for absolutely no reason.)

That is, you have some point P that you want to rotate by A, then by B to obtain point P', where A and B is axis-angle structs. Compute C that rotation of P by C gives P'
If you can't do it, i consider that you don't know axis and angle.


My site / Clouds image gallery / voxelworld images gallery | "tnx++;" |Everything i said in that post is obviously ABSOLUTE TRUTH my unhumble opinion (lol).

 User Rating: 1634   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Quote:
1. [slerp is] only easier for someone who's used it a lot more than the alternative. for a quat you have to interpolate 4 values right? For the arbitrary rotation you only have to interpolate the angle.

You can interpolate a rotation around a *single axis* by interpolating a single value (the angle). But slerp interpolates between two *orientations*. I imagine there's a way to do this with axis/angle, but I've never seen an implementation so I can't say whether it would be more or less efficient than slerp.

Quote:
2. The information necessary to describe an arbitrary rotation is the same size as that of a quaternion.

Yes, initially I thought you were referring to matrices.

Quote:
3. Quats have to be converted back into a matrix at some point. The equivalent arbitrary rotation matrix is directly generated from an axis/angle with the same amount of effort.

Axis-angle to matrix requires sin() and cos(). Quat to matrix requires only multiplications and additions. So for that particular operation, at least, quats are faster.

Quote:
4. This applies equally in both cases. You only have to normalise a quat if the arbitrary vector you were to rotate around was not unit length, I think.

Right. Again, my original statement was in regard to matrices.

One thing that is as you say a 'myth' is that quaternions are the best or only way to prevent gimbal lock. The same thing can be accomplished with axis-angle or matrix representations.

I'm not a mathematician, so I can't give a rigorous comparison of quaternion and axis-angle representations and their equivalence or lack thereof. For example, I've never looked into concatenating axis-angle rotations, so I'm not even sure how to do it and therefore can't say how it compares to the equivalent quaternion operation. (Dmytry's post seems to suggest that it essentially involves conversion to quaternion form; if that's true, quats would be a win in this case.)

Based on my current knowledge, however, quaternions are easier and/or faster to concatentate, interpolate, and convert to matrix form than axis-angle. As I am always trying to learn, though, I welcome any corrections or clarifications.

Regardless, I do question the common assertion that people only use quaternions because they sound 'cool' :-) That may be true for a beginner, but you've got to give more experienced programmers some credit! I have seen slerp discussed and implemented countless times, but I've never seen an equivalent implementation for interpolating axis-angle rotations (that doesn't mean there isn't one). So for me at least, quaternions are a better and more powerful choice.

 User Rating: 1986   |  Rate This User  Send Private MessageView ProfileView Journal Report this Post to a Moderator | Link

Okay well that all pretty much seems correct, I suppose.

However, in order to to perform smooth, higher order, i.e. hermite or bezier, interpolation between quaternions, you have to take the log of the quat and linearly interpolate that, then take the exp of the result. These operations on quats involve log, exp, sqrt, sin, cos, and atan. So in some cases there's no getting away from good old sin & cos anyway.
If I understand correctly, the Euclidean method already gives you smooth angular movement.

Also, I think that for combining two simultaneous seperate rotations you could probably calculate the rotation axis resulting from the combination of the two rotations and just use that instead. It's not something I've done so I'm not going to pretend I know anything about such a method, or whether it's fast or slow by comparison.

I really don't think there's much in it either way, and I'm very happy to learn even more someday, but this thread may be neither the time nor the place.
For now I just hope we'll all keep an open mind, and hope that everyone at least acknowledges that there is more than one way to <insert cliche here>.

 User Rating: 1714   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Quote:

However, in order to to perform smooth, higher order, i.e. hermite or bezier, interpolation between quaternions, you have to take the log of the quat and linearly interpolate that, then take the exp of the result. These operations on quats involve log, exp, sqrt, sin, cos, and atan. So in some cases there's no getting away from good old sin & cos anyway.

Of corse, there's always tasks that requir sin and cos.
But with axis and angle, you need plenty of trig for literally everything, including most frequently used operations. Really, write smooth interpolation between two rotations, done with axis and angle.

Quote:

For now I just hope we'll all keep an open mind, and hope that everyone at least acknowledges that there is more than one way to <insert cliche here>.

I'd even aknowledge that, in any math application, there's *infinitely* many ways to do something. But it does not mean that i say that all ways is equally good, understand? For example, i can invent dmytryternions with conversion to quaternion defined as
q.a=sin(dmy.a)
q.b=sin(dmy.b)
q.c=sin(dmy.c)
q.d=sin(dmy.d)
, hehe. If I'll feel like doing ... , I could derive combination of rotations, conversion to matrix, etc.
Or i can
q.a=cos(dmq.a)
q.b=dmq.b*sin(dmq.a)
q.c=dmq.c*sin(dmq.a)
q.d=dmq.d*sin(dmq.a)


**************************************************
Until people who defend axis and angle will post combination of rotations, i am assuming that they do not understand axis and angle. Really, it's not like "discussion of people who understand and are used to method X with people who understand and are used to method Y"

**************************************************

I don't say that axis and angle is bad. They have it's own use. For example of usage of axis and angle, let i want to interpolate between orientations A and B storen in quaternions. It can be easily enough done using axis and angle at some point:
C=(~A)*B
C.GetAxisAngle(x,y,z,a);
return A*AxisAndAngleToQuaternion(x,y,z,a*t);
(it is not efficient, but works)
Happy?
Note that we use axis and angle for rotation velocity, not for orientation! Then, if you say you understand axis and angle, pls. write that using axis and angle for both rotation velocity and orientation.

[Edited by - Dmytry on February 7, 2005 3:09:43 AM]


My site / Clouds image gallery / voxelworld images gallery | "tnx++;" |Everything i said in that post is obviously ABSOLUTE TRUTH my unhumble opinion (lol).

 User Rating: 1634   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Quote:
Original post by iMalc
However, in order to to perform smooth, higher order, i.e. hermite or bezier, interpolation between quaternions, you have to take the log of the quat and linearly interpolate that, then take the exp of the result. These operations on quats involve log, exp, sqrt, sin, cos, and atan. So in some cases there's no getting away from good old sin & cos anyway.
If I understand correctly, the Euclidean method already gives you smooth angular movement.

afaik, the easiest, fastest and best way to interpolate two quats is by using both quaternions and axis-angle. depriving yourself of either would just be plain silly.

if you store your original orientation as a quat and your rotationspeed as axis angle, all you need to do to get the orientation at t=anything is do quat*(axisangle*t).toquat();

doesnt get any simpler i think. besides, using a quat to store a difference in orientation simply isnt right, because you can only represent a limited subset of rotationdifferences with them. on the other hand, storing an orientation as axis-angle doesnt make much sense either, because, computational inefficiencies aside, there would be infinite ways to store any orientation. a quaternion, in analogy with complex numbers, automagicly wraps around, making it perfect for orientations.

matrices are only excusable really if youre interested in more than rotation and uniform scaling. but any such transforms can be applied after the endresult is converted to matrix form for efficient mass-transformation.

 User Rating: 979   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

All times are ET (US)

Post Reply
 Last Thread Next Thread 
Forum Rules:
You may not post new threads
You may not post replies
You may not edit your posts
You may not use HTML in your posts
Jump To:
Administrative Options: