Short Version

:

Given:

Input values | ||
---|---|---|

Symbol | Meaning | Typical value |

\(fov\) | Field of view | 45 – 90 degrees |

\(aspect\) | Aspect ratio | around 1.8 – use frame buffer Width / Height |

\(z_{near}\) | distance from camera to near clip plane in Z | +1 |

\(z_{far}\) | distance from camera to far clip plane. MUST be greater than zNear. |
1000 |

You’ll need to calculate a few values before populating the matrix:

Intermediate derived values | ||
---|---|---|

Symbol | Meaning | Formula |

\(halfHeight\) | half of frustum height at \(z_{near}\) | \(z_{near} * \tan{\frac{fov}{2}}\) |

\(halfWidth\) | half of frustum width at \(z_{near}\) | \({halfHeight}\times{aspect}\) |

\(depth\) | depth of view frustum | \(z_{far}-z_{near}\) |

Then, just populate your matrix:

\(

\large

\begin{bmatrix}

\frac{z_{near}}{halfWidth} & 0 & 0 & 0 \\

\\

0 & \frac{z_{near}}{halfHeight} & 0 & 0 \\

\\

0 & 0 & \frac{-({z_{far}} + {z_{near}})}{depth} & -1 \\

\\

0 & 0 & -2\times\frac{z_{far} \times z_{near}}{depth} & 0 \\

\end{bmatrix}

\)

## Explanation

Basically, this takes a vertex in camera space (see note on coordinate spaces), and scales it so that every vertex that is contained within the view frustum fits within a (-1:+1) cube. The rasterizer, a non-programmable hardware stage that executes after the vertex shader but before the fragment shader, divides each vertex by it’s W component.

Avoiding a long and sticky explanation for the moment:

Any point that lies within the view frustum will, after processed by the rasterizer, fit within a 2×2 cube centered at the origin. Any part of a primitive that lies within that space will trigger a call to the fragment shader. Anything outside of that cube will be “clipped” by the rasterizer, and will not render – since it does not exist on screen.

Better explanation required.