GL EXT geometry shader4: Unterschied zwischen den Versionen

Aus DGL Wiki
Wechseln zu: Navigation, Suche
K (Wissenswertes)
K (Der Ausdruck ''<cpp>(.*?)</cpp>'' wurde ersetzt mit ''<source lang="cpp">$1</source>''.)
 
(18 dazwischenliegende Versionen von 5 Benutzern werden nicht angezeigt)
Zeile 10: Zeile 10:
 
Die Extension EXT_geometry_shader4 definiert einen neuen [[Shader]]-Typ, Geometryshader genannt. Der Geometryshader wird nach dem Vertexshader jedoch vor dem Cliping ausgeführt. Ein Blick auf die [[:Bild:Pipeline.png|Renderingpipeline]] sollte dies verdeutlichen.
 
Die Extension EXT_geometry_shader4 definiert einen neuen [[Shader]]-Typ, Geometryshader genannt. Der Geometryshader wird nach dem Vertexshader jedoch vor dem Cliping ausgeführt. Ein Blick auf die [[:Bild:Pipeline.png|Renderingpipeline]] sollte dies verdeutlichen.
  
Der Geometryshader funktioniert ähnlich wie der [[Shader|Vertexshader]] arbeitet jedoch auf Primitiven. Als Eingabe erhält er ein einzelnes Primitiv (Point, Line, Line-Adjacency, Triangle oder Triangle-Adjacency). Er kann die Attribute sämtlicher Vertices in diesem Primitiv lesen und verwenden um neue Primitive zu erzeugen. Der Primitiv-Typ den ein Geometryshader erzeugen soll (Point, Line-Strip oder Triangle-Strip) muss im voraus festgelegt werden. Um ein neues Primitiv zu erzeugen müssen nacheinander die einzelnen Vertices emittiert werden. Dabei können auch mehrere zusammenhanglose Primitive erzeugt werden. Die erzeugten Primitive werden geclippt und anschließend ganz normal verarbeitet wie ein äquivalentes, von der Anwendung definiertes Primitiv.
+
Der Geometryshader funktioniert ähnlich wie der [[Shader|Vertexshader]], arbeitet jedoch auf Primitiven. Als Eingabe erhält er ein einzelnes Primitiv (Point, Line, Line-Adjacency, Triangle oder Triangle-Adjacency). Er kann die Attribute sämtlicher Vertices in diesem Primitiv lesen und verwenden um neue Primitive zu erzeugen. Der Primitiv-Typ, den ein Geometryshader erzeugen soll (Point, Line-Strip oder Triangle-Strip), muss im voraus festgelegt werden. Um ein neues Primitiv zu erzeugen müssen nacheinander die einzelnen Vertices emittiert werden. Dabei können auch mehrere zusammenhanglose Primitive erzeugt werden. Die erzeugten Primitive werden geclippt und anschließend ganz normal verarbeitet wie ein äquivalentes, von der Anwendung definiertes Primitiv.
  
Die Extension EXT_geometry_shader4 stellt vier zusätzliche Primitiv-Typen bereit: GL_LINES_ADJACENCY_EXT, GL_LINE_STRIP_ADJACENCY_EXT, GL_TRIANGLES_ADJACENCY_EXT und GL_TRIANGLE_STRIP_ADJACENCY_EXT. Einige der Vertices in diesen neuen Typen sind nicht Teil des eigentlichen Primitives sondern repräsentieren angrenzende Nachbar-Vertices. Diese zusätzlichen Informationen können vom Geometryshader verwendet werden um die emittierten Vertices mit den Nachbar-Primitiven abzustimmen.
+
Die Extension EXT_geometry_shader4 stellt vier zusätzliche Primitiv-Typen bereit: GL_LINES_ADJACENCY_EXT, GL_LINE_STRIP_ADJACENCY_EXT, GL_TRIANGLES_ADJACENCY_EXT und GL_TRIANGLE_STRIP_ADJACENCY_EXT. Einige der Vertices in diesen neuen Typen sind nicht Teil des eigentlichen Primitives, sondern repräsentieren angrenzende Nachbar-Vertices. Diese zusätzlichen Informationen können vom Geometryshader verwendet werden um die emittierten Vertices mit den Nachbar-Primitiven abzustimmen.
  
Ein Geometryshader erwartet einen bestimmten Primitiv-Typ als Eingabe welcher im voraus festgelegt werden muss. Liefert die Anwendung Primitive eines anderen Typs wird ein Fehler ausgelöst.
+
Ein Geometryshader erwartet einen bestimmten Primitiv-Typ als Eingabe, welcher im voraus festgelegt werden muss. Liefert die Anwendung Primitive eines anderen Typs wird ein Fehler ausgelöst.
  
 
=== '''Wissenswertes''' ===
 
=== '''Wissenswertes''' ===
* Alle Primitive die den Geometryshader verlassen sollen muss man selbst erzeugen. Man kann also auch einfach keine Primitive erzeugen und so eine Art ''discard'' für den Vertexshader erhalten.
+
* Alle Primitive die den Geometryshader verlassen sollen muss man selbst erzeugen. Man kann also auch einfach keine Primitive erzeugen und so eine Art ''discard'' für den [[Shader|Vertexshader]] erhalten.
* Will man die Nachbarschaftsinformationen von Dreiecken, z.B. mit GL_TRIANGLES_ADJACENCY_EXT als Input-Datentyp verwenden, müssen die Daten bereits im Vertexbuffer (etc.) so gegeben sein. Ein Dreieck besteht dann z.B. aus 6 statt 3 Vertices. Der Geometryshader kann die Nachbarschaftsinformationen nicht einfach so erraten, sondern ein Mesh muss entsprechend vorbereitet werden. Auf Grund der häufigen Wiederverwendung der gleichen Vertices, empfiehlt sich offensichtlich die Verwendung eines Indexbuffers, um die Pipeline zu entlasten.
+
* Will man die Nachbarschaftsinformationen von Dreiecken, z.B. mit GL_TRIANGLES_ADJACENCY_EXT als Input-Datentyp verwenden, müssen die Daten bereits im [[Vertexbuffer]] (etc.) so gegeben sein. Ein Dreieck besteht dann z.B. aus 6 statt 3 Vertices. Der Geometryshader kann die Nachbarschaftsinformationen nicht einfach so erraten, sondern ein [[Mesh]] muss entsprechend vorbereitet werden. Auf Grund der häufigen Wiederverwendung der gleichen Vertices, empfiehlt sich offensichtlich die Verwendung eines [[Indexbuffer|Indexbuffers]], um die Pipeline zu entlasten.
 
* Verwendet man einen Geometryshader, muss man auch einen [[Shader|Vertexshader]] definieren. Wenn möglich sollte man z.B. die Transformation der Vertices in den Vertexshader auslagern. Ansonsten müsste der gleiche Vertex mehrfach transformiert werden, wenn er zu mehreren Primitiven gehört.
 
* Verwendet man einen Geometryshader, muss man auch einen [[Shader|Vertexshader]] definieren. Wenn möglich sollte man z.B. die Transformation der Vertices in den Vertexshader auslagern. Ansonsten müsste der gleiche Vertex mehrfach transformiert werden, wenn er zu mehreren Primitiven gehört.
 
* Man muss die maximal mögliche Anzahl der im Geometryshader erzeugten Vertices im voraus festlegen. Für diese Vertices muss Speicher im Cache reserviert werden, der dann nicht von anderen parallel laufenden Shaderunits benutzt werden kann. Will man also viele Vertices oder sehr große Vertices erzeugen hat man möglicherweise das Problem, dass nicht alle Shaderunits arbeiten können und das ganze wird entsprechend langsam. Mit der Vertexgröße ist hier die Anzahl der varying-Variablen zwischen Geometry- und Fragmentshader gemeint.
 
* Man muss die maximal mögliche Anzahl der im Geometryshader erzeugten Vertices im voraus festlegen. Für diese Vertices muss Speicher im Cache reserviert werden, der dann nicht von anderen parallel laufenden Shaderunits benutzt werden kann. Will man also viele Vertices oder sehr große Vertices erzeugen hat man möglicherweise das Problem, dass nicht alle Shaderunits arbeiten können und das ganze wird entsprechend langsam. Mit der Vertexgröße ist hier die Anzahl der varying-Variablen zwischen Geometry- und Fragmentshader gemeint.
Zeile 27: Zeile 27:
 
=== '''Verwendung''' ===
 
=== '''Verwendung''' ===
 
==== Geometryshader im Programm ====
 
==== Geometryshader im Programm ====
Einen Geometryshader lädt man im wesentlichen genauso, wie man auch einen [[Shader|Vertex-]] oder [[Shader|Fragmentshader]] lädt. Für Details sollte man sich das [[Tutorial_glsl|GLSL-Tutorial]] noch einmal genauer anschauen. Das folgende ist C++-Quellcode, den bei Gelegenheit mal jemand in Delphi übersetzen könnte.
+
Einen Geometryshader lädt man im wesentlichen genauso, wie man auch einen [[Shader|Vertex-]] oder [[Shader|Fragmentshader]] lädt. Für Details sollte man sich das [[Tutorial_glsl|GLSL-Tutorial]] noch einmal genauer anschauen.
  
Neu sind hier eigentlich nur die Aufrufe von [[glProgramParameteriEXT]]. Hier werden die maximale Anzahl erzeugte Vertices, sowie Input- bzw. Output-Primitiv-Typen festgelegt. Zu beachten ist hier das dies <u>vor dem linken</u> geschieht.
+
Neu sind hier eigentlich nur die Aufrufe von [[glProgramParameterEXT]]. Hier werden die maximale Anzahl erzeugte Vertices, sowie Input- bzw. Output-Primitiv-Typen festgelegt. Zu beachten ist, dass dies <u>vor dem Linken</u> geschieht.
 +
<source lang="cpp">
 
  GLuint m_prgTest = glCreateProgram();
 
  GLuint m_prgTest = glCreateProgram();
 
  GLuint vsTest = loadShader(GL_VERTEX_SHADER, "data/shader/test.vs");
 
  GLuint vsTest = loadShader(GL_VERTEX_SHADER, "data/shader/test.vs");
Zeile 43: Zeile 44:
 
  glDeleteShader(gsTest);
 
  glDeleteShader(gsTest);
 
  glUseProgram(m_prgTest);
 
  glUseProgram(m_prgTest);
 
+
</source>
Die folgenden Hilfsmethoden sollten relativ selbst erklärend sein.
+
Die folgenden Hilfsfunktionen sollten relativ selbst erklärend sein.
 +
<source lang="cpp">
 
  GLuint loadShader(GLenum type, const char* filename) {
 
  GLuint loadShader(GLenum type, const char* filename) {
 
     // Quelltext aus Datei laden
 
     // Quelltext aus Datei laden
Zeile 100: Zeile 102:
 
     }
 
     }
 
  }
 
  }
 
+
</source>
 
==== Geometryshader in GLSL ====
 
==== Geometryshader in GLSL ====
Zunächst definieren wir einen trivialen Vertexshader. Die Transformation wollen wir im Geometryshader vornehmen. Die Varying-Variable dient hier nur der Anschauung wie Varyings an den Geometryshader übergeben werden.
+
Zunächst definieren wir einen trivialen Vertexshader. Die Modelview-Transformation und die Projektion wollen wir im Geometryshader vornehmen. Die Varying-Variable ''foo'' dient hier nur der Anschauung wie Varyings an den Geometryshader übergeben werden und erfüllt keinen weiteren Nutzen.
 +
<source lang="glsl">
 
  varying float foo;
 
  varying float foo;
 
   
 
   
Zeile 109: Zeile 112:
 
     foo = 42.0;
 
     foo = 42.0;
 
  }
 
  }
 
+
</source>
Nun der Geometryshader. Dieser zugegeben etwas sinnlose Shader soll einfach das aktuelle Dreieck ausgeben, sowie zusätzlich eine um den Vektor (1,0,0) verschobene Kopie erzeugen. Zunächst müssen wir sicherstellen das die Extension EXT_geometry_shader4 in GLSL verfügbar ist, ansonsten würde der Compiler den Dienst verweigern. Dies geht wie bei anderen Shadererweiterungen (z.B. [[GL_EXT_gpu_shader4]]) über das Präprozessor-Statement ''#extension''. Neue Primitive werden, wie im Beispiel zu sehen, mit den neuen Funktionen ''EmitVertex()'' und ''EndPrimitive()'' erzeugt. Da es im Geometryshader sowohl Input- als auch Output-Varyings gibt, wurde die varying-Notation entsprechend erweitert. Der Rest sollte selbst erklärend sein.  
+
Nun der Geometryshader. Dieser zugegeben etwas sinnlose Shader soll einfach das aktuelle Dreieck ausgeben, sowie zusätzlich eine um den Vektor (1,0,0) verschobene Kopie erzeugen. Zunächst müssen wir sicherstellen, dass die Extension EXT_geometry_shader4 in GLSL verfügbar ist, ansonsten würde der Compiler den Dienst verweigern. Dies geht wie bei anderen Shadererweiterungen (z.B. [[GL_EXT_gpu_shader4]]) über das Präprozessor-Statement ''#extension''. Neue Primitive werden, wie im Beispiel zu sehen, mit den neuen Funktionen ''EmitVertex()'' und ''EndPrimitive()'' erzeugt. Da es im Geometryshader sowohl Input- als auch Output-Varyings gibt, wurde das Schlüsselwort ''varying'' um die Schlüssel ''in'' und ''out'' erweitert. Der Rest des Shaders sollte selbst erklärend sein.  
 +
<source lang="glsl">
 
  #extension GL_EXT_geometry_shader4: enable
 
  #extension GL_EXT_geometry_shader4: enable
 
   
 
   
Zeile 136: Zeile 140:
 
     // EndPrimitive() hier unnötig, wird am Ende automatisch aufgerufen.
 
     // EndPrimitive() hier unnötig, wird am Ende automatisch aufgerufen.
 
  }
 
  }
 +
</source>
 +
=== Primitiv-Typen ===
 +
Im folgenden werden die neu hinzugekommenen Primitiv-Typen vorgestellt, sowie erläutert wann wo welche Primitiv-Typen verwendet werden dürfen.
  
=== Primitiv-Typen ===
 
 
==== Die neuen Typen: *_ADJACENCY_EXT ====
 
==== Die neuen Typen: *_ADJACENCY_EXT ====
[[Bild:Lines-adjacency.png]]
+
Im folgenden stehen die durchgezogenen Linien für die eigentliche Geometrie. Die gestrichelten Linien stehen für die Nachbarschaft. Da die Nachbar-Vertices offensichtlich anderen Vertices im Mesh entsprechen, empfiehlt sich hier die Verwendung eines [[Indexbuffer|Indexbuffers]]. Dies erlaubt es dem Vertexshader die transformierten Vertices zu cachen und vermeidet somit die mehrfache Berechnung der gleichen Vertices.
 +
* '''GL_LINES_ADJACENCY_EXT'''
 +
: Hier speichern wir zusätzlich zur eigentlichen Line auch noch einen Vorgänger und einen Nachfolger. Pro Line werden also 4 Vertices benötigt.
 +
: [[Bild:Lines-adjacency.png]]
  
[[Bild:Line-strip-adjacency.png]]
 
  
[[Bild:Triangles-adjacency.png]]
+
* '''GL_LINE_STRIP_ADJACENCY_EXT'''
 +
: Ähnlich wie bei GL_LINES_ADJACENCY_EXT speichern wir zusätzlich zum Line-Strip einen Vorgänger und einen Nachfolger. Pro Line-Strip werden also 2 Vertices mehr benötigt.
 +
: [[Bild:Line-strip-adjacency.png]]
  
[[Bild:Triangle-strip-adjacency.png]]
+
 
 +
* '''GL_TRIANGLES_ADJACENCY_EXT'''
 +
: Bei Dreiecken wird für jede Kante ein zusätzlicher Nachbar-Vertex angegeben. Auf dem Bild bilden also zum Beispiel die Vertices 1,2 und 3 ein benachbartes Dreieck. Auf diese Weise werden pro Dreieck also 6 Vertices benötigt.
 +
: [[Bild:Triangles-adjacency.png]]
 +
 
 +
 
 +
* '''GL_TRIANGLE_STRIP_ADJACENCY_EXT'''
 +
: Die Definition von Triangle-Strips mit Adjazenz ist recht kompliziert. Es gibt 6 Fälle bei denen die Vertices jeweils unterschiedlich angeordnet sind. Am besten schaut man sich die Definition in der englischen [http://opengl.org/registry/specs/EXT/geometry_shader4.txt Original-Spezifikation] an.
 +
: [[Bild:Triangle-strip-adjacency.png]]
  
 
==== Input-Typen ====
 
==== Input-Typen ====
Eine Übersicht über die erlaubten Primitiv-Typen (links) und ihre Entsprechungen als Input-Geometrie im Geometryshader (rechts) [[#Quellen|[2]]].
+
Eine Übersicht über die auf Seiten der Anwendung erlaubten Primitiv-Typen (links) und ihre Entsprechungen als Input-Geometrie im Geometryshader (rechts) [[#Quellen|[2]]].
<table border=1 rules=all>
+
{| border=1 rules=all
<tr><td>'''GL_POINTS'''</td><td>'''GL_POINTS'''</td></tr>
+
| '''GL_POINTS'''
<tr><td colspan="2" height="1px"></td></tr>
+
| '''GL_POINTS'''
<tr><td>'''GL_LINES'''</td><td rowspan="3">'''GL_LINES'''</td></tr>
+
|-
<tr><td>'''GL_LINE_STRIP'''</td></tr>
+
| colspan="2" |
<tr><td>'''GL_LINE_LOOP'''</td></tr>
+
|-
<tr><td colspan="2" height="1px"></td></tr>
+
| '''GL_LINES'''
<tr><td>'''GL_LINES_ADJACENCY_EXT'''</td><td rowspan="2">'''GL_LINES_ADJACENCY_EXT'''</td></tr>
+
| rowspan="3" | '''GL_LINES'''
<tr><td>'''GL_LINE_STRIP_ADJACENCY_EXT'''</td></tr>
+
|-
<tr><td colspan="2" height="1px"></td></tr>
+
| '''GL_LINE_STRIP'''
<tr><td>'''GL_TRIANGLES'''</td><td rowspan="3">'''GL_TRIANGLES'''</td></tr>
+
|-
<tr><td>'''GL_TRIANGLE_STRIP'''</td></tr>
+
| '''GL_LINE_LOOP'''
<tr><td>'''GL_TRIANGLE_FAN'''</td></tr>
+
|-
<tr><td colspan="2" height="1px"></td></tr>
+
| colspan="2" |
<tr><td>'''GL_TRIANGLES_ADJACENCY_EXT'''</td><td rowspan="2">'''GL_TRIANGLES_ADJACENCY_EXT'''</td></tr>
+
|-
<tr><td>'''GL_TRIANGLE_STRIP_ADJACENCY_EXT'''</td></tr>
+
| '''GL_LINES_ADJACENCY_EXT'''
<tr><td colspan="2" height="1px"></td></tr>
+
| rowspan="2" | '''GL_LINES_ADJACENCY_EXT'''
<tr><td>'''GL_QUADS'''</td><td rowspan="3">nicht definiert?<br>Möglicherweise '''GL_TRIANGLES'''?</td></tr>
+
|-
<tr><td>'''GL_QUAD_STRIP'''</td></tr>
+
| '''GL_LINE_STRIP_ADJACENCY_EXT'''
<tr><td>'''GL_POLYGON'''</td></tr>
+
|-
</table>
+
| colspan="2" |
 +
|-
 +
| '''GL_TRIANGLES'''
 +
| rowspan="3" | '''GL_TRIANGLES'''
 +
|-
 +
| '''GL_TRIANGLE_STRIP'''
 +
|-
 +
| '''GL_TRIANGLE_FAN'''
 +
|-
 +
| colspan="2" |
 +
|-
 +
| '''GL_TRIANGLES_ADJACENCY_EXT'''
 +
| rowspan="2" | '''GL_TRIANGLES_ADJACENCY_EXT'''
 +
|-
 +
| '''GL_TRIANGLE_STRIP_ADJACENCY_EXT'''
 +
|-
 +
| colspan="2" |
 +
|-
 +
| '''GL_QUADS'''
 +
| rowspan="3" | nicht definiert?<br>Möglicherweise '''GL_TRIANGLES'''?
 +
|-
 +
| '''GL_QUAD_STRIP'''
 +
|-
 +
| '''GL_POLYGON'''
 +
|}
 +
 
 +
 
 
Der Input-Geometrie-Typ wird beispielsweise wie folgt gesetzt:
 
Der Input-Geometrie-Typ wird beispielsweise wie folgt gesetzt:
 
  glProgramParameteriEXT(m_prgTest, GL_GEOMETRY_INPUT_TYPE_EXT, GL_TRIANGLES);
 
  glProgramParameteriEXT(m_prgTest, GL_GEOMETRY_INPUT_TYPE_EXT, GL_TRIANGLES);
Zeile 175: Zeile 219:
 
==== Output-Typen ====
 
==== Output-Typen ====
 
Erlaubte Output-Primitiv-Typen sind die folgenden.
 
Erlaubte Output-Primitiv-Typen sind die folgenden.
<table border=1 rules=all>
+
{| border=1 rules=all
<tr><td>'''GL_POINTS'''</td></tr>
+
|-
<tr><td>'''GL_LINE_STRIP'''</td></tr>
+
| '''GL_POINTS'''
<tr><td>'''GL_TRIANGLE_STRIP'''</td></tr>
+
|-
</table>
+
| '''GL_LINE_STRIP'''
 +
|-
 +
| '''GL_TRIANGLE_STRIP'''
 +
|}
 
Die scheinbare Beschränkung auf die obigen drei Typen ist eigentlich keine. Ein Triangle-Strip zählt beispielsweise als ein Primitiv und man darf durchaus mehrere Primitive, also mehrere Strips erzeugen. Will man also einzelne, unabhängige Dreiecke generieren, schreibt man einfach mehrere Strips mit jeweils einem Dreieck. Der Output-Geometrie-Typ wird beispielsweise wie folgt gesetzt:
 
Die scheinbare Beschränkung auf die obigen drei Typen ist eigentlich keine. Ein Triangle-Strip zählt beispielsweise als ein Primitiv und man darf durchaus mehrere Primitive, also mehrere Strips erzeugen. Will man also einzelne, unabhängige Dreiecke generieren, schreibt man einfach mehrere Strips mit jeweils einem Dreieck. Der Output-Geometrie-Typ wird beispielsweise wie folgt gesetzt:
 
  glProgramParameteriEXT(m_prgTest, GL_GEOMETRY_OUTPUT_TYPE_EXT, GL_POINTS);
 
  glProgramParameteriEXT(m_prgTest, GL_GEOMETRY_OUTPUT_TYPE_EXT, GL_POINTS);
Zeile 197: Zeile 244:
 
: Will man die ''PrimitiveID'' im Fragmentshader verwenden, kann man in diese Variable schreiben. Die Variable ist dann als ''gl_PrimitiveID'' im Fragmentshader verfügbar.
 
: Will man die ''PrimitiveID'' im Fragmentshader verwenden, kann man in diese Variable schreiben. Die Variable ist dann als ''gl_PrimitiveID'' im Fragmentshader verfügbar.
  
 +
<br>
 
=== Eingebaute Konstanten ===
 
=== Eingebaute Konstanten ===
 
Im Geometryshader ist neben den aus dem Vertexshader bekannten Konstanten auch die folgende neue Konstante verfügbar:
 
Im Geometryshader ist neben den aus dem Vertexshader bekannten Konstanten auch die folgende neue Konstante verfügbar:
 
* int gl_VerticesIn
 
* int gl_VerticesIn
 
: Diese Konstante gibt die Anzahl der Input-Vertices, also die Größe der Arrays an. Man kann also einen Shader theoretisch auch für verschiedene Input-Typen auslegen. Die neuen Input-Typen [[#Die neuen Typen: *_ADJACENCY_EXT|*_ADJACENCY_EXT]] wurden weiter oben erklärt.
 
: Diese Konstante gibt die Anzahl der Input-Vertices, also die Größe der Arrays an. Man kann also einen Shader theoretisch auch für verschiedene Input-Typen auslegen. Die neuen Input-Typen [[#Die neuen Typen: *_ADJACENCY_EXT|*_ADJACENCY_EXT]] wurden weiter oben erklärt.
<table style="margin-left:50px;" border=1 rules=all>
+
{| style="margin-left:50px;" border=1 rules=all
<tr><td>Primitiv-Typ</td><td>Wert von '''gl_VerticesIn'''</td></tr>
+
| Primitiv-Typ || Wert von '''gl_VerticesIn'''
<tr><td>'''GL_POINTS'''</td><td>1</td></tr>
+
|-
<tr><td>'''GL_LINES'''</td><td>2</td></tr>
+
| '''GL_POINTS'''
<tr><td>'''GL_LINES_ADJACENCY_EXT'''</td><td>4</td></tr>
+
| style="text-align:center;" | 1
<tr><td>'''GL_TRIANGLES'''</td><td>3</td></tr>
+
|-
<tr><td>'''GL_TRIANGLES_ADJACENCY_EXT'''</td><td>6</td></tr>
+
| '''GL_LINES'''
</table>
+
| style="text-align:center;" | 2
 +
|-
 +
| '''GL_LINES_ADJACENCY_EXT'''
 +
| style="text-align:center;" | 4
 +
|-
 +
| '''GL_TRIANGLES'''
 +
| style="text-align:center;" | 3
 +
|-
 +
| '''GL_TRIANGLES_ADJACENCY_EXT'''
 +
| style="text-align:center;" | 6
 +
|}
 +
 
 +
<br>
 +
 
 +
=== Eingebaute Uniforms ===
 +
Im Geometryshader sind die gleichen eingebauten Uniform-Variablen verfügbar wie im Vertexshader.
  
 +
<br>
 
=== Eingebaute Input-Varyings ===
 
=== Eingebaute Input-Varyings ===
 
Im Geometryshader sind vom Prinzip die gleichen Varyings verfügbar wie im Vertexshader. Nur handelt es sich hier Arrays und zur besseren Unterscheidung zwischen Input und Output des Geometryshaders wurde jeweils das Suffix "''In''" angehängt.
 
Im Geometryshader sind vom Prinzip die gleichen Varyings verfügbar wie im Vertexshader. Nur handelt es sich hier Arrays und zur besseren Unterscheidung zwischen Input und Output des Geometryshaders wurde jeweils das Suffix "''In''" angehängt.
Zeile 222: Zeile 286:
 
* varying in vec4 gl_ClipVertexIn[gl_VerticesIn];
 
* varying in vec4 gl_ClipVertexIn[gl_VerticesIn];
  
 +
<br>
 
=== Eingebaute Output-Varyings ===
 
=== Eingebaute Output-Varyings ===
 
Die eingebauten Output-Varyings für Geometryshader sind die gleichen wie im Vertexshader.
 
Die eingebauten Output-Varyings für Geometryshader sind die gleichen wie im Vertexshader.
Zeile 231: Zeile 296:
 
* varying out float gl_FogFragCoord;
 
* varying out float gl_FogFragCoord;
  
 +
<br>
 
=== Eingebaute Funktionen ===
 
=== Eingebaute Funktionen ===
 
Ein Geometryshader kann alle Funktionen verwenden, die auch im Vertexshader verfügbar sind. Also Mathe-Funktionen, Texturzugriffe, und was es alles so gibt. Zusätzlich wurden die folgenden beiden Funktionen für den Geometryshader eingeführt:
 
Ein Geometryshader kann alle Funktionen verwenden, die auch im Vertexshader verfügbar sind. Also Mathe-Funktionen, Texturzugriffe, und was es alles so gibt. Zusätzlich wurden die folgenden beiden Funktionen für den Geometryshader eingeführt:
Zeile 237: Zeile 303:
 
* void EndPrimitive();
 
* void EndPrimitive();
 
: Ein Aufruf dieser Funktion beendet das aktuelle Primitiv und beginnt ein neues. Nach Beendigung des Geometryshaders wird diese Funktion automatisch aufgerufen. Die Funktion wird also nur dann benötigt, wenn man mehrere Line- bzw. Triangle-Strips erzeugen möchte. Erzeugt man Points ist der Aufruf optional.
 
: Ein Aufruf dieser Funktion beendet das aktuelle Primitiv und beginnt ein neues. Nach Beendigung des Geometryshaders wird diese Funktion automatisch aufgerufen. Die Funktion wird also nur dann benötigt, wenn man mehrere Line- bzw. Triangle-Strips erzeugen möchte. Erzeugt man Points ist der Aufruf optional.
 +
 +
<br>
 +
=== Neue Schlüsselworte ===
 +
* varying in
 +
: Wird verwendet für Input-Varyings vom Vertexshader. Aus einer varying-Variable im Vertexshader muss im Geometryshader ein Array der Größe [[#Eingebaute Konstanten|gl_VerticesIn]] werden. Die varying-Variable muss im Vertex- und Geometryshader jeweils den selben Namen haben.
 +
: Beispiele:
 +
{| style="margin-left:50px;" border=1 rules=all
 +
| '''Vertexshader''' || '''Geometryshader'''
 +
|-
 +
| <tt>varying float beispiel;</tt> || <tt>varying in float beispiel[gl_VerticesIn];</tt>
 +
|-
 +
| <tt>varying vec4 foobar[4];</tt> || <tt>varying in vec4 foobar[gl_VerticesIn][4];</tt>
 +
|}
 +
 +
* varying out
 +
: Wird verwendet für Output-Varyings zum Fragmentshader. Diese funktionieren wie vom Vertexshader gewohnt. Für jeden Vertex den man im Geometryshader erzeugt, weißt man der Variable erneut einen Wert zu. Die varying-Variable muss im Geometry- und Fragmentshader jeweils den selben Namen haben.
  
 
== Neue OpenGL-Funktionen ==
 
== Neue OpenGL-Funktionen ==
* [[glProgramParameteriEXT]]
+
* [[glProgramParameterEXT]]
 
* [[glFramebufferTextureEXT]]
 
* [[glFramebufferTextureEXT]]
 
* [[glFramebufferTextureLayerEXT]]
 
* [[glFramebufferTextureLayerEXT]]
Zeile 246: Zeile 328:
 
== Neue OpenGL-Tokens ==
 
== Neue OpenGL-Tokens ==
 
Akzeptiert als Parameter ''type'' bei [[glCreateShader]] und als Rückgabewert für den Parameter ''params'' von [[glGetShaderiv]]:<br>
 
Akzeptiert als Parameter ''type'' bei [[glCreateShader]] und als Rückgabewert für den Parameter ''params'' von [[glGetShaderiv]]:<br>
<table border=1 rules=all>
+
{| border=1 rules=all
<tr><td>'''GL_GEOMETRY_SHADER_EXT'''</td><td>0x8DD9</td></tr>
+
| '''GL_GEOMETRY_SHADER_EXT''' || 0x8DD9
</table>
+
|}
  
 
<br>
 
<br>
  
Akzeptiert als Parameter ''pname'' bei [[glProgramParameteriEXT]] und [[glGetProgramiv]]:<br>
+
Akzeptiert als Parameter ''pname'' bei [[glProgramParameterEXT]] und [[glGetProgramiv]]:<br>
<table border=1 rules=all>
+
{| border=1 rules=all
<tr><td>'''GL_GEOMETRY_VERTICES_OUT_EXT'''</td><td>0x8DDA</td></tr>
+
| '''GL_GEOMETRY_VERTICES_OUT_EXT''' || 0x8DDA
<tr><td>'''GL_GEOMETRY_INPUT_TYPE_EXT'''</td><td>0x8DDB</td></tr>
+
|-
<tr><td>'''GL_GEOMETRY_OUTPUT_TYPE_EXT'''</td><td>0x8DDC</td></tr>
+
| '''GL_GEOMETRY_INPUT_TYPE_EXT''' || 0x8DDB
</table>
+
|-
 +
| '''GL_GEOMETRY_OUTPUT_TYPE_EXT''' || 0x8DDC
 +
|-
 +
|}
  
 
<br>
 
<br>
  
 
Akzeptiert als Parameter ''pname'' bei [[glGetBooleanv]], [[glGetIntegerv]], [[glGetFloatv]] und [[glGetDoublev]]:<br>
 
Akzeptiert als Parameter ''pname'' bei [[glGetBooleanv]], [[glGetIntegerv]], [[glGetFloatv]] und [[glGetDoublev]]:<br>
<table border=1 rules=all>
+
{| border=1 rules=all
<tr><td>'''GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT'''</td><td>0x8C29</td></tr>
+
| '''GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT''' || 0x8C29
<tr><td>'''GL_MAX_GEOMETRY_VARYING_COMPONENTS_EXT'''</td><td>0x8DDD</td></tr>
+
|-
<tr><td>'''GL_MAX_VERTEX_VARYING_COMPONENTS_EXT'''</td><td>0x8DDE</td></tr>
+
| '''GL_MAX_GEOMETRY_VARYING_COMPONENTS_EXT''' || 0x8DDD
<tr><td>'''GL_MAX_VARYING_COMPONENTS_EXT'''</td><td>0x8B4B</td></tr>
+
|-
<tr><td>'''GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_EXT'''</td><td>0x8DDF</td></tr>
+
| '''GL_MAX_VERTEX_VARYING_COMPONENTS_EXT''' || 0x8DDE
<tr><td>'''GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT'''</td><td>0x8DE0</td></tr>
+
|-
<tr><td>'''GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_EXT'''</td><td>0x8DE1</td></tr>
+
| '''GL_MAX_VARYING_COMPONENTS_EXT''' || 0x8B4B
</table>
+
|-
 +
| '''GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_EXT''' || 0x8DDF
 +
|-
 +
| '''GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT''' || 0x8DE0
 +
|-
 +
| '''GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_EXT''' || 0x8DE1
 +
|-
 +
|}
  
 
<br>
 
<br>
Zeile 276: Zeile 368:
 
Akzeptiert als Parameter ''mode'' bei [[glBegin]], [[glDrawArrays]], [[glMultiDrawArrays]], [[glDrawElements]], [[glMultiDrawElements]] und [[glDrawRangeElements]]:<br>
 
Akzeptiert als Parameter ''mode'' bei [[glBegin]], [[glDrawArrays]], [[glMultiDrawArrays]], [[glDrawElements]], [[glMultiDrawElements]] und [[glDrawRangeElements]]:<br>
  
<table border=1 rules=all>
+
{| border=1 rules=all
<tr><td>'''GL_LINES_ADJACENCY_EXT'''</td><td>0xA</td></tr>
+
| '''GL_LINES_ADJACENCY_EXT''' || 0xA
<tr><td>'''GL_LINE_STRIP_ADJACENCY_EXT'''</td><td>0xB</td></tr>
+
|-
<tr><td>'''GL_TRIANGLES_ADJACENCY_EXT'''</td><td>0xC</td></tr>
+
| '''GL_LINE_STRIP_ADJACENCY_EXT''' || 0xB
<tr><td>'''GL_TRIANGLE_STRIP_ADJACENCY_EXT'''</td><td>0xD</td></tr>
+
|-
</table>
+
| '''GL_TRIANGLES_ADJACENCY_EXT''' || 0xC
 +
|-
 +
| '''GL_TRIANGLE_STRIP_ADJACENCY_EXT''' || 0xD
 +
|-
 +
|}
  
 
<br>
 
<br>
  
 
Als Rückgabewert von [[glCheckFramebufferStatusEXT]]:<br>
 
Als Rückgabewert von [[glCheckFramebufferStatusEXT]]:<br>
<table border=1 rules=all>
+
{| border=1 rules=all
<tr><td>'''GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT'''</td><td>0x8DA8</td></tr>
+
| '''GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT''' || 0x8DA8
<tr><td>'''GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_EXT'''</td><td>0x8DA9</td></tr>
+
|-
</table>
+
| '''GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_EXT''' || 0x8DA9
 +
|-
 +
|}
  
 
<br>
 
<br>
  
 
Akzeptiert als Parameter ''pname'' bei [[glGetFramebufferAttachmentParameterivEXT]]:<br>
 
Akzeptiert als Parameter ''pname'' bei [[glGetFramebufferAttachmentParameterivEXT]]:<br>
<table border=1 rules=all>
+
{| border=1 rules=all
<tr><td>'''GL_FRAMEBUFFER_ATTACHMENT_LAYERED_EXT'''</td><td>0x8DA7</td></tr>
+
| '''GL_FRAMEBUFFER_ATTACHMENT_LAYERED_EXT''' || 0x8DA7
<tr><td>'''GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER_EXT'''</td><td>0x8CD4</td></tr>
+
|-
</table><br>
+
| '''GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER_EXT''' || 0x8CD4
 +
|-
 +
|}<br>
 
: '''Hinweis'''
 
: '''Hinweis'''
 
: GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER_EXT ist ein Alias für GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT, welches von von der Extension [[GL_EXT_framebuffer_object]] bereitgestellt wird. Diese Extension dehnt die Bedeutung von <tt>zoffset</tt> aus um auch die Layer einer Array-Textur einzuschließen.
 
: GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER_EXT ist ein Alias für GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT, welches von von der Extension [[GL_EXT_framebuffer_object]] bereitgestellt wird. Diese Extension dehnt die Bedeutung von <tt>zoffset</tt> aus um auch die Layer einer Array-Textur einzuschließen.
Zeile 304: Zeile 404:
  
 
Akzeptiert als Parameter ''cap'' bei [[glEnable]], [[glDisable]] und [[glIsEnabled]], sowie als Parameter ''pname'' bei [[glGetIntegerv]], [[glGetFloatv]], [[glGetDoublev]] und [[glGetBooleanv]]:<br>
 
Akzeptiert als Parameter ''cap'' bei [[glEnable]], [[glDisable]] und [[glIsEnabled]], sowie als Parameter ''pname'' bei [[glGetIntegerv]], [[glGetFloatv]], [[glGetDoublev]] und [[glGetBooleanv]]:<br>
<table border=1 rules=all>
+
{| border=1 rules=all
<tr><td>'''GL_PROGRAM_POINT_SIZE_EXT'''</td><td>0x8642</td></tr>
+
| '''GL_PROGRAM_POINT_SIZE_EXT''' || 0x8642
</table><br>
+
|-
 +
|}<br>
 
: '''Hinweis'''
 
: '''Hinweis'''
 
: GL_PROGRAM_POINT_SIZE_EXT ist ein Alias für GL_VERTEX_PROGRAM_POINT_SIZE, welches von OpenGL 2.0 definiert wird. Letzteres ist selbst ein Alias für GL_VERTEX_PROGRAM_POINT_SIZE_ARB, welches von der Extension [[GL_ARB_vertex_program]] bereitsgestellt wird. "Program-computed point sizes" können aktiviert werden, wenn Geometryshader aktiv sind.
 
: GL_PROGRAM_POINT_SIZE_EXT ist ein Alias für GL_VERTEX_PROGRAM_POINT_SIZE, welches von OpenGL 2.0 definiert wird. Letzteres ist selbst ein Alias für GL_VERTEX_PROGRAM_POINT_SIZE_ARB, welches von der Extension [[GL_ARB_vertex_program]] bereitsgestellt wird. "Program-computed point sizes" können aktiviert werden, wenn Geometryshader aktiv sind.
 
  
 
== Abhängigkeiten ==
 
== Abhängigkeiten ==
Zeile 325: Zeile 425:
  
 
Die folgenden anderen Extensions interagieren mit GL_EXT_geometry_shader4:
 
Die folgenden anderen Extensions interagieren mit GL_EXT_geometry_shader4:
* [[GL_EXT_tranform_feedback]]
+
* [[GL_EXT_transform_feedback]]
  
 
<br>
 
<br>
 +
 
== Ressourcen ==
 
== Ressourcen ==
 
* [http://opengl.org/registry/specs/EXT/geometry_shader4.txt Orginalspezifikation GL_EXT_geometry_shader4] (englisch)
 
* [http://opengl.org/registry/specs/EXT/geometry_shader4.txt Orginalspezifikation GL_EXT_geometry_shader4] (englisch)

Aktuelle Version vom 10. März 2009, 19:43 Uhr

EXT_geometry_shader4

Info DGL.png Die Orginalspezifikation finden Sie unter "Ressourcen" am Ende des Artikels.

Am 11. Juli 2008 wurde diese Extension vom ARB anerkannt. Die neue Extension GL_ARB_geometry_shader4 ist bis auf die Namensänderung von EXT zu ARB identisch mit GL_EXT_geometry_shader4. Im Augenblick (Feb 2009) wird GL_ARB_geometry_shader4 aber noch nicht von den Treibern unterstützt.


Abfragestring

GL_EXT_geometry_shader4

Beschreibung

Die Extension EXT_geometry_shader4 definiert einen neuen Shader-Typ, Geometryshader genannt. Der Geometryshader wird nach dem Vertexshader jedoch vor dem Cliping ausgeführt. Ein Blick auf die Renderingpipeline sollte dies verdeutlichen.

Der Geometryshader funktioniert ähnlich wie der Vertexshader, arbeitet jedoch auf Primitiven. Als Eingabe erhält er ein einzelnes Primitiv (Point, Line, Line-Adjacency, Triangle oder Triangle-Adjacency). Er kann die Attribute sämtlicher Vertices in diesem Primitiv lesen und verwenden um neue Primitive zu erzeugen. Der Primitiv-Typ, den ein Geometryshader erzeugen soll (Point, Line-Strip oder Triangle-Strip), muss im voraus festgelegt werden. Um ein neues Primitiv zu erzeugen müssen nacheinander die einzelnen Vertices emittiert werden. Dabei können auch mehrere zusammenhanglose Primitive erzeugt werden. Die erzeugten Primitive werden geclippt und anschließend ganz normal verarbeitet wie ein äquivalentes, von der Anwendung definiertes Primitiv.

Die Extension EXT_geometry_shader4 stellt vier zusätzliche Primitiv-Typen bereit: GL_LINES_ADJACENCY_EXT, GL_LINE_STRIP_ADJACENCY_EXT, GL_TRIANGLES_ADJACENCY_EXT und GL_TRIANGLE_STRIP_ADJACENCY_EXT. Einige der Vertices in diesen neuen Typen sind nicht Teil des eigentlichen Primitives, sondern repräsentieren angrenzende Nachbar-Vertices. Diese zusätzlichen Informationen können vom Geometryshader verwendet werden um die emittierten Vertices mit den Nachbar-Primitiven abzustimmen.

Ein Geometryshader erwartet einen bestimmten Primitiv-Typ als Eingabe, welcher im voraus festgelegt werden muss. Liefert die Anwendung Primitive eines anderen Typs wird ein Fehler ausgelöst.

Wissenswertes

  • Alle Primitive die den Geometryshader verlassen sollen muss man selbst erzeugen. Man kann also auch einfach keine Primitive erzeugen und so eine Art discard für den Vertexshader erhalten.
  • Will man die Nachbarschaftsinformationen von Dreiecken, z.B. mit GL_TRIANGLES_ADJACENCY_EXT als Input-Datentyp verwenden, müssen die Daten bereits im Vertexbuffer (etc.) so gegeben sein. Ein Dreieck besteht dann z.B. aus 6 statt 3 Vertices. Der Geometryshader kann die Nachbarschaftsinformationen nicht einfach so erraten, sondern ein Mesh muss entsprechend vorbereitet werden. Auf Grund der häufigen Wiederverwendung der gleichen Vertices, empfiehlt sich offensichtlich die Verwendung eines Indexbuffers, um die Pipeline zu entlasten.
  • Verwendet man einen Geometryshader, muss man auch einen Vertexshader definieren. Wenn möglich sollte man z.B. die Transformation der Vertices in den Vertexshader auslagern. Ansonsten müsste der gleiche Vertex mehrfach transformiert werden, wenn er zu mehreren Primitiven gehört.
  • Man muss die maximal mögliche Anzahl der im Geometryshader erzeugten Vertices im voraus festlegen. Für diese Vertices muss Speicher im Cache reserviert werden, der dann nicht von anderen parallel laufenden Shaderunits benutzt werden kann. Will man also viele Vertices oder sehr große Vertices erzeugen hat man möglicherweise das Problem, dass nicht alle Shaderunits arbeiten können und das ganze wird entsprechend langsam. Mit der Vertexgröße ist hier die Anzahl der varying-Variablen zwischen Geometry- und Fragmentshader gemeint.
  • Die Geschwindigkeit hängt direkt und nicht-linear von der Anzahl/Größe der erzeugten Vertices ab, da der Speicher reserviert werden muss bevor die Grafikkarte weiß wie viele Vertices tatsächlich erzeugt werden. Nicht-linear bedeutet hier, dass es eine gewisse Anzahl geschriebener Komponenten gibt, bei der die Verarbeitung dann plötzlich massiv langsamer wird. Dies dürfte natürlich auch von Grafikkarte zu Grafikkarte variieren. Wähle also die maximale Anzahl deiner Vertices sowie auch deren Größe so klein wie möglich. [1]
  • Aus diesem Grund eignet sich der Geometryshader leider im Augenblick eben nur für kleine Modifikationen und nicht für LOD-Stufen, Tessellation und ähnliche Heavy-Output-Algorithmen. [1]
  • Interessant ist die Verwendung des Geometryshaders in Kombination mit Transform-Feedback. Die Extension GL_EXT_tranform_feedback erlaubt es die verarbeiteten Vertices komplett oder auch nur einzelne Varyings direkt wieder in einen Buffer zu schreiben. Ein solcher Buffer kann anschließend beliebig als Vertexbuffer, Indexbuffer oder auch Texturbuffer eingesetzt werden.

Verwendung

Geometryshader im Programm

Einen Geometryshader lädt man im wesentlichen genauso, wie man auch einen Vertex- oder Fragmentshader lädt. Für Details sollte man sich das GLSL-Tutorial noch einmal genauer anschauen.

Neu sind hier eigentlich nur die Aufrufe von glProgramParameterEXT. Hier werden die maximale Anzahl erzeugte Vertices, sowie Input- bzw. Output-Primitiv-Typen festgelegt. Zu beachten ist, dass dies vor dem Linken geschieht.

 GLuint m_prgTest = glCreateProgram();
 GLuint vsTest = loadShader(GL_VERTEX_SHADER, "data/shader/test.vs");
 GLuint gsTest = loadShader(GL_GEOMETRY_SHADER_EXT, "data/shader/test.gs");
 glAttachShader(m_prgTest, vsTest);
 glAttachShader(m_prgTest, gsTest);
 glProgramParameteriEXT(m_prgTest, GL_GEOMETRY_VERTICES_OUT_EXT, 6);
 glProgramParameteriEXT(m_prgTest, GL_GEOMETRY_INPUT_TYPE_EXT, GL_TRIANGLES);
 glProgramParameteriEXT(m_prgTest, GL_GEOMETRY_OUTPUT_TYPE_EXT, GL_TRIANGLE_STRIP);
 glLinkProgram(m_prgTest);
 checkLinkError("data/shader/test", m_prgTest);
 glDeleteShader(vsTest);
 glDeleteShader(gsTest);
 glUseProgram(m_prgTest);

Die folgenden Hilfsfunktionen sollten relativ selbst erklärend sein.

 GLuint loadShader(GLenum type, const char* filename) {
     // Quelltext aus Datei laden
     FILE* file = fopen(filename, "rb");
     if (file == 0) { throw Exception(std::string("error loading '") + filename + "'"); }
     fseek(file, 0, SEEK_END);
     int size = ftell(file);
     fseek(file, 0, SEEK_SET);
     char* data = new char[size+1];
     int read = fread(data,1,size,file);
     if (read != size) { throw Exception(std::string("error while loading '") + filename + "'"); }
     fclose(file);
     data[size]=0;
     const char* data_const = data;
 
     // Shader-Objekt erzeugen, Quelltext übergeben und compilieren
     GLuint shader = glCreateShader(type);
     glShaderSource(shader, 1, &data_const, NULL);
     glCompileShader(shader);
 
     // Quelltext wieder freigeben
     delete[] data;
 
     // Infolog auslesen und eventuelle Fehler oder Warnungen ausgeben
     int len;
     glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len);
     if (len > 1) {
         char buffer[len];
         glGetShaderInfoLog(shader, len, NULL, buffer);
         std::cout << "file: " << filename << "\n" << buffer << "\n";
     }     
     int status = 0;
     glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
     if (status) { 
         return shader; 
     }
     else {
         throw Exception("shader compilation failed.");
     }
 }

 void checkLinkError(const std::string name, GLuint program) {
     // Link-Status und ggf. Infolog auslesen
     int status = 0;
     glGetProgramiv(program, GL_LINK_STATUS, &status);
     if (!status) { 
         int len;
         glGetProgramiv(program, GL_INFO_LOG_LENGTH, &len);
         if (len > 1) {
             char buffer[len];
             glGetProgramInfoLog(program, len, NULL, buffer);
             std::cout << buffer << "\n";
         }
         throw Exception(name + ": program linking failed.");
     }
 }

Geometryshader in GLSL

Zunächst definieren wir einen trivialen Vertexshader. Die Modelview-Transformation und die Projektion wollen wir im Geometryshader vornehmen. Die Varying-Variable foo dient hier nur der Anschauung wie Varyings an den Geometryshader übergeben werden und erfüllt keinen weiteren Nutzen.

 varying float foo;
 
 void main() {
     gl_Position = gl_Vertex;
     foo = 42.0;
 }

Nun der Geometryshader. Dieser zugegeben etwas sinnlose Shader soll einfach das aktuelle Dreieck ausgeben, sowie zusätzlich eine um den Vektor (1,0,0) verschobene Kopie erzeugen. Zunächst müssen wir sicherstellen, dass die Extension EXT_geometry_shader4 in GLSL verfügbar ist, ansonsten würde der Compiler den Dienst verweigern. Dies geht wie bei anderen Shadererweiterungen (z.B. GL_EXT_gpu_shader4) über das Präprozessor-Statement #extension. Neue Primitive werden, wie im Beispiel zu sehen, mit den neuen Funktionen EmitVertex() und EndPrimitive() erzeugt. Da es im Geometryshader sowohl Input- als auch Output-Varyings gibt, wurde das Schlüsselwort varying um die Schlüssel in und out erweitert. Der Rest des Shaders sollte selbst erklärend sein.

 #extension GL_EXT_geometry_shader4: enable
 
 varying in float foo[3]; // beachte: ein Array der Größe 3, für die drei Vertices eines Dreiecks!
 
 varying out float bar; // varying für den Fragmentshader, kein Array!
                        // Da wir keinen Fragmentshader definieren, wird diese varying ignoriert.
 
 void main() {
     for (int i=0; i<3; ++i) {
         gl_Position = gl_PositionIn[i];
         gl_Position = gl_ModelViewProjectionMatrix * gl_Position;
         bar = (foo[0] + foo[1] + foo[2]) / 3.0;
         EmitVertex(); // Vertex geschrieben, nächsten Vertex anfangen
     }
     EndPrimitive(); // erstes Dreieck wurde geschrieben, TriangleStrip beenden
 
     for (int i=0; i<3; ++i) {
         gl_Position = gl_PositionIn[i] + vec4(1,0,0,0);
         gl_Position = gl_ModelViewProjectionMatrix * gl_Position;
         bar = (foo[0] + foo[1] + foo[2]) / 3.0;
         EmitVertex();
     }
 
     // EndPrimitive() hier unnötig, wird am Ende automatisch aufgerufen.
 }

Primitiv-Typen

Im folgenden werden die neu hinzugekommenen Primitiv-Typen vorgestellt, sowie erläutert wann wo welche Primitiv-Typen verwendet werden dürfen.

Die neuen Typen: *_ADJACENCY_EXT

Im folgenden stehen die durchgezogenen Linien für die eigentliche Geometrie. Die gestrichelten Linien stehen für die Nachbarschaft. Da die Nachbar-Vertices offensichtlich anderen Vertices im Mesh entsprechen, empfiehlt sich hier die Verwendung eines Indexbuffers. Dies erlaubt es dem Vertexshader die transformierten Vertices zu cachen und vermeidet somit die mehrfache Berechnung der gleichen Vertices.

  • GL_LINES_ADJACENCY_EXT
Hier speichern wir zusätzlich zur eigentlichen Line auch noch einen Vorgänger und einen Nachfolger. Pro Line werden also 4 Vertices benötigt.
Lines-adjacency.png


  • GL_LINE_STRIP_ADJACENCY_EXT
Ähnlich wie bei GL_LINES_ADJACENCY_EXT speichern wir zusätzlich zum Line-Strip einen Vorgänger und einen Nachfolger. Pro Line-Strip werden also 2 Vertices mehr benötigt.
Line-strip-adjacency.png


  • GL_TRIANGLES_ADJACENCY_EXT
Bei Dreiecken wird für jede Kante ein zusätzlicher Nachbar-Vertex angegeben. Auf dem Bild bilden also zum Beispiel die Vertices 1,2 und 3 ein benachbartes Dreieck. Auf diese Weise werden pro Dreieck also 6 Vertices benötigt.
Triangles-adjacency.png


  • GL_TRIANGLE_STRIP_ADJACENCY_EXT
Die Definition von Triangle-Strips mit Adjazenz ist recht kompliziert. Es gibt 6 Fälle bei denen die Vertices jeweils unterschiedlich angeordnet sind. Am besten schaut man sich die Definition in der englischen Original-Spezifikation an.
Triangle-strip-adjacency.png

Input-Typen

Eine Übersicht über die auf Seiten der Anwendung erlaubten Primitiv-Typen (links) und ihre Entsprechungen als Input-Geometrie im Geometryshader (rechts) [2].

GL_POINTS GL_POINTS
GL_LINES GL_LINES
GL_LINE_STRIP
GL_LINE_LOOP
GL_LINES_ADJACENCY_EXT GL_LINES_ADJACENCY_EXT
GL_LINE_STRIP_ADJACENCY_EXT
GL_TRIANGLES GL_TRIANGLES
GL_TRIANGLE_STRIP
GL_TRIANGLE_FAN
GL_TRIANGLES_ADJACENCY_EXT GL_TRIANGLES_ADJACENCY_EXT
GL_TRIANGLE_STRIP_ADJACENCY_EXT
GL_QUADS nicht definiert?
Möglicherweise GL_TRIANGLES?
GL_QUAD_STRIP
GL_POLYGON


Der Input-Geometrie-Typ wird beispielsweise wie folgt gesetzt:

glProgramParameteriEXT(m_prgTest, GL_GEOMETRY_INPUT_TYPE_EXT, GL_TRIANGLES);

Output-Typen

Erlaubte Output-Primitiv-Typen sind die folgenden.

GL_POINTS
GL_LINE_STRIP
GL_TRIANGLE_STRIP

Die scheinbare Beschränkung auf die obigen drei Typen ist eigentlich keine. Ein Triangle-Strip zählt beispielsweise als ein Primitiv und man darf durchaus mehrere Primitive, also mehrere Strips erzeugen. Will man also einzelne, unabhängige Dreiecke generieren, schreibt man einfach mehrere Strips mit jeweils einem Dreieck. Der Output-Geometrie-Typ wird beispielsweise wie folgt gesetzt:

glProgramParameteriEXT(m_prgTest, GL_GEOMETRY_OUTPUT_TYPE_EXT, GL_POINTS);

Neues in GLSL

Eingebaute Variablen

Hier finden wir die aus dem Vertexshader bekannten. Im Geometryshader sind aber auch einige zusätzliche Variablen definiert.

  • vec4 gl_Position
In diese Variable muss die transformierte Position des Vertex geschrieben werden. Diese Variable ist zunächst undefiniert. Der Wert der Variable gl_Position aus dem Vertexshader findet sich im Varying-Array gl_PositionIn[].
  • float gl_PointSize
  • vec4 gl_ClipVertex
  • int gl_Layer
Wird benötigt, wenn man in mehrere Layer eines Framebuffers gleichzeitig rendert. Für Details siehe den Abschnitt "Dependencies on EXT_framebuffer_object" in der Orginalspezifikation.
  • int gl_PrimitiveIDIn
Der Shader zählt die verarbeiteten Primitive. Der aktuelle Zählerstand kann über diese Variable abgefragt werden. Die Zahl entspricht der Position des Primitives im Vertex- bzw. Indexbuffer. Diese Variable kann nur gelesen werden.
  • int gl_PrimitiveID
Will man die PrimitiveID im Fragmentshader verwenden, kann man in diese Variable schreiben. Die Variable ist dann als gl_PrimitiveID im Fragmentshader verfügbar.


Eingebaute Konstanten

Im Geometryshader ist neben den aus dem Vertexshader bekannten Konstanten auch die folgende neue Konstante verfügbar:

  • int gl_VerticesIn
Diese Konstante gibt die Anzahl der Input-Vertices, also die Größe der Arrays an. Man kann also einen Shader theoretisch auch für verschiedene Input-Typen auslegen. Die neuen Input-Typen *_ADJACENCY_EXT wurden weiter oben erklärt.
Primitiv-Typ Wert von gl_VerticesIn
GL_POINTS 1
GL_LINES 2
GL_LINES_ADJACENCY_EXT 4
GL_TRIANGLES 3
GL_TRIANGLES_ADJACENCY_EXT 6


Eingebaute Uniforms

Im Geometryshader sind die gleichen eingebauten Uniform-Variablen verfügbar wie im Vertexshader.


Eingebaute Input-Varyings

Im Geometryshader sind vom Prinzip die gleichen Varyings verfügbar wie im Vertexshader. Nur handelt es sich hier Arrays und zur besseren Unterscheidung zwischen Input und Output des Geometryshaders wurde jeweils das Suffix "In" angehängt.

  • varying in vec4 gl_FrontColorIn[gl_VerticesIn];
  • varying in vec4 gl_BackColorIn[gl_VerticesIn];
  • varying in vec4 gl_FrontSecondaryColorIn[gl_VerticesIn];
  • varying in vec4 gl_BackSecondaryColorIn[gl_VerticesIn];
  • varying in vec4 gl_TexCoordIn[gl_VerticesIn][]; // gl_MaxTextureCoords beachten
  • varying in float gl_FogFragCoordIn[gl_VerticesIn];
  • varying in vec4 gl_PositionIn[gl_VerticesIn];
  • varying in float gl_PointSizeIn[gl_VerticesIn];
  • varying in vec4 gl_ClipVertexIn[gl_VerticesIn];


Eingebaute Output-Varyings

Die eingebauten Output-Varyings für Geometryshader sind die gleichen wie im Vertexshader.

  • varying out vec4 gl_FrontColor;
  • varying out vec4 gl_BackColor;
  • varying out vec4 gl_FrontSecondaryColor;
  • varying out vec4 gl_BackSecondaryColor;
  • varying out vec4 gl_TexCoord[]; // gl_MaxTextureCoords beachten
  • varying out float gl_FogFragCoord;


Eingebaute Funktionen

Ein Geometryshader kann alle Funktionen verwenden, die auch im Vertexshader verfügbar sind. Also Mathe-Funktionen, Texturzugriffe, und was es alles so gibt. Zusätzlich wurden die folgenden beiden Funktionen für den Geometryshader eingeführt:

  • void EmitVertex();
Ein Aufruf dieser Funktion erzeugt einen neuen Vertex im aktuellen Primitiv. Intern wird dabei ein Arrayindex inkrementiert. Daher sind nach dem Aufruf alle Output-Variablen und -Varyings undefiniert. Für das nächste Vertex müssen sie also erneut geschrieben werden. Erzeugt man mehr Vertices als in GL_GEOMETRY_VERTICES_OUT_EXT festgelegt, haben weitere Aufrufe von EmitVertex() keine Wirkung. Der Compiler kann nicht sicher feststellen, ob zuviele Vertices erzeugt werden und gibt daher keine Warnung aus.
  • void EndPrimitive();
Ein Aufruf dieser Funktion beendet das aktuelle Primitiv und beginnt ein neues. Nach Beendigung des Geometryshaders wird diese Funktion automatisch aufgerufen. Die Funktion wird also nur dann benötigt, wenn man mehrere Line- bzw. Triangle-Strips erzeugen möchte. Erzeugt man Points ist der Aufruf optional.


Neue Schlüsselworte

  • varying in
Wird verwendet für Input-Varyings vom Vertexshader. Aus einer varying-Variable im Vertexshader muss im Geometryshader ein Array der Größe gl_VerticesIn werden. Die varying-Variable muss im Vertex- und Geometryshader jeweils den selben Namen haben.
Beispiele:
Vertexshader Geometryshader
varying float beispiel; varying in float beispiel[gl_VerticesIn];
varying vec4 foobar[4]; varying in vec4 foobar[gl_VerticesIn][4];
  • varying out
Wird verwendet für Output-Varyings zum Fragmentshader. Diese funktionieren wie vom Vertexshader gewohnt. Für jeden Vertex den man im Geometryshader erzeugt, weißt man der Variable erneut einen Wert zu. Die varying-Variable muss im Geometry- und Fragmentshader jeweils den selben Namen haben.

Neue OpenGL-Funktionen

Neue OpenGL-Tokens

Akzeptiert als Parameter type bei glCreateShader und als Rückgabewert für den Parameter params von glGetShaderiv:

GL_GEOMETRY_SHADER_EXT 0x8DD9


Akzeptiert als Parameter pname bei glProgramParameterEXT und glGetProgramiv:

GL_GEOMETRY_VERTICES_OUT_EXT 0x8DDA
GL_GEOMETRY_INPUT_TYPE_EXT 0x8DDB
GL_GEOMETRY_OUTPUT_TYPE_EXT 0x8DDC


Akzeptiert als Parameter pname bei glGetBooleanv, glGetIntegerv, glGetFloatv und glGetDoublev:

GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT 0x8C29
GL_MAX_GEOMETRY_VARYING_COMPONENTS_EXT 0x8DDD
GL_MAX_VERTEX_VARYING_COMPONENTS_EXT 0x8DDE
GL_MAX_VARYING_COMPONENTS_EXT 0x8B4B
GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_EXT 0x8DDF
GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT 0x8DE0
GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_EXT 0x8DE1


Akzeptiert als Parameter mode bei glBegin, glDrawArrays, glMultiDrawArrays, glDrawElements, glMultiDrawElements und glDrawRangeElements:

GL_LINES_ADJACENCY_EXT 0xA
GL_LINE_STRIP_ADJACENCY_EXT 0xB
GL_TRIANGLES_ADJACENCY_EXT 0xC
GL_TRIANGLE_STRIP_ADJACENCY_EXT 0xD


Als Rückgabewert von glCheckFramebufferStatusEXT:

GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT 0x8DA8
GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_EXT 0x8DA9


Akzeptiert als Parameter pname bei glGetFramebufferAttachmentParameterivEXT:

GL_FRAMEBUFFER_ATTACHMENT_LAYERED_EXT 0x8DA7
GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER_EXT 0x8CD4

Hinweis
GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER_EXT ist ein Alias für GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT, welches von von der Extension GL_EXT_framebuffer_object bereitgestellt wird. Diese Extension dehnt die Bedeutung von zoffset aus um auch die Layer einer Array-Textur einzuschließen.


Akzeptiert als Parameter cap bei glEnable, glDisable und glIsEnabled, sowie als Parameter pname bei glGetIntegerv, glGetFloatv, glGetDoublev und glGetBooleanv:

GL_PROGRAM_POINT_SIZE_EXT 0x8642

Hinweis
GL_PROGRAM_POINT_SIZE_EXT ist ein Alias für GL_VERTEX_PROGRAM_POINT_SIZE, welches von OpenGL 2.0 definiert wird. Letzteres ist selbst ein Alias für GL_VERTEX_PROGRAM_POINT_SIZE_ARB, welches von der Extension GL_ARB_vertex_program bereitsgestellt wird. "Program-computed point sizes" können aktiviert werden, wenn Geometryshader aktiv sind.

Abhängigkeiten

Es wird OpenGL 1.1 benötigt. Die Extension wurde anhand der OpenGL 2.0 Spezifikation geschrieben.

GL_EXT_geometry_shader4 beeinflusst die folgenden Extensions:

GL_EXT_geometry_shader4 beeinflusst die folgenden Extensions auf triviale Weise:

Die folgenden anderen Extensions interagieren mit GL_EXT_geometry_shader4:


Ressourcen

Quellen

[1] NVIDIA GeForce 8 and 9 Series GPU Programming Guide, insbesondere Abschnitt 4.6
[2] GLSL Geometry Shaders, Mike Bailey, Oregon State University