Quaternions for FPS Camera?

Started by
17 comments, last by kpalko 8 years, 8 months ago

I've been studying for 4 days straight trying to figure out how to make a camera that behaves like one does in FPS games. I've been lead to believe I need Quaternions for this, but I also see a lot of talk about Euler angles and Yaw/Pitch/Roll.

If it is Quaternion angles, can someone give me a very basic example of how you would make a camera look up 45 degrees using a Quaternion?

I prefer to leave the mouse calculations out because they just seem to make it more difficult to understand.

Any help would be greatly appreciated as I'm getting close to giving up :(

Thanks

Advertisement

It doesnt really matter how you store the rotation of your camera (matrix/quaternion/euler, maybe even a combination), theyre all capable of doing it, and you can always convert between them if one is more suitable for doing something. Of course if you do a lot of operations that are easy with quaternions (eg interpolating the camera rotation), using quaternions might be the path to take.

For your question, all you need is something like camera.setRotationFromEuler(x,y,z) - theres a way to implement this no matter what underlying mathematical object is used for rotation. You can surely find the math for converting euler->quaternion online.

o3o

If you want a full math library for all your calculations, including quaternions, you can take a look at Eigen (http://eigen.tuxfamily.org)

It has a very free and liberal license.

In your case you are looking for the Geometry section (http://eigen.tuxfamily.org/dox/group__Geometry__Module.html) where you will find a lot of transformation classes and operations.

A quaternion is a representation of a rotation around an axis of your choice. So, to create a rotation for looking up 45 degrees you create a quaternion that rotates around a vector facing right from your character.

This constructor will probably work nice for you: Quaternion (const AngleAxisType &aa)

Then you apply this Quaternion to your existing character orientation, which is preferably also stored as a quaternion.

If you are working with DirectX you may also take a look at DirectXMath, https://msdn.microsoft.com/en-us/library/windows/desktop/ee415597%28v=vs.85%29.aspx

Well, on the lowest level you just need setters for the camera's placement as usual for any game object. However, it is the camera control where all the nasty math really happens. As a 1st (or 3rd) person camera, the placement of the camera is bound to the player's game object by parenting. This is also standard stuff, well solved by concatenating matrixes. There usually is some freedom in rotation so that the player can look up / down and left / right relative to the orientation of the game object. On the other hand the rolling is often constraint to zero. This together would give the local orientation. The local position may be fixed for 1st person camera (as opposed to 3rd person cameras). There may be additions working on the local position like camera shaking or bobbing (a controversial topic though), depending on given circumstances. Without knowing how the camera should actually behave, giving tips for its implementation (besides the standard placement) is almost meaningless. So could you (the OP) describe us what exactly you want, how your general game object placement works, and what you have so far?

Typical FPS style cameras are one of the few cases where storing two angles is perfectly fine and sufficient (and quaternions are likely to be pointless overkill that turn something simple and trivial to visualize into completely abstract math). As long as "typical" means "ground based", so usually without rolling and up/down rotations being limited to 90°.

All you need to keep in mind is that every rotation will also affect the axes that are used by all subsequent rotations. So for an FPS style camera, you always apply your "left/right" rotation first. Otherwise, if you do the "up/down" first, your "up-axis" for the second rotation would be screwed up, plus, your first rotation would be around the global "right", rather than your current local one.

The only issue you can run into is that depending on the API you use and the multiplication order in which you apply transformations can have an impact on which transformation happens "first". On top of that, your own perception of what happens first depends on whether you look at your object from a global or local perspective. For example, if you insist that "right" is always the global x-axis, transformations will appear to happen in reverse order, while considering "right" to be the objects own current x-axis will usually mean they happen in the order you apply them in (I also find the latter to be more useful and less confusing than the "global perspective").

A very appropriate definition of computer graphics I remember from some book was something like "computer graphics is the art of making an even number of sign errors".

f@dzhttp://festini.device-zero.de

Very simple using GLM (http://glm.g-truc.net/0.9.7/index.html)

Here's how to build a rotation matrix using Quaternions and vertical/horizontal angles only


// angles in radians
float mVerticalAngle;
float mHorizontalAngle;

glm::mat4 Camera::Orientation() const
{
    glm::quaternion rotation(glm::angleAxis(mVerticalAngle, glm::vec3(1.0f, 0.0f, 0.0f)));
    rotation = rotation * glm::angleAxis(mHorizontalAngle, glm::vec3(0.0f, 1.0f, 0.0f));

    return glm::toMat4(rotation);
}

Multiply this with the FPS cameras translation matrix and you have your view matrix.


glm::vec3 mTranslation;

glm::mat4 Camera::GetCameraTransform() const
{
    return Orientation() * glm::translate(glm::mat4(1.0f), -mTranslation);
}

You all are awesome! Thanks for the replies.

So could you (the OP) describe us what exactly you want, how your general game object placement works, and what you have so far?

Right now I'm just playing with the camera. I've added it to the scene at 0, 0, 0. I've gotten it to rotate using camera.rotation.x/y. From what I've seen, other people seem to implement a vector that the camera "looks at" (using the .lookAt function). Really I just want to create a camera that allows me to look around in the scene. I put a cube at 0, 0, 5 and just want to be able to move the camera around and look at the cube from different angles. For the development I'm using Threejs (threejs.org).

Very simple using GLM (http://glm.g-truc.net/0.9.7/index.html)

Here's how to build a rotation matrix using Quaternions and vertical/horizontal angles only


// angles in radians
float mVerticalAngle;
float mHorizontalAngle;

glm::mat4 Camera::Orientation() const
{
    glm::quaternion rotation(glm::angleAxis(mVerticalAngle, glm::vec3(1.0f, 0.0f, 0.0f)));
    rotation = rotation * glm::angleAxis(mHorizontalAngle, glm::vec3(0.0f, 1.0f, 0.0f));

    return glm::toMat4(rotation);
}

Multiply this with the FPS cameras translation matrix and you have your view matrix.


glm::vec3 mTranslation;

glm::mat4 Camera::GetCameraTransform() const
{
    return Orientation() * glm::translate(glm::mat4(1.0f), -mTranslation);
}

I'm at work now, but I'll check this out when I get home. So just so I'm understanding the basics correctly...

Orientation returns a Matrix (x,y,z,w), which is two Quaternions multiplied together. I'm guessing the * has been overloaded to support multiplying Quaternions? The second part is a little difficult to understand. I can't find a resource to define what the translate method takes in and returns. Would mTranslation be the vector my camera was currently located at?


Right now I'm just playing with the camera. I've added it to the scene at 0, 0, 0. I've gotten it to rotate using camera.rotation.x/y. From what I've seen, other people seem to implement a vector that the camera "looks at" (using the .lookAt function). [...]

The look-at function is useful to align the camera once or tracking an object. It is just one possibility to control the camera.

IMO you should understand it so: The camera is an object in the world similar to a game object. It has a placement (position and orientation) and additionally field of view and other view related stuff. The camera by itself does not change its placement. Then you can apply the functionality of objects like LookingAt or Tracking or Parenting or … to control the placement in part or totally. That way gives you maximum flexibility.


[…] Really I just want to create a camera that allows me to look around in the scene. I put a cube at 0, 0, 5 and just want to be able to move the camera around and look at the cube from different angles. For the development I'm using Threejs (threejs.org).

Well, that sounds not like a FPS camera but a free camera perhaps with a tracking constraint control.
As said, I'd implement this as a camera object with a placement. The placement should be able to provide a matrix that stores the "local to global" spatial transform. The placement should provide an API for setting and altering position and orientation separately. Then I'd implement a camera control that processes input, generates movement from it, and applies it to the attached Placement (which, of course, belongs to the camera in this case).
I'd further implement a control Tracking that is to be parametrized with (a) a Placement that is to be tracked (its position, to be precise) and (b) a Placement that is the target to be altered (its orientation, to be precise). The math is so that the difference vector from the Placement.position of the target to the Placement.position of the tracked placement is used (after normalization) as forward vector of the typical look-at functionality. What need to be done then is that the control is invoked every time the Placement.position of the target object has been settled after being altered.

Orientation returns a Matrix (x,y,z,w), which is two Quaternions multiplied together.

Yes; it takes the resulting quaternion and converts it to a rotation matrix.

I'm guessing the * has been overloaded to support multiplying Quaternions?

Yes

The second part is a little difficult to understand. I can't find a resource to define what the translate method takes in and returns

It applies a translation component (the cameras position vector, mTranslation) to the input matrix (identity matrix in this case) and returns the result.

See GLM docs http://glm.g-truc.net/0.9.2/api/a00245.html#ga4683c446c8432476750ade56f2537397

Would mTranslation be the vector my camera was currently located at?

Exactly

This topic is closed to new replies.

Advertisement