# FPS camera problems

This topic is 3974 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

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 on other sites
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;

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 productlook = 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 on other sites
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 deltalook = 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 on other sites
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 on other sites
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 on other sites
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.

• 16
• 11
• 10
• 9
• 49
• ### Forum Statistics

• Total Topics
631392
• Total Posts
2999743
×