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:
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?