In this page a set of examples of simple geometry shaders will be provided, to illustrate some of the features mentioned in the previous page.
Example 1 – a pass-through shader
This example assume the same vertex shader as presented previous.
This shader does not change the attributes that came from the vertex shader, nor does it add, or remove, any information. This is sort of a “hello world” for geometry shader.
#version 150
layout(triangles) in;
layout (triangle_strip, max_vertices=3) out;
in VertexData {
vec2 texCoord;
vec3 normal;
} VertexIn[3];
out VertexData {
vec2 texCoord;
vec3 normal;
} VertexOut;
void main()
{
for(int i = 0; i < gl_VerticesIn; i++)
{
// copy attributes
gl_Position = gl_in[i].gl_Position;
VertexOut.normal = VertexIn[i].normal;
VertexOut.texCoord = VertexIn[i].texCoord;
// done with the vertex
EmitVertex();
}
}
The layout definitions state that the inputs are triangles, and the output are triangle strips, each with 3 vertices, i.e. a single triangle.
The geometry shader receives as inputs, for each vertex, user defined attributes for the texture coordinates and normals, in a named block that matches the output from the vertex shader. It also receives gl_Position
.
The outputs are similar to the vertex shader. A named block for the vertex data, and gl_Position
.
Example 2 – duplicate geometry
This shader assumes a vertex shader that acts as a pass-through, i.e. without modifying the vertex attributes, such as the one below.
#version 410
in vec3 position;
in vec3 normal;
in vec2 texCoord;
out VertexData {
vec2 texCoord;
vec3 normal;
} VertexOut;
void main()
{
VertexOut.texCoord = texCoord;
VertexOut.normal = normal;
gl_Position = vec4(position, 1.0);
}
The geometry shader receives each primitive and duplicates it, displacing the second copy by 20 units in the X axis.
#version 420
layout(triangles) in;
layout (triangle_strip, max_vertices=6) out;
layout (std140) uniform Matrices {
mat4 projModelViewMatrix;
mat3 normalMatrix;
};
in VertexData {
vec2 texCoord;
vec3 normal;
} VertexIn[];
out VertexData {
vec2 texCoord;
vec3 normal;
} VertexOut;
void main()
{
for(int i = 0; i < gl_VerticesIn; i++)
{
// copy attributes
gl_Position = projModelViewMatrix * gl_in[i].gl_Position;
VertexOut.normal = normalize(normalMatrix * VertexIn[i].normal);
VertexOut.texCoord = VertexIn[i].texCoord;
// done with the vertex
EmitVertex();
}
EndPrimitive();
for(int i = 0; i < gl_VerticesIn; i++)
{
// copy attributes and displace copy
gl_Position = projModelViewMatrix * (gl_in[i].gl_Position + vec4(20.0, 0.0, 0.0, 0.0));
VertexOut.normal = normalize(normalMatrix * VertexIn[i].normal);
VertexOut.texCoord = VertexIn[i].texCoord;
// done with the vertex
EmitVertex();
}
EndPrimitive();
}
Notice that we added the EndPrimitive
instruction. This is because, in this example, we are outputting two primitives, two triangle strips. Hence, we have to tell the shader where the primitives end. Actually the last EndPrimitive
instruction is optional, since when the shader terminates the primitive is also concluded, but it is nicer to have it there.
Example 3 – divide that triangle (in two)
This next example assumes the same vertex shader as the previous example. For each triangle it receives, it creates a strip with two triangles, which occupy the same space, by adding a vertex in the midpoint of an edge of the original triangle.
#version 420
layout(triangles) in;
layout (triangle_strip, max_vertices=4) out;
layout (std140) uniform Matrices {
mat4 projModelViewMatrix;
mat3 normalMatrix;
};
in VertexData {
vec2 texCoord;
vec3 normal;
} VertexIn[];
out VertexData {
vec2 texCoord;
vec3 normal;
} VertexOut;
void main()
{
// copy attributes for the first vertex
gl_Position = projModelViewMatrix * gl_in[0].gl_Position;
VertexOut.normal = normalize(normalMatrix * VertexIn[0].normal);
VertexOut.texCoord = VertexIn[0].texCoord;
EmitVertex();
// copy attributes for the second vertex
gl_Position = projModelViewMatrix * gl_in[1].gl_Position;
VertexOut.normal = normalize(normalMatrix * VertexIn[1].normal);
VertexOut.texCoord = VertexIn[1].texCoord;
EmitVertex();
// this is the new vertex
gl_Position = projModelViewMatrix * (gl_in[0].gl_Position + gl_in[2].gl_Position);
VertexOut.normal = normalize(normalMatrix * (VertexIn[0].normal + VertexIn[2].normal));
VertexOut.texCoord = VertexIn[0].texCoord + VertexIn[2].texCoord;
EmitVertex();
// copy attributes for the last vertex
gl_Position = projModelViewMatrix * gl_in[2].gl_Position;
VertexOut.normal = normalize(normalMatrix * VertexIn[2].normal);
VertexOut.texCoord = VertexIn[2].texCoord;
EmitVertex();
}