Step 4

Matrices and Transformations

Learn about the fundamental concepts of matrices and transformations in the context of graphics (and physics) programming.

Overview

In Step 3 "Vectors, Points and Spaces" you learned the conceptual difference between vectors, points, and spaces.

Now we take the next major step: understanding matrices — the mathematical machinery that moves objects through space, rotates them, scales them, and ultimately projects them onto the screen.

This tutorial explains:

  • What 4×4 matrices represent
  • Why we use homogeneous coordinates
  • How translation, rotation, and scale are encoded
  • How model, view, and projection matrices work
  • How transformations compose into the MVP pipeline
  • How these concepts appear in btm_framework

By the end, you’ll understand the transformation pipeline used in every rendering engine and physics simulation.

1. Why 4×4 Matrices?

In 3D graphics and physics, we use 4×4 matrices because they allow us to represent:

  • Translation
  • Rotation
  • Scale
  • Perspective projection

All within a single unified structure.

This is possible thanks to homogeneous coordinates, where a point is represented as:

\[\left(x,y,z,1\right)\ \]

and a vector as:

\[\left(x,y,z,0\right)\ \]

The extra component (w) allows translation to be expressed as matrix multiplication — something impossible with 3×3 matrices.

2. Affine Transformations

An affine transformation matrix looks like this:

\[\left[\begin{matrix}R&T\\0&1\end{matrix}\right]\]

Where:

  • \(R\) is a 3×3 rotation/scale matrix
  • \(T\) is a translation vector
  • The bottom row ensures homogeneous behavior

This structure is the backbone of all transformations.

3. Translation, Rotation, and Scale

Translation

Moves a point in space:

\[T\left(x,y,z\right)\]

In matrix form:

\[\left[\begin{matrix}1&0&0&t_x\\0&1&0&t_y\\0&0&1&t_z\\0&0&0&1\end{matrix}\right]\]

Rotation

Rotates around an axis.

For example, rotation around the Z axis:

\[\left[\begin{matrix}\cos\theta & -\sin\theta & 0 & 0\\ \sin\theta & \cos\theta & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 1\end{matrix}\right]\]

Scale

Uniform or non uniform scaling:

\[\left[\begin{matrix}s_x & 0 & 0 & 0\\ 0 & s_y & 0 & 0\\ 0 & 0 & s_z & 0\\ 0 & 0 & 0 & 1\end{matrix}\right]\]

4. Model, View, Projection

Every vertex in a 3D scene goes through a sequence of transformations:

Model Matrix (M)

Transforms from model space → world space.

It positions, rotates, and scales the object in the scene.

View Matrix (V)

Transforms from world space → camera space.

It represents the camera’s position and orientation.

Projection Matrix (P)

Transforms from camera space → clip space.

It applies perspective or orthographic projection.

5. The Model View Projection (MVP) Pipeline

The final transformation applied to a point is:

\[ MVP = P \cdot V \cdot M \]

And the vertex is transformed as:

\[ p_{clip} = MVP \cdot p_{model} \]

This is the heart of all rendering pipelines.

6. Why Order Matters

Matrix multiplication is not commutative:

\[ A \cdot B \neq B \cdot A \]

This means:

  • rotating then translating ≠ translating then rotating
  • scaling then rotating ≠ rotating then scaling

Understanding this prevents 90% of transformation bugs.

7. Spaces Revisited

Each matrix moves data into a new space:

  • Model Matrix: model space → world space
  • View Matrix: world space → camera space
  • Projection Matrix: camera space → clip space

After clip space, the GPU performs:

  • perspective divide
  • viewport transform
  • rasterization

But those come later.

8. Practical Example

We can see how all these concepts apply in practice. Everything is covered in the "matrices_transformations" example in the BeforeTheMesh repository.

In that example, we create a simple scene with a rotating cube and a camera orbiting around it. The cube's model matrix is updated every frame to apply rotation, while the view matrix is set to position the camera. The projection matrix is configured for perspective projection. By examining the code, you can see how these matrices are constructed and combined to render the scene correctly.

void calculate_sample_mvp()
{
    // calculate a sample Model-View-Projection (MVP) matrix for rendering an object.
    // This is just an example to show how the transformations work.
    btm::fmat4 translation = btm::Translation(0.0f, 0.0f, -5.0f);  // translate the object 5 units into the screen
    btm::fmat4 rotation = btm::Rotation(0, 0, 0);  // no rotation for now, but you could set this to rotate the object around its axes
    btm::fmat4 scale = btm::Scale(1.0f, 1.0f, 1.0f); // no scaling for now, but you could set this to make the object larger or smaller
    btm::fmat4 model = translation * rotation * scale;  // the model matrix transforms the object from its local model space to world space

    // sample setup for the camera's view and projection matrices. 
    // In a real application, these would be computed based on the camera's parameters and the viewport size.
    btm::fvec3 cameraPos(0, 0, 20);  // position the camera 20 units away from the origin along the z-axis
    btm::fvec3 cameraTarget(0, 0, 0);  // look at the origin where the object is located
    btm::fvec3 upVector(0, 1, 0);   // define the up direction for the camera (positive y-axis)
    btm::fmat4 view = btm::LookAt(cameraPos, cameraTarget, upVector);  // the view matrix transforms world space to camera (view) space

    // calculate a perspective projection matrix with a 20 degree field of view, 
    // an aspect ratio of 800/600, and near/far planes at 0.1 and 100.
    float fov = btm::dtr(20.f); // convert 20 degrees to radians
    float aspect = 800.0f / 600.0f; // example aspect ratio
    float nearPlane = 0.1f;  // near plane distance
    float farPlane = 100.0f;  // far plane distance
    btm::fmat4 projection = btm::Perspective(fov, aspect, nearPlane, farPlane);

    // finally, we combine the model, view, and projection matrices to get the MVP matrix 
    // that will be used in the vertex shader to transform the vertices of the object from model space to clip space.
    btm::fmat4 mvp = projection * view * model;
}

This is the exact structure used in modern rendering.

9. Common Mistakes

Understanding matrices and transformations is crucial to avoid common pitfalls such as:

  • Applying transformations in the wrong order
  • Forgetting that scaling affects rotation
  • Mixing up row major and column major conventions
  • Treating points like vectors (or vice versa)
  • Forgetting to normalize direction vectors

This tutorial prepares you to avoid all of them.

10. Summary

In this tutorial, we covered the fundamental concepts of matrices and transformations in graphics programming. We learned why 4×4 matrices are used, how translation, rotation, and scale are represented, and how the model, view, and projection matrices work together in the MVP pipeline. We also discussed common mistakes to avoid when working with transformations.

  • Why 4×4 matrices are used
  • How translation, rotation, and scale work
  • How model, view, and projection matrices transform geometry
  • How the MVP pipeline composes
  • Why order matters
  • How these concepts appear in the framework

This is the mathematical engine behind all rendering and simulation.

Next Step

Before we can move on to more advanced topics, we must make sure that we have a solid understatnding of programming basics. In the next few tutorials, we will cover the basics of programming in C++ and how to use C++ for Simulations and Graphics. This will include topics such as memory management, object-oriented programming, and the use of libraries and frameworks.

Once we have a solid understanding of C++ programming, we will be able to move on to more advanced topics such as physics simulations, graphics rendering, and CAE simulations.

Step 5: C++ as a Simulation Language.