What Does gl_PrimitiveID Do in GLSL Geometry Shaders?
In OpenGL Shading Language (GLSL), the gl_PrimitiveID
variable is a built-in integer input and output within geometry shaders
that tracks the index of a geometric primitive. This article explains
the data provided by gl_PrimitiveID on input, how modifying
it on output affects subsequent rendering stages, the differences across
draw calls and instancing, and standard graphical use cases such as flat
face identification and geometry picking.
Input Behavior of gl_PrimitiveID
When read as an input within a geometry shader,
gl_PrimitiveID provides the zero-based index of the
primitive currently being processed. This index originates from the
vertex assembly stage and represents the sequence number of the
primitive generated by the active drawing command.
The counting logic operates according to the topology of the draw call:
- Direct Primitive Arrays: For non-indexed draw calls
(like
glDrawArrays),gl_PrimitiveIDcorresponds to the primitive's ordinal rank (e.g., primitive 0, primitive 1, primitive 2). - Indexed Drawing: For indexed rendering (like
glDrawElements), the variable still tracks the primitive counter generated by the index stream rather than vertex indices directly. - Instanced Drawing: In instanced draw calls
(
glDrawArraysInstancedorglDrawElementsInstanced),gl_PrimitiveIDresets to zero at the start of each individual instance.
Output Behavior and Downstream Propagation
Geometry shaders can also write to gl_PrimitiveID before
emitting vertices. The geometry shader has the unique capability to
either pass along the incoming primitive identifier or overwrite it with
a custom value.
When a value is assigned to gl_PrimitiveID prior to
calling EmitVertex() (or before closing a primitive with
EndPrimitive()), that value is passed downstream to the
fragment shader. If the geometry shader assigns a value, the fragment
shader receives that specific integer in its own built-in
gl_PrimitiveID input variable. If the geometry shader is
present in the pipeline but does not write to
gl_PrimitiveID, the value received by the fragment shader
becomes undefined.
Common Practical Applications
The primitive index provides per-face identity without requiring
dedicated custom vertex attributes. Developers frequently leverage
gl_PrimitiveID for several rendering techniques:
- Object and Face Picking: Rendering primitive IDs into an off-screen buffer (such as an integer color attachment) allows mouse-click queries to identify the exact triangle or polygon selected by the user.
- Per-Primitive Material and Texture Lookup: Fragment
shaders can use
gl_PrimitiveIDto sample a Texture Buffer Object (TBO) or Uniform Buffer Object (UBO) array, applying distinct colors, material parameters, or textures to individual faces without splitting vertices. - Faceted and Flat Shading Effects: When dynamic
procedural geometry is generated in the geometry shader,
gl_PrimitiveIDhelps synchronize procedural texturing and noise functions to unique faces. - Debugging Geometry Pipelines: Rendering primitive IDs as false-color gradients helps visualize tessellation distribution, vertex winding issues, and degenerate primitives across complex meshes.