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_Positionis 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
| Function | Description |
|---|---|
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 );