Concepts3 min read

Vectors and Matrices

How vectors and matrices work in shaders — the math that powers every transformation, lighting calculation, and effect.

Reading Time
3 min
Word Count
452
Sections
16
Try It Live

Turn the guide into code

Take the key idea from this page into the playground and validate it in a real shader instead of leaving it as theory.

Open Playground

Vectors and matrices are the core data types in shader programming. Every position, direction, color, and transformation is expressed through them.

Vectors

A vector is a fixed-size bundle of numbers. BWSL provides float2, float3, and float4:

bwsl
float2 uv = float2(0.5, 0.75);
float3 position = float3(1.0, 2.0, 3.0);
float4 color = float4(1.0, 0.0, 0.0, 1.0); // red, fully opaque

What Vectors Represent

The same float3 can mean completely different things depending on context:

UsageExampleComponents mean
Positionfloat3(10, 5, 3)x, y, z coordinates
Directionfloat3(0, 1, 0)pointing up
Colorfloat3(1, 0.5, 0)red, green, blue
Scalefloat3(2, 2, 2)uniform 2x scale

The GPU doesn't care — it's all just numbers. You give them meaning by how you use them.

Component Access

Access individual components with .x, .y, .z, .w:

bwsl
float3 v = float3(1.0, 2.0, 3.0);
float a = v.x; // 1.0
float b = v.y; // 2.0
float c = v.z; // 3.0

For colors, you can use .r, .g, .b, .a — they're aliases for the same components:

bwsl
float4 color = float4(1.0, 0.5, 0.0, 1.0);
float red = color.r; // same as color.x → 1.0
float alpha = color.a; // same as color.w → 1.0

Swizzling

You can read multiple components in any order, even with repeats:

bwsl
float3 v = float3(1.0, 2.0, 3.0);
float2 a = v.xy; // float2(1.0, 2.0)
float2 b = v.yx; // float2(2.0, 1.0) — swapped
float3 c = v.zzy; // float3(3.0, 3.0, 2.0) — repeated
float4 d = v.xyzz; // float4(1.0, 2.0, 3.0, 3.0) — expanded

This is extremely useful for packing, unpacking, and rearranging data without temporary variables.

Arithmetic

Vectors support component-wise arithmetic. Operations apply to each component independently:

bwsl
float3 a = float3(1.0, 2.0, 3.0);
float3 b = float3(4.0, 5.0, 6.0);
float3 sum = a + b; // float3(5.0, 7.0, 9.0)
float3 product = a * b; // float3(4.0, 10.0, 18.0)
float3 scaled = a * 2.0; // float3(2.0, 4.0, 6.0)

a * b is component-wise multiplication, NOT the dot product or cross product. For those, use dot(a, b) and cross(a, b).

Essential Vector Operations

Length and Distance

length(v) gives the magnitude of a vector. distance(a, b) gives the distance between two points.

bwsl
float3 v = float3(3.0, 4.0, 0.0);
float len = length(v); // 5.0
float dist = distance(a, b); // same as length(a - b)

Normalize

normalize(v) returns a vector pointing the same direction but with length 1. Essential for directions — lighting math assumes unit-length normals and light directions.

bwsl
float3 dir = float3(3.0, 4.0, 0.0);
float3 unit = normalize(dir); // float3(0.6, 0.8, 0.0), length = 1.0

Dot Product

dot(a, b) measures how aligned two vectors are. Returns a single float:

bwsl
float3 normal = normalize(input.normal);
float3 lightDir = normalize(float3(1.0, 1.0, 0.0));
float alignment = dot(normal, lightDir);
// 1.0 = facing light, 0.0 = perpendicular, -1.0 = facing away

Cross Product

cross(a, b) returns a vector perpendicular to both inputs. Only works on float3. Used for computing normals from two edges of a triangle:

bwsl
float3 edge1 = v1 - v0;
float3 edge2 = v2 - v0;
float3 faceNormal = normalize(cross(edge1, edge2));

Reflect

reflect(incident, normal) reflects a vector off a surface. Used for specular lighting and mirror effects:

bwsl
float3 reflected = reflect(-lightDir, normal);
float specular = pow(max(dot(reflected, viewDir), 0.0), 32.0);

Matrices

A matrix transforms vectors — rotating, scaling, translating, or projecting them. BWSL provides mat3 (3x3) and mat4 (4x4).

Transform Chain

3D rendering typically chains three matrices:

bwsl
vertex {
float4 worldPos = resources.modelMatrix * float4(attributes.position, 1.0);
float4 viewPos = resources.viewMatrix * worldPos;
output.position = resources.projectionMatrix * viewPos;
}
MatrixWhat it does
ModelObject space → World space (position, rotation, scale of the object)
ViewWorld space → Camera space (where things are relative to the camera)
ProjectionCamera space → Clip space (perspective or orthographic projection)

Matrix-Vector Multiplication

mat4 * float4 transforms the vector. Order matters — matrix on the left, vector on the right:

bwsl
float4 result = myMatrix * float4(position, 1.0);

Use 1.0 as the W component for positions (affected by translation) and 0.0 for directions (not affected by translation): float4(direction, 0.0).

Normal Transformation

Normals need special treatment — they should be transformed by the inverse transpose of the model matrix to stay perpendicular to the surface after non-uniform scaling:

bwsl
float3 worldNormal = normalize((resources.normalMatrix * float4(attributes.normal, 0.0)).xyz);

See Also