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
-
[GLM Project page](https://glm.g-truc.net/0.9.9/index.html)
-
[GLM Github](https://github.com/g-truc/glm)
-
[GLM API documentation and Manual](https://glm.g-truc.net/0.9.9/api/index.html)
-
[OpenGL Shading Language (GLSL) specification](https://www.khronos.org/registry/OpenGL/specs/gl/GLSLangSpec.4.50.pdf) The GLM library matches the behavior of this specification whenever possible.
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 withglm::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
-
[Euler angle API](https://glm.g-truc.net/0.9.9/api/a00164.html)
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
-
[Matrix transformation API](https://glm.g-truc.net/0.9.9/api/a00245.html)
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 meanlength(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!