Tutorial glsl: Unterschied zwischen den Versionen
K (→Schleifen) |
Flash (Diskussion | Beiträge) (→Schleifen: Kapitel 5 fertig) |
||
Zeile 634: | Zeile 634: | ||
while (Durchlaufbedingung) | while (Durchlaufbedingung) | ||
+ | |||
+ | =Eingebaute Variablen, Attribute und Konstanten= | ||
+ | Nachdem wir uns nun lange genug mit den minderinterssanten Elementen der glSlang-Syntax beschäftigt haben, gehts jetzt endlich an die wirklich interessanten Dinge. Wie schon ARB_VP/ARB_FP bringt auch glSlang jede Menge eingabauter Variablen, Attribute und Konstanten mit, deren Aliase sie recht leicht identifizierbar machen (ganz im Gegensatz zum Indexgewusel bei den DX-Shadern). | ||
+ | |||
+ | |||
+ | ==Variablen im Vertex Shader== | ||
+ | Exklusiv im Vertex Shader stehen die folgenden Variablen zur Verfügung : | ||
+ | |||
+ | * vec4 gl_Position muss geschrieben werden | ||
+ | :Dieser Variable '''muss''' im Vertexshader ein Wert zugewiesen werden, wird dies nicht getan ist das Ergebnis (sprich die Position des Vertex) undefiniert. Vorgesehen ist diese Variable für die ''homogene Position des Vertex'' und wird u.a. zum Clipping und Culling verwendet. Sie darf natürlich auch (mehrfaceh) geschrieben und ausgelesen werden. | ||
+ | |||
+ | * float gl_PointSize kann geschrieben werden | ||
+ | :Diese Variable wurde dazu vorgesehen um dort im VertexShader die Punktgröße in Pixeln hineinzuschreiben. | ||
+ | |||
+ | * vec4 gl_ClipVertex kann geschrieben werden | ||
+ | :Falls genutzt sollten hier die Vertexkoordinaten die im Zusammenhang mit benutzerdefinierten Clippingplanes genutzt werden abgelegt werden. Wichtig ist, das gl_ClipVertex im selben Koordinatenraum wie die Clippingplane definiert ist. | ||
+ | |||
+ | |||
+ | ==Attribute im Vertex Shader== | ||
+ | |||
+ | Folgende Attribute stehen nur im Vertex Shader zur Verfügung und '''können nur gelesen werden''' : | ||
+ | |||
+ | * vec4 gl_Color | ||
+ | : Farbwert des Vertex. | ||
+ | * vec4 gl_SecondaryColor | ||
+ | :Sekundärer Farbwert des Vertex. | ||
+ | * vec4 gl_Normal | ||
+ | :Normale des Vertex. | ||
+ | * vec4 gl_Vertex | ||
+ | :Koordinaten des Vertex; | ||
+ | * vec4 gl_MultiTexCoord0..7 | ||
+ | :Texturkoordinaten auf Textureinheit 0..7. | ||
+ | * float gl_FogCoord | ||
+ | :Nebelkoordinate des Vertex. | ||
+ | |||
+ | |||
+ | ==Variablen im Fragment Shader== | ||
+ | |||
+ | Im Fragment Shader sind folgende Variablen exklusiv nutzbar : | ||
+ | |||
+ | * vec4 gl_FragColor | ||
+ | : Speichert den Farbwert des Fragmentes, der von folgenden Funktionen der festen Pipeline genutzt wird. Wird dieser Variable nichts zugewiesen, so ist ihr Inhalt undefiniert und darauf aufbauende Ergebnisse ebenfalls. | ||
+ | |||
+ | * float gl_FragDepth | ||
+ | : Durch schreiben dieser Variable kann man den von der festen Funktionspipeline ermittelten Tiefenwert überspringen, der mit {{INLINE_CODE|gl_FragCoord.z}} ausgelesen werden kann. Wird dieser Wert nicht geschrieben, nutzen folgende Funktionen der Pipeline den vorher fest berechneten Wert. | ||
+ | |||
+ | * vec4 gl_FragCoord nur lesen | ||
+ | : In dieser Variable ist die Position des Fragmentes relativ zur Fensterposition im Format x,y,z,1/w abgelegt, wobei z den von der festen Funktionspipeline berechneten Tiefenwert enthält. | ||
+ | |||
+ | * bool gl_FrontFacing nur lesen | ||
+ | : Gibt an ob das Fragment zu einer nach vorne zeigenden Primitive gehört (=true). | ||
+ | |||
+ | |||
+ | Im Bezug auf {{INLINE_CODE|gl_FragColor}} und {{INLINE_CODE|gl_FragDepth}} sei noch anzumerken das diese ''nicht'' in den Wertebereich 0..1 gebracht werden müssen, da dies später durch die feste Funktionspipeline automatisch gemacht wird. | ||
+ | |||
+ | |||
+ | ==Eingebaute Varyings== | ||
+ | |||
+ | Wie bereits in Kapitel 4.2 erwähnt, stellen Varyings eine Schnittstelle zwischen dem Vertex und dem Fragment Shader dar. Sie werden im Vertex Shader geschrieben und können dann im Fragment Shader ausgelesen werden, ohne das die folgenden Varyings dafür explizit deklariert werden müssen : | ||
+ | |||
+ | * vec4 gl_FrontColor | ||
+ | : Farbe der Vorderseite des Vertex. | ||
+ | |||
+ | * vec4 gl_BackColor | ||
+ | : Farbe der Rückseite des Vertex. | ||
+ | |||
+ | * vec4 gl_FrontSecondaryColor | ||
+ | : Sekundäre Farbe der Vorderseite des Vertex. | ||
+ | |||
+ | * vec4 gl_BackSecondaryColor | ||
+ | : Sekundäre Farbe der Rückseite des Vertex. | ||
+ | |||
+ | * vec4 gl_TexCoord[x] | ||
+ | : Texturkoordinaten des Vertex auf Textureinheit x, wobei x die von der Hardware zur Verfügung gestellte Zahl der Textureinheiten-1 nicht überschreiten darf. | ||
+ | |||
+ | * float gl_FogFragCoord | ||
+ | : Nebelkoordinate des Fragmentes. | ||
+ | |||
+ | Die Varyings {{INLINE_CODE|gl_FrontColor, gl_FrontSecondaryColor, gl_BackColor}} und {{INLINE_CODE|gl_BackSecondaryColor}} können im FragmentShader nur unter den Aliases gl_Color bzw. gl_SecondaryColor gelesen werden. Welcher Wert des Vertex Shaders im Fragment Shader dort eingesetzt wird ist abhängig davon ob das Fragment zu einer nach vorne oder nach hinten zeigenden Primitive gehört. | ||
+ | |||
+ | |||
+ | ==Eingebaute Konstanten== | ||
+ | Auch diverse Konstanten wurden definiert um darauf schnell im Shader zugreifen zu können. In den Klammern stehen die von einer GL-Implementation als Mindestanforderung anzubietenden Werte. Alle Konstanten sind sowohl im Vertex als auch im Fragment Shader abrufbar : | ||
+ | |||
+ | : OpenGL 1.0/1.2 : | ||
+ | * int gl_MaxLights (8) | ||
+ | * int gl_MaxClipPlanes (6) | ||
+ | * int gl_MaxTextureUnits (2) | ||
+ | |||
+ | |||
+ | : ARB_Fragment_Program : | ||
+ | * int gl_MaxTextureCoordsARB (2) | ||
+ | |||
+ | |||
+ | : Vertex_Shader : | ||
+ | * int gl_MaxVertexAttributesGL2 (16) | ||
+ | * int gl_MaxVertexUniformFloatsGL2 (512) | ||
+ | * int gl_MaxVaryingFloatsGL2 (32) | ||
+ | * int gl_MaxVertexTextureUnitsGL2 (1) | ||
+ | |||
+ | |||
+ | : Fragment_Shader : | ||
+ | * int gl_MaxFragmentTextureUnitsGL2 (2) | ||
+ | * int gl_MaxFragmentUniformFloatsGL2 (64) | ||
+ | |||
+ | |||
+ | ==Eingebaute Uniformvariablen== | ||
+ | |||
+ | Um den Zugriff auf OpenGL-Staten zu vereinfachen wurden in glSlang diverse Uniformvariablen zur direkten Verwendung im Shader eingebaut. Wie gewohnt wurden auch hier sinnvolle Namen verwendet, so dass eine tiefere Erklärung unnötig sein dürfte : | ||
+ | |||
+ | * mat4 gl_ModelViewMatrix | ||
+ | * mat4 gl_ProjectionMatrix | ||
+ | * mat4 gl_ModelViewProjectionMatrix | ||
+ | * mat3 gl_NormalMatrix | ||
+ | * mat4 gl_TextureMatrix[gl_MaxTextureCoordsARB] | ||
+ | :{{INLINE_CODE|gl_NormalMatrix}} repräsentiert die inversen oberen 3x3 Werte der Modelansichtsmatrix. {{INLINE_CODE|gl_TextureMatrix[x]}} adressiert maximal Anzahl Textureinheiten-1-Texturmatrizen. | ||
+ | |||
+ | * float gl_NormalScale | ||
+ | : Gibt den unter OpenGL festgelegten Faktor zur Skalierung der Normalen zurück. | ||
+ | |||
+ | * struct gl_DepthRangeParameters | ||
+ | |||
+ | struct gl_DepthRangeParameters | ||
+ | { | ||
+ | float near; | ||
+ | float far; | ||
+ | float diff; | ||
+ | }; | ||
+ | gl_DepthRangeParameters gl_DepthRange; | ||
+ | |||
+ | : Clippingplanes : | ||
+ | * vec4 gl_ClipPlane[gl_MaxClipPlanes] | ||
+ | |||
+ | *struct gl_PointParameters | ||
+ | struct gl_PointParameters | ||
+ | { | ||
+ | float size; | ||
+ | float sizeMin; | ||
+ | float sizeMax; | ||
+ | float fadeThresholdSize; | ||
+ | float distanceConstantAttenuation; | ||
+ | float distanceLinearAttenuation; | ||
+ | float distanceQuadraticAttenuation; | ||
+ | }; | ||
+ | gl_PointParameters gl_Point; | ||
+ | |||
+ | *struct gl_MaterialParameters | ||
+ | struct gl_MaterialParameters | ||
+ | { | ||
+ | vec4 emission; | ||
+ | vec4 ambient; | ||
+ | vec4 diffuse; | ||
+ | vec4 specular; | ||
+ | float shininess; | ||
+ | }; | ||
+ | gl_MaterialParameters gl_FrontMaterial; | ||
+ | gl_MaterialParameters gl_BackMaterial; | ||
+ | |||
+ | *struct gl_LightSourceParameters | ||
+ | struct gl_LightSourceParameters | ||
+ | { | ||
+ | vec4 ambient; | ||
+ | vec4 diffuse; | ||
+ | vec4 specular; | ||
+ | vec4 position; | ||
+ | vec4 halfVector; | ||
+ | vec3 spotDirection; | ||
+ | float spotExponent; | ||
+ | float spotCutoff; | ||
+ | float spotCosCutoff; | ||
+ | float constantAttenuation; | ||
+ | float linearAttenuation; | ||
+ | float quadraticAttenuation; | ||
+ | }; | ||
+ | gl_LightSourceParameters gl_LightSource[gl_MaxLights]; | ||
+ | |||
+ | *struct gl_LightModelParameters | ||
+ | struct gl_LightModelParameters | ||
+ | { | ||
+ | vec4 ambient; | ||
+ | }; | ||
+ | gl_LightModelParameters gl_LightModel; | ||
+ | |||
+ | *struct gl_LightModelProducts | ||
+ | struct gl_LightModelProducts | ||
+ | { | ||
+ | vec4 sceneColor; | ||
+ | }; | ||
+ | gl_LightModelProducts gl_FrontLightModelProduct; | ||
+ | gl_LightModelProducts gl_BackLightModelProduct; | ||
+ | |||
+ | *struct gl_LightProducts | ||
+ | struct gl_LightProducts | ||
+ | { | ||
+ | vec4 ambient; | ||
+ | vec4 diffuse; | ||
+ | vec4 specular; | ||
+ | }; | ||
+ | gl_LightProducts gl_FrontLightProduct[gl_MaxLights]; | ||
+ | gl_LightProducts gl_BackLightProduct[gl_MaxLights]; | ||
+ | |||
+ | * vec4 gl_TextureEnvColor[gl_MaxFragmentTextureUnitsGL2] | ||
+ | * vec4 gl_EyePlaneS[gl_MaxTextureCoordsARB] | ||
+ | * vec4 gl_EyePlaneT[gl_MaxTextureCoordsARB] | ||
+ | * vec4 gl_EyePlaneR[gl_MaxTextureCoordsARB] | ||
+ | * vec4 gl_EyePlaneQ[gl_MaxTextureCoordsARB] | ||
+ | * vec4 gl_ObjectPlaneS[gl_MaxTextureCoordsARB] | ||
+ | * vec4 gl_ObjectPlaneT[gl_MaxTextureCoordsARB] | ||
+ | * vec4 gl_ObjectPlaneR[gl_MaxTextureCoordsARB] | ||
+ | * vec4 gl_ObjectPlaneQ[gl_MaxTextureCoordsARB] | ||
+ | |||
+ | *struct gl_FogParameters | ||
+ | struct gl_FogParameters | ||
+ | { | ||
+ | vec4 color; | ||
+ | float density; | ||
+ | float start; | ||
+ | float end; | ||
+ | float scale; | ||
+ | }; | ||
+ | gl_FogParameters gl_Fog; | ||
+ | |||
+ | Diese recht umfangreiche GL-Stateliste sollte eigentlich jeden Bedarf decken und momentan gibts kaum einen OpenGL-Status den man so nicht in einem Shader abfragen bzw. nutzen kann. | ||
+ | |||
+ | |||
+ | =Eingebaute Funktionen= | ||
Version vom 22. August 2005, 15:11 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.
Die Shadersprache
Nachdem wir uns mit der Einbindung der glSlang-Shader in unser Programm beschäftigt haben, wollen wir uns in den folgenden Kapiteln um die Sprachelemente von glSlang kümmern. Wie schon gesagt basiert glSlang auf ANSI-C, wurde allerdings um speziell auf den Zielbereich angepasste Vektor- und Matrixtypen und einige C++-Features wie das freie deklarieren von Variablen an jeder Stelle und das Funktionsüberladen auf Basis des Argumenttyps erweitert. Wer sich ein wenig mit C/C++ auskennt sollte also in der nun folgenden Materie keine Probleme bekommen.
Obligatorische Hinweise für verwöhnte Delphi-Nutzer :
- Wie von C/C++ her gewohnt, spielt auch in glSlang die Groß- und Kleinschreibung eine wichtige Rolle, also bitte achtet darauf. gl_Position ist eine komplett andere Variable als z.B. gl_position.
- Es findet keine automatische Typenkonvertierung statt. Das bedeutet also das float MyFloat = 1 ungültig ist und es in dem Falle float MyFloat = 1.0 heissen muss. Typecasts müssen also immer manuell stattfinden, z.B. MyFloat = float(MyInt).
Kleine Programmstrukturkunde für C-Unkundige :
Da sicherlich einige Delpher nie richtig was mit C gemacht haben, zeige ich mal anhand eines kleinen Beispieles (das auf keinen Fall nen brauchbaren Shader darstellt) den grundlegenden Aufbau eines glSlang-Shaders, der natürlich dem Aufbau eines C-Programmes stark ähnelt :
uniform vec4 VariableA; float VariableB; vec3 VariableC; const float KonstanteA = 256.0; float MyFunction(vec4 ArgumentA) { float FunktionsVariableA = float(5.0); return float(ArgumentA * (FunktionsVariableA + KonstanteA)); } // Ich bin ein Kommentar /* Und ich auch */ void main(void) { gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; gl_TexCoord[0] = gl_MultiTexCoord0; }
Sieht doch recht bekannt aus, unser Programmaufbau. Delphi und C haben ja so einige Grundlagen gleich, darunter auch der ungefähre Programmaufbau. Ausserhalb jeglicher Funktionen legen wir am Programmanfang unsere Variablen, Konstanten und Attribute fest, die dann global nutzbar sind, also in jeder Funktion.
Darunter deklarieren wir dann eine kleine Funktion. Wie auch bei den Variablendeklarationen wird hier der Rückgabetyp nicht wie bei Pascal nach dem Funktionsnamen untergebracht, sondern davor. Innerhalb der Funktion können dann wieder Variablen deklariert werden, die dann allerdings lokal, also nur in dieser Funktion nutzbar sind. Vorteil dieser Deklaration ist die Tatsache, dass je nach Grafikkarte nur bestimmt viele globale Variablen deklariert werden können. Wenn möglich sollte man also mit lokalen Vorlieb nehmen. Unsere Funktion gibt dann natürlich noch via return einen Wert zurück, was gemacht werden muss, sofern man diese nicht als void deklariert hat (entspräche dann einer Prozedur in Pascal). Wird dies nicht getan, so spuckt Compiler einen Fehler aus.
Auch wichtig sind natürlich Kommentare. Erste Variante (Doppelslash) ist auch in der Pascalwelt verfügbar und kommentiert eine einzelne Zeile aus. Die Variante darunter kann man für Kommentarblöcke nutzen (/* .. */) und entspricht den Kommentaren in geschweiften Klammern in Delphi.
Danach kommt dann die wichtigste Funktion des Shaders, nämlich main, die in keinem Shader fehlen darf. Sie stellt quasi den Programmkörper dar und ist oft auch die einzige Funktion in einem Shader. Sie erhält weder ein Argument, noch gibt sie einen Wert zurück.
Soviel also zum grundlegenden Aufbau eines Shader. Hoffe das jetzt alle die in C nicht so bewandert sind damit klar kommen, und dann bald ihre ersten glSlang-Shader schreiben können.
Datentypen
Obwohl einige Datentypen aus C übernommen wurden, sieht man der Typenliste an, das diese speziell auf den 3D-Bereich zugeschnitten wurde. Variablen müssen vor ihrer Nutzung eindeutig deklariert sein, Typecasting erfolgt über Konstruktoren (dazu später mehr). Folgende Datentypen stehen sowohl im Vertex- als auch Fragmentshader zur Verfügung :
Datentyp | Erklärung |
---|---|
void | Für Funktionen die keinen Wert zurückgeben |
bool | Konditionaler Typ, entweder true (wahr) oder false (falsch) |
int | Vorzeichenbehafteter Integerwert |
float | Fließkommaskalar mit Singlegenauigkeit (32 Bit) |
vec2 | 2-Komponenten Fließkommavektor |
vec3 | 3-Komponenten Fließkommavektor |
vec4 | 4-Komponenten Fließkommavektor |
bvec2 | 2-Komponenten Booleanvektor |
bvec3 | 3-Komponenten Booleanvektor |
bvec4 | 4-Komponenten Booleanvektor |
ivec2 | 2-Komponenten Integervektor |
ivec3 | 3-Komponenten Integervektor |
ivec4 | 4-Komponenten Integervektor |
mat2 | 2x2 Fließkommamatrix |
mat3 | 3x3 Fließkommamatrix |
mat4 | 4x4 Fließkommamatrix |
sampler1D | Zugriff auf 1D-Textur |
sampler2D | Zugriff auf 2D-Textur |
sampler3D | Zugriff auf 3D-Textur |
samplerCube | Zugriff auf Cubemap |
sampler1DShadow | Zugriff auf 1D-Tiefentextur mit Vergleichsoperation |
sampler2DShadow | Zugriff auf 2D-Tiefentextur mit Vergleichsoperation |
Die sampler-Typen stellen eine besondere Klasse dar und werden im Kapitel 6.7 genauer erklärt, inklusive einiger Anwendungsbeispiele.
Arrays
Natürlich unterstützt glSlang auch Arrays, die wie in C deklariert werden und deren Index bei 0 beginnt. Folgendes Array im Shader :
float temp[3];
beginnt also bei Index 0 und endet bei Index 2. Im Gegensatz zu C lassen sich Arrays in glSlang allerdings nicht bei der Initialisierung vorbelegen. Wenn ein Array als Parameter einer Funktion deklariert wird, so darf dieses keine Dimensionierung erhalten.
Strukturen
Neu ggü. ARB_FP/VP ist nun auch die Möglichkeit Strukturen in einem Shader zu deklarieren. Vor allem die Übersicht komplexerer Shader kann dadurch stark verbessert werden. Strukturen werden wie gewohnt mit dem Schlüsselwort struct eingeleitet und können dann zur Typisierung von Variablen genutzt werden. Folgendes Beispiel dürfte die Nutzung verdeutlichen :
struct light { bool active; float intensity; vec3 position; vec3 color; };
Im Shader können dann neue Variablen vom diesem Typ ganz einfach deklariert werden :
light LightSource[3];
Der Zugriff auf die Elemente der Struktur erfolgt dann wie gewohnt über den Punkt :
LightSource[3].position = vec3(1.0, 1.0, 5.0);
Typenqualifzierer
Zusätzlich zur Typendeklaration kann eine Variable noch einen Typenqualifizerer vorangestellt bekommen, der an den Anfang der Deklaration gehört. Di
- const
- Festgelegte (nur lesen) Konstante bzw. nur lesbarer Funktionsparameter.
- uniform
- Ein den ganzen Shader über gleichbleibender Wert, der eine Schnittstelle zwischen dem Shader und der OpenGL-Anwendung darstellt. Ein Uniformwert wird in der Hauptanwendung an den entsprechenden Shader übergeben und kann dort dann genutzt werden.
- attribute
- Nur lesbare Werte die eine Verbindung zwischen dem Shader und der OpenGL-VertexAPI darstellen (z.B. VertexParameter eines VertexArrays). Natürlich nur in einem Vertex Shader nutzbar.
- varying
- Stellt die Verbindung zwischen einem Vertex- und einem FragmentShader dar. Werden im VertexShader geschrieben und dann perspektivisch korrekt über die Primitive interpoliert, um dann im Fragment Shader gelesen werden zu können. Nutzbar sind hier nur die Typen float, vec2, vec3, vec4, mat2, mat3, und mat4, Strukturen und andere Datentypen können nicht varying sein. Die Namen einer varying-Variable müssen sowohl im VertexShader als auch im FragmentShader gleich sein.
- in
- Für Variablen die an eine Funktion übergeben und dort ausgelesen werden.
- out
- Für Variablen die von einer Funktion nach aussen zurückgegeben werden.
- inout
- Für Variablen die sowohl an eine Funktion übergeben als auch von dieser zurückgegeben werden.
Um obige Auflistung nicht leer im Raum stehen zu lassen zeige ich ein paar Beispiele die hoffentlich zum Verständnis beitragen :
Beispiel A : Vertexnormale soll an einen FragmenShader (interpoliert) übergeben werden :
- Im VertexShader :
varying vec3 VertexNormal; ... VertexNormal = normalize(MV_IT * gl_Normal);
- Im FragmentShader :
varying vec3 VertexNormal; ... TempVector = VertexNormal*...
Beispiel B : Uniformparameter zur nachträglichen Farbänderung der Szene wird im Programm übergeben :
- Im VertexShader :
uniform vec4 GlobalColor; ... gl_FrontColor = GlobalColor * gl_Color;
- Im Programm :
glUniform4fARB(glSlang_GetUniLoc(ProgramObject, 'GlobalColor'), Col[0], Col[1], Col[2], Col[3]);
Beispiel C : Konstante zur festen Farbänderung :
- Im VertexShader :
const vec4 ColorBias = vec4(0.2, 0.3, 0.0, 0.0); ... gl_FrontColor = ColorBias * gl_Color;
Konstruktoren
Um in einem Shader Vektoren oder Matrizen mit Werten zu belegen, gibt es sogenannte Konstruktoren (nicht zu verwechseln mit z.B. Klassenkonstruktoren unter Delphi), die im Endeffekt nichts anderes als Funktionen zur Vorbelegung von Vektoren oder Matrizen darstellen. Dabei trägt der Konstruktor den selben Namen wie die Typendeklaration, also lässt sich eine Variable vom Typ vec4 mit dem Konstruktor vec4(float, float, float, float) initialisieren.
Allerdings hat man sich recht viel Mühe bei dieser Konstruktorgeschichte gemacht, so dass man einen vec4 nicht unbedingt mit einem vec4-Konstruktor vorbelegen muss, sondern es vielseitige Möglichkeiten gibt. Um dies zu verdeutlichen gibts ein paar Beispiele :
vec4 Color = vec4(1.0, 0.0, 0.0, 0.0); vec4 Color = vec4(MyVec3, 1.0); vec4 Color = vec4(MyVec2_A, MyVec2_B); vec3 LVec = vec3(MyVec4); vec2 Tmp = vec2(MyVec3);
Trotz der recht wenigen Beispiele sollte schnell erkennbar sein, das man hier wirklich sehr viele Kombinationsmöglichkeiten hat, die dann gültig sind wenn man mindestens auf die benötigte Anzahl der Argumente kommt. Im vorletzten Beispiel wird z.B. ein 3-Komponentenvektor aus einem 4-Komponentenvektor initialisiert. Das erzeugt keinen Fehler, sondern führt dazu das vec3.x, vec3.y, vec3.z aus MyVec4 übernommen werden und MyVec4.w einfach ignoriert wird.
Das Umkehrbeispiel, also
vec4 Color = vec4(MyVec3)
funktioniert allerdings nicht, da hier die Zahl der benötigten Argumente nicht erreicht wird. In diesem Falle müsste es dann
vec4 Color = vec4(MyVec3, 0.0)
heissen.
Obiges gilt natürlich auch für Matrixkonstruktoren, hier sind z.B. folgende Konstuktoren denkbar, obwohl eigentlich alle Möglichkeiten nutzbar sind, solange die benötigte Zahl an Argumenten erreicht wird :
mat4 MyMatrix = mat4(MyVec4, MyVec4, MyVec4, MyVec4); mat2 MyMatrix = mat4(1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0);
Vektor- und Matrixkomponenten
Was natürlich in keiner Shadersprache fehlen darf, ist der leichte Zugriff auf die einzelnen Komponenten eines Vektors. glSlang bietet, je nach Anwendungsgebiet gleich drei Namensets für den Zugriff auf die Komponenten eines solchen Vektors, welches Set man nutzen will bleibt natürlich frei und ist unabhängig von der Deklaration eines Vektors. Man sollte nur darauf achten, beim gleichzeitigen Zugriff auf mehrere Komponenten im gleichen Namenset zu verbleiben :
- {x, y, z, w}
- Für den Zugriff auf Vektoren die Punkte, Normale oder sonstige Vertexdaten repräsentieren.
- {r, g, b, a}
- Für den Zugriff auf Vektoren die Farbwerte repräsentieren.
- {s, t, p, q}
- Für den Zugriff auf Vektoren die Texturkoordinaten repräsentieren.
Ein paar Beispiele zur Unterstreichung des oben gesagten :
v4.rgba = vec4(1.0, 0.0, 0.0, 0.0); // gültig v4.rgzw = vec4(1.0, 1.0, 1.0, 2.0); // Ungültig, da verschiedenen Namensets v2.rgb = vec3(1.0, 2.0, 1.0); // Ungültig, da vec2 nur r+g besitzt v2.xx = vec2(5.0, 3.0); // Ungültig, da 2 mal gleiche Komponente
Auch der Zugriff auf die Komponenten einer Matrix geht leicht von der Hand. Namensets wie bei den Vektoren gibt es hier natürlich keine, aber folgende Beispiele sollen den Zugriff aufzeigen :
MyMat4[2] = vec4(1.0); // Setzt die 3.Zeile der Matrix komplett auf 1.0 MyMat4[3][3] = 3.5; // Setzt das Element unren rechts auf 3.5
Ein Zugriff auf Matrixelemente ausserhalb ihrer Dimension (also z.B. MyMat4[4][4]) liefert unvorhersehabre Ergebnise, also sollte man auf diese Fälle prüfen.
Vektor- und Matrixoperationen
Wie von C gewohnt sind in glSlang so ziemlich alle Operatoren die man auf Matrizen oder Vektoren anwenden kann überladen, so das man nicht umständlich über selbstgeschriebene Funktionen kombinieren muss. Darüberhinaus ist es in den meisten Fällen auch möglich ohne Konvertierung Fließkommawerte mit kompletten Matrizen oder Vektoren zu kombinieren. Folgende Beispiele zeigen einige der vielfältigen Kombinationsmöglichkeiten auf :
vec3 dest; vec3 source; float factor; vec3 dest = source + factor; // Ist gleich dest.x = source.x + factor; dest.y = source.y + factor; dest.z = source.z + factor;
Matrix * Vektor ist auch ohne manuelle Konvertierung möglich :
vec3 dest; vec3 source; mat3 MyMat; dest = source * MyMat; // Ist gleich dest.x = dot(source, MyMat[0]); dest.y = dot(source, MyMat[1]); dest.z = dot(source, MyMat[2]);
Auch hier sind die Möglichkeiten fast unbeschränkt und zeigen wieder wie flexibel glSlang ausgelegt ist.
Operatoren
glSlang bietet (momentan) folgende Operatoren, die Liste ist nach ihrer Gewichtung sortiert (Anfang = höchste). Alle reservierten Operatoren werden erst in kommender Hardware/glSlang-Versionen nutzbar sein :
Operatorklasse | Operatoren | Assoziation |
---|---|---|
Gruppering | () | - |
Arrayindizierung Funktionsaufrufe und Konstruktoren Strukturfeldwahl und Swizzle Postinkrement und -dekrement |
[] () . ++ -- |
Links n. Rechts |
Prefixinkrement- und dekrement Einheitlich (~ reserviert) |
++ -- + - ~ ! |
Rechts n. Links |
Mulitplikation (% reserviert) | * / % | Links n. Rechts |
Additiv | + - | Links n. Rechts |
Bitweises Verschieben (reserviert) | << >> | Links n. Rechts |
Relation | < > <= >= | Links n. Rechts |
Vergleich | == != | Links n. Rechts |
Bitweises AND (reserviert) | & | Links n. Rechts |
Bitweises XOR (reserviert) | ^ | Links n. Rechts |
Bitweises OR (reserviert) | | | Links n. Rechts |
Logisches AND | && | Links n. Rechts |
Logisches XOR | ^^ | Links n. Rechts |
Logisches OR | || | Links n. Rechts |
Auswahl | ?: | Rechts n. Links |
Zuweisung Arithmetrische Zuweisung (Modulis, Shift und bitweise Op. reserviert) |
= += -= *= /= %= <<= >>= &= ^= |= |
Rechts n. Links |
Aufzählung | , | Links n. Rechts |
Funktionen
Ein großer Vorteil von Hochsprachen ist u.A. die Möglichkeit oft genutzte Codeteile in Funktionen (bzw. auch Prozeduren unter Pascal) zu verpacken um so Flexibilität als auch Übersichtlichkeit zu steigern. Wer schonmal was in C geschrieben hat, der wird sich jetzt sicherlich kein Kopfzerbrechen machen müssen. Funktionen werden in glSlang genauso nach folgendem Prinzip deklariert :
RückgabeTyp FunktionsName(Typ0 Argument0, Typ1, Argument1, ... , TypN, ArgumentN) { return RückgabeWert; }
Funktionen die nichts zurückgeben müssen mit dem RückgabeTyp void deklariert werden, ausserdem entfällt dann logischerweise das return. Falls die Funktion eines ihrere Argumente nach aussen übergeben soll, muss dieses Argument mit dem Typenqualifizierer out (Siehe Kapitel 4.2) versehen werden. Arrays können nur als Eingabeargumente übergeben werden und dürfen nich dimensioniert als Argument verwendet werden, sondern müssen mit leeren Klammern argumentiert werden.
Ein paar Beispiele :
void MeineFunktion(float EingabeWert; out float AusgabeWert) { AusgabeWert = EingabeWert*MyConstValue; }
Diese Funktion gibt nichts zurück, aber gibt EingabeWert*MyConstValue im Ausgabeargument AusgabeWert nach aussen.
float MeineFunktion(float EingabeWert) { return EingabeWert*MyConstValue; }
Bietet genau die selbe Funktionalität wie das Beispiel darüber. Allerdings wird hier der berechnete Wert als Ergebnis der Funktion zurückgeliefert.
float VektorSumme(float v[]) { return v[0]+v[1]+v[2]+v[3]; }
Wie bereits gesagt darf ein Array als Argument keine Dimensionierung enthalten. Wenn man der Funktion also ein Array übergibt, sollte man vorher drauf achten das es entsprechend der in der Funktion genutzten Indizes dimensioniert wurde.
if-Anweisung
Selektion über eine if-Anweisung darf auch in keiner Hochsprache fehlen. Genauso wie in C oder Delphi erwartet auch hier die If-Anweisung einen boolschen Ausdruck (Wahr oder Falsch) und wird dann ausgeführt (wahr) bzw. verzweigt auf ein (wenn vorhanden) else (falsch). Verschachtelung ist wie erwartet auch möglich.
Schleifen
Auch Schleifen, ein wichtiges Konzept jeder Hochsprache haben ihren Weg in glSlang gefunden. Unterstützt werden folgende Schleifentypen :
- for-Schleife
for (Startausdruck; Durchlaufbedingung; Wiederholungsausdruck;) { statement }
- while-Schleife
while (Durchlaufbedingung) { statement }
- do-while-Schleife
do { statement } while (Durchlaufbedingung)
Eingebaute Variablen, Attribute und Konstanten
Nachdem wir uns nun lange genug mit den minderinterssanten Elementen der glSlang-Syntax beschäftigt haben, gehts jetzt endlich an die wirklich interessanten Dinge. Wie schon ARB_VP/ARB_FP bringt auch glSlang jede Menge eingabauter Variablen, Attribute und Konstanten mit, deren Aliase sie recht leicht identifizierbar machen (ganz im Gegensatz zum Indexgewusel bei den DX-Shadern).
Variablen im Vertex Shader
Exklusiv im Vertex Shader stehen die folgenden Variablen zur Verfügung :
- vec4 gl_Position muss geschrieben werden
- Dieser Variable muss im Vertexshader ein Wert zugewiesen werden, wird dies nicht getan ist das Ergebnis (sprich die Position des Vertex) undefiniert. Vorgesehen ist diese Variable für die homogene Position des Vertex und wird u.a. zum Clipping und Culling verwendet. Sie darf natürlich auch (mehrfaceh) geschrieben und ausgelesen werden.
- float gl_PointSize kann geschrieben werden
- Diese Variable wurde dazu vorgesehen um dort im VertexShader die Punktgröße in Pixeln hineinzuschreiben.
- vec4 gl_ClipVertex kann geschrieben werden
- Falls genutzt sollten hier die Vertexkoordinaten die im Zusammenhang mit benutzerdefinierten Clippingplanes genutzt werden abgelegt werden. Wichtig ist, das gl_ClipVertex im selben Koordinatenraum wie die Clippingplane definiert ist.
Attribute im Vertex Shader
Folgende Attribute stehen nur im Vertex Shader zur Verfügung und können nur gelesen werden :
- vec4 gl_Color
- Farbwert des Vertex.
- vec4 gl_SecondaryColor
- Sekundärer Farbwert des Vertex.
- vec4 gl_Normal
- Normale des Vertex.
- vec4 gl_Vertex
- Koordinaten des Vertex;
- vec4 gl_MultiTexCoord0..7
- Texturkoordinaten auf Textureinheit 0..7.
- float gl_FogCoord
- Nebelkoordinate des Vertex.
Variablen im Fragment Shader
Im Fragment Shader sind folgende Variablen exklusiv nutzbar :
- vec4 gl_FragColor
- Speichert den Farbwert des Fragmentes, der von folgenden Funktionen der festen Pipeline genutzt wird. Wird dieser Variable nichts zugewiesen, so ist ihr Inhalt undefiniert und darauf aufbauende Ergebnisse ebenfalls.
- float gl_FragDepth
- Durch schreiben dieser Variable kann man den von der festen Funktionspipeline ermittelten Tiefenwert überspringen, der mit gl_FragCoord.z ausgelesen werden kann. Wird dieser Wert nicht geschrieben, nutzen folgende Funktionen der Pipeline den vorher fest berechneten Wert.
- vec4 gl_FragCoord nur lesen
- In dieser Variable ist die Position des Fragmentes relativ zur Fensterposition im Format x,y,z,1/w abgelegt, wobei z den von der festen Funktionspipeline berechneten Tiefenwert enthält.
- bool gl_FrontFacing nur lesen
- Gibt an ob das Fragment zu einer nach vorne zeigenden Primitive gehört (=true).
Im Bezug auf gl_FragColor und gl_FragDepth sei noch anzumerken das diese nicht in den Wertebereich 0..1 gebracht werden müssen, da dies später durch die feste Funktionspipeline automatisch gemacht wird.
Eingebaute Varyings
Wie bereits in Kapitel 4.2 erwähnt, stellen Varyings eine Schnittstelle zwischen dem Vertex und dem Fragment Shader dar. Sie werden im Vertex Shader geschrieben und können dann im Fragment Shader ausgelesen werden, ohne das die folgenden Varyings dafür explizit deklariert werden müssen :
- vec4 gl_FrontColor
- Farbe der Vorderseite des Vertex.
- vec4 gl_BackColor
- Farbe der Rückseite des Vertex.
- vec4 gl_FrontSecondaryColor
- Sekundäre Farbe der Vorderseite des Vertex.
- vec4 gl_BackSecondaryColor
- Sekundäre Farbe der Rückseite des Vertex.
- vec4 gl_TexCoord[x]
- Texturkoordinaten des Vertex auf Textureinheit x, wobei x die von der Hardware zur Verfügung gestellte Zahl der Textureinheiten-1 nicht überschreiten darf.
- float gl_FogFragCoord
- Nebelkoordinate des Fragmentes.
Die Varyings gl_FrontColor, gl_FrontSecondaryColor, gl_BackColor und gl_BackSecondaryColor können im FragmentShader nur unter den Aliases gl_Color bzw. gl_SecondaryColor gelesen werden. Welcher Wert des Vertex Shaders im Fragment Shader dort eingesetzt wird ist abhängig davon ob das Fragment zu einer nach vorne oder nach hinten zeigenden Primitive gehört.
Eingebaute Konstanten
Auch diverse Konstanten wurden definiert um darauf schnell im Shader zugreifen zu können. In den Klammern stehen die von einer GL-Implementation als Mindestanforderung anzubietenden Werte. Alle Konstanten sind sowohl im Vertex als auch im Fragment Shader abrufbar :
- OpenGL 1.0/1.2 :
- int gl_MaxLights (8)
- int gl_MaxClipPlanes (6)
- int gl_MaxTextureUnits (2)
- ARB_Fragment_Program :
- int gl_MaxTextureCoordsARB (2)
- Vertex_Shader :
- int gl_MaxVertexAttributesGL2 (16)
- int gl_MaxVertexUniformFloatsGL2 (512)
- int gl_MaxVaryingFloatsGL2 (32)
- int gl_MaxVertexTextureUnitsGL2 (1)
- Fragment_Shader :
- int gl_MaxFragmentTextureUnitsGL2 (2)
- int gl_MaxFragmentUniformFloatsGL2 (64)
Eingebaute Uniformvariablen
Um den Zugriff auf OpenGL-Staten zu vereinfachen wurden in glSlang diverse Uniformvariablen zur direkten Verwendung im Shader eingebaut. Wie gewohnt wurden auch hier sinnvolle Namen verwendet, so dass eine tiefere Erklärung unnötig sein dürfte :
- mat4 gl_ModelViewMatrix
- mat4 gl_ProjectionMatrix
- mat4 gl_ModelViewProjectionMatrix
- mat3 gl_NormalMatrix
- mat4 gl_TextureMatrix[gl_MaxTextureCoordsARB]
- gl_NormalMatrix repräsentiert die inversen oberen 3x3 Werte der Modelansichtsmatrix. gl_TextureMatrix[x] adressiert maximal Anzahl Textureinheiten-1-Texturmatrizen.
- float gl_NormalScale
- Gibt den unter OpenGL festgelegten Faktor zur Skalierung der Normalen zurück.
- struct gl_DepthRangeParameters
struct gl_DepthRangeParameters { float near; float far; float diff; }; gl_DepthRangeParameters gl_DepthRange;
- Clippingplanes :
- vec4 gl_ClipPlane[gl_MaxClipPlanes]
- struct gl_PointParameters
struct gl_PointParameters { float size; float sizeMin; float sizeMax; float fadeThresholdSize; float distanceConstantAttenuation; float distanceLinearAttenuation; float distanceQuadraticAttenuation; }; gl_PointParameters gl_Point;
- struct gl_MaterialParameters
struct gl_MaterialParameters { vec4 emission; vec4 ambient; vec4 diffuse; vec4 specular; float shininess; }; gl_MaterialParameters gl_FrontMaterial; gl_MaterialParameters gl_BackMaterial;
- struct gl_LightSourceParameters
struct gl_LightSourceParameters { vec4 ambient; vec4 diffuse; vec4 specular; vec4 position; vec4 halfVector; vec3 spotDirection; float spotExponent; float spotCutoff; float spotCosCutoff; float constantAttenuation; float linearAttenuation; float quadraticAttenuation; }; gl_LightSourceParameters gl_LightSource[gl_MaxLights];
- struct gl_LightModelParameters
struct gl_LightModelParameters { vec4 ambient; }; gl_LightModelParameters gl_LightModel;
- struct gl_LightModelProducts
struct gl_LightModelProducts { vec4 sceneColor; }; gl_LightModelProducts gl_FrontLightModelProduct; gl_LightModelProducts gl_BackLightModelProduct;
- struct gl_LightProducts
struct gl_LightProducts { vec4 ambient; vec4 diffuse; vec4 specular; }; gl_LightProducts gl_FrontLightProduct[gl_MaxLights]; gl_LightProducts gl_BackLightProduct[gl_MaxLights];
- vec4 gl_TextureEnvColor[gl_MaxFragmentTextureUnitsGL2]
- vec4 gl_EyePlaneS[gl_MaxTextureCoordsARB]
- vec4 gl_EyePlaneT[gl_MaxTextureCoordsARB]
- vec4 gl_EyePlaneR[gl_MaxTextureCoordsARB]
- vec4 gl_EyePlaneQ[gl_MaxTextureCoordsARB]
- vec4 gl_ObjectPlaneS[gl_MaxTextureCoordsARB]
- vec4 gl_ObjectPlaneT[gl_MaxTextureCoordsARB]
- vec4 gl_ObjectPlaneR[gl_MaxTextureCoordsARB]
- vec4 gl_ObjectPlaneQ[gl_MaxTextureCoordsARB]
- struct gl_FogParameters
struct gl_FogParameters { vec4 color; float density; float start; float end; float scale; }; gl_FogParameters gl_Fog;
Diese recht umfangreiche GL-Stateliste sollte eigentlich jeden Bedarf decken und momentan gibts kaum einen OpenGL-Status den man so nicht in einem Shader abfragen bzw. nutzen kann.
Eingebaute Funktionen
(Mehr Informationen/weitere Artikel) {{{1}}} |
Rest folgt. Derweil kann man das Tutorial auch unter http://www.delphigl.com/script/do_show.php?name=glslang&action=2 nachlesen.
|
||
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. |