OpenGL Cheatsheet
Depth and Blending
Use this OpenGL reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
Depth Test
Controls which fragment wins when multiple primitives cover the same pixel.
glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); // default: pass if fragment depth < stored depth glDepthMask(GL_TRUE); // GL_FALSE = read-only depth (no writes) glClearDepth(1.0); // value written by glClear (default 1.0)
Depth functions
| Function | Passes when |
|---|---|
GL_LESS | fragment.depth < stored.depth (default) |
GL_LEQUAL | fragment.depth <= stored.depth |
GL_GREATER | fragment.depth > stored.depth |
GL_GEQUAL | fragment.depth >= stored.depth |
GL_EQUAL | fragment.depth == stored.depth |
GL_NOTEQUAL | fragment.depth != stored.depth |
GL_ALWAYS | Always passes |
GL_NEVER | Always fails |
Depth precision and Z-fighting
// Reverse-Z (more uniform precision distribution) glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE); // GL 4.5+ — use [0,1] range glDepthRange(1.0, 0.0); // flip depth range glDepthFunc(GL_GREATER); // reverse comparison glClearDepth(0.0); // clear to zero (near)
Gotcha: Z-fighting occurs between coplanar or near-coplanar polygons. Fix with
glPolygonOffset, reverse-Z, or simply increase the near plane distance.
// Polygon offset (for decals / shadows) glEnable(GL_POLYGON_OFFSET_FILL); // or _LINE, _POINT glPolygonOffset(factor, units); // Effective offset = factor × maxSlope + units × minResolution // Common shadow map values: glPolygonOffset(1.0f, 4.0f)
Early Depth Testing
The GPU may test depth before running the fragment shader (early-Z) to avoid wasted work. Writing gl_FragDepth in the shader disables early-Z.
// Force early depth test even when writing gl_FragDepth (GL 4.2+) layout(early_fragment_tests) in;
Stencil Test
A per-pixel integer buffer (typically 8 bits) used for masking, outlining, portals, shadows.
glEnable(GL_STENCIL_TEST); glClearStencil(0); glClear(GL_STENCIL_BUFFER_BIT); // Configure stencil test glStencilFunc(func, ref, mask); // Pass when (stencil & mask) {func} (ref & mask) // Configure stencil write operations glStencilOp(sfail, dpfail, dppass); // sfail = stencil test failed // dpfail = stencil passed, depth failed // dppass = both passed
glStencilFunc functions
func | Passes when |
|---|---|
GL_NEVER | Never |
GL_ALWAYS | Always |
GL_LESS | ref < stencil |
GL_LEQUAL | ref <= stencil |
GL_GREATER | ref > stencil |
GL_GEQUAL | ref >= stencil |
GL_EQUAL | ref == stencil |
GL_NOTEQUAL | ref != stencil |
glStencilOp operations
| Operation | Effect |
|---|---|
GL_KEEP | Keep current stencil value |
GL_ZERO | Set to 0 |
GL_REPLACE | Set to ref from glStencilFunc |
GL_INCR | Increment (clamps at max) |
GL_INCR_WRAP | Increment with wrap-around |
GL_DECR | Decrement (clamps at 0) |
GL_DECR_WRAP | Decrement with wrap-around |
GL_INVERT | Bitwise invert |
// Separate front/back face stencil ops glStencilFuncSeparate(GL_FRONT, GL_ALWAYS, 1, 0xFF); glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_REPLACE); glStencilFuncSeparate(GL_BACK, GL_ALWAYS, 1, 0xFF); glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_REPLACE); // Control which bits are written glStencilMask(0xFF); // all bits writable (default) glStencilMask(0x00); // read-only stencil
Object outline recipe
// Pass 1: draw object, write stencil = 1, depth writes on glStencilFunc(GL_ALWAYS, 1, 0xFF); glStencilMask(0xFF); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); drawObject(); // Pass 2: draw scaled-up object, stencil = 0, depth writes off glStencilFunc(GL_NOTEQUAL, 1, 0xFF); glStencilMask(0x00); glDepthMask(GL_FALSE); drawObjectScaled(1.05f); // uniform scaled outline glDepthMask(GL_TRUE); glStencilMask(0xFF); glStencilFunc(GL_ALWAYS, 0, 0xFF);
Blending
Combines the output of the fragment shader (source) with what is already in the framebuffer (destination).
glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // standard alpha blend glBlendEquation(GL_FUNC_ADD); // default
glBlendFunc factor tokens
| Token | Value |
|---|---|
GL_ZERO | 0 |
GL_ONE | 1 |
GL_SRC_COLOR | Source color |
GL_ONE_MINUS_SRC_COLOR | 1 − source color |
GL_DST_COLOR | Destination color |
GL_ONE_MINUS_DST_COLOR | 1 − destination color |
GL_SRC_ALPHA | Source alpha |
GL_ONE_MINUS_SRC_ALPHA | 1 − source alpha |
GL_DST_ALPHA | Destination alpha |
GL_ONE_MINUS_DST_ALPHA | 1 − destination alpha |
GL_CONSTANT_COLOR | Constant set by glBlendColor |
GL_ONE_MINUS_CONSTANT_COLOR | 1 − constant |
GL_CONSTANT_ALPHA | Constant alpha |
GL_ONE_MINUS_CONSTANT_ALPHA | 1 − constant alpha |
GL_SRC_ALPHA_SATURATE | min(src.a, 1 − dst.a) |
GL_SRC1_COLOR / GL_ONE_MINUS_SRC1_COLOR | Dual-source blend |
// Separate RGB and alpha blend functions glBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha); // Per-draw-buffer blend functions (MRT, GL 4.0+) glBlendFunci(bufIndex, srcFactor, dstFactor); glBlendFuncSeparatei(bufIndex, srcRGB, dstRGB, srcA, dstA);
Blend equations
| Equation | Formula |
|---|---|
GL_FUNC_ADD | src × srcFactor + dst × dstFactor (default) |
GL_FUNC_SUBTRACT | src × srcFactor − dst × dstFactor |
GL_FUNC_REVERSE_SUBTRACT | dst × dstFactor − src × srcFactor |
GL_MIN | min(src, dst) (factors ignored) |
GL_MAX | max(src, dst) (factors ignored) |
glBlendEquation(GL_FUNC_ADD); glBlendEquationSeparate(modeRGB, modeAlpha); glBlendEquationi(bufIndex, mode); // Constant blend color (used with GL_CONSTANT_COLOR etc.) glBlendColor(r, g, b, a);
Common blend presets
| Effect | glBlendFunc call |
|---|---|
| Alpha (standard) | (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) |
| Premultiplied alpha | (GL_ONE, GL_ONE_MINUS_SRC_ALPHA) |
| Additive | (GL_SRC_ALPHA, GL_ONE) |
| Additive (premul) | (GL_ONE, GL_ONE) |
| Multiplicative | (GL_DST_COLOR, GL_ZERO) |
| Screen | (GL_ONE, GL_ONE_MINUS_SRC_COLOR) |
Gotcha: With standard alpha blend, draw transparent objects back-to-front (painter's algorithm) or depth writes produce incorrect occlusion. Disable
glDepthMask(GL_FALSE)while drawing transparent geometry if exact order is impossible.
Color Masking
Prevent writes to specific color channels or the depth/stencil buffer.
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // default — all channels glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // no color writes glColorMaski(bufIndex, r, g, b, a); // per-buffer mask (GL 4.0+) glDepthMask(GL_FALSE); // disable depth writes glDepthMask(GL_TRUE); // re-enable glStencilMask(0x00); // disable stencil writes
Order-Independent Transparency (OIT)
Eliminates the need to sort transparent geometry.
Alpha-to-coverage (MSAA-only)
glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE); // use alpha as coverage mask glEnable(GL_SAMPLE_ALPHA_TO_ONE); // optional: snap coverage to 1 // Works transparently with MSAA — no sorting needed for foliage/fences
Weighted Blended OIT (McGuire & Bavoil, 2013)
// Accumulation pass — additive blend to two MRT targets layout(location = 0) out vec4 accumColor; // premul color × weight layout(location = 1) out float accumAlpha; // alpha × weight float weight = clamp(pow(min(1.0, alpha * 10.0) + 0.01, 3.0) * 1e8 * pow(1.0 - gl_FragCoord.z * 0.9, 3.0), 1e-2, 3e3); accumColor = vec4(color * alpha, alpha) * weight; accumAlpha = alpha * weight;
// Composite pass vec4 accum = texture(uAccumTex, uv); float alpha = texture(uAlphaTex, uv).r; vec3 color = accum.rgb / clamp(accum.a, 1e-4, 5e4); float blend = 1.0 - alpha / clamp(alpha + 0.00001, 0.0, 1.0); FragColor = vec4(color * blend, blend);
Multisample Anti-Aliasing (MSAA)
glfwWindowHint(GLFW_SAMPLES, 4); // request 4× MSAA from windowing system glEnable(GL_MULTISAMPLE); // usually enabled by default // Query actual sample count GLint samples; glGetIntegerv(GL_SAMPLES, &samples); // Sample positions glGetMultisamplefv(GL_SAMPLE_POSITION, i, pos); // [0,1]² for sample i
// Access individual samples in a shader (fragment) uniform sampler2DMS uMSTex; vec4 s = texelFetch(uMSTex, ivec2(gl_FragCoord.xy), gl_SampleID);
Resolve MSAA FBO to single-sample
glBindFramebuffer(GL_READ_FRAMEBUFFER, msFBO); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveFBO); glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
Depth Clamp
Prevents near/far clipping of geometry (useful for shadow volumes).
glEnable(GL_DEPTH_CLAMP); // Fragments outside [near, far] are clamped to the boundary instead of clipped
Clip Planes (User-Defined)
glEnable(GL_CLIP_DISTANCE0); // up to GL_CLIP_DISTANCE7
// Vertex shader uniform vec4 uClipPlane; // (A, B, C, D) of plane equation Ax+By+Cz+D=0 out float gl_ClipDistance[1]; void main() { gl_ClipDistance[0] = dot(vec4(worldPos, 1.0), uClipPlane); }