View Full Version : Camera with fixed axis for yaw and vertical movement

08-02-2013, 09:00 AM

I have a transformation matrix for a camera that consists of a Translation and a Rotation. The rotation was a product of yaw and pitch. To make the camera intuitive (at least for me) I decided that vertical translation should be done with Space Bar and C, respectively up and down, no matter where you are looking, so its a translation along the global (0,1,0) axis. As for horizontal translation, A and D count as strafing left and right along the camera's local Left axis, which can be taken from camWorldMatrix[0][0..2]. As for S and W, they make the camera go back and forth along the normalized projection of the camera's local Z axis onto the XZ plane, which prevents you from using S and W for vertical movement. As for the rotation, pitch is done by rotating the camera around the camera's local X axis, so that you look up and down, but yaw is done by rotating it around the global Y axis, to avoid having roll. I also need to clamp the pitch amount or the local Z axis so that pitch can only go from -90 to 90 degrees instead of doing flips. I've managed to do most of this by only using one matrix for the camera instead of dividing into Translate, Yaw and Pitch matrices or storing an up, side and front vector since they are already stored in the camera's world matrix. The problem is, my mouse controls pitch and yaw using the delta mouse movement, i.e. the cursor's translation between frames, which means I have to grab last frame's camera transformation matrix which is Translation*Rotation and separate them by creating a Translation matrix with camera's position given by camWorldMatrix[3][0..2] and then multiplying the inverse of that with my Translation*Rotation matrix to get the Rotation matrix. After having the Translation and Rotation separately, I can find the new frame's camWorldMatrix by doing newT*T*newR*R where T and R are the Translation and Rotation matrices I just got and newR is the new frame's Rotation calculated using mouse delta input and newT is the new frame's Translation calculated using keyboard delta input.

Also, for calculating the Rotation matrix out of Yaw and Pitch matrices, I seem to have to do Yaw^-1 times Pitch, otherwise the camera will Roll, I've figured this by doing some algebra but I have no idea why it happens so if someone could explain it to me I would be happy! The Yaw matrix is basically a rotation over vec3(0,1,0) while the Pitch matrix is a rotation over vec3(R*vec4(1,0,0,0)) where R is the previous frame's Rotation.

I'd also like to know if there's a better way to do all this and be able to clamp the pitch between -90 and 90 since I can't do that with my implementation. I think I may be over-complicating :P

EDIT: my framework automatically multiplies all matrices along the tree that leads to the camera and then inverts the matrix to find the View matrix, so I can easily divide the matrix into T and R or T, Pitch and Yaw, if that would simplify things

08-02-2013, 01:28 PM
Aside: I'd suggest changing your coordinate system so that the ground plane is X-Y while the vertical direction is Z. Using Y for the vertical axis makes no sense; it's a consequence of the (baseless) assumption that the initial view orientation is facing toward the horizon. While that happens to be the direction which people normally face for most of their waking life, there isn't actually any reason to assume that it should be the initial orientation of OpenGL's view.

In the following, Z is vertical.

Pitch and yaw should be stored as angles. The rotation matrix should be constructed from these (as well as the horizontal and vertical positions) each frame. Maintaining position and orientation as a matrix is unwise if movement and orientation are referenced to the world (however, it makes sense for e.g. a flight simulator where the controls are referenced to the current orientation rather than the world).

The origin needs to be updated incrementally, i..e generating a motion vector with the X component controlled by A/D and a Y component from W/S, then rotating the X,Y motion vector by the yaw angle and adding the result to the previous position. A common beginner mistake is to fail to realise that you cannot accumulate a sequence of move,turn,move,turn,... by accumulating the moves and turns independently. Updating Z is simply a matter of incrementing/decrementing the value in response to Space/C.

The end result should be equivalent to:

translate(x, y, 0);
translate(0, 0, z);
rotate(0, 0, 1, yaw);
rotate(1, 0, 0, pitch);

As OpenGL doesn't have a camera, you have to prepend the inverse of the camera matrix to the model-view matrix before rendering the scene. This is easy enough if you have a matrix library with an inverse operation. Alternatively, as (A*B)-1 = B-1*A-1, the same effect can be achieved by just performing the inverse transformations in the reverse order, i.e.

glRotatef(1, 0, 0, -pitch);
glRotatef(0, 0, 1, -yaw);
glTranslatef(-x, -y, 0);
glTranslatef(0, 0, -z);