Transformation

You may need to read the article about uniforms before this one. Additionally, although it isn't necessary, it might be useful to have prior knowledge of linear algebra.


A transformation is a way to change something. There are three basic transformations: translation, rotation, and scaling.

Translation

A translation is performed by adding a vector value to each vertex. For an initial vector (x0,y0,z0)(x_0,y_0,z_0) and a translation vector (xt,yt,zt)(x_t,y_t,z_t), the resulting vector is (x0+xt,y0+yt,z0+zt)(x_0+x_t,y_0+y_t,z_0+z_t). For example:

#version 300 es

in vec4 a_position;

uniform vec4 u_translation;

void main() {
	gl_Position = a_position + u_translation;
}

Rotation

A rotation is performed by multiplying each vertex by a point on the unit circle. Given a rotation of rr radians, an equivalent point on the unit circle is (cos(r),sin(r))(\cos(r),\sin(r)). To rotate an initial vector (x,y)(x,y) by rr radians, use (xs+yc,ysxc)(xs+yc,ys-xc), where s=sin(r)s=\sin(r) and c=cos(r)c=\cos(r). For example:

#version 300 es

in vec4 a_position;

uniform float u_rotation;

void main() {
	float s = sin(u_rotation);
	float c = cos(u_rotation);

	float x = a_position.x * s + a_position.y * c;
	float y = a_position.y * s - a_position.x * c;

	gl_Position = vec4(x, y, a_position.zw);
}

Note that the above code uses a_position.zw. This is equivalent to vec2(a_position.z, a_position.w). It is possible to use any dimensions of a vector in any order to make another vector, such as a_position.xyz or a_position.xzx. This is called swizzling.

Scaling

A scaling is performed by multiplying each vertex by a vector. For an initial vector (x0,y0,z0)(x_0,y_0,z_0) and a scaling vector (xs,ys,zs)(x_s,y_s,z_s), the resulting vector is (x0xs,y0ys,z0zs)(x_0x_s,y_0y_s,z_0z_s). For example:

#version 300 es

in vec4 a_position;

uniform vec4 u_scaling;

void main() {
	gl_Position = a_position * u_scaling;
}

Matrices

Using the techniques described above works fine until you want to combine multiple, at which point a different shader is needed for each permutation of transformations.

The standard approach to applying transformations is with transformation matrices, which are square matrices that represent transformations. By using transformation matrices, the transformations can be applied to the matrix in any order in JavaScript before being passed to the shader. To transform a vertex by a transformation matrix, simply multiply the matrix by the vertex. For example:

#version 300 es

in vec4 a_position;

uniform mat4 u_matrix;

void main() {
	gl_Position = u_matrix * a_position;
}

Matrix multiplication is noncommutative. When two transformation matrices are multiplied to combine their transformations, the transformations of the multiplicand are applied before those of the multiplier. In order for two matrices to be multipliable, the multiplicand must be of size m×nm\times{n} and the multiplier must be of size n×pn\times{p}. Note that vectors are matrices with only one column (size q×1q\times{1}).

Matrices can be constructed in specific ways such that multiplying them by a vector is equivalent to one of the basic transformation algorithms outlined above.

It can be helpful to use a library such as glMatrix or μMath for linear algebra.

Matrix Translation

The following matrix translates an initial vector (x0,y0,z0)(x_0,y_0,z_0) by (xt,yt,zt)(x_t,y_t,z_t), resulting in a vector (x1,y1,z1)(x_1,y_1,z_1).

[100xt010yt001zt0001]\begin{bmatrix}1&0&0&x_t\\0&1&0&y_t\\0&0&1&z_t\\0&0&0&1\end{bmatrix}

Matrix Rotation

The following matrix rotates an initial vector (x0,y0,z0)(x_0,y_0,z_0) by rr radians around the x=(1,0,0)x=(1,0,0) axis, resulting in a vector (x1,y1,z1)(x_1,y_1,z_1), where c=cos(r)c=\cos(r) and s=sin(r)s=\sin(r).

[10000cs00sc00001]\begin{bmatrix}1&0&0&0\\0&c&s&0\\0&-s&c&0\\0&0&0&1\end{bmatrix}

The following matrix rotates an initial vector (x0,y0,z0)(x_0,y_0,z_0) by rr radians around the y=(0,1,0)y=(0,1,0) axis, resulting in a vector (x1,y1,z1)(x_1,y_1,z_1), where c=cos(r)c=\cos(r) and s=sin(r)s=\sin(r).

[c0s00100s0c00001]\begin{bmatrix}c&0&-s&0\\0&1&0&0\\s&0&c&0\\0&0&0&1\end{bmatrix}

The following matrix rotates an initial vector (x0,y0,z0)(x_0,y_0,z_0) by rr radians around the z=(0,0,1)z=(0,0,1) axis, resulting in a vector (x1,y1,z1)(x_1,y_1,z_1), where c=cos(r)c=\cos(r) and s=sin(r)s=\sin(r).

[cs00sc0000100001]\begin{bmatrix}c&s&0&0\\-s&c&0&0\\0&0&1&0\\0&0&0&1\end{bmatrix}

The following matrix rotates an initial vector (x0,y0,z0)(x_0,y_0,z_0) by rr radians around the (xa,ya,za)(x_a,y_a,z_a) axis, resulting in a vector (x1,y1,z1)(x_1,y_1,z_1), where c=cos(r)c=\cos(r), s=sin(r)s=\sin(r), t=1ct=1-c, and l=xa2+ya2+za2l=\sqrt{{x_a}^2+{y_a}^2+{z_a}^2}.

[(xal)2t+cyaxal2t+zalszaxal2tyals0xayal2tzals(yal)2t+czayal2t+xals0xazal2t+yalsyazal2txals(zal)2t+c00001]\begin{bmatrix}(x_al)^2t+c&y_ax_al^2t+z_als&z_ax_al^2t-y_als&0\\x_ay_al^2t-z_als&(y_al)^2t+c&z_ay_al^2t+x_als&0\\x_az_al^2t+y_als&y_az_al^2t-x_als&(z_al)^2t+c&0\\0&0&0&1\end{bmatrix}

Matrix Scaling

The following matrix scales an initial vector (x0,y0,z0)(x_0,y_0,z_0) by a scaling vector (xs,ys,zs)(x_s,y_s,z_s), resulting in a vector (x1,y1,z1)(x_1,y_1,z_1).

[xs0000ys0000zs00001]\begin{bmatrix}x_s&0&0&0\\0&y_s&0&0\\0&0&z_s&0\\0&0&0&1\end{bmatrix}

Orthographic Projection

A projection is a linear transformation from a vector space to itself. An orthographic projection is a projection that uses parallel lines to project its shape. A projection matrix is a transformation matrix that applies a projection.

An orthographic projection can be used to convert from screen space to clip space.

The following orthographic projection matrix converts a vector (x0,y0,z0,w0)(x_0,y_0,z_0,w_0) to a vector (x1,y1,z1,w1)(x_1,y_1,z_1,w_1), where ll, rr, bb, tt, nn, and ff are the left, right, bottom, top, near, and far bounds of the view frustum, respectively.

[2lr00002bt00002nf0l+rlrb+tbtn+fnf1]\begin{bmatrix}\frac{-2}{l-r}&0&0&0\\0&\frac{-2}{b-t}&0&0\\0&0&\frac{2}{n-f}&0\\\frac{l+r}{l-r}&\frac{b+t}{b-t}&\frac{n+f}{n-f}&1\end{bmatrix}

The next article is about the scene graph.