Worksheet 3, Part 3 — Affine and viewing transformations

This page lists, with general expressions, the transformation matrices used in Part 1 (isometric orthographic view) and Part 2 (three classical perspective views), and shows how they are concatenated into the composite matrix applied to every vertex in the vertex shader.

WebGPU uses depth in \([0,1]\) instead of OpenGL's \([-1,1]\). We therefore finish every pipeline with the scale–translate matrix \(M_{st}\), so the full clip transform is \(\;x_{\text{clip,wgpu}} = M_{st}\,P\,V\,M\,x_{\text{model}}\).

1. The building-block matrices

Affine model transformations

$$ T(t_x,t_y,t_z)=\begin{bmatrix}1&0&0&t_x\\0&1&0&t_y\\0&0&1&t_z\\0&0&0&1\end{bmatrix},\qquad S(s_x,s_y,s_z)=\begin{bmatrix}s_x&0&0&0\\0&s_y&0&0\\0&0&s_z&0\\0&0&0&1\end{bmatrix} $$ $$ R_y(\theta)=\begin{bmatrix}\cos\theta&0&\sin\theta&0\\0&1&0&0\\-\sin\theta&0&\cos\theta&0\\0&0&0&1\end{bmatrix},\qquad R_x(\theta)=\begin{bmatrix}1&0&0&0\\0&\cos\theta&-\sin\theta&0\\0&\sin\theta&\cos\theta&0\\0&0&0&1\end{bmatrix} $$

View matrix (the lookAt function)

Given eye point \(e\), look-at point \(a\), and up vector \(u\), build the orthonormal eye basis

$$ \vec b_3=\frac{e-a}{\lVert e-a\rVert},\qquad \vec b_1=\frac{u\times\vec b_3}{\lVert u\times\vec b_3\rVert},\qquad \vec b_2=\vec b_3\times\vec b_1 , $$

so the view matrix is the change of coordinates \(V=RT\):

$$ V=\operatorname{lookAt}(e,a,u)= \begin{bmatrix} \vec b_1^{\,T} & -\,e\cdot\vec b_1\\ \vec b_2^{\,T} & -\,e\cdot\vec b_2\\ \vec b_3^{\,T} & -\,e\cdot\vec b_3\\ \mathbf 0 & 1 \end{bmatrix}. $$

Projection matrices

$$ P_{\text{ortho}}=\operatorname{ortho}(l,r,b,t,n,f)= \begin{bmatrix} \tfrac{2}{r-l}&0&0&-\tfrac{r+l}{r-l}\\ 0&\tfrac{2}{t-b}&0&-\tfrac{t+b}{t-b}\\ 0&0&-\tfrac{2}{f-n}&-\tfrac{f+n}{f-n}\\ 0&0&0&1 \end{bmatrix} $$ $$ P_{\text{persp}}=\operatorname{perspective}(\alpha,A,n,f)= \begin{bmatrix} \tfrac{1}{A}\cot\tfrac{\alpha}{2}&0&0&0\\ 0&\cot\tfrac{\alpha}{2}&0&0\\ 0&0&\tfrac{n+f}{n-f}&\tfrac{2nf}{n-f}\\ 0&0&-1&0 \end{bmatrix} $$

with vertical field of view \(\alpha\) and aspect ratio \(A=w/h\).

WebGPU depth remap

$$ M_{st}=\begin{bmatrix}1&0&0&0\\0&1&0&0\\0&0&\tfrac12&\tfrac12\\0&0&0&1\end{bmatrix} $$

2. Part 1 — isometric wireframe cube

The cube already sits in world space with its diagonal from \((0,0,0)\) to \((1,1,1)\), so the model matrix is the identity, \(M=I\). The isometric look comes entirely from the camera: an orthographic projection viewed along the cube's main diagonal,

$$ V=\operatorname{lookAt}\big(e=(2,2,2),\;a=(\tfrac12,\tfrac12,\tfrac12),\;u=(0,1,0)\big),\qquad P=\operatorname{ortho}(-s,s,-s,s,0.1,10),\;\; s=1.2 . $$

Composite matrix used in the vertex shader:

$$ \boxed{\,x_{\text{clip,wgpu}} = M_{st}\,P_{\text{ortho}}\,V\,I\;x_{\text{model}}\,} $$

3. Part 2 — three classical perspective views

All three cubes share one pinhole camera (vertical field of view \(\alpha=45^\circ\)) and one view matrix:

$$ P=\operatorname{perspective}(45^\circ,\,A,\,0.1,\,100),\qquad V=\operatorname{lookAt}\big(e=(0,0,4.5),\;a=(0,0,0),\;u=(0,1,0)\big). $$

Each cube is first centred on the origin and shrunk to fit, using the shared placement \(\;P_{\text{lace}} = S(0.9,0.9,0.9)\,T(-\tfrac12,-\tfrac12,-\tfrac12)\). The view type is decided by the model matrix's rotation:

One-point (front) perspective

No rotation — the faces stay parallel to the image plane, so only the depth direction has a vanishing point.

$$ M_1 = T(-1.5,0,0)\,S(0.9,0.9,0.9)\,T(-\tfrac12,-\tfrac12,-\tfrac12) $$

Two-point (X) perspective

Rotate about \(y\) only — the vertical edges stay parallel, while the two horizontal edge directions each get a vanishing point.

$$ M_2 = R_y(30^\circ)\,S(0.9,0.9,0.9)\,T(-\tfrac12,-\tfrac12,-\tfrac12) $$

Three-point perspective

Rotate about \(y\) and \(x\) — no edge stays parallel to the image plane, giving three vanishing points.

$$ M_3 = T(1.5,0,0)\,R_x(-20^\circ)\,R_y(-30^\circ)\,S(0.9,0.9,0.9)\,T(-\tfrac12,-\tfrac12,-\tfrac12) $$

Composite matrix used in the vertex shader for cube \(i\):

$$ \boxed{\,x_{\text{clip,wgpu}} = M_{st}\,P_{\text{persp}}\,V\,M_i\;x_{\text{model}},\qquad i=1,2,3\,} $$
Matrix multiplication is not commutative, so order matters: within each \(M_i\) the cube is centred (rightmost), then scaled, then rotated, then translated into place; the camera (\(V\)), projection (\(P\)) and depth remap (\(M_{st}\)) are applied last, left to right.