OpenGL Cheatsheet

Buffers (VBO, VAO, EBO)

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

Vertex Buffer Object (VBO)

A VBO is a GPU-side buffer holding raw vertex data. All buffer objects share the same API — only the bind target differs.

GLuint vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

Buffer targets

TargetTypical use
GL_ARRAY_BUFFERVertex attribute data (VBO)
GL_ELEMENT_ARRAY_BUFFERIndex data (EBO/IBO)
GL_UNIFORM_BUFFERUBO — block uniforms shared across shaders
GL_SHADER_STORAGE_BUFFERSSBO — large, shader-writable (GL 4.3+)
GL_DRAW_INDIRECT_BUFFERIndirect draw commands
GL_DISPATCH_INDIRECT_BUFFERIndirect compute dispatch
GL_COPY_READ_BUFFERSource for glCopyBufferSubData
GL_COPY_WRITE_BUFFERDestination for glCopyBufferSubData
GL_PIXEL_PACK_BUFFERGPU → CPU pixel transfer (readback)
GL_PIXEL_UNPACK_BUFFERCPU → GPU pixel transfer (upload)
GL_TRANSFORM_FEEDBACK_BUFFERCapture transformed vertices
GL_QUERY_BUFFERWrite query result directly to buffer (GL 4.4+)
GL_ATOMIC_COUNTER_BUFFERAtomic counter bindings
GL_TEXTURE_BUFFERBuffer texture data

Usage hints

HintAccessNature
GL_STATIC_DRAWCPU → GPU onceGPU reads many times
GL_DYNAMIC_DRAWCPU → GPU repeatedlyGPU reads many times
GL_STREAM_DRAWCPU → GPU onceGPU reads a few times
GL_STATIC_READGPU → CPU onceCPU reads many times
GL_DYNAMIC_READGPU → CPU repeatedlyCPU reads many times
GL_STATIC_COPYGPU → GPU onceGPU reads many times
GL_DYNAMIC_COPYGPU → GPU repeatedlyGPU reads many times

These are hints to the driver for memory placement — they do not prevent any access pattern.

Uploading and Updating Data

// Full upload (allocates storage)
glBufferData(target, sizeBytes, data, usage);
// data can be nullptr to allocate without initializing

// Partial update (does NOT reallocate — must fit in existing allocation)
glBufferSubData(target, offsetBytes, sizeBytes, data);

// Copy between two bound buffers
glBindBuffer(GL_COPY_READ_BUFFER,  src);
glBindBuffer(GL_COPY_WRITE_BUFFER, dst);
glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER,
                    readOffset, writeOffset, sizeBytes);

DSA (Direct State Access, GL 4.5+) — preferred, no bind required

GLuint vbo;
glCreateBuffers(1, &vbo);                         // allocates immediately
glNamedBufferData(vbo, sizeBytes, data, GL_STATIC_DRAW);
glNamedBufferSubData(vbo, offset, size, data);

Buffer Mapping

// Map entire buffer
void* ptr = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
memcpy(ptr, data, size);
glUnmapBuffer(GL_ARRAY_BUFFER);  // returns GL_FALSE if mapping corrupted

// Map a range (more efficient — allows unsynchronized access)
void* ptr = glMapBufferRange(GL_ARRAY_BUFFER, offset, length,
    GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT | GL_MAP_UNSYNCHRONIZED_BIT);
glUnmapBuffer(GL_ARRAY_BUFFER);

// DSA version
void* ptr = glMapNamedBufferRange(vbo, offset, length, accessFlags);
glUnmapNamedBuffer(vbo);

Map access flags

FlagMeaning
GL_MAP_READ_BITCPU will read from the buffer
GL_MAP_WRITE_BITCPU will write to the buffer
GL_MAP_INVALIDATE_RANGE_BITDriver may discard mapped range
GL_MAP_INVALIDATE_BUFFER_BITDriver may discard entire buffer
GL_MAP_FLUSH_EXPLICIT_BITCPU flushes via glFlushMappedBufferRange
GL_MAP_UNSYNCHRONIZED_BITNo sync — unsafe if GPU is reading
GL_MAP_PERSISTENT_BITMapping stays valid across draws (GL 4.4+)
GL_MAP_COHERENT_BITNo manual flush needed (GL 4.4+)

Vertex Array Object (VAO)

A VAO records: which VBOs are bound to which attribute slots, the format of each attribute, and which EBO is active. Binding the VAO restores all of this.

GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);               // start recording

    glBindBuffer(GL_ARRAY_BUFFER, vbo);

    // Attribute 0: 3 floats at offset 0, stride 20 bytes
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 20, (void*)0);
    glEnableVertexAttribArray(0);

    // Attribute 1: 2 floats at offset 12
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 20, (void*)12);
    glEnableVertexAttribArray(1);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);  // EBO stored in VAO

glBindVertexArray(0);                 // stop recording

glVertexAttribPointer signature

glVertexAttribPointer(
    GLuint  index,      // attribute location (layout(location=N))
    GLint   size,       // number of components: 1, 2, 3, or 4
    GLenum  type,       // GL_FLOAT, GL_INT, GL_UNSIGNED_BYTE, ...
    GLboolean normalized, // normalize integer types to [-1,1] or [0,1]
    GLsizei stride,     // bytes between consecutive vertices (0 = tightly packed)
    const void* offset  // byte offset within the buffer
);

// Integer attributes (no normalization, no float conversion)
glVertexAttribIPointer(index, size, type, stride, offset);

// Double-precision attributes (GL 4.1+)
glVertexAttribLPointer(index, size, GL_DOUBLE, stride, offset);

Attribute types

typeDescription
GL_FLOAT32-bit float
GL_DOUBLE64-bit double
GL_BYTESigned 8-bit integer
GL_UNSIGNED_BYTEUnsigned 8-bit integer
GL_SHORTSigned 16-bit
GL_UNSIGNED_SHORTUnsigned 16-bit
GL_INTSigned 32-bit
GL_UNSIGNED_INTUnsigned 32-bit
GL_HALF_FLOAT16-bit float
GL_FIXED16.16 fixed-point
GL_INT_2_10_10_10_REVPacked 4-component (normals)
GL_UNSIGNED_INT_2_10_10_10_REVPacked 4-component (unsigned)

DSA VAO setup (GL 4.5+)

GLuint vao;
glCreateVertexArrays(1, &vao);

// Bind VBO to binding point 0 with stride
glVertexArrayVertexBuffer(vao, /*bindingIndex=*/0, vbo, /*offset=*/0, /*stride=*/20);

// Format of attribute 0 (bound to binding point 0)
glVertexArrayAttribFormat(vao, /*attribIndex=*/0, 3, GL_FLOAT, GL_FALSE, /*relOffset=*/0);
glVertexArrayAttribBinding(vao, 0, 0); // attrib 0 → binding point 0
glEnableVertexArrayAttrib(vao, 0);

// Attribute 1
glVertexArrayAttribFormat(vao, 1, 2, GL_FLOAT, GL_FALSE, 12);
glVertexArrayAttribBinding(vao, 1, 0);
glEnableVertexArrayAttrib(vao, 1);

// Attach EBO
glVertexArrayElementBuffer(vao, ebo);

Element Buffer Object (EBO)

Stores indices for indexed drawing. The EBO binding is part of VAO state.

GLuint ebo;
glGenBuffers(1, &ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// (while VAO is bound, this EBO is recorded into the VAO)
// Draw indexed
glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, (void*)0);

EBO index types

typeRangeBytes per index
GL_UNSIGNED_BYTE0–2551
GL_UNSIGNED_SHORT0–655352
GL_UNSIGNED_INT0–42949672954

Primitive restart

glEnable(GL_PRIMITIVE_RESTART);
glPrimitiveRestartIndex(0xFFFF);  // when this index is encountered, restart strip
// GL 4.3+: glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX) — always uses max uint

Uniform Buffer Object (UBO)

// GLSL
layout(std140, binding = 0) uniform Matrices {
    mat4 view;
    mat4 projection;
};

// CPU: create and fill
GLuint ubo;
glCreateBuffers(1, &ubo);
glNamedBufferData(ubo, sizeof(MatricesBlock), &data, GL_DYNAMIC_DRAW);

// Bind to binding point 0
glBindBufferBase(GL_UNIFORM_BUFFER, 0, ubo);
// or a range:
glBindBufferRange(GL_UNIFORM_BUFFER, 0, ubo, offsetBytes, sizeBytes);

std140 layout rules: vec3 is padded to 16 bytes; arrays have stride of 16 bytes per element; always use std140 for UBOs to get predictable offsets. Use std430 (only for SSBOs) for tighter packing.

Shader Storage Buffer Object (SSBO, GL 4.3+)

layout(std430, binding = 1) buffer Particles {
    vec4 positions[];
};
GLuint ssbo;
glCreateBuffers(1, &ssbo);
glNamedBufferData(ssbo, size, data, GL_DYNAMIC_COPY);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, ssbo);

// After compute writes — flush before read
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);

Buffer Invalidation

// Hint driver to discard contents (enables orphaning / avoid stall)
glInvalidateBufferData(vbo);
glInvalidateBufferSubData(vbo, offsetBytes, sizeBytes);

Persistent Mapped Buffers (GL 4.4+)

GLbitfield flags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT;
glNamedBufferStorage(vbo, size, nullptr, flags); // immutable storage
void* ptr = glMapNamedBufferRange(vbo, 0, size, flags);
// ptr remains valid forever; use fences to avoid races
// Fence sync to prevent writing into in-flight data
GLsync fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
GLenum result = glClientWaitSync(fence, GL_SYNC_FLUSH_COMMANDS_BIT, timeout_ns);
glDeleteSync(fence);