Help end child hunger

GLSL Tutorial – Geometry Shader Examples

 
Prev: Geometry Shader Next: Rasterization and Interpolation
 

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 assumes the same vertex shader as presented previously.

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_in.length(); 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_in.length(); 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_in.length(); 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) * 0.5;
    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();
}

 

Prev: Geometry Shader Next: Rasterization and Interpolation
 

  3 Responses to “GLSL Tutorial – Geometry Shader Examples”

  1. gl_VerticesIn didn’t work for me. I had to use gl_in.length()

  2. Shouldn’t the positions in the above example be divided by 2?


    (gl_in[0].gl_Position + gl_in[2].gl_Position) / 2.0

    • Actually no, because the w component of the sum will be 2. The division by 2 is performed by OpenGL when it divides by w.

Leave a Reply

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

%d bloggers like this: