Difference between revisions of "Geometry Shader Examples"

From OpenGL Wiki
Jump to: navigation, search
m (moved Geometry Shaders to Geometry Shader Examples: The article is an examples page, not a real description of the feature.)
(Short Example 2)
 
(6 intermediate revisions by 3 users not shown)
Line 1: Line 1:
 +
{{deprecated}}
 +
 
GS = Geometry Shader<br>
 
GS = Geometry Shader<br>
 
This is a short document about Geometry Shaders in OpenGL.<br>
 
This is a short document about Geometry Shaders in OpenGL.<br>
Line 9: Line 11:
 
<br>
 
<br>
 
<br>
 
<br>
Fact #1 : The current version of GL is 3.2 and GS is added to the core.<br>
+
Fact #1 : Geometry Shaders are now core functionality.<br>
 
<br>
 
<br>
Fact #2 : AMD/ATI currently only supports GL 3.1, and does not support GS.<br>
+
Fact #2 : GL_EXT_geometry_shader4 and GL_ARB_geometry_shader4 both seem like identical extensions.<br>
<br>
 
Fact #3 : GL_EXT_geometry_shader4 and GL_ARB_geometry_shader4 both seem like identical extensions.<br>
 
 
<br>
 
<br>
 
Here is the specification<br>
 
Here is the specification<br>
Line 27: Line 27:
 
Since GL_ARB_geometry_shader4 is an extension, you need to put<br>
 
Since GL_ARB_geometry_shader4 is an extension, you need to put<br>
 
<font color=#FF0000>extension GL_ARB_geometry_shader4 : enable</font><br>
 
<font color=#FF0000>extension GL_ARB_geometry_shader4 : enable</font><br>
 
+
<source lang="glsl">
   //GEOMETRY SHADER
+
   // Geometry Shader
 
   #version 120
 
   #version 120
 
   #extension GL_ARB_geometry_shader4 : enable
 
   #extension GL_ARB_geometry_shader4 : enable
   ///////////////////////
+
   // --------------------
 
   void main()
 
   void main()
 
   {
 
   {
     //increment variable
+
     // increment variable
 
     int i;
 
     int i;
 
     vec4 vertex;
 
     vec4 vertex;
     /////////////////////////////////////////////////////////////
+
     // --------------------
     //This example has two parts
+
     // This example has two parts
 
     // step a) draw the primitive pushed down the pipeline
 
     // step a) draw the primitive pushed down the pipeline
 
     // there are gl_VerticesIn # of vertices
 
     // there are gl_VerticesIn # of vertices
Line 47: Line 47:
 
     // I just do the same loop, but I negate the vertex.z
 
     // I just do the same loop, but I negate the vertex.z
 
     // result => the primitive is now mirrored.
 
     // result => the primitive is now mirrored.
     //Pass-thru!
+
     // Pass-thru!
 
     for(i = 0; i < gl_VerticesIn; i++)
 
     for(i = 0; i < gl_VerticesIn; i++)
 
     {
 
     {
Line 53: Line 53:
 
       EmitVertex();
 
       EmitVertex();
 
     }
 
     }
 +
 
     EndPrimitive();
 
     EndPrimitive();
     //New piece of geometry!
+
 
 +
     // New piece of geometry!
 
     for(i = 0; i < gl_VerticesIn; i++)
 
     for(i = 0; i < gl_VerticesIn; i++)
 
     {
 
     {
Line 60: Line 62:
 
       vertex.z = -vertex.z;
 
       vertex.z = -vertex.z;
 
       gl_Position = vertex;
 
       gl_Position = vertex;
 +
 
       EmitVertex();
 
       EmitVertex();
 
     }
 
     }
 +
 
     EndPrimitive();
 
     EndPrimitive();
 
   }
 
   }
 
+
</source>
 
The first for loop just emits the incoming primitive unchanged.<br>
 
The first for loop just emits the incoming primitive unchanged.<br>
 
For every vertex, we call EmitVertex();<br>
 
For every vertex, we call EmitVertex();<br>
Line 77: Line 81:
  
 
== Short Example 2 ==
 
== Short Example 2 ==
 
+
<source lang="glsl">
   //VERTEX SHADER
+
   // Vertex Shader
 
   #version 110
 
   #version 110
   //UNIFORM
+
   // Uniform
 
   uniform mat4 ProjectionModelviewMatrix;
 
   uniform mat4 ProjectionModelviewMatrix;
 
   uniform vec4 TexMatrix0_a;
 
   uniform vec4 TexMatrix0_a;
Line 86: Line 90:
 
   uniform vec4 LightPosition0;
 
   uniform vec4 LightPosition0;
 
   uniform mat4 ModelviewMatrix;
 
   uniform mat4 ModelviewMatrix;
   //VARYING
+
   // Varying
 
   varying vec2 VTexCoord0;
 
   varying vec2 VTexCoord0;
 
   varying vec3 VHalfVector0;
 
   varying vec3 VHalfVector0;
 
   varying vec3 VEyeNormal;
 
   varying vec3 VEyeNormal;
 
   varying vec3 VEyeVertex;
 
   varying vec3 VEyeVertex;
  //GLOBALFUNCTION
+
 
 
   void main()
 
   void main()
 
   {
 
   {
Line 103: Line 107:
 
     eyeVector = normalize(-eyeVertex);
 
     eyeVector = normalize(-eyeVertex);
 
     lightVector = LightPosition0.xyz;
 
     lightVector = LightPosition0.xyz;
     VHalfVector0 = lightVector + eyeVector; //No need to normalize the sum
+
     VHalfVector0 = lightVector + eyeVector;   // No need to normalize the sum
 
     VEyeNormal = vec3(ModelviewMatrix * vec4(gl_Normal, 0.0));
 
     VEyeNormal = vec3(ModelviewMatrix * vec4(gl_Normal, 0.0));
 
   }
 
   }
 
+
</source>
   //GEOMETRY SHADER
+
<source lang="glsl">
 +
   // Geometry Shader
 
   #version 120
 
   #version 120
 
   #extension GL_ARB_geometry_shader4 : enable
 
   #extension GL_ARB_geometry_shader4 : enable
   //UNIFORM
+
   // Uniform
 
   uniform mat4 ProjectionMatrix;
 
   uniform mat4 ProjectionMatrix;
   //VARYING
+
   // Varying
   varying in vec2 VTexCoord0[3];   //[3] because this makes a triangle
+
   varying in vec2 VTexCoord0[3];   // [3] because this makes a triangle
 
   varying in vec3 VHalfVector0[3];
 
   varying in vec3 VHalfVector0[3];
 
   varying in vec3 VEyeNormal[3];
 
   varying in vec3 VEyeNormal[3];
Line 120: Line 125:
 
   varying out vec3 HalfVector0;
 
   varying out vec3 HalfVector0;
 
   varying out vec3 EyeNormal;
 
   varying out vec3 EyeNormal;
  //GLOBALFUNCTION
+
 
 
   void main()
 
   void main()
 
   {
 
   {
 
     int i;
 
     int i;
 
     vec3 newVertex;
 
     vec3 newVertex;
     //Pass through the original vertex
+
     // Pass through the original vertex
 
     for(i=0; i<gl_VerticesIn; i++)
 
     for(i=0; i<gl_VerticesIn; i++)
 
     {
 
     {
Line 132: Line 137:
 
       HalfVector0 = VHalfVector0[i];
 
       HalfVector0 = VHalfVector0[i];
 
       EyeNormal = VEyeNormal[i];
 
       EyeNormal = VEyeNormal[i];
 +
 
       EmitVertex();
 
       EmitVertex();
 
     }
 
     }
 +
 
     EndPrimitive();
 
     EndPrimitive();
     //Push the vertex out a little using the normal
+
 
 +
     // Push the vertex out a little using the normal
 
     for(i=0; i<gl_VerticesIn; i++)
 
     for(i=0; i<gl_VerticesIn; i++)
 
     {
 
     {
Line 143: Line 151:
 
       HalfVector0 = VHalfVector0[i];
 
       HalfVector0 = VHalfVector0[i];
 
       EyeNormal = VEyeNormal[i];
 
       EyeNormal = VEyeNormal[i];
 +
 
       EmitVertex();
 
       EmitVertex();
 
     }
 
     }
 +
 
     EndPrimitive();
 
     EndPrimitive();
 
   }
 
   }
 
+
</source>
   //FRAGMENT SHADER
+
<source lang="glsl">
 +
   // Fragment Shader
 
   #version 110
 
   #version 110
   //UNIFORM
+
   // Uniform
 
   uniform sampler2D Texture0;
 
   uniform sampler2D Texture0;
 
   uniform vec4 LightPosition0;
 
   uniform vec4 LightPosition0;
Line 157: Line 168:
 
   uniform vec4 LightMaterialSpecular0;
 
   uniform vec4 LightMaterialSpecular0;
 
   uniform float MaterialShininess;
 
   uniform float MaterialShininess;
   //VARYING
+
   // Varying
 
   varying vec2 TexCoord0;
 
   varying vec2 TexCoord0;
 
   varying vec3 HalfVector0;
 
   varying vec3 HalfVector0;
 
   varying vec3 EyeNormal;
 
   varying vec3 EyeNormal;
  //GLOBALFUNCTION
+
 
   //eyeNormal must be normalized already
+
   // eyeNormal must be normalized already
   //lightVector must be normalized already. xyz is lightvector and w is light distance from vertex
+
   // lightVector must be normalized already. xyz is lightvector and w is light distance from vertex
   //halfVector must be normalized already
+
   // halfVector must be normalized already
 
   //
 
   //
   //output diffuse color and output specular color
+
   // output diffuse color and output specular color
   //Then do diffuse * texture_color + specular
+
   // Then do diffuse * texture_color + specular
   //diffuse.a = material_diffuse.a
+
   // diffuse.a = material_diffuse.a
 
   void ComputeDirectionalLight(out vec4 diffuseColor, out vec4 specularColor, in vec3 eyeNormal, in vec3 lightVector, in vec3 halfVector, in vec4 lightMaterialDiffuse, in vec4 lightMaterialSpecular)
 
   void ComputeDirectionalLight(out vec4 diffuseColor, out vec4 specularColor, in vec3 eyeNormal, in vec3 lightVector, in vec3 halfVector, in vec4 lightMaterialDiffuse, in vec4 lightMaterialSpecular)
 
   {
 
   {
Line 179: Line 190:
 
       specularColor = pow(dotProduct, MaterialShininess) * lightMaterialSpecular;
 
       specularColor = pow(dotProduct, MaterialShininess) * lightMaterialSpecular;
 
   }
 
   }
   ///////////////////////
+
   // --------------------
 
   void main()
 
   void main()
 
   {
 
   {
Line 193: Line 204:
 
     gl_FragColor = clamp(ColorSum, 0.0, 1.0);
 
     gl_FragColor = clamp(ColorSum, 0.0, 1.0);
 
   }
 
   }
 +
</source>
  
 +
== Additional Info ==
 +
The GS unit in current generation of GPUs, such as the Geforce 8000/9000/GTX 100, 200 and 300 series, is considered too slow to be practical by some people. It is also considered too limited by some because there is a limit to how many new primitives you can emit.<br>
 +
The next generation of GPUs will be more flexible and will support other shader units, such as the tessellation control and evaluation units.<br>
  
== Additional Info ==
+
 
The GS unit in current generation of GPUs, such as the Gf 8 is considered too slow to be practical by some people. It is also considered too limited by some because there is a limit to how many new primitives you can emit.<br>
+
[[Category:Examples]]
The next generation of GPUs will be more flexible and will support other shader units, such as the tesselation unit which may or may not make the GS unit obsolete.<br>
 

Latest revision as of 11:51, 3 January 2018

GS = Geometry Shader
This is a short document about Geometry Shaders in OpenGL.
The first extension to be introduced was GL_EXT_geometry_shader4 on Oct 1, 2007.
Then it became ARB approved GL_ARB_geometry_shader4 on July 8, 2008.
GL_EXT_geometry_shader4 is available on nVidia Gf 8 series and up. Gf 8 is a SM 4.0 GPU
With ATI/AMD, all the Radeons with a HD in their name are SM 4.0 GPUs but their drivers did not support GS.
On June 10, 2009, Catalyst 9.6 gets released and supports various new extensions including GS
http://www.geeks3d.com/20090610/ati-catalyst-96-beta-a-stack-of-new-opengl-extensions


Fact #1 : Geometry Shaders are now core functionality.

Fact #2 : GL_EXT_geometry_shader4 and GL_ARB_geometry_shader4 both seem like identical extensions.

Here is the specification
http://www.opengl.org/registry/specs/ARB/geometry_shader4.txt

Introduction

The GS unit comes AFTER the VS unit. In the VS unit, you simply transform vertices, normals, texcoords and whatever else is in the input stream (such as tangent vectors, color). The GS unit has some connectivity information (what makes a triangle, what is adjacent to this triangle). The GS unit can be used for generating new geometry. For example, if input is a triangle, you can put out a few triangles. If input is a line, you can output a few lines. If input is a point, you can output some points. Once you emit the new extra geometry, it goes down to the rest of the fixed function vertex and primitive processor and reaches the fragment stage for rasterization. The order in which it reaches rasterization is not necessarily predictable, which is a problem if you are using order dependent blending such as glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA).
In the GS, you can sample a texture, you can transform a vertex with another matrix, with the projection matrix, with modelview matrix ..... you can do whatever you want just like in the other shader units.

Short Example

The language used here is GLSL.
The language used in the nVidia demo (on nv website!), is the low level GL_NV_geometry_program4.
Since GL_ARB_geometry_shader4 is an extension, you need to put
extension GL_ARB_geometry_shader4 : enable

  // Geometry Shader
  #version 120
  #extension GL_ARB_geometry_shader4 : enable
  // --------------------
  void main()
  {
    // increment variable
    int i;
    vec4 vertex;
    // --------------------
    // This example has two parts
    //	step a) draw the primitive pushed down the pipeline
    //		 there are gl_VerticesIn # of vertices
    //		 put the vertex value into gl_Position
    //		 use EmitVertex => 'create' a new vertex
    // 		use EndPrimitive to signal that you are done creating a primitive!
    //	step b) create a new piece of geometry
    //		I just do the same loop, but I negate the vertex.z
    //	result => the primitive is now mirrored.
    // Pass-thru!
    for(i = 0; i < gl_VerticesIn; i++)
    {
      gl_Position = gl_PositionIn[i];
      EmitVertex();
    }

    EndPrimitive();

    // New piece of geometry!
    for(i = 0; i < gl_VerticesIn; i++)
    {
      vertex = gl_PositionIn[i];
      vertex.z = -vertex.z;
      gl_Position = vertex;

      EmitVertex();
    }

    EndPrimitive();
  }

The first for loop just emits the incoming primitive unchanged.
For every vertex, we call EmitVertex();
For every primitive, we call EndPrimitive();
Those two are special instructions for the GS unit.

We are then generating a new primitive that is mirrored around the xy-plane.

The example doesn't show it but if you will be using custom varyings, you have to name them something like varying vec2 GeomTexCoord0 and varying vec2 FragTexCoord0
In the vertex shader, since the name must match, you should call it varying vec2 GeomTexCoord0
In the fragment shader, varying vec2 FragTexCoord0

Short Example 2

  // Vertex Shader
  #version 110
  // Uniform
  uniform mat4 ProjectionModelviewMatrix;
  uniform vec4 TexMatrix0_a;
  uniform vec4 TexMatrix0_b;
  uniform vec4 LightPosition0;
  uniform mat4 ModelviewMatrix;
  // Varying
  varying vec2 VTexCoord0;
  varying vec3 VHalfVector0;
  varying vec3 VEyeNormal;
  varying vec3 VEyeVertex;

  void main()
  {
    vec3 eyeVertex;
    vec3 lightVector, eyeVector;
    gl_Position = ProjectionModelviewMatrix * gl_Vertex;
    VTexCoord0.x = dot(TexMatrix0_a, gl_MultiTexCoord0);
    VTexCoord0.y = dot(TexMatrix0_b, gl_MultiTexCoord0);
    eyeVertex = vec3(ModelviewMatrix * gl_Vertex);
    VEyeVertex = eyeVertex;
    eyeVector = normalize(-eyeVertex);
    lightVector = LightPosition0.xyz;
    VHalfVector0 = lightVector + eyeVector;    // No need to normalize the sum
    VEyeNormal = vec3(ModelviewMatrix * vec4(gl_Normal, 0.0));
  }
  // Geometry Shader
  #version 120
  #extension GL_ARB_geometry_shader4 : enable
  // Uniform
  uniform mat4 ProjectionMatrix;
  // Varying
  varying in vec2 VTexCoord0[3];    // [3] because this makes a triangle
  varying in vec3 VHalfVector0[3];
  varying in vec3 VEyeNormal[3];
  varying in vec3 VEyeVertex[3];
  varying out vec2 TexCoord0;
  varying out vec3 HalfVector0;
  varying out vec3 EyeNormal;

  void main()
  {
    int i;
    vec3 newVertex;
    // Pass through the original vertex
    for(i=0; i<gl_VerticesIn; i++)
    {
      gl_Position = gl_PositionIn[i];
      TexCoord0 = VTexCoord0[i];
      HalfVector0 = VHalfVector0[i];
      EyeNormal = VEyeNormal[i];

      EmitVertex();
    }

    EndPrimitive();

    // Push the vertex out a little using the normal
    for(i=0; i<gl_VerticesIn; i++)
    {
      newVertex = VEyeNormal[i] + VEyeVertex[i];
      gl_Position = ProjectionMatrix * vec4(newVertex, 1.0);
      TexCoord0 = VTexCoord0[i];
      HalfVector0 = VHalfVector0[i];
      EyeNormal = VEyeNormal[i];

      EmitVertex();
    }

    EndPrimitive();
  }
  // Fragment Shader
  #version 110
  // Uniform
  uniform sampler2D Texture0;
  uniform vec4 LightPosition0;
  uniform vec4 AllLightAmbient_MaterialAmbient;
  uniform vec4 LightMaterialDiffuse0;
  uniform vec4 LightMaterialSpecular0;
  uniform float MaterialShininess;
  // Varying
  varying vec2 TexCoord0;
  varying vec3 HalfVector0;
  varying vec3 EyeNormal;

  // eyeNormal must be normalized already
  // lightVector must be normalized already. xyz is lightvector and w is light distance from vertex
  // halfVector must be normalized already
  //
  // output diffuse color and output specular color
  // Then do diffuse * texture_color + specular
  // diffuse.a = material_diffuse.a
  void ComputeDirectionalLight(out vec4 diffuseColor, out vec4 specularColor, in vec3 eyeNormal, in vec3 lightVector, in vec3 halfVector, in vec4 lightMaterialDiffuse, in vec4 lightMaterialSpecular)
  {
    float dotProduct;
    dotProduct = clamp(dot(eyeNormal, lightVector), 0.0, 1.0);
    diffuseColor = dotProduct * lightMaterialDiffuse;
    specularColor = vec4(0.0);
    dotProduct = clamp(dot(eyeNormal, halfVector), 0.0, 1.0);
    if(dotProduct>0.0)
      specularColor = pow(dotProduct, MaterialShininess) * lightMaterialSpecular;
  }
  // --------------------
  void main()
  {
    vec4 texel, diffuseColor, specularColor;
    vec4 ColorSum;
    vec3 eyeNormal, halfVector;
    texel = texture2D(Texture0, TexCoord0);
    eyeNormal = normalize(EyeNormal);
    halfVector = normalize(HalfVector0);
    ComputeDirectionalLight(diffuseColor, specularColor, eyeNormal, LightPosition0.xyz, halfVector, LightMaterialDiffuse0, LightMaterialSpecular0);
    ColorSum = (AllLightAmbient_MaterialAmbient + diffuseColor) * texel + specularColor;
    ColorSum.a = texel.a * LightMaterialDiffuse0.a;
    gl_FragColor = clamp(ColorSum, 0.0, 1.0);
  }

Additional Info

The GS unit in current generation of GPUs, such as the Geforce 8000/9000/GTX 100, 200 and 300 series, is considered too slow to be practical by some people. It is also considered too limited by some because there is a limit to how many new primitives you can emit.
The next generation of GPUs will be more flexible and will support other shader units, such as the tessellation control and evaluation units.