March 4th 2015, Software Rendering Pipeline
← March 3rd 2015 Track Rendering Engine | ● | March 4th 2015 Homogeneous Clip Coordinates →
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$.
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!
← March 3rd 2015 Track Rendering Engine | ● | March 4th 2015 Homogeneous Clip Coordinates →