Rotating Camera using Quaternions?

Started by
6 comments, last by Waaayoff 14 years, 1 month ago
I don't know how to do all of it, so far i have only done this: Quaternion Class:
class Quaternion
{
public:
	double x, y, z, w;

	Quaternion() { x = 0; y = 0; z = 0; w = 0;}				
	void NormalizeQuat()
	{
		double Length = sqrt( x * x + y * y + z * z + w * w);
		
		x /= Length;
		y /= Length;
		z /= Length;
		w /= Length;
	}

	void ConjugateQuat()
	{
		x = -x;
		y = -y;
		z = -z;
	}

	Quaternion operator * (Quaternion B)
	{
		Quaternion Temp;

		Temp.x = w*B.x + x*B.w + y*B.z - z*B.y;
		Temp.y = w*B.y - x*B.z + y*B.w + z*B.x;
		Temp.z = w*B.z + x*B.y - y*B.x + z*B.w;
		Temp.w = w*B.w - x*B.x - y*B.y - z*B.z;

		return Temp;
	}
};
This Function handles the rotation, but as you can see, it's mostly empty (i don't know what to do next). What i did is Get the change in mouse position in x and y direction using the WM_INPUT message. I then created 2 long variables that hold the angle which is simply the change in coordinates divided by a factor:
void RotateCameraAroundScene()
{
	long xAngle = Input.MouseChangeX / Factor;
	long yAngle = Input.MouseChangeY / Factor;
Please note that i want the camera to rotate around the origin and keep a certain distance away from it. (This is for a 3D tetris game) I know that i can rotate the scene instead of the camera, but i can use the same code to do it, right?
"Spending your life waiting for the messiah to come save the world is like waiting around for the straight piece to come in Tetris...even if it comes, by that time you've accumulated a mountain of shit so high that you're fucked no matter what you do. "
Advertisement
You need to add to your quaternion a constructor which takes an AxisAngle. Create two quaterions, one for each rotation. Multiply them together and by the current transform to obtain the rotated transform.
That's what i mean, i don't know how. What do i normalize? what exactly is the conjugate used for? How do i specify around which axis to rotate..

Please tell me what to do step by step or maybe give me the code so i can study it? (a code a beginner can understand)
"Spending your life waiting for the messiah to come save the world is like waiting around for the straight piece to come in Tetris...even if it comes, by that time you've accumulated a mountain of shit so high that you're fucked no matter what you do. "
Quote:Original post by Waaayoff
That's what i mean, i don't know how. What do i normalize? what exactly is the conjugate used for? How do i specify around which axis to rotate..

Please tell me what to do step by step or maybe give me the code so i can study it? (a code a beginner can understand)
What graphics API are you using?

Also, is there any particular reason you're using quaternions for this? If you're new to 3-d math, it seems it might make more sense to hold off on using quaternions and just stick with basic matrix and vector math for the time being.
Direct3D.

Well i wanted to use Euler angles, but everyone kept telling me not to because of something called gimbal lock, whatever that is. I don't mind the math, it's not like i don't understand it, it's just that i don't know what the math IS.
"Spending your life waiting for the messiah to come save the world is like waiting around for the straight piece to come in Tetris...even if it comes, by that time you've accumulated a mountain of shit so high that you're fucked no matter what you do. "
Quote:Original post by Waaayoff
Direct3D.

Well i wanted to use Euler angles, but everyone kept telling me not to because of something called gimbal lock, whatever that is.
Don't listen to 'everyone' :-) There's a lot of misinformation and misunderstanding out there regarding both Euler angles and quaternions, so my advice would be to learn the material yourself so that you can make your own determinations about which tools to use and when.

In any case, based on what you've described, an Euler-angle pair (e.g. pitch and yaw) sounds like an appropriate solution. It sounds to me like what you want is a standard 'orbital' camera that can rotate laterally around the target point and also pitch up and down. For this type of motion, Euler/spherical angles are perfectly appropriate, gimbal lock is not an issue, and quaternions are not needed (well, quaternions aren't really ever *needed*, per se, but they do have some advantages in certain situations).

Although using 'look at' functions such as gluLookAt() or the DirectX equivalent can sometimes add unnecessary confusion and just get in the way (IMO), for an orbital camera I think a look-at function can be a good choice. All you need to do is track the yaw, pitch, and distance to the target, perform a spherical-to-Cartesian coordinate conversion to generate the camera position from these parameters, and then plug the parameters into a DX 'look-at' function to get your view matrix. (Note that if it's possible for the camera to be more or less directly above or below the target point, you may have to handle those cases separately.)

Obviously I glossed over the details there, but I'd recommend maybe searching the forum archives for (e.g.) 'orbital camera' and see if you can find some examples. And if you get stuck, I or someone else should be able to offer some more details on how an orbital camera can be implemented.
I just did an orbital camera recently from going through one of the gameDev tutorials! so I will have a go at going over it.

Here's the mouse Input function, from the looks of it you already have your own way to get change in mouse movmeent though.

mouseMove(HWND hWnd, Camera *camera){  static int mousex, mousey;  POINT mouse;	  GetCursorPos(&mouse);	// Get the 2D mouse cursor (x,y) position  ScreenToClient(hWnd, &mouse); // Mouse coords within the window    if(m_keys[VK_LBUTTON])  {    tVector3 MouseDirection;    MouseDirection.x = ((float)mousex - (float)mouse.x)/100.0f;     MouseDirection.y = ((float)mousey - (float)mouse.y)/100.0f;    camera->update(&MouseDirection);  }  mousex = mouse.x;  mousey = mouse.y;}

All that's been done here is getting the change Mouses position in the x and y directions and I have then passed them into camera->update were the magic will happen.

There are a few maths functions you'll need for this, and they are: Vector crossProduct, create unit Length vector, quaternion conjugate, convert vector to quaternion and a quaternion multiply, I see you have some there already.

For the camera.update function
void Camera::init(){  m_lookat = tVector3(0.0f, 0.0f, 0.0f);  m_position = tVector3(-100.0f,100.0f,-100.0f);  m_up = tVector3(0.0f,1.0f,0.0f);}void Camera::update(tVector3 *MouseDir){  // axis to rotate around will be the normal to the UP vector and  // vector between the camera position and lookat.  tVector3 UP(0.0f,1.0f,0.0f);// World Space Up Vector  tVector3 v = m_lookat - m_position;  tVector3 axis = Math::crossProduct(&v, &m_up);  // Turn it into a unit vector  axis = Math::unitVector(&axis);  rotateCamera(MouseDir->y, &axis);  rotateCamera(MouseDir->x, &UP);}

Here I'm creating the axis that you will want to rotate about.
The rotation about the UP vector is fairly straight forward.
The 'x-axis' for the other rotation is given by the cross product of the up vector and the vector between the camera's lookat and current position. This is a local x-axis to the camera, not the world x-axis.

Here's a wee diagram to illustrate this.
axis

What we want to do now is rotate the view vector(v = m_lookat - m_position) by an angle(MouseDir.x/y) about an axis(UP and axis). So now we have everything needed to rotate the camera so lets look at the rotateCamera function.

A quaternion to represent a rotation is given by
  R = (w,v) were  w = cos(theta/2) //scalar component  v = u sin(theta)/2 // u is a unit Vector

In this case, u is 'axis'/'UP' and theta is the MouseDir.x/y

Rotating a point in 3d space using this quaternion is done by:
  P(rotated) = R P R' // R' is the conjugate of R

In this case P refers to our view vector (m_lookat - m_position)
(note this will only work is q if a unit quaternion which it will be as long as your axis is a unit vector, else you would need to use the inverse or R instead of the conjugate.)

So rotating the vector now boils down to 2 quaternion multiplications.
void Camera::rotateCamera(float angle, tVector3 *axis){  tVector3 view = m_lookat - m_position;  quaternion R, result;    R.x = axis->x * sin(angle/2.0f);  R.y = axis->y * sin(angle/2.0f);  R.z = axis->z * sin(angle/2.0f);  R.w = cos(angle/2.0f);  quaternion conjR = Math::conjugate(&R);  quaternion v = Math::vecToQuat(&view);              // v is the point we wish to rotate  //Result = R * V * R'  quaternion VmultConjR = Math::multiply(&v, &conjR); // V * R'  result = Math::multiply(&R, &VmultConjR);           // R multipled by the above                                                      // Quats are 'associative'  m_position.x = m_lookat.x + result.x;  m_position.y = m_lookat.y + result.y;  m_position.z = m_lookat.z + result.z;}

You can do the quaternion multiplications either (R*V)*R' or R*(V*R') as long as the order is the same.
The rotated view vector is now called result. To then convert this into the new position of the camera in world space it will be the camera's lookat + result.

Now the camera class will have the camera position and lookat you can extract and slap into your gluLookat or DirectX equivilent.

Theres 1 extra thing missing here though. The camera can't go past the vertical.
To do this you will need to change the local up vector (m_up) this will be done by applying the same rotation to the m_up as you did with view. This will allow the camera to go around in a full sphere..ideal if yer idea is like tetrisSphere!
Thank you soo much SantosaSabrosa!
"Spending your life waiting for the messiah to come save the world is like waiting around for the straight piece to come in Tetris...even if it comes, by that time you've accumulated a mountain of shit so high that you're fucked no matter what you do. "

This topic is closed to new replies.

Advertisement