Pong

## March 4th 2015, Software Rendering Pipeline

For calculating the perspective projection of the 3D point vectors we define the matrices $M_V$ and $M_P$ as follows:

$M_V = M_R M_T$ with

• $M_R = \left( \begin{array}{c c c c} r_x & u_x & -d_x & 0 \\ r_y & u_y & -d_y & 0 \\ r_z & u_z & -d_z & 0 \\ 0 & 0 & 0 & 1 \end{array} \right)$
• $\vec{r}$ being the x-axis of the camera coordinate system (right vector)
• $\vec{u}$ being the y-axis of the camera coordinate system (up vector)
• $\vec{d}$ being the z-axis of the camera coordinate system (direction vector)
• by convention, the camera is looking in the direction of the negative z-axis
• $M_T = \left( \begin{array}{c c c c} 1 & 0 & 0 & -e_x \\ 0 & 1 & 0 & -e_y \\ 0 & 0 & 1 & -e_z \\ 0 & 0 & 0 & 1 \end{array} \right)$
• $\vec{e}$ being the position of the camera in world coordinates (eye point)
• $M_P = \left( \begin{array}{c c c c} c/a & 0 & 0 & 0 \\ 0 & c & 0 & 0 \\ 0 & 0 & -\frac{n+f}{f-n} & -2\frac{fn}{f-n} \\ 0 & 0 & -1 & 0 \end{array} \right)$
• $c = \frac{1}{tan(fovy/2)}$ (fovy cathetus)
• $fovy$ = field of view in y-direction (vertical viewing angle of the camera)
• $a = \frac{w}{h}$ (window aspect)
• $w$ = window width
• $h$ = window height
• $n$ = distance of near plane
• $f$ = distance of far plane

To implement the above calculations, we use the glslmath C++ library, which provides linear math operators for 4×4 matrices and 4-component vectors using homogeneous coordinates. It also provides convenience methods for the calculation of the above transformation and projection matrices:

As an example, we transform a single point $\vec{p}=(0,0,-10)^T$ into the camera coordinate system with the camera positioned at the eye point $\vec{e}=(0,10,10)^T$ looking at the point $\vec{l}=(0,0,0)^T$ with the up vector $\vec{u}=(0,1,0)^T$ and the perspective projection with a near plane distance of $n=1$, a far plane distance of $f=100$ and a vertical viewing angle of $fovy$=90 degrees. Additionally, we transform the point with an affine modelling transformation, which translates the point up 10 units with a translation vector of $\vec{t}=(0,10,0)^T$.

#include <glslmath.h>

vec4 p(0,0,-10);
vec3 t(0,10,0);

mat4 M = mat4::translate(t);

vec3 e(0,10,10);
vec3 l(0,0,0);
vec3 u(0,1,0);

mat4 V = mat4::lookat(e,l,u);

double fovy=90.0;
double aspect=1.0; // for a quadratic viewing window
double n=1.0;
double f=100.0;

mat4 P = mat4::perspective(fovy,aspect,n,f);

mat4 MVP = P*V*M;
p = MVP*p;

std::cout << "projected vertex: " << vec3(p) << std::endl;

The output of the projected vertex is given in normalized device coordinates in the range $[-1,1]^3$, so the coordinates need to be scaled up to integer window coordinates, if the point is to be painted as a pixel on the rendering window.

In this example the projected point is visible at position $(0,1)^T$ on the image plane, that is the top center of the window.

More precisely, the projected point is a 4-component homogeneous vector which is cast to a 3-component vector undergoing dehomogenization by dividing it by its w-component. Although being projected onto the 2D image plane, the resulting 3-component vector has a z-coordinate, which is interpreted as the depth of the point.

The z-coordinate is usually inspected by the Z-buffer algorithm for hidden surface removal. In this simplified rendering pipeline, we are simply going to neglect the z-values. They do not play a role, if we just render points and lines and no other graphic primitives like triangles.

Q But what if we turn our head so that the point becomes invisible?

We need to clip the a point at the viewing frustum (the visible volume, which is a pyramid frustum) in homogeneous clip coordinates!