The 2×2 Rotation Matrix
Spin a 2-D vector about the origin.
Almost every shader needs to rotate something — a coordinate frame, a texture lookup, a ray. In two dimensions that is one small matrix built from the sine and cosine of the angle.
Multiplying a vector by this matrix turns it counter-clockwise by a radians while preserving its length. Rotating *coordinates* by −a is the same as rotating the *content* by +a, which is why you often see it applied to uv.
mat2 rot(float a) {
float s = sin(a), c = cos(a);
return mat2(c, -s, s, c);
}
// uv *= rot(angle); // rotate the whole plane Signed Distance Fields
Describe a shape by its distance, not its pixels.
A signed distance field (SDF) is a function that returns, for any point, the distance to the nearest surface of a shape — negative inside, zero on the edge, positive outside. The shape is never stored; it is computed wherever you ask.
SDFs compose beautifully: min(a, b) is union, max(a, b) is intersection, max(a, -b) is subtraction. Soft-thresholding the distance with smoothstep gives a crisp, resolution-independent edge. The same field can be drawn in 2-D or marched in 3-D.
// rounded box, radius r
float sdBox(vec2 p, vec2 s, float r) {
vec2 q = abs(p) - s + r;
return min(max(q.x, q.y), 0.) + length(max(q, 0.)) - r;
} Raymarching
Render 3-D surfaces by stepping a ray through a distance field.
Raymarching renders solid 3-D scenes with no meshes. For each pixel it fires a ray and walks a point forward in steps. At each step it evaluates a distance function — "how far to the nearest surface?" — and advances by that amount, so it leaps through empty space and slows to a crawl near a surface.
When the step size drops below a small threshold the ray has hit something. The surface normal then comes from finite differences: sample the field a few times around the hit point and take the gradient. Lighting, reflections and fog all build from there.
float d = 0.0;
for (int i = 0; i < 100; i++) {
vec3 p = ro + rd * d; // point along the ray
float h = map(p); // distance to nearest surface
if (h < 0.001) break; // close enough = a hit
d += h; // safe to leap this far
} Refraction & Total Internal Reflection
Bend a ray as it crosses into glass, and trap it inside.
When light passes between two media it bends, by an amount set by their index-of-refraction ratio (Snell's law). GLSL has refract(rd, n, eta) built in: give it the incoming ray, the surface normal, and the ratio of indices, and it returns the bent ray. To render glass you raymarch *into* the surface, refract, flip the sign of the distance field so you are now marching through the inside, and continue.
Past a critical angle there is no valid exit ray — the light cannot escape and reflects back inward. refract signals this by returning a zero vector; the shader checks dot(r, r) < epsilon and falls back to reflect. Looping this refract-march-refract a few times gives the layered, lensy look of a thick bubble, each bounce contributing a little less.
vec3 r = refract(rd, n, eta);
if (dot(r, r) < 1e-4) // total internal reflection
rd = reflect(rd, n);
else { rd = r; eta = 1.0/eta; inside = -inside; } The Gyroid
An infinite, smooth lattice from one line of trig.
The gyroid is a triply-periodic minimal surface — a smooth lattice that repeats in all three directions with no flat spots. In a shader it collapses to a single expression, which makes it a favourite source of organic 3-D structure.
Scaling the input changes the lattice frequency; nesting one gyroid inside another, or adding sine modulation, warps it into endlessly varied tunnels and membranes. Note it is not a true distance field, so raymarching it needs cautious, scaled-down steps.
#define gyr(p) dot(sin(p), cos(p.zxy))
// gyr(p*scale) - thickness; // a gyroid surface
Feedback Buffers
Give a shader a memory by reading its own last frame.
Shaders are normally stateless — the same picture every frame. A feedback buffer breaks that: an off-screen buffer samples its *own* output from the previous frame, so state can copy itself forward through time, living entirely inside a texture.
In practice this is a ping-pong of two textures: you read from last frame and write to a fresh one, then swap. It is the foundation of simulations, trails, growth, and any shader that "remembers" — from window positions to chemical concentrations.
// Buffer A, channel 0 = Buffer A (itself, last frame)
vec4 prev = texture(iChannel0, uv);
// ...evolve prev...
fragColor = next; // becomes next frame's input
The Laplacian Kernel
Measure how a cell differs from its neighbours.
Many grid simulations need to know whether a pixel sits in a dip or on a bump relative to its surroundings. The discrete Laplacian answers that with a small convolution: a weighted sum of the 3×3 neighbourhood where the centre is strongly negative and the neighbours positive.
The result drives diffusion — quantity flows from high concentration to low. Tuning the neighbour weights (edges vs. diagonals) changes how isotropic the spread is. The same convolution idea underlies blur, sharpen, and edge-detect kernels.
// 3x3 weights: center -1, edges +0.2, corners +0.05
float sum = -1.0 * here;
sum += 0.2 * (up + down + left + right);
sum += 0.05 * (corners...);
Symmetry Folds
Draw a slice once, mirror it into many.
Rotational symmetry is cheap if you fold the plane before drawing. Convert a point to polar coordinates, wrap its angle into a single wedge with mod, and convert back. Whatever you draw in that one wedge now appears repeated around the circle — kaleidoscope for free.
The wedge size sets the symmetry order: mod(angle, PI/2.) gives four-fold, PI/3. gives six-fold, and so on. Reflecting the wedge as well (with abs) adds mirror symmetry on top of rotational.
// 4-fold rotational symmetry
float a = mod(atan(p.y, p.x), PI/2.);
p = length(p) * vec2(cos(a), sin(a));