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_PrimitiveID corresponds 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 (glDrawArraysInstanced or glDrawElementsInstanced), gl_PrimitiveID resets 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_PrimitiveID to 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_PrimitiveID helps 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.