I'm trying to construct a FPS view matrix for my OpenGL renderer using yaw and pitch angles instead of the typical `LookAt` view matrix.

The view matrix is the inverse of the camera world transform $\mathbf{M}_{\textrm{view}} = (\mathbf{T}\mathbf{R}_p\mathbf{R}_y)^{-1}$, hence:

$$\small\begin{align}
(\mathbf{T}\mathbf{R}_p\mathbf{R}_y)^{-1} &= \mathbf{R}_y^{T}\mathbf{R}_p^{T}\mathbf{T}^{-1} \\
&= \begin{bmatrix}
\cos{y} & 0 & \sin{y} & 0 \\
0 & 1 & 0 & 0 \\
-\sin{y} & 0 & \cos{y} & 0 \\
0 & 0 & 0 & 1
\end{bmatrix}^T
\begin{bmatrix}
1 & 0 & 0 & 0 \\
0 & \cos{p} & -\sin{p} & 0 \\
0 & \sin{p} & \cos{p} & 0 \\
0 & 0 & 0 & 1
\end{bmatrix}^T
\begin{bmatrix}
1 & 0 & 0 & e_0 \\
0 & 1 & 0 & e_1 \\
0 & 0 & 1 & e_2 \\
0 & 0 & 0 & 1
\end{bmatrix}^{-1} \\
&=
\begin{bmatrix}
\cos{y} & 0 & -\sin{y} & 0\\
0 & 1 & 0 & 0 \\
\sin{y} & 0 & \cos{y} & 0\\
0 & 0 & 0 & 1
\end{bmatrix}
\begin{bmatrix}
1 & 0 & 0 & 0\\
0 & \cos{p} & \sin{p} & 0\\
0 & -\sin{p} & \cos{p} & 0\\
0 & 0 & 0 & 1
\end{bmatrix}
\begin{bmatrix}
1 & 0 & 0 & -e_0\\
0 & 1 & 0 & -e_1\\
0 & 0 & 1 & -e_2\\
0 & 0 & 0 & 1\\
\end{bmatrix} \\
&=
\begin{bmatrix}
\cos{y} & \sin{p}\sin{y} & -\cos{p}\sin{y} & -e_0\cos{y} - e_1\sin{p}\sin{y} + e_2\cos{p}\sin{y}\\
0 & \cos{p} & \sin{p} & -e_0 \cdot 0 - e_1 \cos{p} - e_2 \sin{p}\\
\sin{y} & -\cos{y}\sin{p} & \cos{p}\cos{y} & -e_0\sin{y} + e_1\cos{y}\sin{p} - e_2\cos{p}\cos{y}\\
0 & 0 & 0 & 1
\end{bmatrix}
\end{align}$$

Every step is double checked using WolframAlpha.

The implementation:

<!-- language: lang-cpp -->

    inline Matrix4
    FPSViewRH(const Vector3& eyePosition, float yaw, float pitch) noexcept
    {
      yaw = Utils::Radians(yaw);
      pitch = Utils::Radians(pitch);

      const auto sinYaw = std::sin(yaw);
      const auto cosYaw = std::cos(yaw);

      const auto sinPitch = std::sin(pitch);
      const auto cosPitch = std::cos(pitch);

      const Vector3 i{cosYaw, sinPitch * sinYaw, -cosPitch * sinYaw};
      const Vector3 j{0, cosPitch, sinPitch};
      const Vector3 k{sinYaw, -cosYaw * sinPitch, cosPitch * cosYaw};

      return {
        {i[0], i[1], i[2], -i.Dot(eyePosition)},
        {j[0], j[1], j[2], -j.Dot(eyePosition)},
        {k[0], k[1], k[2], -k.Dot(eyePosition)},
        {0,    0,    0,    1},
      };
    }
   
   It works fine except that _the yaw is inverted_, i.e. increasing the angle causes the object to go right while it should go left.

I can negate the yaw angle or change every $\sin{y}$ to $-\sin{y}$, but I don't really understand _why this is happening_? or if there is an interpretation for this phenomena?