title: Quaternion-based First Person View Camera
body: <p>I have been learning OpenGL by following the tutorial, located at <a href="https://paroj.github.io/gltut/" rel="noreferrer">https://paroj.github.io/gltut/</a>.</p>
  
  <p>Passing the basics, I got a bit stuck at understanding quaternions and their relation to spatial orientation and transformations, especially from world- to camera-space and vice versa. In the chapter <em>Camera-Relative Orientation</em>, the author makes a camera, which rotates a model in <em>world</em> space relative to the camera orientation. Quoting: </p>
  
  <blockquote>
    <p>We want to apply an orientation offset (R), which takes points in camera-space. If we wanted to apply this to the camera matrix, it would simply be multiplied by the camera matrix: R * C * O * p. That's nice and all, but we want to apply a transform to O, not to C.</p>
  </blockquote>
  
  <p>My uneducated guess would be that if we applied the offset to camera space, we would get the first-person camera. Is this correct? Instead, the offset is applied to the model in <em>world</em> space, making the spaceship spin relative to that space, and not to camera space. We just observe it spin from camera space.</p>
  
  <p>Inspired by at least some understanding of quaternions (or so I thought), I tried to implement the first person camera. It has two properties:</p>
  
  <pre><code>struct Camera{ 
    glm::vec3 position; // Position in world space.
    glm::quat orientation; // Orientation in world space.
  }
  </code></pre>
  
  <p>Position is modified in reaction to keyboard actions, while the orientation changes due to mouse movement on screen.</p>
  
  <p>Note: GLM overloads <code>*</code> operator for <code>glm::quat * glm::vec3</code> with the relation for rotating a vector by a quaternion (more compact form of <code>v' = qvq^-1</code>)</p>
  
  <p>For example, moving forward and moving right: </p>
  
  <pre><code>glm::vec3 worldOffset;
  float scaleFactor = 0.5f;
  if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) {
      worldOffset = orientation * (axis_vectors[AxisVector::AXIS_Z_NEG]); // AXIS_Z_NEG = glm::vec3(0, 0, -1)
      position += worldOffset * scaleFactor;
  }
  if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) {
      worldOffset = orientation  * (axis_vectors[AxisVector::AXIS_X_NEG]); // AXIS_Z_NEG = glm::vec3(-1, 0, 0)
      position += worldOffset * scaleFactor;
  }
  </code></pre>
  
  <p>Orientation and position information is passed to <code>glm::lookAt</code> matrix for constructing the world-to-camera transformation, like so:</p>
  
  <pre><code>auto camPosition = position;
  auto camForward = orientation * glm::vec3(0.0, 0.0, -1.0);
  viewMatrix = glm::lookAt(camPosition, camPosition + camForward, glm::vec3(0.0, 1.0, 0.0));
  </code></pre>
  
  <p>Combining model, view and projection matrices and passing the result to vertex shader displays everything okay - the way one would expect to see things from the first-person POV. However, things get messy when I add mouse movements, tracking the amount of movement in x and y directions. I want to rotate around the <em>world</em> y-axis and <em>local</em> x-axis:</p>
  
  <pre><code>auto xOffset = glm::angleAxis(xAmount, axis_vectors[AxisVector::AXIS_Y_POS]); // mouse movement in x-direction
  auto yOffset = glm::angleAxis(yAmount, axis_vectors[AxisVector::AXIS_X_POS]); // mouse movement in y-direction
  orientation = orientation * xOffset; // Works OK, can look left/right
  orientation = yOffset * orientation; // When adding this line, things get ugly
  </code></pre>
  
  <p>What would the problem be here?
  I admit, I don't have enough knowledge to debug the mouse movement code properly, I mainly followed the lines, saying "right multiply to apply the offset in world space, left multiply to do it in camera space."</p>
  
  <p>I feel like I know things half-way, drawing conclusions from a plethora of e-resources on the subject, while getting more educated and more confused at the same time.
  Thanks for any answers.</p>