Tutorial glsl: Unterschied zwischen den Versionen
Flash (Diskussion | Beiträge) (Erster Teil des GLSL Tutorials von Sascha) |
Flash (Diskussion | Beiträge) () |
||
Zeile 49: | Zeile 49: | ||
==Resourcen== | ==Resourcen== | ||
Die Shadersprache ist keinesfalls final und es wurden bereits diverse Ausdrücke für zukünftige Verwendung reserviert, denn ein Ziel bei ihrer Entwicklung war es, sie so zukunftsorientiert zu gestalten das auch Grafikkarten der nächsten und übernächsten Generation voll ausgenutzt werden können. Damit einher geht die Tatsache das sich die Spezifikationen in Zukunft ändern/erweitern werden, weshalb man da immer einen Blick hineinwerfen sollte. Die Anlaufstelle dafür ist natürlich die [http://www.3dlabs.com/support/developer/ogl2/index.htm GL2-Seite von 3D-Labs], wo u.a. auch ein OGL2-SDK und diverse Whitepapers als PDFs angeboten werden, in denen auch stattgefundene Änderungen an glSlang dokumentiert sind. | Die Shadersprache ist keinesfalls final und es wurden bereits diverse Ausdrücke für zukünftige Verwendung reserviert, denn ein Ziel bei ihrer Entwicklung war es, sie so zukunftsorientiert zu gestalten das auch Grafikkarten der nächsten und übernächsten Generation voll ausgenutzt werden können. Damit einher geht die Tatsache das sich die Spezifikationen in Zukunft ändern/erweitern werden, weshalb man da immer einen Blick hineinwerfen sollte. Die Anlaufstelle dafür ist natürlich die [http://www.3dlabs.com/support/developer/ogl2/index.htm GL2-Seite von 3D-Labs], wo u.a. auch ein OGL2-SDK und diverse Whitepapers als PDFs angeboten werden, in denen auch stattgefundene Änderungen an glSlang dokumentiert sind. | ||
+ | |||
+ | |||
+ | |||
+ | =glSlang im Programm= | ||
+ | Bevor wir uns mit der Syntax von glSlang beschäftigen, zeige ich euch erstmal wie ihr Shader in euer Programm einbindet und nutzen tut. Warum das zuerst? Ganz einfach deshalb, weil ihr dann das was ihr im glSlang-Syntaxteil lernt direkt in eurer Testanwendung verwenden könnt. Hoffe diese Entscheidung klingt logisch und findet Anklang. | ||
+ | |||
+ | Zuerst benötigen wir natürlich unsere Objekte. Zum einen ein ''Programmobjekt'', an das unsere Shader gebunden werden, und zwei ''Shaderobjekte'' die den Quellcode unseres Vertex bzw. Fragment Shaders aufnehmen. Dazu wurde eigens der neue "Datentyp" {{INLINE_CODE|glHandleARB}} eingeführt, der ein Objekthandle repräsentiert. Wir deklarieren also wie folgt : | ||
+ | |||
+ | ProgramObject : GLhandleARB; | ||
+ | VertexShaderObject : GLhandleARB; | ||
+ | FragmentShaderObject : GLhandleARB; | ||
+ | |||
+ | |||
+ | Nach dieser Deklaration können wir dann damit beginnen unsere Objekte zu erstellen. Den Anfang macht das Programmobjekt : | ||
+ | |||
+ | ProgramObject := glCreateProgramObjectARB; | ||
+ | |||
+ | Die Funktion [[glCreateProgramObjectARB]] erstellt uns oben ein leeres Programmobjekt und gibt ein gültiges Handle darauf zurück. | ||
+ | |||
+ | Weiter gehts mit der Erstellung unseres Vertex bzw. Fragment Shaders : | ||
+ | |||
+ | VertexShaderObject := glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB); | ||
+ | FragmentShaderObject := glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB); | ||
+ | |||
+ | [[glCreateShaderObjectARB]] dient zur Generierung eines leeren Shaderobjektes. Momentan unterstützt diese Funktion VertexShader und FragmentShader. | ||
+ | |||
+ | Nachdem wir nun also zwei gültige Shaderobjekte haben, wollen wir diese auch mit entsprechendem Quellcode versorgen : | ||
+ | |||
+ | glShaderSourceARB(VertexShaderObject, 1, @ShaderText, @ShaderLength); | ||
+ | glShaderSourceARB(FragmentShaderObject, 1, @ShaderText, @ShaderLength); | ||
+ | |||
+ | Via [[glShaderSourceARB]] setzen wir den Quellcode eines Shaderobjektes ''komplett'' neu. Zum Laden des Quellcodes bietet sich unter Delphi übrigens eine TStringList gradezu an. Es sollte beachtet werden dass der Quellcode zu diesem Zeitpunkt ''nicht geparst'' wird, also keine Fehleruntersuchung stattfindet. | ||
+ | |||
+ | Der Quellcode wurde jetzt also an unsere Shaderobjekte gebunden und sollte dann natürlich auch noch kompiliert werden : | ||
+ | |||
+ | glCompileShaderARB(VertexShaderObject); | ||
+ | glCompileShaderARB(FragmentShaderObject); | ||
+ | |||
+ | Der glSlang-Compiler des Treibers wird bei einem Aufruf von [[glCompileShaderARB]] versuchen unsere Shader zu kompilieren. Sofern diese keine Fehler aufweisen sollte dies auch erfolgreich sein. Wenn nicht, dann spuckt uns der ShaderKompiler je nach Treiber recht detaillierte Infos aus. Wie man an diese Infos kommt könnt ihr gleich nachlesen. | ||
+ | |||
+ | Wenn unsere Shader dann kompiliert werden konnten, ist es Zeit diese an unser anfangs erstelltes Programmobjekt anzuhängen : | ||
+ | |||
+ | glAttachObjectARB(ProgramObject, VertexShaderObject); | ||
+ | glAttachObjectARB(ProgramObject, FragmentShaderObject); | ||
+ | |||
+ | |||
+ | Nachdem die Shaderobjekte nun an das Programmobjekt angehangen wurden, werden diese nicht mehr benötigt und ihre Resourcen können freigegeben werden : | ||
+ | |||
+ | glDeleteObjectARB(VertexShaderObject); | ||
+ | glDeleteObjectARB(FragmentShaderObject); | ||
+ | |||
+ | |||
+ | Am Schluß müssen wir dann noch unsere ans Programmobjekt gebundenen Shader linken : | ||
+ | |||
+ | glLinkProgramARB(ProgramObject); | ||
+ | |||
+ | Während [[glCompileShaderARB]] unsere Shader auf syntaktische Fehler innerhalb ihres lokalen Raums geprüft hat, werden beim Linken durch [[glLinkProgramARB]] die angehangenen Shader zu einem ausführbaren Shader gelinkt. Folgende Bedingungen führen zu einem '''Linkerfehler''': | ||
+ | |||
+ | * Die Zahl der von der Implementation unterstützten Attributvariablen wurde überschritten | ||
+ | * Der Speicherplatz für Uniformvariablen wurde überschritten | ||
+ | * Die Zahl der von der Implementation angebotenen Sampler wurde überschritten | ||
+ | * Die main-Funktion fehlt | ||
+ | * Die Liste der Varying-Variablen des Vertexshaders stimmt nicht mit der des Fragmentshaders überein | ||
+ | * Funktions- oder Variablenname nicht gefunden | ||
+ | * Eine gemeinsame Globale ist mit unterschiedlichen Werten oder Typen initialisiert worden | ||
+ | * Zwei Sampler unterschiedlichen Typs zeigen auf die selbe Textureneinheit | ||
+ | * Ein oder mehrere angehangene(r) Shader wurden nicht erfolgreich kompiliert | ||
+ | |||
+ | Die Nutzung von glSlang im eigenen Programm ist wie oben erkennbar also nicht wirklich schwer und innerhalb kurzer Zeit realisiert. Natürlich ist es auch möglich z.B. nur einen VertexShader oder nur einen FragmentShader an ein Programmobjekt zu binden. | ||
+ | |||
+ | |||
+ | ==Fehlererkennung== | ||
+ | Natürlich wird es ohne Fehlerausgabe recht schwer, etwaige Probleme in einem Vertex oder Fragment Shader zu finden. Doch auch in diesem Bereich wurde glSlang recht gut durchdacht und es wurden zwei Funktionen eingeführt, welche im Zusammenspiel die Fehlersuche recht einfach machen, nämlich [[glGetInfoLogARB]] und [[glGetObjectParameterivARB]] mit dem Argument {{INLINE_CODE|GL_OBJECT_INFO_LOG_LENGTH_ARB}}. Erstere Funktion liefert uns einen Logstring, während uns letztere Funktion dessen Länge angibt. Der Logstring wird verändert, sobald ein Shader kompiliert oder ein Programm gelinkt wird. | ||
+ | |||
+ | Um die Ausgabe dieses Logs so einfach wie möglich zu machen, bietet es sich an beide in einer einfach Funktion unterzubringen : | ||
+ | |||
+ | <pascal>function glSlang_GetInfoLog(glObject : GLHandleARB) : String; | ||
+ | var | ||
+ | blen,slen : GLInt; | ||
+ | InfoLog : PGLCharARB; | ||
+ | begin | ||
+ | glGetObjectParameterivARB(glObject, GL_OBJECT_INFO_LOG_LENGTH_ARB , @blen); | ||
+ | if blen > 1 then | ||
+ | begin | ||
+ | GetMem(InfoLog, blen*SizeOf(GLCharARB)); | ||
+ | glGetInfoLogARB(glObject, blen, slen, InfoLog); | ||
+ | Result := PChar(InfoLog); | ||
+ | Dispose(InfoLog); | ||
+ | end; | ||
+ | end;</pascal> | ||
+ | |||
+ | |||
+ | Die Funktion ist recht leicht erklärt : Zuerst lassen wir uns über {{INLINE_CODE|glGetObjectParameterivARB}} mitteilen wie lang der aktuelle Inoflog ist. Sollte dort tatsächlich etwas drinstehen (blen > 1), dann lassen wir uns dessen Inhalt via {{INLINE_CODE|glGetInfoLogARB}} in {{INLINE_CODE|InfoLog}} ausgeben und liefern diesen als Ergebnis zurück. | ||
+ | |||
+ | Wie bereits gesagt wird nur nach dem Kompilieren eines Shaders bzw. dem Linken eines Programmobjektes ein Infolog erstellt. Es bietet sich dadurch an, direk danach einen solchen Aufruf zu machen : | ||
+ | |||
+ | glCompileShaderARB(VertexShaderObject); | ||
+ | ShowMessage(glSlang_GetInfoLog(VertexShaderObject)); | ||
+ | |||
+ | Wenn unser Vertex Shader komplett fehlerfrei kompiliert werden konnte, dann sehen wir als Ergebnis nur einen leeren Dialog. Ist dies nicht der Fall, so werden wir vom Treiber mit recht detaillierten Fehlerinformationen "belohnt", z.B. so : | ||
+ | |||
+ | [[Bild:GLSL_error_vshader.jpg|center]] | ||
+ | |||
+ | Auch das Infolog nach dem Linken des Programmobjektes dürfte, selbst wenn keine Fehler vorkommen, recht interessant sein, das sieht dann nämlich so aus : | ||
+ | |||
+ | [[Bild:GLSL info programobject.jpg|center]] | ||
+ | |||
+ | Wie zu sehen wird uns nach dem erfolgreichen Linken auch gesagt ob und welcher Shader in Hardware bzw. Software läuft. Für Debuggingzwecke sicherlich eine mehr als brauchbare Information. | ||
+ | |||
+ | |||
+ | ==Parameterübergabe== | ||
+ | Uniformparmater (mehr dazu später) stellen die Schnittstelle zwischen eurem Programm und dem Shader dar, werden also genutzt um Daten aus dem Programm heraus an einen Shader zu übergeben. Zur Übergabe dieser Parameter bietet OpenGL diverse Funktionen, die alle Abkömmlinge von [[glUniformARB]] sind. Während mit {{INLINE_CODE|glUniform4fARB}} z.B. ein Vier-Komponentenvektor an das Programmobjekt übergeben wird, kann man mittels {{INLINE_CODE|glUniformMatrix4fvARB}} ganze Matrizen schnell und einfach übergeben. Ausserdem gibt es nun die Möglichkeit Uniformparameter direkt über ihren Namen, statt wie unter ARB_FP/VP über einen festen Index zu adressieren. Die Funktion [[glGetUnifromLocationARB]] gibt anhand des übergebenen Parameternamens dessen Position zurück. Man kann also ganz einfach über den Namen drauf zugreifen : | ||
+ | |||
+ | glUniform3fARB(glGetUniformLocationARB(ProgramObject, PGLCharARB('LightPosition')), LPos[0], LPos[1], LPos[2]); | ||
+ | glUniform1iARB(glGetUniformLocationARB(ProgramObject, PGLCharARB('texSamplerTMU3')), 3); | ||
+ | |||
+ | |||
+ | Wichtig ist hier, das man je nach Parametertyp auch die passende Anzahl von Argumenten übergibt. Also für einen 4-Komponenten Floatvektor {{INLINE_CODE|glUniform4fARB}} und für einen einfachen Integerwert (z.B. Textureinheit für einen Sampler) glUnifrom1iARB. Auch nicht vergessen dürft ihr, das die Namen der Parameter genauso wie im Shader geschrieben werden müssen, also Groß- und Kleinschreibung beachtet werden müssen. | ||
+ | |||
+ | |||
+ | |||
Version vom 19. August 2005, 10:31 Uhr
Inhaltsverzeichnis
Präambel
Ave und willkommen bei meiner "Einführung" in die recht frische und mit OpenGL1.5 eingeführte Shadersprache "glSlang". In diesem umfangreichen Dokument werde ich versuchen sowohl auf die Nutzung (sprich das Laden und Anhängen von Shadern im Quellcode), als auch auf die Programmierung von Shadern selbst einzugehen, inklusive aller Sprachelemente der OpenGL Shadersprache. Es wird also auch recht viele Informationen zu der C-ähnlichen Programmstruktur und den von glSlang angebotenen Variablen und Attributen gehen. Am Ende dieser Einführung sollten alle die, die sich für das Thema interessieren in der Lage sein zumindest einfach Shader zu schreiben und auch in ihren Programmen zu nutzen. Ausserdem soll dieses Dokument gleichzeitig als ein deutsches "Pendant" zu den von 3DLabs veröffentlichten Shaderspezifikationen, und damit als alltägliches Nachschlagewerk dienen.
Vorkenntnisse
Wie auch schon mein ARB_VP-Tutorial richtet sich auch diese Einführung aufgrund ihrer Thematik eher an die fortgeschritteneren GL-Programmierer und neben sehr guten GL-Kenntnissen sollten sich alle, die sich daran versuchen wollen, mit den technischen Hintergründen der GL, wie z.B. dem Aufbau der Renderpipeline auskennen. Weiterhin sind C-Kenntnisse absolut erforderlich, da die Shader ja in einer an ANSI-C angelehnten Syntax geschrieben werden. Auch Begriffsdefinitionen zu Vertex oder Fragment werden zum Verständis dieser Einführung benötigt. Wer also noch am Anfang seiner GL-Karriere steht, dem wird dieses Dokument nicht viel nützen. Ganz nebenbei solltet ihr auch noch eine gehörige Portion Zeit (am besten nen kompletten Nachmittag) mitbringen, denn die folgende Kost ist nicht nur umfangreich sondern auch manchmal recht schwer verdaulich.
Was ist glSlang?
Wie Eingangs kurz angesprochen handelt es sich bei glSlang um eine Shadersprache, also um eine Hochsprache in der man die programmierbaren Teile aktueller Grafikbeschleuniger nach eigenem Belieben programmieren kann. Sie stellt quasi den Nachfolger zu den in assembler geschriebenen Vertex- und Fragmentprogrammen (GL_ARB_Vertex_Program/GL_ARB_Fragment_Program) dar und basiert auf ANSI C, erweitert um Vektor- und Matrixtypen sowie einige C++-Mechanismen.
Die in glSlang geschriebenen Programme nennen sich, angepasst an die Termonologie von RenderMan und DirectX, Shader (im Gegensatz zu "Programme" bei ARB_VP/FP) und werden entweder auf Vertexe (VertexShader) oder Fragmente (FragmentShader) angewendet, andere noch nicht programmierbare Teile der GL-Pipeline wie z.B. die Rasterisierung können momentan noch nicht über Shader beeinflusst werden.
Voraussetzungen
glSlang ist ein brandneues Feature, das mit OpenGL1.5 eingeführt wurde, weshalb eine entsprechend moderne Grafikkarte (DX9-Generation) inklusive aktuellster Treiber von Nöten ist. Der Stand zum Zeitpunkt als diese Einführung geschrieben wurde (24.12.2003) ist folgender : ATI hat seit Catalyst 3.10 glSlang-fähige Treiber, deren glSlang-Compiler allerdings noch nicht alle Features unterstützt. Bei 3DLabs ist dieser Fall ähnlich, deren Treiber bieten allerdings schon etwas länger glSlang-Support. NVidia lassen sich diesmal allerdings recht lange Zeit und haben noch keine Treiber am Start die glSlang beherrschen, hier ist also noch etwas Geduld angesagt. Allerdings sollten auch die Kalifornier bald mit glSlang-fähigen Treibern für ihre GeForce FX-Reihe rausrücken.
Natürlich benötigt ihr auch einen passenden OpenGL-Header der die für glSlang nötigen Extensions und Funktionen exportiert. Ich verweise dazu auf unseren internen OpenGL-Header DGLOpenGL.pas der da einwandfrei seine Dienste verrichtet und auch in der Beispielanwendung Verwendung findet.
Neue Extensions
Die GL-Shadersprache "besteht" in ihrer aktuellen Version aus folgenden Extensions, fürs Verständnis wäre es nicht schlecht wenn ihr euch zumindest die Einleitungen dazu durchlest :
- Definiert die API-Aufrufe die zum Erstellen, Kompilieren, Linken, Anhängen und Aktivieren von Shader- und Programmobjekten nötig sind.
- Fügt der OpenGL Programmierbarkeit auf Vertexebene hinzu.
- Fügt der OpenGL Programmierbarkeit auf Fragmentebene hinzu.
- Gibt die unterstützte Version von glSlang an, momentan 1.00.
Objekte
Im Zuge der Vereinheitlichung der GL wird immer häufiger in Objekte gekapselt, deren API dann auch aneinander angelehnt ist. Ziel ist dabei die Programmierung der GL uniform zu machen, so das z.B. zwischen dem Erstellen und Verwalten eines Vertex-Buffer-Objektes oder eines Shader-Objektes kaum ein Unterschied besteht (demnächst kommen dann auch Pixel-Buffer-Objekte). Mit glSlang wurden dann im Zuge dieser Aktion zwei neue Objekte eingeführt, deren Definition ihr euch unbedingt einprägen solltet :
- Programmobjekt
- Ein Objekt an das die Shader später angebunden werden. Bietet Funktionalität zum Linken der Shader und prüft dabei die Kompatibilität zwischen Vertex- und Fragmentshader.
- Shaderobjekt
- Dieses Objekt verwaltet den Quellcodestring eines Shaders und ist entweder vom Typ GL_VERTEX_SHADER_ARB oder GL_FRAGMENT_SHADER_ARB.
Resourcen
Die Shadersprache ist keinesfalls final und es wurden bereits diverse Ausdrücke für zukünftige Verwendung reserviert, denn ein Ziel bei ihrer Entwicklung war es, sie so zukunftsorientiert zu gestalten das auch Grafikkarten der nächsten und übernächsten Generation voll ausgenutzt werden können. Damit einher geht die Tatsache das sich die Spezifikationen in Zukunft ändern/erweitern werden, weshalb man da immer einen Blick hineinwerfen sollte. Die Anlaufstelle dafür ist natürlich die GL2-Seite von 3D-Labs, wo u.a. auch ein OGL2-SDK und diverse Whitepapers als PDFs angeboten werden, in denen auch stattgefundene Änderungen an glSlang dokumentiert sind.
glSlang im Programm
Bevor wir uns mit der Syntax von glSlang beschäftigen, zeige ich euch erstmal wie ihr Shader in euer Programm einbindet und nutzen tut. Warum das zuerst? Ganz einfach deshalb, weil ihr dann das was ihr im glSlang-Syntaxteil lernt direkt in eurer Testanwendung verwenden könnt. Hoffe diese Entscheidung klingt logisch und findet Anklang.
Zuerst benötigen wir natürlich unsere Objekte. Zum einen ein Programmobjekt, an das unsere Shader gebunden werden, und zwei Shaderobjekte die den Quellcode unseres Vertex bzw. Fragment Shaders aufnehmen. Dazu wurde eigens der neue "Datentyp" glHandleARB eingeführt, der ein Objekthandle repräsentiert. Wir deklarieren also wie folgt :
ProgramObject : GLhandleARB; VertexShaderObject : GLhandleARB; FragmentShaderObject : GLhandleARB;
Nach dieser Deklaration können wir dann damit beginnen unsere Objekte zu erstellen. Den Anfang macht das Programmobjekt :
ProgramObject := glCreateProgramObjectARB;
Die Funktion glCreateProgramObjectARB erstellt uns oben ein leeres Programmobjekt und gibt ein gültiges Handle darauf zurück.
Weiter gehts mit der Erstellung unseres Vertex bzw. Fragment Shaders :
VertexShaderObject := glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB); FragmentShaderObject := glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
glCreateShaderObjectARB dient zur Generierung eines leeren Shaderobjektes. Momentan unterstützt diese Funktion VertexShader und FragmentShader.
Nachdem wir nun also zwei gültige Shaderobjekte haben, wollen wir diese auch mit entsprechendem Quellcode versorgen :
glShaderSourceARB(VertexShaderObject, 1, @ShaderText, @ShaderLength); glShaderSourceARB(FragmentShaderObject, 1, @ShaderText, @ShaderLength);
Via glShaderSourceARB setzen wir den Quellcode eines Shaderobjektes komplett neu. Zum Laden des Quellcodes bietet sich unter Delphi übrigens eine TStringList gradezu an. Es sollte beachtet werden dass der Quellcode zu diesem Zeitpunkt nicht geparst wird, also keine Fehleruntersuchung stattfindet.
Der Quellcode wurde jetzt also an unsere Shaderobjekte gebunden und sollte dann natürlich auch noch kompiliert werden :
glCompileShaderARB(VertexShaderObject); glCompileShaderARB(FragmentShaderObject);
Der glSlang-Compiler des Treibers wird bei einem Aufruf von glCompileShaderARB versuchen unsere Shader zu kompilieren. Sofern diese keine Fehler aufweisen sollte dies auch erfolgreich sein. Wenn nicht, dann spuckt uns der ShaderKompiler je nach Treiber recht detaillierte Infos aus. Wie man an diese Infos kommt könnt ihr gleich nachlesen.
Wenn unsere Shader dann kompiliert werden konnten, ist es Zeit diese an unser anfangs erstelltes Programmobjekt anzuhängen :
glAttachObjectARB(ProgramObject, VertexShaderObject); glAttachObjectARB(ProgramObject, FragmentShaderObject);
Nachdem die Shaderobjekte nun an das Programmobjekt angehangen wurden, werden diese nicht mehr benötigt und ihre Resourcen können freigegeben werden :
glDeleteObjectARB(VertexShaderObject); glDeleteObjectARB(FragmentShaderObject);
Am Schluß müssen wir dann noch unsere ans Programmobjekt gebundenen Shader linken :
glLinkProgramARB(ProgramObject);
Während glCompileShaderARB unsere Shader auf syntaktische Fehler innerhalb ihres lokalen Raums geprüft hat, werden beim Linken durch glLinkProgramARB die angehangenen Shader zu einem ausführbaren Shader gelinkt. Folgende Bedingungen führen zu einem Linkerfehler:
- Die Zahl der von der Implementation unterstützten Attributvariablen wurde überschritten
- Der Speicherplatz für Uniformvariablen wurde überschritten
- Die Zahl der von der Implementation angebotenen Sampler wurde überschritten
- Die main-Funktion fehlt
- Die Liste der Varying-Variablen des Vertexshaders stimmt nicht mit der des Fragmentshaders überein
- Funktions- oder Variablenname nicht gefunden
- Eine gemeinsame Globale ist mit unterschiedlichen Werten oder Typen initialisiert worden
- Zwei Sampler unterschiedlichen Typs zeigen auf die selbe Textureneinheit
- Ein oder mehrere angehangene(r) Shader wurden nicht erfolgreich kompiliert
Die Nutzung von glSlang im eigenen Programm ist wie oben erkennbar also nicht wirklich schwer und innerhalb kurzer Zeit realisiert. Natürlich ist es auch möglich z.B. nur einen VertexShader oder nur einen FragmentShader an ein Programmobjekt zu binden.
Fehlererkennung
Natürlich wird es ohne Fehlerausgabe recht schwer, etwaige Probleme in einem Vertex oder Fragment Shader zu finden. Doch auch in diesem Bereich wurde glSlang recht gut durchdacht und es wurden zwei Funktionen eingeführt, welche im Zusammenspiel die Fehlersuche recht einfach machen, nämlich glGetInfoLogARB und glGetObjectParameterivARB mit dem Argument GL_OBJECT_INFO_LOG_LENGTH_ARB. Erstere Funktion liefert uns einen Logstring, während uns letztere Funktion dessen Länge angibt. Der Logstring wird verändert, sobald ein Shader kompiliert oder ein Programm gelinkt wird.
Um die Ausgabe dieses Logs so einfach wie möglich zu machen, bietet es sich an beide in einer einfach Funktion unterzubringen :
function glSlang_GetInfoLog(glObject : GLHandleARB) : String; var blen,slen : GLInt; InfoLog : PGLCharARB; begin glGetObjectParameterivARB(glObject, GL_OBJECT_INFO_LOG_LENGTH_ARB , @blen); if blen > 1 then begin GetMem(InfoLog, blen*SizeOf(GLCharARB)); glGetInfoLogARB(glObject, blen, slen, InfoLog); Result := PChar(InfoLog); Dispose(InfoLog); end; end;
Die Funktion ist recht leicht erklärt : Zuerst lassen wir uns über glGetObjectParameterivARB mitteilen wie lang der aktuelle Inoflog ist. Sollte dort tatsächlich etwas drinstehen (blen > 1), dann lassen wir uns dessen Inhalt via glGetInfoLogARB in InfoLog ausgeben und liefern diesen als Ergebnis zurück.
Wie bereits gesagt wird nur nach dem Kompilieren eines Shaders bzw. dem Linken eines Programmobjektes ein Infolog erstellt. Es bietet sich dadurch an, direk danach einen solchen Aufruf zu machen :
glCompileShaderARB(VertexShaderObject); ShowMessage(glSlang_GetInfoLog(VertexShaderObject));
Wenn unser Vertex Shader komplett fehlerfrei kompiliert werden konnte, dann sehen wir als Ergebnis nur einen leeren Dialog. Ist dies nicht der Fall, so werden wir vom Treiber mit recht detaillierten Fehlerinformationen "belohnt", z.B. so :
Auch das Infolog nach dem Linken des Programmobjektes dürfte, selbst wenn keine Fehler vorkommen, recht interessant sein, das sieht dann nämlich so aus :
Wie zu sehen wird uns nach dem erfolgreichen Linken auch gesagt ob und welcher Shader in Hardware bzw. Software läuft. Für Debuggingzwecke sicherlich eine mehr als brauchbare Information.
Parameterübergabe
Uniformparmater (mehr dazu später) stellen die Schnittstelle zwischen eurem Programm und dem Shader dar, werden also genutzt um Daten aus dem Programm heraus an einen Shader zu übergeben. Zur Übergabe dieser Parameter bietet OpenGL diverse Funktionen, die alle Abkömmlinge von glUniformARB sind. Während mit glUniform4fARB z.B. ein Vier-Komponentenvektor an das Programmobjekt übergeben wird, kann man mittels glUniformMatrix4fvARB ganze Matrizen schnell und einfach übergeben. Ausserdem gibt es nun die Möglichkeit Uniformparameter direkt über ihren Namen, statt wie unter ARB_FP/VP über einen festen Index zu adressieren. Die Funktion glGetUnifromLocationARB gibt anhand des übergebenen Parameternamens dessen Position zurück. Man kann also ganz einfach über den Namen drauf zugreifen :
glUniform3fARB(glGetUniformLocationARB(ProgramObject, PGLCharARB('LightPosition')), LPos[0], LPos[1], LPos[2]); glUniform1iARB(glGetUniformLocationARB(ProgramObject, PGLCharARB('texSamplerTMU3')), 3);
Wichtig ist hier, das man je nach Parametertyp auch die passende Anzahl von Argumenten übergibt. Also für einen 4-Komponenten Floatvektor glUniform4fARB und für einen einfachen Integerwert (z.B. Textureinheit für einen Sampler) glUnifrom1iARB. Auch nicht vergessen dürft ihr, das die Namen der Parameter genauso wie im Shader geschrieben werden müssen, also Groß- und Kleinschreibung beachtet werden müssen.
(Mehr Informationen/weitere Artikel) {{{1}}} |
Rest folgt.
|
||
Vorhergehendes Tutorial: - |
Nächstes Tutorial: tutorial_glsl2 |
|
Schreibt was ihr zu diesem Tutorial denkt ins Feedbackforum von DelphiGL.com. Lob, Verbesserungsvorschläge, Hinweise und Tutorialwünsche sind stets willkommen. |