Vectors and Swizzling
The building blocks of shader math
Vectors are the native language of the GPU. Nearly every value in a shader is a vector: positions, colors, directions, texture coordinates. Understanding how to create, access, and manipulate vectors is essential before we can do anything interesting.
Vector Types
GLSL provides four vector types based on how many components they hold:
vec2- two components (x, y)vec3- three components (x, y, z)vec4- four components (x, y, z, w)
Each component is a floating-point number. There are also integer versions (ivec2, ivec3, ivec4) and boolean versions (bvec2, bvec3, bvec4), but we will focus on the float versions since they dominate shader code.
Vector Types
Creating vectors is straightforward:
vec2 position = vec2(0.5, 0.3);
vec3 color = vec3(1.0, 0.5, 0.0);
vec4 rgba = vec4(1.0, 0.0, 0.0, 1.0);You can also construct larger vectors from smaller ones:
vec2 uv = vec2(0.5, 0.5);
vec3 uvw = vec3(uv, 1.0); // vec3(0.5, 0.5, 1.0)
vec4 full = vec4(uvw, 1.0); // vec4(0.5, 0.5, 1.0, 1.0)
vec4 also = vec4(uv, uv); // vec4(0.5, 0.5, 0.5, 0.5)And you can create vectors from a single value, which fills all components:
vec3 gray = vec3(0.5); // vec3(0.5, 0.5, 0.5)
vec4 white = vec4(1.0); // vec4(1.0, 1.0, 1.0, 1.0)Component Access
Each component of a vector can be accessed individually using dot notation. The components have two naming conventions that mean exactly the same thing:
Position names: .x, .y, .z, .w
Color names: .r, .g, .b, .a
These are completely interchangeable. Use whichever makes your code clearer:
vec4 color = vec4(1.0, 0.5, 0.0, 1.0);
float red = color.r; // 1.0
float red2 = color.x; // also 1.0, same thing
float green = color.g; // 0.5
float alpha = color.a; // 1.0
float alpha2 = color.w; // also 1.0Interactive: Component Access
Use .xyzw when thinking about positions and directions. Use .rgba when thinking about colors. The GPU does not care which you use; it is purely for human readability.
Swizzling
Here is where GLSL becomes delightfully expressive. You can access multiple components at once by listing them together. This is called swizzling:
vec4 color = vec4(1.0, 0.5, 0.3, 1.0);
vec2 rg = color.rg; // vec2(1.0, 0.5)
vec3 rgb = color.rgb; // vec3(1.0, 0.5, 0.3)
vec2 xy = color.xy; // vec2(1.0, 0.5) - same as .rgBut it gets better. You can reorder the components:
vec3 bgr = color.bgr; // vec3(0.3, 0.5, 1.0) - reversed!
vec2 yx = color.yx; // vec2(0.5, 1.0) - swappedYou can even repeat components:
vec3 rrr = color.rrr; // vec3(1.0, 1.0, 1.0)
vec4 rrrg = color.rrrg; // vec4(1.0, 1.0, 1.0, 0.5)Interactive: Swizzle Playground
This might seem like a frivolous feature, but it is incredibly powerful in practice. Converting between color spaces, flipping coordinates, extracting subsets of data, all become one-liners.
Common Swizzle Patterns
Some swizzles appear constantly in shader code:
Extracting UV from gl_FragCoord:
vec2 uv = gl_FragCoord.xy / u_resolution;Getting RGB from a vec4 color:
vec3 rgb = color.rgb;Flipping Y coordinate:
vec2 flipped = vec2(uv.x, 1.0 - uv.y);
// Or more elegantly:
vec2 flipped = uv * vec2(1.0, -1.0) + vec2(0.0, 1.0);Creating grayscale from a color:
float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114));
vec3 grayscale = vec3(gray);
// Or quick and dirty: vec3(color.rrr) if you just want the red channelComponent-Wise Operations
When you perform math on vectors, the operation happens to each component independently. This is called component-wise or element-wise operation:
vec3 a = vec3(1.0, 2.0, 3.0);
vec3 b = vec3(0.5, 0.5, 0.5);
vec3 sum = a + b; // vec3(1.5, 2.5, 3.5)
vec3 product = a * b; // vec3(0.5, 1.0, 1.5)
vec3 doubled = a * 2.0; // vec3(2.0, 4.0, 6.0)This is why shader code can be so compact. Operations that would require loops in other languages happen implicitly across all components:
Component-Wise Operations
// Darken a color by 50%
vec3 darkened = color.rgb * 0.5;
// Add two colors
vec3 combined = color1.rgb + color2.rgb;
// Linear interpolation between colors
vec3 mixed = colorA.rgb + (colorB.rgb - colorA.rgb) * t;Building Intuition
Think of vectors as bundles of related numbers that travel together. A position is not just an x and a y stored separately; it is a unified concept that moves and transforms as one unit.
Swizzling lets you reshape these bundles effortlessly. Need just the x and y? Use .xy. Need them reversed? Use .yx. Need to broadcast a single value? Use .xxx.
The component-wise nature means the GPU can process all components in parallel. When you write a * b, the GPU multiplies x by x, y by y, and z by z simultaneously. This is not just convenient syntax; it maps directly to how the hardware works.
Looking Ahead
With vectors and swizzling under your belt, you have the vocabulary to express nearly any shader operation. In the next chapter, we will explore UV coordinates more deeply and learn how to transform the coordinate space itself.
Key Takeaways
vec2,vec3, andvec4hold 2, 3, and 4 float components respectively- Access components with
.xyzw(positions) or.rgba(colors), they are equivalent - Swizzling extracts, reorders, or repeats components in a single expression
- Math operations on vectors happen component-wise, in parallel
- Vectors can be constructed from scalars, other vectors, or combinations