OpenGL FPS Camera Quaternion

Last time we created a First Person Shooter (FPS) camera style using rotation matrices for orientation. In this tutorial we will create the same camera but instead of using rotation matrices we will use quaternions.

  • Small introduction to quaternions
  • First Person Camera implement I
  • Gimbal lock with quaternions
  • First Person Camera implement II
  • Conclusions

first person camera tutorial


Small introduction to quaternions

In computer graphics a quaternion is a set of 4 numbers which stores the rotation axis (x,y,z) and the rotation angle (w) to rotate around that axis. This is also called unit quaternion, and we can represent it with versors in the Cartesian coordinate system:

FPS Quaternion camera opengl

Unit Quaternion

You may wonder “Why should I use quaternions instead of rotation matrices?” Well, graphics programmers usually prefer quaternions over rotation matrices because:

  • They require less storage space (4 floats vs 16 floats )
  • Easy to rotate around an arbitrary axis
  • Easy to keep track of rotations
  • Matrix rotations are ugly when you rotate around arbitrary axes
  • Fewer operations when they are concatenated, ex: rigid body simulation
  • Easy to interpolate for producing smooth animations
  • Simple to use for a 3rd person camera (follow camera)

Most 3D math libraries have a quaternion class. We’ve used GLM from the beginning with this little engine. GLM library has a nice quaternion class, simple to use and you can look over the source code. I will not dive into the math behind quaternions because there are plenty of resources over the Internet and all gaming books cover them very well.

First Person Camera implement I

The easiest way to implement a first person camera with quaterions is to replace the rotation matrices with quaternions and keep track of our pitch and yaw angles. We will use the same exact methods from the last tutorial, the only thing that we need to change for the first implementation is the UpdateView() method.

void CameraFPSQuaternion::UpdateView()
  //FPS camera:  RotationX(pitch) * RotationY(yaw)
  glm::quat qPitch = glm::angleAxis(pitch, glm::vec3(1, 0, 0));
  glm::quat qYaw = glm::angleAxis(yaw, glm::vec3(0, 1, 0));
  glm::quat qRoll = glm::angleAxis(roll,glm::vec3(0,0,1));  

  //For a FPS camera we can omit roll
  glm::quat orientation = qPitch * qYaw;
  orientation = glm::normalize(orientation);
  glm::mat4 rotate = glm::mat4_cast(orientation);

  glm::mat4 translate = glm::mat4(1.0f);
  translate = glm::translate(translate, -eye);

  viewMatrix = rotate * translate;

In the method above we replaced the matrices with quaternions (qPitch and qYaw). GLM method glm::angleAxis is building a quaternion from the angle around an axis. We can get the orientation quaternion by multiplying the qPitch and qYaw quaterions. After that we normalize it to be sure that we don’t have rounding errors. Shaders only work with matrices, that’s why we need the cast. Finally we get the View Matrix by multiplying rotation and translation.

Gimbal Lock with quaternions

Gimbal lock is the loss of one degree of freedom in a 3D space. There is a nice explanation on YouTube about gimbal lock. The good news is that you can’t have a gimbal lock problem in a first person camera however if we want to implement, a free camera then roll needs to be introduced in the rotation equation. In a free camera scenario, where we have 3 degrees of freedom, when we will rotate our camera, one degree of freedom can be lost.

The Internet thinks “Use quaternions to fix the gimbal lock, check this example with roll, pitch and yaw”. If I add the roll rotation in the equation, the code from above will still give me a nice gimbal lock. And guess what? I’m using quaternions. Check out this video:

The problem is that we used Euler angles and Cartesian coordinate system, to express our quaternions. In this case, quaternions will give the same results as the rotation matrices and gimbal lock shows up. Again, this is not quaternion’s fault, it’s the way in witch we use them.


Naturally, the quaternion is represented on a 3-sphere in 4D Euclidian space. The problem with quaternions is that it’s very hard to visualize them, it’s not very intuitive and it may take a while to understand how they work. However it’s very easy to implement them. To create a first person camera we have to:

  • Keep an orientation quaternion
  • Get pitch, yaw and roll from that frame
  • Convert to a temporary quaternion
  • Multiply this temporary quaternion with the orientation quaternion

The main difference from the first implementation is that we don’t treat each angle separately, instead we update an orientation unit quaternion.  Here we need to modify two methods:

  • UpdateView
  • MouseMove

And we have to define in our class CameraFPSQuaternion the following members : camera_quat as a quaternion and key_pitch,key_yaw,key_roll as floats

void CameraFPSQuaternion::UpdateView()
  //temporary frame quaternion from pitch,yaw,roll 
  //here roll is not used
  glm::quat key_quat = glm::quat(glm::vec3(key_pitch, key_yaw, key_roll));
  //reset values
  key_pitch = key_yaw = key_roll = 0;

  //order matters,update camera_quat
  camera_quat = key_quat * camera_quat;
  camera_quat = glm::normalize(camera_quat);
  glm::mat4 rotate = glm::mat4_cast(camera_quat);

  glm::mat4 translate = glm::mat4(1.0f);
  translate = glm::translate(translate, -eyeVector);

  viewMatrix = rotate * translate;
void CameraFPSQuaternion::MouseMove(int x, int y, int width, int height)
  if (isMousePressed == false)
  //always compute delta
  //mousePosition is the last mouse position
  glm::vec2 mouse_delta = glm::vec2(x, y) - mousePosition;

  //notice that we reduce the sensitvity
  const float mouseX_Sensitivity = 0.0020f;
  const float mouseY_Sensitivity = 0.0020f;

  key_yaw   = mouseX_Sensitivity * mouse_delta.x;
  key_pitch = mouseY_Sensitivity * mouse_delta.y;

  mousePosition = glm::vec2(x, y);


  • It’s important to remember that for rotation we have to use an unit quaternions.
  • You can have gimbal lock with quaternions if you use Euler angles.
  • You can’t have a gimbal lock problem in a first person camera.
  • It’s hard to get a gimbal lock in practice even with matrices.
  • Quaternions can be hard to visualize.
  • Implementation I updates Euler angles separately.
  • Implementation II updates a unit quaternion each frame.
  • Camera should be the last object to update in a scene.

In the next tutorial we will integrate the camera in our little engine.

Follow me on Twitter and stay tuned for next tutorials.

blog comments powered by Disqus