Sign in to follow this  
patrrr

FPS camera problems

Recommended Posts

patrrr    1323
I'm trying to implement a camera like the ones you find in FPS games where you can look in different separate directions with the mouse. I've managed to separate the direction rotation matrices, but this has introduced a weird bug: my "forward" vector isn't always the same as the camera's forward vector! This is my code:
CVector up(0.0f, 1.0f, 0.0f), look(0.0f, 0.0f, 1.0f), right(1.0f, 0.0f, 0.0f);
CMatrix rotX = Rotation(right, orientY); // rotation around the X axis, orientY radians
look = rotX * look;
up = rotX * up;

// look and up are correct

CMatrix rotY = Rotation(up, orientX); // rotate around the up axis, this yields the behavior I want. 
// CVector(0.0f, 1.0f, 0.0f) makes the camera rotate on the sloping plane caused by rotX
look = rotY * look;
right = rotY * right;
up = rotY * up;

// the Y component of "look" changes when I rotate on the Y axis (change orientX), this is probably the
// source of the bug. It remains the same as after rotX if I use CVector(0.0f, 1.0f, 0.0f) as up in the
// rotY-rotation. I only want look.y to change when orientY has changed.

CMatrix mat(-right.getNormalized(), -up.getNormalized(), look.getNormalized());
transform = mat * Translation(position);  // "transform" is used as a basis when rendering
I'm then adding the look vector to the position when moving forward. I tried to save the Y component from the first rotation and using that to override the Y component calculated in the second rotation, it worked but was ugly and there were smaller bugs. And this is weird: how can the look vector (which is the same as the Z axis of the camera's transformation matrix, I've checked) have another direction than what the camera points at - what I see? This is really getting frustrating, it looks OK to me.

Share this post


Link to post
Share on other sites
Gage64    1235
First of all, this line:

up = rotY * up;


is not needed. Rotating a vector around itself should not change the vector (but if the above is executed it probably will slightly because of numerical errors).

Second, after you've rotated one vector around another (for example up around right), don't rotate the other one. In other words, don't do this:


look = rotX * look;
up = rotX * up;


Instead, do this:


look = rotX * look;
up = crossProduct(look, right); // The order is important! (although I may have
// gotten it wrong)


In theory, this is the same thing, but because of numerical errors it's more robust.

Third, in this line:


CMatrix mat(-right.getNormalized(), -up.getNormalized(), look.getNormalized());


you're not negating look. Is this an oversight or am I missing something?

Quote:

the Y component of "look" changes when I rotate on the Y axis (change orientX), this is probably the source of the bug. It remains the same as after rotX if I use CVector(0.0f, 1.0f, 0.0f) as up in the rotY-rotation. I only want look.y to change when orientY has changed.


Not sure if I completely understand you, but this isn't surprising. If you rotate a vector around any vector that's not (0, 1, 0), that vector's Y coordinate will change. If you don't want this to happen, always rotate around (0, 1, 0) even if up is not actually (0, 1, 0).

One additional step that you should be doing is making sure that the vectors are all orthogonal to each other. Like I said, in theory they should be but numerical errors may cause them to be slightly non-orthogonal, and these errors will accumulate unless you correct them.

you can do it this way (after the rotations):


// Again, watch the order in which you take the cross product
look = crossProduct(right, up);
right = crossProduct(look, up);
up = crossProduct(look, right);


This will make sure they are all orthogonal. Note that you only need to do this when using them to build the view matrix, not every time you do the rotations.

Finally, there's code on this site that implements two camera behaviors (first person and spaceship) using the same approach, and demonstrates the things I mentioned above (note that it uses D3D and therefore a left-handed coordinate system, and by the looks of it you're using a right-handed-one. But you should be able to understand where the differences are).

Share this post


Link to post
Share on other sites
patrrr    1323
Thanks for your reply!
I tried the code from that page:

CVector up(0.0f, 1.0f, 0.0f), look(0.0f, 0.0f, 1.0f), right(1.0f, 0.0f, 0.0f);

CMatrix rot;
rot = Rotation(right, orientX); // keep in mind, orientX is not a delta
look = rot * look;
// up = rot * up;

rot = RotationY(orientY);
look = rot * look;
right = rot * right;
up = CrossProduct(look, right);

transform = CMatrix(right.Normalize(), up.Normalize(), look.Normalize()) * Translation(-position);


The results: rotating around Y (yaw, moving the mouse to the left or right) has the wrong effect, it simply rotates the camera around it's own Y instead of the global Y, and the original bug is still there: I'm not always walking in the direction I'm facing (this gets worse when facing straight up or straight down *) even if I use the matrix's Z-axis!
I get skewing if I use the global up (0, 1, 0) instead of the cross product when I rotate.
I'm afraid there's something wrong with my matrix code, I've written it myself and there might be bugs. Any easy way to detect these? I've run some tests and they seem to come out fine.

*)
I'll try to explain the bug a little better:
1.
I orient the camera in positive Z, then rotate straight down 90° facing the floor.
I can now move towards the floor by pressing the forward key (which adds the Z axis from the transformation matrix to the position)

2.
I now orient the camera in positive X, and straight down.
Pressing the forward key results in a movement along the X axis instead of straight down through the floor!


If I do the same as in 1 but in negative Z I move away from the floor when pressing forward.

Share this post


Link to post
Share on other sites
Gage64    1235
Quote:

I get skewing if I use the global up (0, 1, 0) instead of the cross product when I rotate.


You shouldn't use it instead. I meant that you should rotate around it instead of rotating around the up vector (IMO this behavior is more typical for an FPS camera anyway):



CMatrix rotY = Rotation(CVector(0, 1, 0), orientX);
look = rotY * look;
right = rotY * right;
up = crossProduct(right, up);



One other thing - use orientX/orientY as delta angles, not as an absolute angles. Right now I'm not sure if that should make a big difference but that's how the code in that page does it.

Also, before generating the rotation matrix, are you making sure that the vectors are all orthogonal like I showed above?

And finally make sure you're generating the matrix correctly. Are you using the vectors as rows or columns? I think you should use them as rows (assuming you're using OpenGL and a right-handed coordinate system).

Share this post


Link to post
Share on other sites
patrrr    1323
Thanks alot for your help, it's now working! [smile]
I forgot to use the inverse of the transformation matrix, and this was probably one of the bugs. Making sure the axis are orthogonal is a nice tip, it'd probably bite me on the ass later on if I didn't do it.
And BTW, how did you see I was using a right-handed coordinate system?

Share this post


Link to post
Share on other sites
Gage64    1235
Quote:

And BTW, how did you see I was using a right-handed coordinate system?


Because when you do vector-matrix multiplication, you're multiplying by the matrix on the left, which is the common practice when using a right-handed coordinate system.

Glad to hear you got it to work.

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