UV warping
Bend a picture by moving the coordinate it is read at, with waves or noise.
Cards
2 live examples to tweak, learn and copy.
How it works
A shader never moves pixels. It decides, for each pixel, where to look: which point of a pattern to draw, or which
point of a texture to read. UV warping changes that “where”. Add a small offset to the UVA 2D position on a surface or the screen, usually from 0 to 1 on both axes. Shaders draw shapes and patterns as functions of it. UV coordinates A 2D position on a surface or the screen, usually from 0 to 1 on both axes. Shaders draw shapes and patterns as functions of it.uv = gl_FragCoord.xy / resolution
vec2 warped = uv + offset(uv, time);
vec3 color = pattern(warped); // or texture(image, warped).rgb
The 2D transforms fundamental moves the whole space by one offset. Warping is the same subtraction, with an offset that varies across the surface. Everything about the look lives in the offset:
- A sine wave along one axis gives a ripple that travels: a flag, a wavy banner, water lines.
- Several sines at unrelated frequencies give a shimmer that doesn’t visibly repeat: heat, a mirage, glass.
- Noise gives organic, unpredictable bends, and feeding noise back into itself is domain warping: smoke, ink, marble.
Two stages, one idea
On a surface, the warped coordinate feeds a procedural pattern or the object’s own texture. Waving flag draws its stripes in the bent UV, so the cloth seems to fold although the quad stays flat.
As a screen post pass, the warped coordinate reads the finished frame. The scene is drawn into a
render targetA texture a draw writes into instead of the screen, so a later pass can read the result. Blur, bloom and every multi-pass effect use them. Render target A texture a draw writes into instead of the screen, so a later pass can read the result. Blur, bloom and every multi-pass effect use them. Post-process chain Full-screen passes run in order over the finished frame, each reading the image the previous one wrote. Bloom, tone mapping and color grading live here.
On a sprite, the same lookup reads the sprite’s texture, for a swaying plant or a flag in a 2D game.
What to watch
- Strength is in UV units. An offset of 0.01 moves the lookup by 1% of the surface, which on a 1000-pixel screen is 10 pixels. Screen distortion reads as strong long before that.
- Edges. A warped lookup near the border reads outside 0..1. A clamped texture repeats its edge texel, a repeating one wraps around to the far side; pick the one that hides it, or fade the warp out near the edges.
- Where the offset is zero, nothing moves. Masking the offset (by height, distance or a texture) is how you keep the pole of a flag still or limit heat to the air over a fire.
Further reading
- Domain warping (opens in a new tab)Article
- Fractal Brownian Motion (opens in a new tab)Book
- Improving Noise (opens in a new tab)Paper
- Perlin noise (opens in a new tab)Reference
- Simplex noise (opens in a new tab)Reference
- sin (opens in a new tab)Reference
- Sine wave (opens in a new tab)Reference
- Shaping functions (opens in a new tab)Book
- 2D Matrices (opens in a new tab)Book
- Rotation matrix (opens in a new tab)Reference
- Transformations (opens in a new tab)Article
- Framebuffers (opens in a new tab)Article
- Bloom (opens in a new tab)Article
- Custom post-processing (opens in a new tab)Docs
- cos (opens in a new tab)Reference
- CanvasItem shaders (opens in a new tab)Docs
- Using VisualShaders (opens in a new tab)Docs
- Mirage: heat haze (opens in a new tab)Reference
- Screen-reading shaders (opens in a new tab)Docs
- Generic Refraction Simulation (GPU Gems 2, chapter 19) (opens in a new tab)Book
- texture (opens in a new tab)Reference