GL EXT geometry shader4: Unterschied zwischen den Versionen
K (→Neues in GLSL: Wiki-Tabellen) |
DGLBot (Diskussion | Beiträge) K (Der Ausdruck ''<cpp>(.*?)</cpp>'' wurde ersetzt mit ''<source lang="cpp">$1</source>''.) |
||
(7 dazwischenliegende Versionen von 4 Benutzern werden nicht angezeigt) | |||
Zeile 17: | Zeile 17: | ||
=== '''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 29: | Zeile 29: | ||
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. | 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 [[ | + | 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. |
− | <cpp> | + | <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 44: | Zeile 44: | ||
glDeleteShader(gsTest); | glDeleteShader(gsTest); | ||
glUseProgram(m_prgTest); | glUseProgram(m_prgTest); | ||
− | </ | + | </source> |
Die folgenden Hilfsfunktionen sollten relativ selbst erklärend sein. | Die folgenden Hilfsfunktionen sollten relativ selbst erklärend sein. | ||
− | <cpp> | + | <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 102: | Zeile 102: | ||
} | } | ||
} | } | ||
− | </ | + | </source> |
==== Geometryshader in GLSL ==== | ==== 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. | 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. | ||
− | <glsl> | + | <source lang="glsl"> |
varying float foo; | varying float foo; | ||
Zeile 112: | 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, 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. | 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. | ||
− | <glsl> | + | <source lang="glsl"> |
#extension GL_EXT_geometry_shader4: enable | #extension GL_EXT_geometry_shader4: enable | ||
Zeile 140: | Zeile 140: | ||
// EndPrimitive() hier unnötig, wird am Ende automatisch aufgerufen. | // EndPrimitive() hier unnötig, wird am Ende automatisch aufgerufen. | ||
} | } | ||
− | </ | + | </source> |
=== Primitiv-Typen === | === Primitiv-Typen === | ||
Im folgenden werden die neu hinzugekommenen Primitiv-Typen vorgestellt, sowie erläutert wann wo welche Primitiv-Typen verwendet werden dürfen. | 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 ==== | ==== Die neuen Typen: *_ADJACENCY_EXT ==== | ||
− | Im folgenden stehen die durchgezogenen Linien für die eigentliche Geometrie. Die gestrichelten Linien stehen für die Nachbarschaft. | + | 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 | + | * '''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:Lines-adjacency.png]] | ||
− | * GL_LINE_STRIP_ADJACENCY_EXT | + | * '''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:Line-strip-adjacency.png]] | ||
− | * GL_TRIANGLES_ADJACENCY_EXT | + | * '''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]] | : [[Bild:Triangles-adjacency.png]] | ||
− | * GL_TRIANGLE_STRIP_ADJACENCY_EXT | + | * '''GL_TRIANGLE_STRIP_ADJACENCY_EXT''' |
− | : Die Definition von Triangle-Strips mit Adjazenz ist recht kompliziert. Am besten schaut man sich die Definition in der englischen [http://opengl.org/registry/specs/EXT/geometry_shader4.txt Original-Spezifikation] an. | + | : 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]] | : [[Bild:Triangle-strip-adjacency.png]] | ||
Zeile 318: | Zeile 321: | ||
== Neue OpenGL-Funktionen == | == Neue OpenGL-Funktionen == | ||
− | * [[ | + | * [[glProgramParameterEXT]] |
* [[glFramebufferTextureEXT]] | * [[glFramebufferTextureEXT]] | ||
* [[glFramebufferTextureLayerEXT]] | * [[glFramebufferTextureLayerEXT]] | ||
Zeile 325: | 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> | ||
− | + | {| border=1 rules=all | |
− | + | | '''GL_GEOMETRY_SHADER_EXT''' || 0x8DD9 | |
− | + | |} | |
<br> | <br> | ||
− | Akzeptiert als Parameter ''pname'' bei [[ | + | Akzeptiert als Parameter ''pname'' bei [[glProgramParameterEXT]] und [[glGetProgramiv]]:<br> |
− | + | {| border=1 rules=all | |
− | + | | '''GL_GEOMETRY_VERTICES_OUT_EXT''' || 0x8DDA | |
− | + | |- | |
− | + | | '''GL_GEOMETRY_INPUT_TYPE_EXT''' || 0x8DDB | |
− | + | |- | |
+ | | '''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> | ||
− | + | {| border=1 rules=all | |
− | + | | '''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 | ||
+ | |- | ||
+ | |} | ||
<br> | <br> | ||
Zeile 355: | 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> | ||
− | + | {| border=1 rules=all | |
− | + | | '''GL_LINES_ADJACENCY_EXT''' || 0xA | |
− | + | |- | |
− | + | | '''GL_LINE_STRIP_ADJACENCY_EXT''' || 0xB | |
− | + | |- | |
− | + | | '''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> | ||
− | + | {| border=1 rules=all | |
− | + | | '''GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT''' || 0x8DA8 | |
− | + | |- | |
− | + | | '''GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_EXT''' || 0x8DA9 | |
+ | |- | ||
+ | |} | ||
<br> | <br> | ||
Akzeptiert als Parameter ''pname'' bei [[glGetFramebufferAttachmentParameterivEXT]]:<br> | Akzeptiert als Parameter ''pname'' bei [[glGetFramebufferAttachmentParameterivEXT]]:<br> | ||
− | + | {| border=1 rules=all | |
− | + | | '''GL_FRAMEBUFFER_ATTACHMENT_LAYERED_EXT''' || 0x8DA7 | |
− | + | |- | |
− | + | | '''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 383: | 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> | ||
− | + | {| border=1 rules=all | |
− | + | | '''GL_PROGRAM_POINT_SIZE_EXT''' || 0x8642 | |
− | + | |- | |
+ | |}<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 404: | Zeile 425: | ||
Die folgenden anderen Extensions interagieren mit GL_EXT_geometry_shader4: | Die folgenden anderen Extensions interagieren mit GL_EXT_geometry_shader4: | ||
− | * [[ | + | * [[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
Inhaltsverzeichnis
EXT_geometry_shader4
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.
- 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.
- 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.
- 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.
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
- glProgramParameterEXT
- glFramebufferTextureEXT
- glFramebufferTextureLayerEXT
- glFramebufferTextureFaceEXT
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
- Orginalspezifikation GL_EXT_geometry_shader4 (englisch)
- Orginalspezifikation GL_ARB_geometry_shader4 (englisch)
- Shader-Grundlagen
- Tutorial GLSL
Quellen
[1] NVIDIA GeForce 8 and 9 Series GPU Programming Guide, insbesondere Abschnitt 4.6
[2] GLSL Geometry Shaders, Mike Bailey, Oregon State University