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”
Leave a Reply Cancel reply
This site uses Akismet to reduce spam. Learn how your comment data is processed.
gl_VerticesIn didn’t work for me. I had to use gl_in.length()
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.