Help end child hunger

GLSL Tutorial – Geometry Shader

 
Prev: Tessellation Next: GS Examples
 

The geometry shader is part of the specification since OpenGL 3.2.

This stage is optional. When present, a geometry shader receives as input the primitives assembled in the previous stage. A geometry shader does not receive strips, fans, or loops. The geometry shader receives the assembled primitives. So if the draw command specifies triangle strips, the geometry shader actually receives triangles.

The geometry shader, as opposed to the vertex shader, has full knowledge of the primitive it is working on. For each input primitive, the geometry shader has access to all the vertices that make up the primitive, including adjacency information, if specified.

The available geometry shader input primitives are:

  • points (1)
  • lines (2)
  • lines_adjacency (4)
  • triangles (3)
  • triangles_adjacency (6)

In parenthesis, it is represented the number of vertices per primitive.

Note that the inputs of the geometry shader must match the primitive of the OpenGL draw instruction as described in the primitive assembly section. When receiving primitives with adjacency information, lines_adjacency or triangles_adjacency, the order of the vertices is as presented in the figure below:

The output primitive types available are:

  • points
  • line_strip
  • triangle_strip

A match between the input and output is not required. For instance, a geometry shader can receive triangles and output points or a line_strip.

The output of a geometry shader can be zero or more primitives. For instance, if outputting triangle strips, a geometry shader can output three strips, with two triangles each. The input primitive is always discarded after the shader execution.

This means that the geometry shader, if it choses not to produce any output primitives for a particular input primitive, is in effect providing some sort of culling.

It also means that the geometry shader can output multiple primitives per input primitive. Note however, that the geometry shader is not designed to provide mass amplification of geometry, for instance, it is not adequate for tesselation purposes.

Both the input and output primitive types must be declared in the shader using the layout qualifier. Assuming that the input primitive type is triangles and that the output primitive type is line_strip, our shader’s code could begin as follows:

// geometry shaders require at least version 1.5
#version 150

layout (triangles) in;
layout (line_strip, max_vertices = 4) out;
...

The max_vertices limits the number of vertices that the geometry shader will output. This is for real! If we try to output more vertices than stated, the exceeding vertices will not be sent to the remaining of the pipeline.

As of OpenGL 4.0, a geometry shader can be invoked more than once for each input primitive. The number of invocations is determined by the input layout qualifier. For instance to state that the geometry shader should run twice for each input primitive we write:

#version 400

layout (triangles, invocations = 2) in;
layout (line_strip, max_vertices = 4) out;
...

If the invocations is absent in the layout, then the shader will run once for each input primitive.

Besides user defined vertex attributes, defined as outputs in the vertex shader, geometry shaders can access uniform variables and textures.

The following built-in variables are available:

in gl_PerVertex {
    vec4  gl_Position;
    float gl_PointSize;
    float gl_ClipDistance[];
} gl_in[];

in int gl_PrimitiveIDIn;
// only for OpenGL 4.0+
in int gl_InvocationID;

Note that the inputs of the geometry shader in struct gl_PerVertex match the outputs of the homonimous output struct in the vertex shader, but this time are in array format. The gl_PrimitiveID variable stores the index of the primitive generated by the Draw* family of OpenGL commands.

Although the geometry shader conceptually outputs primitives, in practice it is as if it outputs vertices. So, if the output primitive is a triangle, the geometry shader should write the attributes of three vertices. Vertices are then assembled into primitives according to the output layout. For each vertex the following built-in variables may be used:

out gl_PerVertex {
    vec4  gl_Position;
    float gl_PointSize;
    float gl_ClipDistance[];
};

out int gl_PrimitiveID;
out int gl_Layer;
// only for OpenGL 4.1+
out int gl_ViewportIndex;

As in the vertex shader, gl_Position is optional, but the following stages may rely on it for interpolation purposes. gl_PrimitiveID should be written at least for the provoking vertex. As for gl_Layer and gl_ViewportIndex, if specified, all vertices of a primitive must write the same value. If not specified, i.e. if the geometry shader does not write to gl_ViewportIndex or gl_Layer, then theirs values will be zero.

The geometry shader can specify, per primitive, a layer for drawing. This allows the geometry shader to specify multiple primitives, and direct them to different layers. Layered rendering implies the usage of framebuffers, otherwise the gl_Layer value is ignored. An example of layered rendering usage is for rendering, in a single pass, the six faces of a cube map.

As of OpenGL 4.1, a geometry shader can also select a viewport to output to. Multiple views can be rendered simultaneously with this feature.

Besides the built-in variables, the geometry shader may declare, and write to, user-defined variables. As in the previous shaders, the output of a geometry shader can be redirected to a buffer (or set of buffers) using transform feedback.

 

Prev: Tessellation Next: GS Examples
 

  5 Responses to “GLSL Tutorial – Geometry Shader”

  1. A geometry shader can only process one kind of primitives. So if I want to process both triangles and lines, I must write two geometry shaders and generate two shader programs. Is that right?

  2. I am with kaoD. I figured that geometry shaders were used for displacement maps (amongst other things). I imagine that the max_vertices parameter in the out layout comes into play here, so are there maximums on what that parameter can be set to?

    Also, does anyone have an example of using the invocations parameter of the in layout? It’s not obvious how this parameter works, or where you would want to use it. Does it recursively process the primitives? (seems like no if the out primitive type is different than the in primitive).

  3. “geometry shader is not designed to provide mass amplification of geometry, for instance, it is not adequate for tesselation purposes”

    Can you explain this further, please? I thought geometry shaders were actually intended for tesselation. What’s the use for them then?

    • This reasoning is mostly performance-related. Performance is much greater using tessellation shaders (introduced later than geometry shaders, with DirectX11-era hardware) which make use of dedicated hardware tessellation units. Presumably this is why dedicated hardware for tessellation was deemed necessary.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: