Rotation is one of the most powerful transformations in shader programming.
Spinning loading icons.
Rotating radar displays.
Clock hands.
Compass needles.
Particle effects.
Many procedural animations rely on rotation.
The surprising part is that, just like translation, we don't rotate the shape.
We rotate the coordinate system.
Draw a square on a sheet of paper.
Now rotate the paper by ninety degrees.
Did the square change?
No.
Only the paper rotated.
That's exactly what happens in GLSL.
The mathematical description of the shape stays the same.
Only the coordinates change.
Rotation always happens around a point.
The easiest point is the center of the screen.
vec2 uv = vUv - 0.5;
Now (0.0, 0.0) represents the center.
Everything rotates around this point.
Suppose we don't subtract 0.5.
The origin remains in the bottom left corner.
If we rotate now, every shape spins around the corner of the screen instead of around itself.
Centering the coordinates moves the pivot point to the middle.
This is exactly how many animation tools work.
Rotation uses a small amount of trigonometry.
float angle = uTime;
float c = cos(angle);
float s = sin(angle);
mat2 rotation = mat2(
c, -s,
s, c
);
This matrix rotates any two dimensional coordinate.
You don't need to memorize it.
After using it a few times, it becomes second nature.
Apply the matrix to the UV coordinates.
uv = rotation * uv;
That's it.
Every procedural shape drawn using these coordinates now rotates automatically.
Let's reuse our familiar circle.
float circle =
1.0 -
step(
0.25,
distance(uv, vec2(0.0))
);
Notice something interesting.
The circle doesn't appear to rotate.
A circle looks identical from every direction.
Rotating it changes nothing.
This is actually good news.
It proves the coordinate system is rotating correctly.
The shape simply has rotational symmetry.
Let's use something more obvious.
Replace the circle with the rectangle from an earlier lesson.
float rect =
step(-0.20, uv.x) *
(1.0 - step(0.20, uv.x)) *
step(-0.10, uv.y) *
(1.0 - step(0.10, uv.y));
Now the rectangle visibly rotates.
Unlike a circle, every angle looks different.
#ifdef GL_ES
precision mediump float;
#endif
uniform float uTime;
varying vec2 vUv;
void main(){
vec2 uv = vUv - 0.5;
float angle = uTime;
float c = cos(angle);
float s = sin(angle);
mat2 rotation = mat2(
c, -s,
s, c
);
uv = rotation * uv;
float rect =
step(-0.20, uv.x) *
(1.0 - step(0.20, uv.x)) *
step(-0.10, uv.y) *
(1.0 - step(0.10, uv.y));
gl_FragColor = vec4(vec3(rect),1.0);
}
A few lines of mathematics create a smooth rotating animation.
Multiply the angle.
float angle = uTime * 2.0;
The rectangle spins twice as fast.
float angle = uTime * 0.5;
Now the movement becomes calm and gentle.
Translation and rotation work together.
Move the coordinate system first.
uv -= vec2(0.25,0.0);
Then rotate it.
The rectangle now spins around its new position instead of the center of the screen.
This is how planets orbit, wheels spin, and interface elements animate independently.
Everything we've created can now rotate.
Circles.
Rounded rectangles.
Stars.
Polygons.
Entire tiled patterns.
Instead of rewriting each shader, simply rotate the coordinates before drawing.
One transformation affects every procedural shape.
Rotation appears almost everywhere.
Once you understand coordinate rotation, many advanced shaders become much easier to read.
Rotate a rectangle.
Rotate a star.
Rotate a hexagon.
Rotate a repeated pattern.
Slow the rotation.
Speed it up.
Translate the coordinates before rotating.
Observe how changing the order changes the final animation.
Can you create these effects?
Every challenge uses the exact same rotation matrix.
Only the shape changes.