What Does EndPrimitive Do in a GLSL Geometry Shader?
In a GLSL geometry shader, the EndPrimitive() built-in
function completes the generation of the current geometric primitive and
signals the pipeline to assemble the emitted vertices. By finalizing the
current topology—such as a triangle strip, line strip, or point list—it
allows developers to end the current shape and start emitting vertices
for a completely new, disconnected primitive within the same shader
invocation.
The Role of Geometry Shaders in Primitive Assembly
Geometry shaders sit between the vertex shader and the rasterizer. Unlike vertex shaders, which operate on a single vertex at a time, geometry shaders receive full primitives (such as points, lines, or triangles) along with optional adjacency information. Their defining capability is the dynamic creation or destruction of geometry, outputting zero, one, or multiple primitives per invocation.
Geometry shaders emit data sequentially using two primary functions:
EmitVertex(): Writes the current output vertex attributes to the output stream.EndPrimitive(): Concludes the current sequence of emitted vertices.
How EndPrimitive Operates
When defining an output layout in GLSL, shaders typically output
primitives in strip topologies, such as points,
line_strip, or triangle_strip. Because strip
topologies connect consecutive vertices by default, a mechanism is
required to create separate, disjoint shapes without re-invoking the
shader.
Calling EndPrimitive() indicates that the preceding
sequence of EmitVertex() calls forms a complete, closed
primitive structure. Any subsequent EmitVertex() call
begins a new, independent primitive strip.
Behavior by Output Primitive Type
- triangle_strip: Emitting three vertices forms a
triangle. Additional vertices would normally extend the strip by sharing
previous vertices. Calling
EndPrimitive()closes the strip so the next vertices can form a separate triangle with entirely independent points. - line_strip: Emitting vertices creates connected
line segments. Calling
EndPrimitive()breaks the line continuity, allowing a new, disconnected line sequence to start. - points: For point outputs, each vertex represents
an independent point. While
EndPrimitive()is valid, eachEmitVertex()effectively acts as a distinct element.
Practical Use Cases
The ability to segment output streams enables several advanced rendering techniques within a single invocation:
- Particle Billboarding: Generating separate
camera-facing quads from input point positions by emitting four vertices
and calling
EndPrimitive()per quad. - Geometry Culling and Splitting: Selectively discarding parts of a mesh or splitting complex shapes into discrete sub-components.
- Hair, Fur, and Wire Generation: Emitting multiple disconnected line strips from a single surface triangle to simulate strand-based geometry.
If a geometry shader finishes execution without an explicit
EndPrimitive() call, the OpenGL implementation
automatically finalizes the currently active primitive if enough
vertices were emitted to form a valid shape. However, explicit calls
remain best practice for code clarity and multi-primitive
generation.