So I've been messing with perspective projection matrices recently. I used numpy and GTK/Cairo to make a very small Python renderer. I'm very confused with the results I'm getting though.
I took this Homogeneous Coordinates technique from an online lecture. If I understood correctly, the objective is to transform every point inside a "Viewing Pyramid" that's frustum shaped so they fit in a cube. (Image from of songho.ca)
You need a Field of View angle ($\alpha$), the Near and Far plane distances ($n$ and $f$ respectively), and the aspect ratio ($r$). Firstly you turn every 3D Point into a Homogeneous Point by adding a 1 like so:
\begin{align*} \begin{pmatrix} x & y & z \end{pmatrix} \xrightarrow{\text{4D}} \begin{bmatrix} x & y & z & 1 \end{bmatrix} \end{align*}
Then you multiply your point matrix by a perspective projection matrix:
\begin{align*} \begin{bmatrix}x & y &z & 1 \end{bmatrix} \begin{bmatrix} 1\over\tan(\alpha/2) & 0 & 0 & 0\\ 0 & r\over\tan(\alpha/2) & 0 & 0\\ 0 & 0 & (f+n)\over(f-n) & -1 \\ 0 & 0 & (2nf)\over(f-n) & 0 \end{bmatrix} = \begin{bmatrix} x' & y' & z' & w \end{bmatrix} \end{align*}
And to go back to a 3D point in space you divide by the fourth dimension:
\begin{align*} \begin{bmatrix} x' & y' & z' & w \end{bmatrix} \xrightarrow{\text{3D}} \begin{pmatrix} x' \over w & y' \over w & z' \over w \end{pmatrix} \end{align*}
This is exactly what I've done with numpy:
def projection_matrix(fov, aspect, near, far):
t = 1/math.tan(math.radians(fov)/2)
a = (far + near)/(far - near)
b = (2*near*far)/(far-near)
r = aspect
return numpy.matrix([[t, 0, 0, 0],
[0, r*t, 0, 0],
[0, 0, a, -1],
[0, 0, b, 0]])
But for some reason the renderer is totally messed up. This is supposed to be a spinning cube... What am I missing here?