OpenGL Cheatsheet

Transformations and Matrices

Use this OpenGL reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

Coordinate Spaces

Vertices travel through a chain of spaces before reaching the screen.

Object Space → [Model] → World Space → [View] → View/Eye Space
→ [Projection] → Clip Space → [Perspective Divide] → NDC
→ [Viewport Transform] → Window/Screen Space
  • Object space: local to the mesh
  • World space: global scene coordinates
  • View/eye space: camera at origin, looking down -Z
  • Clip space: after projection; gl_Position is here
  • NDC: after perspective divide; X,Y,Z ∈ [-1, 1]
  • Window space: pixels; Y=0 at bottom-left by default

GLM Essentials

GLM is the standard math library for OpenGL; mirrors GLSL types exactly.

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>         // glm::value_ptr

glm::vec3 v(1.0f, 0.0f, 0.0f);
glm::vec4 h(v, 1.0f);                   // homogeneous
glm::mat4 I(1.0f);                      // identity

Translation

glm::mat4 model(1.0f);
model = glm::translate(model, glm::vec3(tx, ty, tz));
// equivalent: model = T * model

Matrix form:

| 1  0  0  tx |
| 0  1  0  ty |
| 0  0  1  tz |
| 0  0  0   1 |

Rotation

// Rotate `angle` radians around axis
model = glm::rotate(model, glm::radians(45.0f), glm::vec3(0, 1, 0)); // Y-axis

// Quaternion rotation (avoid gimbal lock)
#include <glm/gtc/quaternion.hpp>
glm::quat q = glm::angleAxis(glm::radians(45.0f), glm::vec3(0, 1, 0));
glm::mat4 rotMat = glm::mat4_cast(q);

// Combine quaternions (multiply for sequential rotations)
glm::quat total = q2 * q1;  // q1 first, then q2

// Spherical linear interpolation
glm::quat interpolated = glm::slerp(q1, q2, t);  // t ∈ [0, 1]

Euler angles to quaternion

glm::quat q = glm::quat(glm::vec3(pitch, yaw, roll));  // in radians
// Beware: order = pitch (X) → yaw (Y) → roll (Z)

Scaling

model = glm::scale(model, glm::vec3(sx, sy, sz));
// Non-uniform scale distorts normals — use the normal matrix (see below)

Model-View-Projection

glm::mat4 model      = glm::mat4(1.0f);
model = glm::translate(model, position);
model = glm::rotate(model, angle, axis);
model = glm::scale(model, scale);

glm::mat4 view       = glm::lookAt(eye, center, up);
glm::mat4 projection = glm::perspective(glm::radians(fov), aspect, near, far);

glm::mat4 mvp = projection * view * model;  // right-to-left in GLSL convention

glUniformMatrix4fv(mvpLoc, 1, GL_FALSE, glm::value_ptr(mvp));

View Matrix — glm::lookAt

glm::mat4 view = glm::lookAt(
    glm::vec3(4, 3, 3),    // eye position
    glm::vec3(0, 0, 0),    // look-at target
    glm::vec3(0, 1, 0)     // world up
);

Internally:

forward = normalize(center - eye)
right   = normalize(cross(forward, up))
camUp   = cross(right, forward)

Projection Matrices

Perspective

glm::mat4 proj = glm::perspective(
    glm::radians(45.0f),  // vertical FOV
    800.0f / 600.0f,      // aspect ratio (width / height)
    0.1f,                 // near plane (must be > 0)
    100.0f                // far plane
);

Reverse-Z (better precision): swap near/far and use glDepthRange(1, 0) + GL_GREATER depth test.

glm::mat4 proj = glm::perspectiveZO(  // zero-to-one depth range
    glm::radians(45.0f), aspect, 0.1f, 100.0f);
// Use GL_DEPTH_RANGE [0,1] and GL_LESS (or [1,0] and GL_GREATER for reverse-Z)

Orthographic

glm::mat4 ortho = glm::ortho(left, right, bottom, top, near, far);
// Common for 2D: glm::ortho(0.0f, 800.0f, 0.0f, 600.0f)
// Or for UI (top-left origin): glm::ortho(0.0f, 800.0f, 600.0f, 0.0f)

Infinite perspective (no far clip)

glm::mat4 proj = glm::infinitePerspective(glm::radians(fov), aspect, nearPlane);

Normal Matrix

Non-uniform scale distorts surface normals. Transform normals with the normal matrix:

// Normal matrix = transpose of inverse of upper-left 3x3 of model matrix
glm::mat3 normalMatrix = glm::transpose(glm::inverse(glm::mat3(model)));
glUniformMatrix3fv(normalMatLoc, 1, GL_FALSE, glm::value_ptr(normalMatrix));
// Vertex shader
in vec3 aNormal;
uniform mat3 uNormalMatrix;
out vec3 vNormal;

void main() {
    vNormal = normalize(uNormalMatrix * aNormal);
}

Gotcha: If model matrix is orthogonal (no scale/shear), the normal matrix equals the upper-left 3x3 of the model matrix — skip the inverse for performance.

Frustum Culling (CPU)

// Extract planes from MVP matrix (Gribb-Hartmann method)
// Row vectors of MVP (column-major: take rows of the transposed matrix)
glm::mat4 m = projection * view;
struct Plane { float a, b, c, d; };
Plane planes[6];
// Left:   row3 + row0
// Right:  row3 - row0
// Bottom: row3 + row1
// Top:    row3 - row1
// Near:   row3 + row2
// Far:    row3 - row2
// (each row = transposed column of m)

Common Transform Recipes

Billboarding (always face camera)

// Vertex shader — cancel out the rotation part of the view matrix
vec3 right = vec3(uView[0][0], uView[1][0], uView[2][0]);
vec3 up    = vec3(uView[0][1], uView[1][1], uView[2][1]);
vec3 worldPos = uCenter + right * aPos.x + up * aPos.y;
gl_Position = uProj * uView * vec4(worldPos, 1.0);

Screen-space quad (post-processing)

// No uniforms needed — covers NDC [-1,1]
const vec2 positions[4] = vec2[](
    vec2(-1, -1), vec2(1, -1), vec2(-1, 1), vec2(1, 1)
);
void main() {
    gl_Position = vec4(positions[gl_VertexID], 0.0, 1.0);
}

Skybox (rendered at max depth)

void main() {
    vec4 pos = uProj * mat4(mat3(uView)) * vec4(aPos, 1.0);
    gl_Position = pos.xyww;  // force z = w so depth = 1.0 after divide
}

GLM Utility Functions

FunctionDescription
glm::value_ptr(m)Pointer to first element for GL upload
glm::inverse(m)Matrix inverse
glm::transpose(m)Matrix transpose
glm::determinant(m)Scalar determinant
glm::dot(a, b)Dot product
glm::cross(a, b)Cross product
glm::normalize(v)Unit vector
glm::length(v)Euclidean length
glm::distance(a, b)Distance between points
glm::mix(a, b, t)Lerp
glm::clamp(x, lo, hi)Clamp
glm::degrees(r) / glm::radians(d)Angle conversion
glm::eulerAngles(q)Quaternion → pitch/yaw/roll
glm::mat4_cast(q)Quaternion → rotation matrix
glm::quat_cast(m)Rotation matrix → quaternion

Viewport Transform (Fixed Function)

window.x = viewport.x + (ndcX + 1) / 2 * viewport.w
window.y = viewport.y + (ndcY + 1) / 2 * viewport.h
window.z = (nearVal * (1 - ndcZ) + farVal * (1 + ndcZ)) / 2
         = nearVal + (farVal - nearVal) * (ndcZ + 1) / 2
glViewport(x, y, width, height);   // set viewport rectangle
glDepthRange(0.0, 1.0);            // map NDC Z to window Z (default)

Unproject (Window → World)

glm::vec3 worldPos = glm::unProject(
    glm::vec3(mouseX, height - mouseY, depth),  // window coords (Y flipped)
    view * model,
    projection,
    glm::vec4(0, 0, width, height)              // viewport
);