OpenGL Cheatsheet
Drawing
Use this OpenGL reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
Draw Call Fundamentals
Every draw call reads vertex data from the currently bound VAO and submits primitives to the pipeline.
glBindVertexArray(vao); glUseProgram(prog); // Non-indexed glDrawArrays(GL_TRIANGLES, 0, 6); // first=0, count=6 vertices // Indexed glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, (void*)0); // last arg = byte offset into the EBO (not a pointer when EBO is bound)
Primitive Modes
| Mode | Description | Min verts to draw |
|---|---|---|
GL_POINTS | One point per vertex | 1 |
GL_LINES | Independent line segments (2 verts each) | 2 |
GL_LINE_STRIP | Connected line segments | 2 |
GL_LINE_LOOP | Strip + closing segment | 2 |
GL_TRIANGLES | Independent triangles (3 verts each) | 3 |
GL_TRIANGLE_STRIP | Shared-edge triangles | 3 |
GL_TRIANGLE_FAN | Fan from first vertex | 3 |
GL_LINES_ADJACENCY | Lines with 2 adjacency verts each | 4 |
GL_LINE_STRIP_ADJACENCY | Strip with adjacency | 4 |
GL_TRIANGLES_ADJACENCY | Triangles with edge neighbors | 6 |
GL_TRIANGLE_STRIP_ADJACENCY | Strip with adjacency | 6 |
GL_PATCHES | Patch for tessellation shaders | GL_PATCH_VERTICES |
// Set patch size for tessellation glPatchParameteri(GL_PATCH_VERTICES, 3); // triangles (default)
All Draw Functions
Non-indexed
glDrawArrays(mode, first, count); // Instanced — gl_InstanceID in shader glDrawArraysInstanced(mode, first, count, instanceCount); // Instanced with base instance (gl_BaseInstance) glDrawArraysInstancedBaseInstance(mode, first, count, instanceCount, baseInstance); // Multi-draw — separate range per sub-call const GLint firsts[] = { 0, 6 }; const GLsizei counts[] = { 6, 3 }; glMultiDrawArrays(mode, firsts, counts, 2); // Indirect (command from GPU buffer) glBindBuffer(GL_DRAW_INDIRECT_BUFFER, indirectBuf); glDrawArraysIndirect(mode, (void*)offset); // Multi indirect (GL 4.3+) glMultiDrawArraysIndirect(mode, (void*)offset, drawCount, stride);
Indexed
glDrawElements(mode, count, indexType, offsetPtr); glDrawElementsBaseVertex(mode, count, indexType, offsetPtr, baseVertex); // baseVertex added to each index before fetching (allows shared VBO regions) glDrawElementsInstanced(mode, count, indexType, offsetPtr, instanceCount); glDrawElementsInstancedBaseVertex(mode, count, indexType, offsetPtr, instanceCount, baseVertex); glDrawElementsInstancedBaseVertexBaseInstance(mode, count, indexType, offsetPtr, instanceCount, baseVertex, baseInstance); glDrawRangeElements(mode, start, end, count, indexType, offsetPtr); // Hint: indices are in [start, end] — may improve performance glMultiDrawElements(mode, counts, indexType, offsets, drawCount); glMultiDrawElementsBaseVertex(mode, counts, indexType, offsets, drawCount, baseVertices); glDrawElementsIndirect(mode, indexType, (void*)offset); glMultiDrawElementsIndirect(mode, indexType, (void*)offset, drawCount, stride);
Indirect draw command structs
// For glDrawArraysIndirect struct DrawArraysIndirectCommand { GLuint count; GLuint instanceCount; GLuint first; GLuint baseInstance; }; // For glDrawElementsIndirect struct DrawElementsIndirectCommand { GLuint count; GLuint instanceCount; GLuint firstIndex; // index into EBO (in element units, not bytes) GLint baseVertex; GLuint baseInstance; };
Instanced Rendering
Draw many copies of the same geometry in one call. Use gl_InstanceID or per-instance vertex attributes.
// Per-instance vertex attribute (divisor = 1 means advance once per instance) glVertexAttribDivisor(attribIndex, 1); // 0 = per-vertex (default), N = per N instances // DSA equivalent glVertexArrayBindingDivisor(vao, bindingIndex, 1);
// Vertex shader layout(location = 2) in vec3 aOffset; // divisor=1 attribute void main() { gl_Position = uVP * vec4(aPos + aOffset, 1.0); }
Transform Feedback
Capture transformed vertices back to a buffer before rasterization.
// Setup GLuint tfo; glGenTransformFeedbacks(1, &tfo); glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, tfo); const char* varyings[] = { "outPos", "outVel" }; glTransformFeedbackVaryings(prog, 2, varyings, GL_INTERLEAVED_ATTRIBS); // GL_SEPARATE_ATTRIBS: one binding point per varying glLinkProgram(prog); // must relink after setting varyings glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, captureBuffer); // Draw glEnable(GL_RASTERIZER_DISCARD); // optional: skip fragment stage glBeginTransformFeedback(GL_TRIANGLES); glDrawArrays(GL_TRIANGLES, 0, vertCount); glEndTransformFeedback(); glDisable(GL_RASTERIZER_DISCARD);
Conditional Rendering
Draw only if a query recorded at least one sample.
GLuint query; glGenQueries(1, &query); glBeginQuery(GL_SAMPLES_PASSED, query); // bounding box draw glEndQuery(GL_SAMPLES_PASSED); glBeginConditionalRender(query, GL_QUERY_WAIT); // expensive draw — skipped if query result == 0 glEndConditionalRender();
Conditional render modes
| Mode | Behavior |
|---|---|
GL_QUERY_WAIT | Stall until result available |
GL_QUERY_NO_WAIT | Skip if result unavailable (may draw anyway) |
GL_QUERY_BY_REGION_WAIT | Stall; only renders in screen region |
GL_QUERY_BY_REGION_NO_WAIT | No stall; only in region |
Occlusion Queries
GLuint query; glGenQueries(1, &query); glBeginQuery(GL_SAMPLES_PASSED, query); // or GL_ANY_SAMPLES_PASSED // draw occluder glEndQuery(GL_SAMPLES_PASSED); // Non-blocking check GLint available = 0; glGetQueryObjectiv(query, GL_QUERY_RESULT_AVAILABLE, &available); if (available) { GLuint result; glGetQueryObjectuiv(query, GL_QUERY_RESULT, &result); }
Query types
| Type | What it counts |
|---|---|
GL_SAMPLES_PASSED | Fragments that pass depth/stencil |
GL_ANY_SAMPLES_PASSED | Returns 1 or 0 (boolean) |
GL_ANY_SAMPLES_PASSED_CONSERVATIVE | Conservative (faster) boolean |
GL_PRIMITIVES_GENERATED | Primitives emitted by geometry stage |
GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN | Primitives written to XFB buffer |
GL_TIME_ELAPSED | GPU time in nanoseconds |
GL_TIMESTAMP | Absolute GPU timestamp |
Timer Queries
GLuint queries[2]; glGenQueries(2, queries); glQueryCounter(queries[0], GL_TIMESTAMP); // draw calls glQueryCounter(queries[1], GL_TIMESTAMP); GLuint64 t0, t1; glGetQueryObjectui64v(queries[0], GL_QUERY_RESULT, &t0); glGetQueryObjectui64v(queries[1], GL_QUERY_RESULT, &t1); double ms = (t1 - t0) / 1e6;
Scissor Test
glEnable(GL_SCISSOR_TEST); glScissor(x, y, width, height); // pixels, lower-left origin // Only fragments within this rect are written glDisable(GL_SCISSOR_TEST);
Multi-viewport / Layered Rendering (GL 4.1+)
// Set multiple viewports at once float vps[8] = { 0,0,400,600, 400,0,400,600 }; glViewportArrayv(0, 2, vps); float depths[4] = { 0,1, 0,1 }; glDepthRangeArrayv(0, 2, depths); // Geometry shader selects viewport/layer // gl_ViewportIndex = 0 or 1; // gl_Layer = cubemap face index;
Sync Objects / Memory Barriers
// Insert a fence GLsync fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); // CPU waits for GPU GLenum result = glClientWaitSync(fence, GL_SYNC_FLUSH_COMMANDS_BIT, 5000000000ULL); // result: GL_ALREADY_SIGNALED, GL_CONDITION_SATISFIED, GL_TIMEOUT_EXPIRED, GL_WAIT_FAILED // GPU waits for fence (no CPU stall) glWaitSync(fence, 0, GL_TIMEOUT_IGNORED); glDeleteSync(fence); // Memory barrier — order shader writes before subsequent reads glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT | GL_TEXTURE_FETCH_BARRIER_BIT);
glMemoryBarrier bits
| Bit | Protects |
|---|---|
GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT | VBO reads after shader writes |
GL_ELEMENT_ARRAY_BARRIER_BIT | EBO reads |
GL_UNIFORM_BARRIER_BIT | Uniform reads after SSBO writes |
GL_TEXTURE_FETCH_BARRIER_BIT | Texture sampling after image writes |
GL_SHADER_IMAGE_ACCESS_BARRIER_BIT | Image load/store ordering |
GL_COMMAND_BARRIER_BIT | Indirect draw commands |
GL_PIXEL_BUFFER_BARRIER_BIT | PBO reads/writes |
GL_TEXTURE_UPDATE_BARRIER_BIT | glTexImage calls |
GL_BUFFER_UPDATE_BARRIER_BIT | glBufferData / map |
GL_FRAMEBUFFER_BARRIER_BIT | FBO reads after rendering |
GL_TRANSFORM_FEEDBACK_BARRIER_BIT | XFB buffer reads |
GL_ATOMIC_COUNTER_BARRIER_BIT | Atomic counter reads |
GL_SHADER_STORAGE_BARRIER_BIT | SSBO reads/writes |
GL_ALL_BARRIER_BITS | All of the above |