Skip to content
Fundamental · VectorsBeginner

Vector basics

vec2, vec3 and vec4, with swizzles, length, distance, normalize and cross.

How it works

GLSL thinks in vectors: vec2, vec3 and vec4 hold two, three or four floats. The same type stores a position, a direction, a color or a UV coordinate; the meaning is yours to keep track of. Arithmetic and most built-in functions work on them component by component, so one line of vector code does the work of two to four lines of scalar code.

Building and combining

A constructor takes any mix of scalars and smaller vectors, as long as the component counts add up:

vec3 white = vec3(1.0);              // one value fills every component
vec4 color = vec4(white * 0.5, 1.0); // a vec3 plus an alpha
vec2 a = vec2(1.0, 2.0);
vec2 b = a * 2.0 + vec2(0.5);        // (2.5, 4.5)
vec2 c = a * b;                      // per component: (2.5, 9.0)

a * b between two vectors multiplies component by component. It is neither the dot product nor the cross product, which have their own functions. GLSL ES 3.00 also never converts an int to a float on its own: a constructor like vec2(1, 2) converts its arguments, but a * 2 is a compile error. Write 2.0.

Swizzles

Components are named x y z w, r g b a or s t p q: the same four slots under three sets of names. Any combination of names, in any order and with repeats, builds a new vector. This is a swizzle:

vec4 v = vec4(1.0, 2.0, 3.0, 4.0);
vec2 xy  = v.xy;  // (1, 2)
vec3 bgr = v.bgr; // (3, 2, 1)
vec2 yy  = v.yy;  // (2, 2)
v.zw = vec2(0.0); // writing works too, without repeats

One swizzle can’t mix sets: v.xg doesn’t compile.

Length, distance and normalize

length(v) is the vector’s magnitude: the square root of the sum of its squared components. distance(a, b) is length(a - b), the space between two points. In a fragment shader, length(p) on a centered UV is the distance from the center, the start of every circle and ring.

normalize(v) divides a vector by its length, which keeps its direction and makes its length 1. Directions such as a surface normal, a light direction or a view direction should be unit vectors: the dot product of two unit vectors is the cosine of the angle between them, and lighting relies on that. Normalizing a zero vector divides by zero and gives an undefined result, often NaN, so guard any vector that can collapse to zero.

Cross product

cross(a, b), defined for vec3 only, returns a vector perpendicular to both, whose length is |a| |b| sin θ. Its direction follows the right-hand rule, so swapping the arguments flips it. Shaders use it to get a normal from two edges of a triangle, or to complete a frame of three axes from two:

vec3 n = normalize(cross(edge1, edge2)); // perpendicular to the triangle

Further reading