Shader concept
UV coordinates in shaders: where am I on the screen?
UV coordinates are the position of a pixel inside a picture or surface, written as two numbers from 0 to 1. A fragment shader reads them to decide what colour to draw at that point.
Running live in your browser. The code below is exactly what draws it.
What the two numbers mean
u runs from 0 at the left edge to 1 at the right edge. v runs from 0 to 1 the other way, bottom to top in this site and in Shadertoy, top to bottom in Unity textures, Godot and Unreal. Write float2 uv and you have both numbers for the pixel being drawn.
What you do with them
- Colour by position:
float3(uv.x, uv.y, 0.5)is the picture above. - Centre the origin:
p = uv - 0.5puts (0, 0) in the middle, which makes circles and rotation easy. Multiplyxby the aspect ratio so shapes are not stretched. - Repeat:
frac(uv * 5.0)tiles the picture five times in each direction. - Look up a texture: in a game, the same
uvpicks which texel of an image to read.
Common mistakes
- Assuming the y axis points the same way in every engine. If your picture is upside down in Unity or Godot, flip with
1.0 - uv.y. - Drawing circles in raw uv: the screen is wider than tall, so a "circle" comes out as an oval. Scale
xby the aspect ratio.
The code, in every shader language
The same example for each language family. Open one to see it.
HLSL Unity, Unreal
float4 frag(float2 uv)
{
// u (left to right) drives red, v (bottom to top) drives green,
// and the thin lines mark every tenth of the way across
float2 g = abs(frac(uv * 10.0) - 0.5);
float line = 1.0 - smoothstep(0.46, 0.5, max(g.x, g.y));
float3 col = float3(uv.x, uv.y, 0.35);
col = lerp(col, float3(1.0, 1.0, 1.0), line * 0.6);
return float4(col, 1.0);
} GLSL Shadertoy, three.js, OpenGL
vec4 frag(vec2 uv)
{
// u (left to right) drives red, v (bottom to top) drives green,
// and the thin lines mark every tenth of the way across
vec2 g = abs(fract(uv * 10.0) - 0.5);
float line = 1.0 - smoothstep(0.46, 0.5, max(g.x, g.y));
vec3 col = vec3(uv.x, uv.y, 0.35);
col = mix(col, vec3(1.0, 1.0, 1.0), line * 0.6);
return vec4(col, 1.0);
} WGSL WebGPU, Bevy
fn frag(uv: vec2f) -> vec4f
{
// u (left to right) drives red, v (bottom to top) drives green,
// and the thin lines mark every tenth of the way across
let g = abs(fract(uv * 10.0) - 0.5);
let line = 1.0 - smoothstep(0.46, 0.5, max(g.x, g.y));
var col = vec3f(uv.x, uv.y, 0.35);
col = mix(col, vec3f(1.0, 1.0, 1.0), line * 0.6);
return vec4f(col, 1.0);
} Godot Godot 4 shading language
shader_type canvas_item;
vec4 frag(vec2 uv)
{
// u (left to right) drives red, v (bottom to top) drives green,
// and the thin lines mark every tenth of the way across
vec2 g = abs(fract(uv * 10.0) - 0.5);
float line = 1.0 - smoothstep(0.46, 0.5, max(g.x, g.y));
vec3 col = vec3(uv.x, uv.y, 0.35);
col = mix(col, vec3(1.0, 1.0, 1.0), line * 0.6);
return vec4(col, 1.0);
}
void fragment()
{
// Godot's UV starts at the top-left, the lessons use bottom-left
COLOR = frag(vec2(UV.x, 1.0 - UV.y));
} Metal iOS, macOS
float4 frag(float2 uv)
{
// u (left to right) drives red, v (bottom to top) drives green,
// and the thin lines mark every tenth of the way across
float2 g = abs(fract(uv * 10.0) - 0.5);
float line = 1.0 - smoothstep(0.46, 0.5, max(g.x, g.y));
float3 col = float3(uv.x, uv.y, 0.35);
col = mix(col, float3(1.0, 1.0, 1.0), line * 0.6);
return float4(col, 1.0);
} Practise it in the lessons
- Every pixel, one functionMake the whole screen the same blue as the target.
- UV: where am I?Make a gradient: black on the left, full red on the right.
- Two axesRed grows to the right, green grows upward, blue is a constant 0.5.
- Center, distance & aspect ratioMake the circle truly round by correcting for the aspect ratio.
Questions
What does UV stand for?
U and V are just the names of the two texture-space axes, chosen to avoid confusion with x, y and z, which name positions in the 3D world.
Is uv the same as gl_FragCoord?
No. gl_FragCoord is the pixel position in pixels (for example 0 to 1919). uv is that position divided by the picture size, so it runs from 0 to 1 whatever the resolution.