Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 5

Orientation Conversions

Recommended resources

In our previous lesson, we forged the fundamental mathematical tools for our physics engine: a library for 3D vectors and 4x4 matrices. You now have the means to represent positions, directions, and the transformations that act upon them. Now, we turn our attention to one of the most crucial and nuanced aspects of 3D simulation: orientation.

While a 4x4 matrix can certainly represent an object's orientation, it's not always the most efficient or intuitive tool for the job. In this lesson, we will explore a "zoo" of different ways to represent 3D rotation, including the intuitive axis-angle, the user-friendly Euler angles, and the powerful quaternion. Your goal is to implement a robust set of functions to convert between these representations. Mastering these conversions is essential, as you'll find that different parts of a physics engine are best served by different rotational formats.

This diagram illustrates the primary representations for 3D rotation and the conversion paths between them. We will be implementing the functions that make these arrows a reality in our code.

A Tour of Rotation Representations

Before we dive into code, it's important to understand the conceptual landscape. Each method of representing orientation has distinct advantages and disadvantages, making them suitable for different tasks.

Rotation in Three Dimensions

This chapter from "3D Math Primer for Graphics and Game Development" provides an excellent overview of the different ways to represent 3D orientation. It will give you the context for why we need more than just matrices.

As you read, focus on building a high-level understanding of each method: Matrix Form: Briefly review this section. Pay attention to the discussion of pros and cons, particularly the concepts of memory usage and "matrix creep." Euler Angles: Read this section carefully. Understand the heading-pitch-bank convention and, most importantly, the description of Gimbal lock. This phenomenon is a primary reason why physics engines rely on quaternions. Axis-Angle and Exponential Map: Skim this section to understand the core idea behind Euler's rotation theorem: any 3D angular displacement can be described as a single rotation around a specific axis.

To summarize the key players:

  • Rotation Matrix: A 3x3 (or the upper-left of a 4x4) matrix. Great for applying rotations to vectors and for hardware APIs like OpenGL, but they are memory-intensive (9 floats) and can suffer from numerical drift ("matrix creep"), causing objects to deform over time.
  • Axis-Angle: The most intuitive representation, defined by a unit vector for the axis and a scalar angle . It's the physical reality of how rotations happen.
  • Euler Angles: Three angles (e.g., heading, pitch, bank) that represent sequential rotations around cardinal axes. They are easy for humans to understand and author, making them great for level editors or user input, but they suffer from Gimbal lock and are problematic for interpolation.
  • Quaternions: A four-component number () that elegantly avoids the problems of both matrices and Euler angles. They are compact, easy to normalize (preventing creep), and free from Gimbal lock, making them the standard choice for representing orientation internally in a physics engine.

Implementing the Conversion Functions

Now, let's build the bridges between these representations. Your task is to implement a set of conversion functions within your math library. For each conversion, I will provide the core formulas and point you to resources with detailed derivations and implementation code.

First, let's define the quaternion struct you'll be working with. It's similar to a vec4, but we'll give it named components for clarity.

struct quaternion {
    union {
        struct {
            float x, y, z, w;
        };
        float elements[4];
    };
};

1. Axis-Angle Rotation Matrix

This conversion path connects the most intuitive representation to the most practical one for transforming vectors. The mathematical basis for this is Rodrigues' rotation formula.

This diagram shows the geometry behind Rodrigues' formula. The final rotated vector \(\vec{OQ}\) is a sum of the component parallel to the axis of rotation \(\vec{r}\), and the rotated components in the plane perpendicular to the axis.

From Axis-Angle to Matrix:
Given an axis and an angle , the corresponding 3x3 rotation matrix is:

You can implement a function mat4_from_axis_angle(vec3 axis, float angle) that computes this 3x3 matrix and places it into the upper-left of a mat4 identity matrix.

Rotation Representations

This document provides a concise mathematical derivation of Rodrigues' formula.

Read section 2.1. The final formula, R = I + (sin θ)S + (1 −cos θ)S^2, is an elegant matrix form of the equation above.

From Matrix to Axis-Angle:
This is the reverse process. Given a rotation matrix , we can find and .
The angle is found from the trace (the sum of the diagonal elements) of the matrix:

The axis components are found from the off-diagonal elements:

You'll need to handle the special cases where (identity matrix, any axis is fine) and (where ). The resource below details how to handle these cases robustly.

Rotation Representations

This resource provides the formulas and logic for handling the special cases.

Read section 2.2 to see how to implement this conversion, including the logic for when the angle is 0 or \pi. Note that the resource uses w for the axis components, which corresponds to our n.

2. Quaternion Rotation Matrix

This is the most important conversion path for a physics engine, as you will store orientation in quaternions but need matrices for rendering and vector transformation.

From Quaternion to Matrix:
Given a quaternion , the equivalent 3x3 rotation matrix is:

Implement a function mat4_from_quaternion(quaternion q).

From Matrix to Quaternion:
This direction requires a bit more care to maintain numerical stability. The best approach involves checking which component of the quaternion will be the largest and using a different formula for each case to avoid division by small numbers.

Rotation in Three Dimensions

This resource contains detailed derivations and C++ code for converting between matrices and quaternions.

To implement quaternion to matrix, read Section 8.7.3. The final matrix in Equation (8.20) is what you need to implement. To implement matrix to quaternion, read Section 8.7.4. Pay close attention to the robust strategy described and implemented in Listing 8.5, which avoids numerical issues.

3. Quaternion Axis-Angle

This conversion highlights the geometric nature of a quaternion.

From Axis-Angle to Quaternion:
This is the fundamental definition of a rotation quaternion. For an axis and angle :

A great way to structure this is as a constructor or factory function for your quaternion type.

Math for Game Developers - Rotation Quaternions

This video provides a clear, intuitive explanation and a C++ implementation of this exact conversion.

Watch from the beginning to understand the formulas. Then, see how this is implemented in a C++ constructor from this segment.

From Quaternion to Axis-Angle:
This is the reverse of the above. For a quaternion :

Again, you must handle the case where (or is very close), which means . In this case, the axis is undefined and can be set to any unit vector.

Rotation Representations

This section provides the concise formulas for this conversion.

Read Section 3.2 for the direct formulas.

4. Conversions Involving Euler Angles

Since Euler angles are so common for user input, you'll need a way to get them into and out of your engine's internal quaternion format. The most efficient way is to convert directly, rather than going through a matrix.

Rotation in Three Dimensions

This resource provides direct conversion formulas and code for Euler angles and quaternions.

For Euler angles to quaternion, read Section 8.7.5. This shows how to construct the final quaternion by multiplying the quaternions for heading, pitch, and bank. For quaternion to Euler angles, read Section 8.7.6. This derivation and the code in Listing 8.6 show how to extract the angles and correctly handle the Gimbal lock case.


Visualizing Your Work

With your conversion functions implemented, use your debug-drawing system to verify them.

  1. Define an orientation using Euler angles (e.g., heading=0, pitch=45, bank=0).
  2. Convert these Euler angles to a quaternion using your new function.
  3. Convert the quaternion to a mat4 using another new function.
  4. Render a transform using this mat4. You should see a coordinate system pitched 45 degrees downward.
  5. Experiment with different angles and conversion paths (e.g., Axis-Angle -> Matrix) to build confidence in your library.

Conclusion

In this lesson, you've taken a deep dive into the world of 3D rotations and expanded your math library with a new quaternion type and a suite of essential conversion functions. You are now equipped to handle orientation data in whatever format is most appropriate for the task at hand.

Key Takeaways:

  • There are multiple ways to represent 3D orientation (Matrix, Axis-Angle, Euler Angles, Quaternion), each with specific strengths and weaknesses.
  • Quaternions are the preferred internal representation for physics engines because they are compact, robust against numerical drift, and free from Gimbal lock.
  • The conversion functions between these formats are the critical glue that allows different systems (user input, physics, rendering) to communicate.
  • Robust conversion code must handle edge cases, such as Gimbal lock for Euler angles and zero-degree rotations for axis-angle.

You now have a quaternion type and can convert to and from it. However, we haven't yet used it for any calculations. In the next lesson, we will implement the fundamental quaternion operations, including multiplication for composing rotations and the rotation of a vector by a quaternion. These operations are the final mathematical prerequisite before we can begin simulating the motion of rigid bodies.