OpenGL mathematics (GLM) is a C++, header-only library based on the OpenGL shading language (GLSL) specification. This library also supports features not included in GLSL, such as transformation and projection matrices, euler angles, quaternions, and noise.

This page compiles my own notes ialong with examples from my own experiences using this library. It is meant to supplement the API docs, which lack code examples.

Resources and documentation

Conventions

Default behaviors and conventions (version 0.9.9):

  • Angles are in radians. We can convert from degrees to radians using glm::radians(angle). We can convert from radians and degrees with glm::degrees(angle)
  • Right-handed coordinate system
  • Quaternions store their components in w,x,y,z order
  • When creating matrices, values are specified in column order, not row. In other words, the code mat3(a,b,c,d,e,f,g,h,i) produces the matrix
    a d g
    b e h
    c f i
    

    This is very convenient, since we often want to pass columns to a matrix. For example, if an object has axes X,Y,Z in world coordinates, the rotation matrix that converts from the object’s local to global coordinate system can be created with mat3 local2global(X,Y,Z)

  • The order of the parameters to euler functions match the rotation order. For example, if our rotation order is XZY, then we would call glm::eulerAngleXZY(xRot, zRot, yRot) to get a matrix corresponding to this rotation.

Recipes

Creating vectors, quaternions and matrices

using namespace glm;

vec3 zero(0.0);   // a 3-component vector of zeros
vec3 zero(1,0,0); // a 3-component vector (1,0,0)
mat3 m(0.0);      // a 3x3 matrix of zeros
mat3 I(1.0);      // the identity matrix
mat3 s(3.0);      // a diagonal matrix with 3.0's down diagonal
quat q(1,0,0,0);  // identity rotation for quaternion, order is wxyz

q = angleAxis(radians(45.0f), vec3(1,0,0)); // 45 degree rotation around X
m = mat3(q); // convert from quaternion to matrix
q = quat(m); // convert from matrix to quaternion

mat4 T(1.0); // a 4x4 identity matrix
m = mat3(T); // extract the top, left 3x3 matrix, corresponding to rotation

vec3 color(1.0, 0.5, 0.25);
vec4 fragColor = vec4(color, 1.0); // extend vec3 to vec4

float x = fragCoord.x; // we can use x,y,z,w to access individual components
float g = fragCoord.g; // or r,g,b,a

Euler angles and matrices

float xRot = 45.0f;
float yRot = 30.0f;
float zRot = 10.0f;

// Initialize matrix from euler angles
//  - angle order matches rotation order
//  - angles are in radians
mat4 R = eulerAngleZYX(radians(vec3(zRot, yRot, xRot)));

// Extract angles from the matrix. 
// Result is put in yRot, xRot, zRot (in radians)
extractAngleYXZ(R, yRot, xRot, zRot);

Matrix and vector operations

vec3 a(1.0, 1.0, 0.5);
vec3 b(2.0, 3.0, 2.0);
float k = 2.0f;

result = k * a; // result is (2,2,1)
result *= 2.0f; // (4,4,2)
result = a * b; // (2,3,1)
result = k / a; // (2.0, 2.0, 4.0)
result = a / k; // (0.5, 0.5, 0.25)
result = a / b; // (0.5,0.33,0.25)
result = cross(vec3(1,0,0), vec3(0,1,0)); // returns (0,1,0)

float len = length(a); // len is 1.5
float projection = dot(b, normalize(a)); // projection = 4

// watch out for zero-length vectors!
dir = normalize(zero); // dir is (-nan, -nan, -nan) because of divide by 0!
if (any(isnan(dir))) std::cout << "error\n";

vec3 x = vec3(1,0,0);
quat q = angleAxis(radians(45.0f), vec3(0,0,1));
vec3 rotated = q * x; // rotated x is (0.707, 0.707, 0.0)

mat3 R(q);
rotated = R * x; // rotated x is (0.707, 0.707, 0.0)

// clamp values to range [0.0, 1.0]
vec3 color(2.0, -3.0, 0.5);
color = clamp(color, vec3(0.0), vec3(1.0));

// floating point modulo
// result is (0.0, 0.25, 0.2)
vec3 c = mod(vec3(1.0, 2.75, -0.3), vec3(0.5));

// raise all components to a power
vec3 d = pow(vec3(1,-2,3), vec3(2.0));

// Create a transform that performs translation, rotation, scale 
mat4 TRS(1.0); // start with identity
TRS = translate(TRS, vec3(1.0,-2.0, 0.0));
TRS = rotate(TRS, radians(10.0f), vec3(0,0,1));
TRS = scale(TRS, vec3(2.0));

// inverse transform
mat4 invT = inverse(T);

Printing

GLM provides features for printing.

#include <glm/gtx/string_cast.hpp>

glm::vec3 test(1,2,3);
std::cout << glm::to_string(test) << std::endl;

matrices are output to a single line. I like having stream operators defined for vector, quaternion, matrix types.

std::ostream& operator<<(std::ostream& o, const glm::mat3& m)
{
   char line[1024];
   o << "mat3x3(\n";
   for (int i = 0; i < 3; i++)
   {
      // indexing reflects fact that values are column order, not row
      // e.g. (i,j) is (col,row)
      sprintf(line, "%10f, %10f, %10f\n",
         m[0][i], m[1][i], m[2][i]);
      o << line;
   }
   o << ")\n";
   return o;
}

// usage
glm::mat3 m(1.0);
std::cout << glm::to_string(m) << std::endl;
std::cout << m << std::endl;

// output
// mat3x3((1.000000, 0.000000, 0.000000), (0.000000, 1.000000, 0.000000), (0.000000, 0.000000, 1.000000))
// mat3x3(
//   1.000000,   0.000000,   0.000000
//   0.000000,   1.000000,   0.000000
//   0.000000,   0.000000,   1.000000
// )

Boolean comparisons

The core library contains functions such as any which returns true if any of the components is true (e.g. non-zero) and lessThan, which returns true or false for each pair of components. These functions behave similarly to the GLSL specification.

GLM also includes testing whether two vectors are so close, they are effectively equal.

#include <glm/gtc/epsilon.hpp>
// See https://glm.g-truc.net/0.9.9/api/a00398.html

float eps; // a small number
vec3 a, b;
bool notEqual = glm::any(glm::epsilonNotEqual(a,b,eps));

For matrices, you need to check each column individually.

bool notEqual = false;
for (int i = 0; i < 3 && !notEqual; i++)
{
   notEqual = glm::any(glm::epsilonNotEqual(a[i],b[i],eps));
}

Gotchas

  • Be careful with integers. Because GLM is a template-based library, integers affect which function the compile will pull into your application. For example, using integers with glm::ortho results in zeros instead of fractional values
int width = 640;
int height = 480;
glm::mat4 ortho1 = glm::ortho(0, width, 0, height, -100,100);
glm::mat4 ortho2 = glm::ortho(0.0, width, 0.0, height, -100.0, 100.0); 

// ortho1:
//  0.000000,   0.000000,   0.000000,   0.000000
//  0.000000,   0.000000,   0.000000,   0.000000
//  0.000000,   0.000000,   0.000000,   0.000000
// -1.000000,  -1.000000,   0.000000,   1.000000

// ortho2:
//  0.002500,   0.000000,   0.000000,   0.000000
//  0.000000,   0.003333,   0.000000,   0.000000
//  0.000000,   0.000000,  -0.010000,   0.000000
// -1.000000,  -1.000000,  -0.000000,   1.000000

vec3 appears to initialize to floats even when integers are used.

  • Quaternions may not always have unit length; however, converting these quaternions to a matrix is always safe.
  • Be careful when using overloaded multiply a * b. Depending on the types for a and b, this might rotate b, multiply component-wise, or do a matrix multiply. Also remember operations are evaluated from left to right!
using namespace glm;

quat rotation = angleAxis(radians(30.0f), vec3(1, 0, 0));
vec3 scale(1, 2, 3);
vec3 pos(1, 2, 1);

vec3 result1 = rotation * scale * pos; 
// result1 = 1.000000, 0.464102, 3.598076

vec3 result2 = rotation * (scale * pos); 
// result2 =  1.000000, 1.964102, 4.598076
  • Don’t call vec3::length when you mean length(vec3))
    glm::vec3 v(1.0, 0.0, 0.0);
    v.length(); // returns 3
    length(v);  // returns 1.0
    
  • Watch out for zero-length vectors. They can result in nans!