Sign in to follow this  
Kaelic

Quaternion Camera Woes

Recommended Posts

I've read more quaternion tutorials I can count. I've applied them all, failed so many times. Tried to understand them. I finally got one relatively working, just to find something bizzarely screwed up with it. I did the Nehe Quaternion Camera Class tutorial on this site and applied it to my project. For some reason when I rotate the pitch and it gets to about a full unit of the Y axis using gluLookAt, it slows down and eventually starts coming back the other way. I literally hold the key down to increment the pitch and watch it go up up up...slower..slower...then back down again. To repeat the same thing at the bottom of the screen. What on earth could cause this? Here's my different bits of code: Does all the matrix work and determines Target vector coordinates.
void CCamera::setPerspective()
{
	float matrix[16];
	CQuaternion qResult, qPitch, qYaw;

	qPitch.getAxisAngle(pitch, 1.0f, 0.0f, 0.0f);
	qYaw.getAxisAngle(yaw, 0.0f, 1.0f, 0.0f);

	qResult = qPitch * qYaw;
	qResult.newMatrix(matrix);

	glMultMatrixf(matrix);

	qPitch.newMatrix(matrix);
	vTarget.setY(matrix[9]);

	qResult = qYaw * qPitch;
	qResult.newMatrix(matrix);
	vTarget.setX(matrix[8]);
	vTarget.setZ(matrix[10]);

	return;
}
This is the function to increment the pitch. It's going up by 0.2 every program cycle when holding down a key.
void CCamera::changePitch(float degrees)
{
	if(degrees < maxPitch)
	{
		pitch += degrees;
	}
	else
	{
		if(degrees < 0)
		{
			pitch -= maxPitch;
		}
		else
		{
			pitch += maxPitch;
		}
	}

	if(pitch > 360.0f)
	{
		pitch -= 360.0f;
	}
	else if(pitch < -360.0f)
	{
		pitch += 360.0f;
	}

	return;
}
This sets up the quaternion
void CQuaternion::getAxisAngle(float rotationAngle, float xAxis, float yAxis, float zAxis)
{
	if((xAxis != 0 && xAxis != 1) ||
		(yAxis != 0 && yAxis != 1) ||
		(zAxis != 0 && zAxis != 1))
	{
		float factor = (float)((xAxis * xAxis) + (yAxis * yAxis) + (zAxis * zAxis));

		float length = (float)sqrt(factor);

		xAxis /= length;
		yAxis /= length;
		zAxis /= length;
	}

	//Convert angle degrees into radians
	float angleRadians = (float)((rotationAngle * pi) / 180.0f);

	//Half sin of radians, so not to evaluate twice
	float sine = (float)sin(angleRadians / 2.0f);

	//Create quaternion
	x = xAxis * sine;
	y = yAxis * sine;
	z = zAxis * sine;
	w = (float)cos(angleRadians / 2.0f);

	//Normalise new quaternion
    normalise();

	return;
}
matrix generation code
void CQuaternion::newMatrix(float *matrix)
{
	if(!matrix) return;
	
	//first row
	matrix[0] = 1.0f - 2.0f * (this->y * this->y + this->z * this->z);
	matrix[1] = 2.0f * (this->x * this->y + this->z * this->w);
	matrix[2] = 2.0f * (this->x * this->z - this->y * this->w);
	matrix[3] = 0.0f;

	//second row
	matrix[4] = 2.0f * (this->x * this->y - this->z * this->w);
	matrix[5] = 1.0f - 2.0f * (this->x * this->x + this->z * this->z);
	matrix[6] = 2.0f * (this->z * this->y + this->x * this->w);
	matrix[7] = 0.0f;

	//third row
	matrix[8] = 2.0f * (this->x * this->z + this->y * this->w);
	matrix[9] = 2.0f * (this->y * this->z - this->x * this->w);
	matrix[10] = 1.0f - 2.0f * (this->x * this->x + this->y * this->y);
	matrix[11] = 0.0f;

	//fourth row
	matrix[12] = 0.0f;
	matrix[13] = 0.0f;
	matrix[14] = 0.0f;
	matrix[15] = 1.0f;

	return;
}
That's about all that is relevant I know of. Best I can think the problem to be is my gluPerspective setup or SOMETHING. The quaternion code seems 100% correct from what I know. Pleeease help, this is a university assignment I need finished soon. Driving me mad. GL setup
			glViewport(0,0,width,height);
			glMatrixMode(GL_PROJECTION);
			glLoadIdentity();
			gluPerspective(45.0f, (float)width/(float)height, 1.0f, 1000.0f);
			glMatrixMode(GL_MODELVIEW);
			glClearColor(0.0f, 0.0f, 0.0f, 1.0f);	
			glLoadIdentity();

			glClearDepth(1.0f);
			glEnable(GL_DEPTH_TEST); //enable
			glDepthFunc(GL_LEQUAL);
			glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
            
			SetCursorPos(800/2, 600/2);

Share this post


Link to post
Share on other sites
Quote:
For some reason when I rotate the pitch and it gets to about a full unit of the Y axis using gluLookAt, it slows down and eventually starts coming back the other way. I literally hold the key down to increment the pitch and watch it go up up up...slower..slower...then back down again. To repeat the same thing at the bottom of the screen. What on earth could cause this?


Why does it go up, slow down, then come back down? It's simple maths. Sin(45) = Sin(90 + 45). Try this.

Look up at 45 degrees. 'Forwards' is where your chest is pointing. Now, if you were to somehow look 'up' at 135 degrees... you'd be looking behind you. Your chest (forwards) would in fact be pointing in the opposite direction.. not to mention that your 'up' (where the top of your skull is pointing) has changed from towards the ceiling to towards the floor. A 'pitch' of more than 90 degrees necessarily changes your 'pitch' to the opposite direction, basically. You can either hack together a fix - rotate the yaw by 180 when you pitch up over 90 degrees - or you can use quaternions as they are intended.

I'll try to explain. From what I can tell, you're using two quaternions - one for yaw, and one for pitch. This, to start with, is unneccessary.

Euler angles (yaw, pitch and roll) can describe any orientation. Similarly, a matrix can describe any orientation. Same goes for a quaternion. The only difference between the three ways of representing / storing an orientation is the way they behave when you concatenate (put together) two or more.

In your case - pitch and yaw. I imagine you're doing a FPS-style camera. If you apply a pitch of 90 degrees up, then a yaw of 90 degrees, your camera will be 'on it's side' - not what you're after. But if you perform the yaw first, then the pitch - your camera will be 'right-side up'. Two different orientations by applying the components in different orders.

Quaternions have the advantage of avoiding 'gimbal lock'. I'm sure you've heard this term before. To explain - the example of yaw and pitch I used before. In the first case (pitch, then yaw), the 'yaw' rotation gave pretty much the same effect as a 'roll' would have from the original orientation. You can no longer effectively apply a 'roll', and have your orientation behave the way you want. You've lost a degree of freedom.

(It may help to use your head or hands to try to understand this - though looking 'up', then rolling your head to the right, ear-to-shoulder may get you strange looks if you're in an office).

You should keep your orientation in a single quaternion. Then, when you want to change your orientation by a certain amount (yaw, pitch, roll, or some of each), create a 'rotation' quaternion from the change. Say, if you want to pitch up 45 degrees, do something like :

RotationQuaternion = QuaternionFromEuler(0, 45, 0);

Then, multiply the original orientation quaternion by the rotation quaternion :

OrientationQuaternion *= RotationQuaternion;

And tadaa! Your orientation will be changed appropriately. Works for a change in yaw, pitch and roll, all at once, and avoids the need to apply them in a particular order.

This works for a 'flight' style camera. If you're after a FPS camera style, you don't really need a quaternion - you just need a yaw and pitch value, and a couple checks / fixes for things like 90 or -90 degree pitches, etc.

Wow, that was a novel.

Hope that helps,

Hew T.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this