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.5 puts (0, 0) in the middle, which makes circles and rotation easy. Multiply x by 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 uv picks 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 x by 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

Start with "Every pixel, one function"

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.

Related concepts