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

FunctionPasses when
GL_LESSfragment.depth < stored.depth (default)
GL_LEQUALfragment.depth <= stored.depth
GL_GREATERfragment.depth > stored.depth
GL_GEQUALfragment.depth >= stored.depth
GL_EQUALfragment.depth == stored.depth
GL_NOTEQUALfragment.depth != stored.depth
GL_ALWAYSAlways passes
GL_NEVERAlways 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

funcPasses when
GL_NEVERNever
GL_ALWAYSAlways
GL_LESSref < stencil
GL_LEQUALref <= stencil
GL_GREATERref > stencil
GL_GEQUALref >= stencil
GL_EQUALref == stencil
GL_NOTEQUALref != stencil

glStencilOp operations

OperationEffect
GL_KEEPKeep current stencil value
GL_ZEROSet to 0
GL_REPLACESet to ref from glStencilFunc
GL_INCRIncrement (clamps at max)
GL_INCR_WRAPIncrement with wrap-around
GL_DECRDecrement (clamps at 0)
GL_DECR_WRAPDecrement with wrap-around
GL_INVERTBitwise 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

TokenValue
GL_ZERO0
GL_ONE1
GL_SRC_COLORSource color
GL_ONE_MINUS_SRC_COLOR1 − source color
GL_DST_COLORDestination color
GL_ONE_MINUS_DST_COLOR1 − destination color
GL_SRC_ALPHASource alpha
GL_ONE_MINUS_SRC_ALPHA1 − source alpha
GL_DST_ALPHADestination alpha
GL_ONE_MINUS_DST_ALPHA1 − destination alpha
GL_CONSTANT_COLORConstant set by glBlendColor
GL_ONE_MINUS_CONSTANT_COLOR1 − constant
GL_CONSTANT_ALPHAConstant alpha
GL_ONE_MINUS_CONSTANT_ALPHA1 − constant alpha
GL_SRC_ALPHA_SATURATEmin(src.a, 1 − dst.a)
GL_SRC1_COLOR / GL_ONE_MINUS_SRC1_COLORDual-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

EquationFormula
GL_FUNC_ADDsrc × srcFactor + dst × dstFactor (default)
GL_FUNC_SUBTRACTsrc × srcFactor − dst × dstFactor
GL_FUNC_REVERSE_SUBTRACTdst × dstFactor − src × srcFactor
GL_MINmin(src, dst) (factors ignored)
GL_MAXmax(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

EffectglBlendFunc 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);
}