The problem is that by storing a target position, you are making something that is conceptually really simple a lot more messy. For one, you can't simply rotate a target position, because it would rotate around the origin instead of the camera position. You can't just linearly move it around either, as you might have noticed from things not working. So stop dealing with "which point am I looking at" and think about "which direction am I looking in".

For an fps camera, you probably want to limit your up/down rotation to +/-89°, so in your case, the otherwise ugly Euler Angles are the easiest approach. Why not 90° you might ask? Because of something many people don't quite understand about the lookAt helper function. "Up" must be roughly your local up (ie. imagine sticking a pencil in the top of your head, that's the "up" lookAt needs. Now, simply using (0,1,0) works great, until you rotate exactly +/-90° up/down (where it should be (0,0,1) or (0,0,-1)) or more than 90° (where it has to be (0,-1,0)).

So what you **really** need for lookAt is your position, a point to look at (or the direction to look in) and a vector pointing "up". That just happens to be the 4th, 3rd and 2nd column of the cameras transformation matrix, but we just want fps style and take some shortcuts:

//Don't try to use this for a flight simulator without stocking up on aspirin first float pitch = 0; float yaw = 0; if (left) yaw -= rotationSpeed * mouseDelta; if (right) yaw += rotationSpeed * mouseDelta; if (up) pitch -= ... ... while (yaw < 0) yaw += 360; while (yaw > 360) yaw -= 360; if (pitch > 89) pitch = 89; ... matrix rotation = createRotationMatrix(pitch, 1,0,0) * createRotationMatrix(yaw, 0,1,0); //At this point, you can either transform some default view direction or just have the camera look right by default and extract the columns of this matrix. //For simplicity, we use lookAt viewMatrix = lookAt(cameraPosition, cameraPosition + rotation.3rdColumn, rotation.2ndColumn);

Probably a lot more reusable and not really much more complicated:

matrix rotation; vector translation; //Note that left/right and up/down are a different order to use the global "up" and the local "right" if (left/right) rotation = rotation * createRotation(mouseDelta, 0,1,0); if (up/down) rotation = createRotation(mouseDelta, 1,0,0) * rotation; if (forward/backward) translation += distance * rotation.3rdColumn; if (strafeLeft/Right) translation += distance * rotation.1stColumn; matrix transformation = rotation;transformation.4thColumn = translation; viewMatrix = inverse(transformation); //Use the simple version (same as in lookAt)

You can also keep it all in one matrix, which makes translation easier, but requires extra steps when rotating around a global axis.

matrix transformation; if (left/right) { vector position = transformation.4thColumn; transformation = transformation * createRotation(yaw, 0,1,0); //This will also rotate our position around the origin transformation.4thColumn = position; } if (up/down) transformation = createRotation(mouseDelta, 1,0,0) * transformation; //This will rotate around our current position if (movement) transformation = createTranslation(x,y,z) * transformation; //This will use local x,y,z if (gettingPushed) transformation = transformation * createTranslation(x,y,z); //This will use local x,y,z viewMatrix = inverse(transformation);

This is obviously pseudo code. Since 3D graphics is basically nothing but vector and matrix math, there simply is no way to get around learning and embracing them. The absolute minimum to start out is getting familiar with rotation and translation matrices and getting an intuitive grasp of what the components represent and what multiplication will do to them.

You might notice that the last versions will also work just fine for all your other game objects and won't fall apart when you start turning things upside down. So it's pretty much the most generic way if you stay away from scaling (or keep it separated). There is also no lookAt, because most of what that does would be completely redundant.

One thing to keep in mind is that you need to occasionally reorthonormalize the rotation/transformation matrix, as floating point errors will accumulate and start creating some trippy effects. When using lookAt, that happens automatically, since it is reconstructing the whole matrix every frame.

Eventually people will tell you how quaternions are essentially the "one true way of rotation" or even the second coming, but in the end they are completely equivalent to rotation matrices and the only important thing is to know when they are more efficient. There is however absolutely no reason to worry about them at this point (and no, they are not the "only solution to gimbal lock", that's "don't use frigging Euler angles").

edit: ok, great. Never edit a post with code, unless you want every single line break to disappear...