<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
		<id>https://wiki.delphigl.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Nevermind10844</id>
		<title>DGL Wiki - Benutzerbeiträge [de]</title>
		<link rel="self" type="application/atom+xml" href="https://wiki.delphigl.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Nevermind10844"/>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php/Spezial:Beitr%C3%A4ge/Nevermind10844"/>
		<updated>2026-04-20T22:42:07Z</updated>
		<subtitle>Benutzerbeiträge</subtitle>
		<generator>MediaWiki 1.27.4</generator>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_Lektion_7&amp;diff=25705</id>
		<title>Tutorial Lektion 7</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_Lektion_7&amp;diff=25705"/>
				<updated>2012-09-19T21:25:42Z</updated>
		
		<summary type="html">&lt;p&gt;Nevermind10844: /* Und es ward Licht… */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Verblendet! =&lt;br /&gt;
&lt;br /&gt;
== Vorwort ==&lt;br /&gt;
&lt;br /&gt;
Nachdem wir nun ein wenig Erfahrung in der Praxis gesammelt haben, wenden wir uns einen anderen Bereich in der OpenGL-Programmierung zu, der uns helfen wird einige wirklich tolle Spezial-Effekte zu realisieren. Die meisten unter Euch werden den Begriff bereits das eine oder andere Mal gehört haben - die Rede ist vom so genannten Blending. Das berühmteste davon wird sicherlich das &amp;quot;Alpha Blending&amp;quot; sein. Wer nun glaubt Blending ist, wenn man sich ne Halogen-Werfer nimmt und damit versucht seinen Nachbarn zu quälen, ist auf dem Holzweg ;)&lt;br /&gt;
&lt;br /&gt;
Blending weist OpenGL dazu an, zu entscheiden was passieren soll, wenn man zwei Objekte übereinander zeichnet. Im Normalfall wird das obere Objekt das untere überdecken, aber vielleicht wollen wir ja auch, dass das obere Objekt transparent ist und man hindurch blicken kann auf das untere? Wer denkt, dass hier bereits Schluss sei mit dem Blending, der ist schief gewickelt, denn hier beginnt erst die Welt des kreativen Geistes! Lasst Euch verzaubern von wunderschönen OpenGL-Effekten und heizt Euren Programmen grafisch ordentlich ein. Ich hoffe ihr habt Verständnis, dass ich nur auf Teilbereiche eingehen kann, denn die Palette von Möglichkeiten ist keine Grenze gesetzt ;)&lt;br /&gt;
&lt;br /&gt;
Ich hoffe dennoch, dass auch das Tutorial gefallen wird und es wenigstens halbwegs verständlich rüberkommt. Auch gibt es für viele Dinge besser Lösungsmöglichkeiten, vieles hängt von der benutzen Textur ab oder aber auch wie viele davon verwendet werden. Um ein ausgiebiges Testen der Funktionen wird man nicht herum kommen. Also ran an die Arbeit und viel Spaß dabei ;)&lt;br /&gt;
&lt;br /&gt;
== Die vorhandene Welt und was darüber liegt ==&lt;br /&gt;
&lt;br /&gt;
=== Was ist den nun blenden? ===&lt;br /&gt;
&lt;br /&gt;
Wichtig beim Blending ist es zu wissen, was genau eigentlich beim rendern geschieht. OpenGL erhält eine Anweisung etwas zu zeichnen. Dieses wird dann in den so genannten &amp;quot;Frame-Buffer&amp;quot; gespeichert, das ist der Buffer,den wir dann auch meistens bei jedem Rendering mit glClear &amp;quot;löschen&amp;quot;. (Wir erinnern uns, führen wir kein glClear aus, sehen wir nach jedem Rendering den alten Inhalt… habe versprochen, dass wir darauf zurück kommen ;) ). Das heißt eigentlich, dass wir bei jedem Rendern eines Objektes immer wieder etwas in den Frame-Buffer schreiben und dann wenn wir denken, dass alles fertig ist diesen Inhalt auf dem Bildschirm projizieren.&lt;br /&gt;
&lt;br /&gt;
Das Blending setzt nun genau an der Stelle an, an dem etwas in den Frame-Buffer geschrieben werden soll. OpenGL hat praktisch bereits ein 3D-Objekt im Frame-Buffer und zeichnet dann das neue 3D Objekt. Nun können wir OpenGL mitteilen, was in einer solchen Situation geschehen soll. Standard gemäß würde das obere Objekt, dass darunter liegende verdecken.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glBlendFunc(source,destination);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit dieser Anweisung können wir dieses Verhalten ändern. Wir geben zwei Faktoren an, den für die Quelle und den für das Ziel. Ich versuche das mal abstrakt zu erklären. Wie bereits gesagt, handelt es sich dabei um Faktoren. Nehmen wir mal an Source (Textur) wäre 1 und Destination (Framebuffer) 0, dann würde OpenGL beim zusammensetzen der Szene, das was bereits im Frame-Buffer ist total vernachlässigen und das neue, was darüber gezeichnet werden soll komplett drauf zeichnen. Je höher der Faktor einer dieser Beiden Werte ist, desto stärke werden die Farben beim Blending berücksichtigt.Das ganze funktioniert natürlich auch nur, wenn wir OpenGL dazu anhalten das Blending zu verwenden…&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glEnable(GL_BLEND);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das ganze klingt sicherlich sehr komplex, (ist es auch *sg*), aber an Hand einiger Beispiele sollte es einem klar werden. Nehmen wir folgende Bilder:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;center&amp;quot;&lt;br /&gt;
|[[Bild:Tutorial_lektion7_rock_tex.jpg]]&lt;br /&gt;
|[[Bild:Tutorial_lektion7_gras_tex.jpg]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese beiden Texturen zeichnen wir jeweils auf einem Quad und zwar exakt aufeinander. Wir zeichnen zunächst die steinige Fläche und erhalten:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_stone.jpg]]&lt;br /&gt;
&lt;br /&gt;
Soweit so gut… nun setzen wir folgenden Blend-Faktor:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glBlendFunc(GL_ONE,GL_ZERO);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und zeichnen die Gras-Textur darüber:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_gras.jpg]]&lt;br /&gt;
&lt;br /&gt;
Hmm… scheint nicht, als ob das Blending wirklich gut funktioniert hat, da wir nur die Textur sehen, die wir als letztes gezeichnet haben. Scheint soweit alles wie bisher zu sein. :-/&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glBlendFunc(GL_ONE,GL_ZERO);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Quatsch! Natürlich hat alles funktioniert wie wir es erwartet haben! Wir haben schließlich OpenGL dazu angewiesen von der neuen Textur &amp;quot;alles&amp;quot; zu nehmen und von dem, was im Frame-Buffer ist &amp;quot;nichts&amp;quot;. Auf neudeutsch, wir haben ihn angewiesen die alte Textur über die andere zu zeichnen. Würden wir hingegen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glBlendFunc(GL_ZERO,GL_ONE);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwenden, so würden wir OpenGL dazu anhalten vom Frame-Buffer &amp;quot;alles&amp;quot; zu übernehmen und von der neuen Textur &amp;quot;nichts&amp;quot;, das würde bedeuten dass wir ins leere Rendern (sehr gute Methode seine Engine zu optimieren… *sg*)&lt;br /&gt;
&lt;br /&gt;
Ach so noch etwas… nehmt das mit dem GL_ZERO und GL_ONE und meinem &amp;quot;nichts und alles&amp;quot; nicht ganz so wörtlich. Es ist mehr eine kleine Eselsbrücke, die einem am Anfang hilft nicht total zu verwirren. Technisch macht OpenGL nichts anderes als bei GL_ONE die Farbe der Texture auf (1,1,1,1) zu setzen bei GL_ZERO auf (0,0,0,0); Und sobald es &amp;quot;schwarz&amp;quot; ist, sieht man sie halt nicht mehr, klingt logisch oder?&lt;br /&gt;
&lt;br /&gt;
=== Claim for the World Order ===&lt;br /&gt;
&lt;br /&gt;
Wie alles hat auch das Blending einen kleinen Schönheitsfehler. Viele Leute denken immer wieder, dass auch beim Blending praktisch eine 3D-Welt existiert, d.h. man hinrendern kann wo man will. Das ist jedoch falsch, da die Quelle ja im Frame-Buffer bereits vorliegt. Praktisch bereits fertig gerendert. Es macht also für das Blending einen enormen Unterschied, in welcher Reihenfolge die Objekte zur Kamera hin gerendert werden.&lt;br /&gt;
&lt;br /&gt;
Würden wir zum Beispiel ein Haus mit einem Fenster render wollen und das Fensterglas mit Hilfe von Blending darstellen und wir würden zunächst das Glas rendern, so würde OpenGL dieses mit dem Hintergrund (vermutlich dem Himmel) blenden und dann anschließend das Haus drum herum zeichnen. Der ahnungslose Programmierer wird dann mit großen Augen vor dem Fenster stehen und sich wundern, wieso er durch die hintere Wand des Hauses ins Freie sehen kann. &lt;br /&gt;
&lt;br /&gt;
Kurz: Auch durchsichtige Objekte sind Objekte. Auch sie schreiben Werte in den [[Tiefenpuffer]]. Deshalb kann man nicht &amp;quot;hinter&amp;quot; durchsichtige Objekte etwas rendern.&lt;br /&gt;
&lt;br /&gt;
Ohne Blending, würde so etwas nicht weiter auffallen, mit Blending muss man Acht geben in welcher Reihenfolge wir die Objekte rendern.&lt;br /&gt;
&lt;br /&gt;
Darum gilt: '''alle undurchsichtigen Objekte rendern, bevor man zum Blending greift.''' Man sollte sich also durchaus Gedanken dazu machen, wie man seine Objekte rendert. Es gibt verschiedene Verfahren seine Objekte so ordnen zu lassen, dass das hier beschrieben Problem nicht auftritt, darauf möchte ich nicht näher eingehen, ihr seid gewarnt worden ;D Eigentlich ist es ein recht logischer Vorgang, allerdings erliegt man doch recht schnell dem kleinen Irrglauben hinter eine Scheibe rendern zu können. ;)&lt;br /&gt;
&lt;br /&gt;
=== Gleichheit für Alles! ===&lt;br /&gt;
&lt;br /&gt;
Das war ja nun auch ein interessanter Ausflug… gelernt haben wir immer noch nichts wirklich Neues, zumindest nichts, was man auch sehen kann. Üben wir uns doch nun einmal richtig ein und versuchen zwei Texturen zu gleichen Maßen zusammen zu blenden, so dass es aussieht, als hätten wir eine Textur. Sicherlich gibt es viele Möglichkeiten dies zu realisieren, allerdings möchte ich an dieser Stelle einen Alpha-Farbwert verwenden. Wie ihr sicherlich wisst, werden Farben meist im RGB-Werten gespeichert, sprich Rot, Grün und Blau! Gesetzt wird dieses mit glColor3f. Ebenfalls gibt es noch die Funktion glColor4f mit der wir einen RGBA-Wert vergeben können. Dieser wird uns vor allem beim Blending nützliche Dienste erweisen.&lt;br /&gt;
&lt;br /&gt;
Weil beide Texturen zu gleichen Prozentsätzen geblendet werden sollen ergibt sich daraus folglich ein Aufruf von &amp;quot;glColor4f(1,1,1,0.5)&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
Wie ihr sehen könnt setzen wir diesmal glBlendFunc(GL_SRC_ALPHA,GL_ONE); ein. Von der Quelle wird der Alpha-Wert als Blend-Faktor genommen, dass Ziel wird übernommen.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_stonegras.jpg]]&lt;br /&gt;
&lt;br /&gt;
Wenn das nicht aussieht wie eine Textur, die wie aus dem gleichen Guss kommt! Würde noch jemand erahnen, dass es sich dabei eigentlich um zwei Texturen handelt? Für alle, interessierten noch einmal kurz eine Tabelle zur Übersicht der Funktionen, die wir bisher eingesetzt haben und wir noch einsetzen werden, sowie der mathematischen Erklärung ihrer Bedeutung:&lt;br /&gt;
&lt;br /&gt;
{| {{Prettytable_B1}}&lt;br /&gt;
! Konstante&lt;br /&gt;
! Auf Parameter anwendbar&lt;br /&gt;
! Berechneter Blend-Faktor&lt;br /&gt;
|-&lt;br /&gt;
| GL_ZERO&lt;br /&gt;
| source or destination&lt;br /&gt;
| (0, 0, 0, 0)&lt;br /&gt;
|-&lt;br /&gt;
| GL_ONE&lt;br /&gt;
| source or destination&lt;br /&gt;
| (1, 1, 1, 1)&lt;br /&gt;
|-&lt;br /&gt;
| GL_DST_COLOR&lt;br /&gt;
| source&lt;br /&gt;
| (Rd, Gd, Bd, Ad)&lt;br /&gt;
|-&lt;br /&gt;
| GL_SRC_COLOR&lt;br /&gt;
| destination&lt;br /&gt;
| (Rs, Gs, Bs, As)&lt;br /&gt;
|-&lt;br /&gt;
| GL_ONE_MINUS_DST_COLOR&lt;br /&gt;
| source&lt;br /&gt;
| (1, 1, 1, 1)-(Rd, Gd, Bd, Ad)&lt;br /&gt;
|-&lt;br /&gt;
| GL_ONE_MINUS_SRC_COLOR&lt;br /&gt;
| destination&lt;br /&gt;
| (1, 1, 1, 1)-(Rs, Gs, Bs, As)&lt;br /&gt;
|-&lt;br /&gt;
| GL_SRC_ALPHA&lt;br /&gt;
| source or destination&lt;br /&gt;
| (As, As, As, As)&lt;br /&gt;
|-&lt;br /&gt;
| GL_ONE_MINUS_SRC_ALPH&lt;br /&gt;
| source or destination&lt;br /&gt;
| (1, 1, 1, 1)-(As, As, As, As)&lt;br /&gt;
|-&lt;br /&gt;
| GL_DST_ALPHA&lt;br /&gt;
| source or destination&lt;br /&gt;
| (Ad, Ad, Ad, Ad)&lt;br /&gt;
|-&lt;br /&gt;
| GL_ONE_MINUS_DST_ALPHA&lt;br /&gt;
| source or destination&lt;br /&gt;
| (1, 1, 1, 1)-(Ad, Ad, Ad, Ad)&lt;br /&gt;
|-&lt;br /&gt;
| GL_SRC_ALPHA_SATURATE&lt;br /&gt;
| source&lt;br /&gt;
| (f, f, f, 1); f=min(As, 1-Ad)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Sieht anfangs etwas verwirrend aus, aber der Mensch kann sich an vieles gewöhnen. Immerhin lest ihr bereits seit einiger Zeit Texte, die mit meinen Rechtschreibfehlern und meiner Sprache angereichert sind ;D&lt;br /&gt;
&lt;br /&gt;
Um sicher zu gehen, dass auch wirklich alles verstanden ist nehmen wir uns noch einmal kurz folgenden Aufruf zu herzen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glBlendFunc(GL_ONE, GL_SRC_COLOR); // Zur Info: GL_SRC_COLOR darf nur auf &lt;br /&gt;
                                     // den Paramter destination angewendet &lt;br /&gt;
                                     // werden, siehe dazu obenstehende Tabelle&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wieder lassen wir den Frame-Buffer wie er ist, verwenden diesmal jedoch keinen Alpha-Wert zu blenden, sondern jeweils die RGB-Werte der Textur. OpenGL macht also praktisch nichts anderes als die Farbwerte zu addieren und anschließend durch zwei zu teilen. Wie man der Tabelle entnehmen kann spielt bei einem solchen Aufruf nur die Destination eine Rolle. Folglich sieht das Ergebnis wie folgt aus:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_stonegras2.jpg]]&lt;br /&gt;
&lt;br /&gt;
Soweit nun zum einfachen Blending. Ihr könnt Euch vorstellen, dass wir damit bereits eine Menge sehr interessanter Effekte erzeugen können, aber nun wollen wir einige davon auch gleich einmal testen und einsetzen ;)&lt;br /&gt;
&lt;br /&gt;
== Blending-Verfahren ==&lt;br /&gt;
&lt;br /&gt;
=== Die Welt unter einer Maske ===&lt;br /&gt;
&lt;br /&gt;
Ein Verfahren, welches sehr häufig zum Einsatz kommt ist das &amp;quot;Masking-Verfahren&amp;quot;. Aus dem Namen kann man sich bereits grob vorstellen, worum es geht. Wenn der Mensch sich eine Maske aufsetzt, so versucht er etwas damit zu verdecken (zum Beispiel sein Gesicht). Genauso machen wir es auch mit OpenGL um etwas zu verdecken. In diesem Fall jedoch, das Objekt welches wir unter der Maske liegen haben. Es gibt einen Haufen von sinnvollen Verwendungs-Möglichkeiten. Beim Masking an sich geht es darum, nur einen bestimmten Bereich sichtbar zu machen. Nehmen wir also einmal wieder unsere schön Stein-Textur und erzeugen in einem Malprogramm unserer Wahl eine Maske. Diese wird in diesem Fall komplett weiß sein und die eigentliche Masken-Bereiche sind schwarz.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_dgl.gif]]&lt;br /&gt;
&lt;br /&gt;
Diese Maske zeichnen wir nun selbstredend über der Steintextur und verwenden dabei folgende Blending-Funktion:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glBlendFunc(GL_DST_COLOR, GL_ONE); // Zur Info: GL_DST_COLOR darf nur &lt;br /&gt;
                                     // auf den Parameter source angewendet&lt;br /&gt;
                                     // werden. Siehe dazu obenstehende Tabelle.&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Als Ergebnis erhalten wir folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_whitemask.jpg]]&lt;br /&gt;
&lt;br /&gt;
Alles was an der Quelle weiß ist, wird bei dem Source-Faktor enorm hoch bewertet also sehr &amp;quot;weiß&amp;quot; gemacht. Da Schwarz das Gegenteil von Weiß ist, wird dieses eben total vernachlässigt. Sprich wir können hindurch sehen. Da der Destination-Faktor auf die Farbe des &amp;quot;Ziel-Pixels&amp;quot; gesetzt ist sehen wir dann an den entsprechenden Stellen auch den Hintergrund. Wollen wir das Spielchen mal umdrehen, so dass man nur noch die schwarze Schrift sieht und ansonsten durch die zweite Textur komplett hindurch blicken kann? Kein Problem!&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_blackmask.jpg]]&lt;br /&gt;
&lt;br /&gt;
Und die dazugehörige Blend-Funktion :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
glBlendFunc(GL_ZERO,GL_SRC_COLOR);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besonders hoch bei der Quelle werten wir in diesem Fall das Schwarz. Bei der Zielfarbe bewerten wir die Farbe sehr hoch die die zweite Textur hat. D.h. alles was schön weiß ist wird entsprechend geblendet. Wer sich nun am Kopf kratzt sollte sich ein wenig Code schnappen und ein wenig damit herum spielen. Recht schnell merkt man, wie es funktioniert, denn Blending ist unter OpenGL gar nicht so schwer! Am besten auch gleich die obere Tabelle ausdrucken und daneben liegen haben ;)&lt;br /&gt;
&lt;br /&gt;
=== Der Überläufer ===&lt;br /&gt;
&lt;br /&gt;
Ich hoffe sehr, dass ich Euch jetzt hier nicht zu sehr langweile in dem ich andauernd die eine oder andere Funktion immer wieder und wieder in anderer Form zeige. Ich möchte schlicht und ergreifend damit erreichen, dass ihr ein wenig für die Möglichkeiten die man hat sensilibisiert werdet. Die Vielfalt an Effekten, die man erzielen kann ist nämlich wirklich groß, vor allem, wenn man einzelne Effekte miteinander kombiniert!&lt;br /&gt;
&lt;br /&gt;
Nehme wir uns noch einmal unsere Stein- und Gras-Textur zur Hand. Diesmal wollen wir versuchen, dass auf der linken Seite die Stein-Textur ist und auf der Rechten die Gras-Textur. Das ganze soll einen flüssigen Übergang ergeben.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_graswall.jpg]]&lt;br /&gt;
&lt;br /&gt;
Auch ein solcher Effekt ist nicht sonderlich schwer. Alles was wir hierfür brauchen sind unterschiedliche Alpha-Werte an den Eckpunkten der Quads. Beim ersten müssen diese links 1 sein und rechts 0, beim anderen entgegengesetzt. Setzen tun wir die Alpha-Werte mit glColor4f:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  Rock.SetTexture;&lt;br /&gt;
  glBegin(gl_Quads);&lt;br /&gt;
    glColor4f(1,1,1,1);&lt;br /&gt;
    glTexCoord2f(0,0);&lt;br /&gt;
    glVertex3f(-1,-1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor4f(1,1,1,0);&lt;br /&gt;
    glTexCoord2f(1,0);&lt;br /&gt;
    glVertex3f(1,-1,0);&lt;br /&gt;
    glTexCoord2f(1,1);&lt;br /&gt;
    glvertex3f(1,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor4f(1,1,1,1);&lt;br /&gt;
    glTexCoord2f(0,1);&lt;br /&gt;
    glvertex3f(-1,1,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&lt;br /&gt;
  glDisable(GL_DEPTH_TEST); // Wichtig! Da wir sonst das erste Quad verwerfen&lt;br /&gt;
                            // und damit auch die Textur, der Aufruf könnte auch &lt;br /&gt;
                            // in einer Init Funktion stehen.&lt;br /&gt;
  glEnable(GL_BLEND);&lt;br /&gt;
  glBlendFunc(GL_SRC_ALPHA,GL_DST_ALPHA);&lt;br /&gt;
&lt;br /&gt;
  gras.SetTexture;&lt;br /&gt;
  glBegin(gl_Quads);&lt;br /&gt;
    glColor4f(1,1,1,0);&lt;br /&gt;
    glTexCoord2f(0,0);&lt;br /&gt;
    glVertex3f(-1,-1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor4f(1,1,1,1);&lt;br /&gt;
    glTexCoord2f(1,0);&lt;br /&gt;
    glVertex3f(1,-1,0);&lt;br /&gt;
    glTexCoord2f(1,1);&lt;br /&gt;
    glvertex3f(1,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor4f(1,1,1,0);&lt;br /&gt;
    glTexCoord2f(0,1);&lt;br /&gt;
    glvertex3f(-1,1,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Während das erste Quad ohne Blending gezeichnet wird, wird das zweite geblendet, jeweils mit den Alpha-Werten der Quelle und des Zieles. Um dies zu verdeutlichen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_redbluealpha.jpg]]&lt;br /&gt;
&lt;br /&gt;
Je blauer die Farbe, desto weniger wird die Wand gefadet, je roter, desto mehr Gras wird angezeigt. In der Mitte verlaufen beide Texturen zusammen. Natürlich kann man nun anfangen zu spielen und gibt den Seiten andere Alpha-Werte. Genauso wäre es möglich drei Texturen ineinander einzublenden etc. Seit kreativ ;D&lt;br /&gt;
&lt;br /&gt;
=== Der Überläufer - Teil 2 ===&lt;br /&gt;
&lt;br /&gt;
Der eben beschriebene Effekt lässt sich auch noch sehr schön erweitern. Haben wir dort eine gerade Trennlinie zwischen den beiden Texturen gehabt, so werden wir uns nun mit einer Technik befassen, die es einem ermöglicht unregelmäßige Übergange zu erzeigen. Dieses Verfahren wird besondern oft bei Terrains angewandt, um zum Beispiel eine Schneelandschaft langsam ins Geröll überwandern zu lassen. Je nach Verfahren benötigen wir hierfür drei oder vier Texturen. Wir beschränken uns hierbei einfach auf drei.&lt;br /&gt;
&lt;br /&gt;
Würden wir zwei Texturen übereinander zeichnen, so würde die obere die untere Verdecken, würden wir bei einem Quad mit Alpha-Werten arbeiten würden die Übergange nicht flüssig sein. Würden wir die Farben addieren, würde so ziemlich alles dabei rauskommen, nur nicht einen Tetxuren-Verlauf, wie wir ihn erreichen wollen.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_blendmap.jpg]]&lt;br /&gt;
&lt;br /&gt;
Auf diesem Bild erkennt man sehr schön den Texturen-Verlauf, während im oberen Bereich herum bis nach links unten und rechts oben die steinige Felstextur ist, ist im unteren Bereich mehr die Gras-Textur, mit einer Ausnahme mitten in der Gras-Textur ist wiederum ein kleiner Abschnitt mit einer felsigen Textur (hier lila). Auch erkennt man auf dem zweiten Blick, dass an den Grenz-Bereichen ein wenig das Grüne auf dem Felsen durchschimmert, wir haben einen fließenden Übergang zu beiden Texturen erzeugt und können sogar im &amp;quot;Gebiet des Anderen&amp;quot; eine Textur durchscheinen lassen. Die Lösung ist wiederum eine Art von Masking:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_blendmap2.jpg]]&lt;br /&gt;
&lt;br /&gt;
Vergleicht man dieses Bild mit dem oberen wird man erkennen, dass die hellern Flecken &amp;quot;Stein-Textur&amp;quot; und die dunklen &amp;quot;Gras&amp;quot; bedeuten. Man redet von solchen Texturen meist von &amp;quot;Blend-Maps&amp;quot;. Technisch braucht man also nur eine Blend-Map für eine Textur und ein invertiertes Bild davon. An diesem Punkt können wir uns das invertieren bereits sparen, weil wir nur zwei Texturen blenden wollen und das Gegenteil von weiß, schwarz ist ;)&lt;br /&gt;
&lt;br /&gt;
== Lichterkarten ==&lt;br /&gt;
=== Und es ward Licht… ===&lt;br /&gt;
&lt;br /&gt;
Ein wirklich tolles Spielzeug, welches einem mit Blending in die Hand gelegt wird sind &amp;quot;Lightmaps&amp;quot;. Vielleicht hat jemand von Euch bereits einmal versucht in seinem Projekt so schöne Lichtquellen einzubauen, wie er es aus bekannten kommerziellen Spielen kennt und hat sofort anschließend ein langes Gesicht gezogen als die Augen auf die Framerate blickten. Dazu kommt, dass das OpenGL-Licht meist nicht einmal besonders ansehnlich ist. Ärgerlich…&lt;br /&gt;
&lt;br /&gt;
Doch es gibt natürlich eine Möglichkeit schnell und effizient mit wenig Aufwand genauso schöne Lichter in seinem Projekt einzubauen. Das Geheimnis ist schlicht und ergreifend, dass wir nicht auf OpenGL-Lichter aufbauen, sondern einen kleinen Trick auf unsere Benutzer spielen. Wir zeichnen vor dem Zielobjekt eine neue Texture, die nur aus Graustufen besteht. Je heller ein Punkt ist, desto mehr soll er anschließend leuchten.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;center&amp;quot;&lt;br /&gt;
|[[Bild:Tutorial_lektion7_lightmap.jpg]]&lt;br /&gt;
|[[Bild:Tutorial_lektion7_wall_tex.jpg]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Macht grafisch doch schon einiges mehr her als die ursprüngliche Textur oder? Wie bereits gesagt, dass Geheimnis sind die Lightmaps, die wir vor der Wand zeichnen:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;center&amp;quot;&lt;br /&gt;
|[[Bild:Tutorial_lektion7_light.jpg]]&lt;br /&gt;
|[[Bild:Tutorial_lektion7_light2.jpg]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit etwas Fantasie kann man sie sogar immer noch erkennen. Man beachte, dass die erste Lightmap vor allem im oberen Bereich besonders hell ist und vor allem einen sehr hellen Punkt im oberen Bereich hat. Blicken wir auf das fertige Bild, so sehen wir dort einen roten Punkt. Die zweite Lightmap ist eher gleichmäßig und matt, nicht sonderlich hell, zumindest nicht auffällig. Sie sorgt dafür, dass ein gleichmäßiger Grünton auf der Wand liegt. Doch wie kann aus der weißen Lightmap z.B. eine Rote werden? Das ist relativ leicht, wir zeichnen das Quad vor der Wand einfach rot mit Hilfe von glColor3f(1,0.7,0.7); Damit die Farbe auch richtig zur Geltung kommt müssen diese natürlich auch beim Blending entsprechend einarbeiten!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glBlendFunc(GL_DST_COLOR,GL_SRC_COLOR);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei der Quelle wird vor allem die Farbe berücksichtigt, bei der Destination die Zielfarbe. Jeweils zwei Quads werden nach diesem Verfahren über die Wand gezeichnet, um den Effekt zu erreichen. Bin mir durchaus bewusst, dass man schönere Lightmaps machen kann, auch ist es zum Beispiel sehr interessant bei der Zielfarbe GL_SRC_COLOR zu verwenden, wodurch die gesamte Wand verdunkelt wird und man nur noch um das rote leuchten oben etwas sehen könnte (in diesem Fall nur eine Lightmap!)&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_lightmap2.jpg]]&lt;br /&gt;
&lt;br /&gt;
Der Kreativität sind eigentlich keine Grenzen gesetzt. Alle modernen 3D-Engines berechnen bei einem Level an Hand der Lichtquellen solche Lightmaps und projizieren diese dann vor den Wänden, um sie aussehen zu lassen, wie sie aussehen sollen. Keiner dieses wunderschönen Effekts wird man mit einem OpenGL-Licht realisieren können. Wozu auch, wenn es so doch viel schneller geht ;)&lt;br /&gt;
&lt;br /&gt;
=== Die Kraft des Gemeinsamen! ===&lt;br /&gt;
&lt;br /&gt;
Wir haben uns hier sehr viel mit Blending auseinander gesetzt und sicherlich juckt es Euch bereits unter den Fingern Wände mit wunderschönen Lichteffekten an die Wand zu zaubern. So mindestens 300 Lichter sollten sicherlich zum Minimum zählen, oder ;D&lt;br /&gt;
&lt;br /&gt;
Nun schnell werdet ihr vor allem in größeren Projekten merken, dass Euch das auf die Rechenleistung schlägt. Das Problem ist logisch! Nicht das Blending an sich ist die größte CPU-Last, sondern das Transformieren der Quads, auf denen sich die Lightmaps befinden. Eigentlich sind es fast nur die Transformationen, die so richtig mit Leistung zu Buch schlagen. Es gilt also so wenig wie mögliche Quads hintereinander zu erzeugen und somit die Anzahl der Durchläufe zu minimieren (Renderpass). Das ließe sich zum Beispiel damit erreichen, dass man versucht möglichst viele Lichtinformationen auf einer Lightmap zu setzen. So würde es keinen Sinn machen zwei rote Lichter einmal oben und einmal unten.&lt;br /&gt;
&lt;br /&gt;
Dies sind jedoch nur logische Einsparungen, die jeder Programmierer in seinem Projekt selbst entscheiden muss. Seit einiger Zeit gibt es jedoch auch ein Hardware-Verfahren, welches uns helfen kann ein Spiel zu optimieren. Zu Beginn der 3D-Beschleunigung verlief das Rendering wie folgt:&lt;br /&gt;
&lt;br /&gt;
 # Texture setzen&lt;br /&gt;
 # Objekt transformieren&lt;br /&gt;
 # Texture setzen&lt;br /&gt;
 # Objekt transformieren&lt;br /&gt;
&lt;br /&gt;
Klingt soweit auch alles logisch! Wie bereits jedoch erwähnt frisst vor allem das transformieren die Rechenpower und in unserem Fall liegt das Objekt übereinander, es wäre eine Transformation der Vertizen eigentlich gar nicht nötig. Wir müssten also nur die beiden Texturen auf ein transformiertes Objekt bekommen. Würden wir einfach nur zwei Texturen hintereinander setzen, so würde OpenGL die zuletzt gewählte verwenden und die andere schlichtweg ignorieren. Die meisten heutigen Grafikkarten (z.B: GeForce8800 GTX) haben 64 Textur-Units. Das muss man sich vorstellen wie 64 Schubladen. In die ersten beiden legen wir eine Textur rein und wenn OpenGL rendert wird reingeschaut und beide werden auf das Ziel gepappt. Somit haben wir uns eine Transformation gespart, was vor allem wenn man sehr viel mit Lightmaps arbeitet schon ein beachtlicher Geschwindigkeitsgewinn von knapp 50% ist! Einige sehr moderne Grafikkarten haben bereits 128 Texturen-Units und die Zeit bis es welche mit 256 gibt ist auch schon bereits voraussehbar ;)&lt;br /&gt;
&lt;br /&gt;
Wie funktioniert das ganze nun für uns? OpenGL verwendet als Standard die Unit 0. Binden wir eine Textur oder definieren das Blending so beziehen sich alle diese Angaben immer auf die momentan selektierte Textur-Unit. Alles was wir machen müssen ist, die gewünschte auszuwählen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glActiveTextureARB(GL_TEXTURE0_ARB);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ich denke der Parameter erklärt sich praktisch von selbst! GL_TEXTURE1_ARB wäre entsprechend die zweite Textur-Unit. Nun setzen wir alle Einstellungen für diese Unit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glEnable(GL_TEXTURE_2D);&lt;br /&gt;
  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);&lt;br /&gt;
  wall.SetTexture;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zunächst teilen wir der Unit mit, dass sie bei der 2D-texturierung zur Verfügung stehen soll. Anschließend setzen wir die Textur-Envroiment auf die &amp;quot;Standard-Einstellung&amp;quot;. Wenn mich jetzt jemand fragt, wofür die Parameter gut sind, dann zitiere ich das RedBook &amp;quot;es muss so sein&amp;quot; ;D Wichtig ist eigentlich nur, dass hinten GL_MODULATE steht, damit die Textur ordnungsgemäß, so wie wir sie sehen auf das Quad wandern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glActiveTextureARB(GL_TEXTURE1_ARB);&lt;br /&gt;
  glEnable(GL_TEXTURE_2D);&lt;br /&gt;
  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);&lt;br /&gt;
  glEnable(gl_BLEND);&lt;br /&gt;
  glBlendFunc(GL_ONE,GL_ONE);&lt;br /&gt;
  light.SetTexture;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir wechseln danach zur zweiten Textur-Unit und machen ähnliche Einstellungen, aktivieren zusätzlich jedoch Blending für diese Funktion. Diesmal etwas sehr simples.&lt;br /&gt;
&lt;br /&gt;
Wer nun gehofft hat, dass dies alles gewesen wäre, was man für das MultiTexturing benötigt, der wird enttäuscht sein. Eine Kleinigkeit gibt es noch zu beachten. Da wir zwei Texturen verwenden kann es ja auch sein, dass die UV-Koordinaten unterschiedlich sein sollen. In unserem Fall ist dies nicht so, aber es kann durchaus passieren. Deswegen müssen wir auch für jeden Eckpunkt eine UV-Koordinate vergeben. Hierfür verwenden wir nicht glTexCoord2f, sondern die Funktion:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glMultiTexCoord2fARB(GL_TEXTURE0_ARB,0,0);&lt;br /&gt;
  glMultiTexCoord2fARB(GL_TEXTURE1_ARB,0,0);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zunächst geben wir bei der Funktion den Parameter ein gefolgt von der eigentlichen UV-Koordinate. So gehen wir dann bei jedem Punkt vor und vorausgesetzt, dass unsere Grafikkarte auch Multitexturing unterstütz sollten wir uns an folgendem Bild auf dem Bildschirm ergötzen können (natürlich nur, wenn man auch die gleichen Texturen verwendet! ;D)&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_multitex.jpg]]&lt;br /&gt;
&lt;br /&gt;
== Nachwort ==&lt;br /&gt;
&lt;br /&gt;
Oh je, ich weiß, ich habe inzwischen einen Ruf dafür, dass ich immer gleich so große Portionen liefere und nicht in der Lage bin, einmal kurz ein kleines Tutorial zu schreiben. Das liegt einfach daran, dass wenn ich einmal angefangen bin und mich für etwas begeistern konnte nicht mehr aufhören kann. Und gerade bei solchen Themen, wo man so richtig schön mit Effekten experimentieren kann fällt es einem besonders schwer einen Punkt zu finden.&lt;br /&gt;
&lt;br /&gt;
Ich denke spätestens nach diesem Tutorial sollte man gelernt haben wie leicht man mit OpenGL einige wunderschöne Effekte zaubern kann ohne wirklich großen Aufwand zu betreiben. Viel leichter wird es kaum möglich sein. Sollte jemand nun sich am Kopf kratzen und sich fragen, ob es einen Grund hat, warum die ganzen hier vorgestellten Beispiele 2D sind? Nein, eigentlich nicht, denn alle der hier vorgestellten Techniken lassen sich auch prima auf 3D-Objekte übertragen. So wäre es zum Beispiel auch Möglich einen 3D-Soldaten mit einer Lightmap und Multitexturing zu überziehen und somit unterschiedliche Helligkeit auf ihn zu projizieren. Aber darauf will ich nicht weiter eingehen, nun kommt wieder Euer Part. ;)&lt;br /&gt;
&lt;br /&gt;
Auch bin ich sehr froh zu sehen, dass die Community wächst und auch endlich einen Punkt erreicht an der man gezielt auf Probleme einwirken kann. Ich hoffe sehr, dass dies auch weiterhin so bleibt und bin auch sehr froh, dass zunehmend Feedback von Eurer Seite kommt, gar einige damit beginnen Kontent für uns zur Verfügung zu stellen oder uns auf kleinere Fehler aufmerksam macht oder aber auch einfach nur auf eine gute Seite aufmerksam machen, die wir bisher nicht gelinkt haben. Wenn jemand von Euch einmal ein kleines OpenGL-Projekt vorstellen möchte, etwas an dem er arbeitet, schreibt uns doch ruhig auch einmal an. Mehr als Ablehnen könnten wir auch nicht ;D&lt;br /&gt;
&lt;br /&gt;
In diesem Sinne viel Spaß und auf Wiedersehen.&lt;br /&gt;
&lt;br /&gt;
Euer [[Benutzer:Phobeus|Phobeus]]&lt;br /&gt;
&lt;br /&gt;
== Dateien == &lt;br /&gt;
* {{ArchivLink|file=tut_lektion_7_delphi_api|text=Alter Delphi-API-Quelltext zum Tutorial}}&lt;br /&gt;
* {{ArchivLink|file=tut_lektion_7_exe|text=Windows-Binary zum Tutorial}}&lt;br /&gt;
&lt;br /&gt;
{{TUTORIAL_NAVIGATION | [[Tutorial Lektion 5]] | [[Tutorial Lektion 8]]}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Tutorial|Lektion7]]&lt;/div&gt;</summary>
		<author><name>Nevermind10844</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_Lektion_7&amp;diff=25704</id>
		<title>Tutorial Lektion 7</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_Lektion_7&amp;diff=25704"/>
				<updated>2012-09-19T21:23:25Z</updated>
		
		<summary type="html">&lt;p&gt;Nevermind10844: /* Die Welt unter einer Maske */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Verblendet! =&lt;br /&gt;
&lt;br /&gt;
== Vorwort ==&lt;br /&gt;
&lt;br /&gt;
Nachdem wir nun ein wenig Erfahrung in der Praxis gesammelt haben, wenden wir uns einen anderen Bereich in der OpenGL-Programmierung zu, der uns helfen wird einige wirklich tolle Spezial-Effekte zu realisieren. Die meisten unter Euch werden den Begriff bereits das eine oder andere Mal gehört haben - die Rede ist vom so genannten Blending. Das berühmteste davon wird sicherlich das &amp;quot;Alpha Blending&amp;quot; sein. Wer nun glaubt Blending ist, wenn man sich ne Halogen-Werfer nimmt und damit versucht seinen Nachbarn zu quälen, ist auf dem Holzweg ;)&lt;br /&gt;
&lt;br /&gt;
Blending weist OpenGL dazu an, zu entscheiden was passieren soll, wenn man zwei Objekte übereinander zeichnet. Im Normalfall wird das obere Objekt das untere überdecken, aber vielleicht wollen wir ja auch, dass das obere Objekt transparent ist und man hindurch blicken kann auf das untere? Wer denkt, dass hier bereits Schluss sei mit dem Blending, der ist schief gewickelt, denn hier beginnt erst die Welt des kreativen Geistes! Lasst Euch verzaubern von wunderschönen OpenGL-Effekten und heizt Euren Programmen grafisch ordentlich ein. Ich hoffe ihr habt Verständnis, dass ich nur auf Teilbereiche eingehen kann, denn die Palette von Möglichkeiten ist keine Grenze gesetzt ;)&lt;br /&gt;
&lt;br /&gt;
Ich hoffe dennoch, dass auch das Tutorial gefallen wird und es wenigstens halbwegs verständlich rüberkommt. Auch gibt es für viele Dinge besser Lösungsmöglichkeiten, vieles hängt von der benutzen Textur ab oder aber auch wie viele davon verwendet werden. Um ein ausgiebiges Testen der Funktionen wird man nicht herum kommen. Also ran an die Arbeit und viel Spaß dabei ;)&lt;br /&gt;
&lt;br /&gt;
== Die vorhandene Welt und was darüber liegt ==&lt;br /&gt;
&lt;br /&gt;
=== Was ist den nun blenden? ===&lt;br /&gt;
&lt;br /&gt;
Wichtig beim Blending ist es zu wissen, was genau eigentlich beim rendern geschieht. OpenGL erhält eine Anweisung etwas zu zeichnen. Dieses wird dann in den so genannten &amp;quot;Frame-Buffer&amp;quot; gespeichert, das ist der Buffer,den wir dann auch meistens bei jedem Rendering mit glClear &amp;quot;löschen&amp;quot;. (Wir erinnern uns, führen wir kein glClear aus, sehen wir nach jedem Rendering den alten Inhalt… habe versprochen, dass wir darauf zurück kommen ;) ). Das heißt eigentlich, dass wir bei jedem Rendern eines Objektes immer wieder etwas in den Frame-Buffer schreiben und dann wenn wir denken, dass alles fertig ist diesen Inhalt auf dem Bildschirm projizieren.&lt;br /&gt;
&lt;br /&gt;
Das Blending setzt nun genau an der Stelle an, an dem etwas in den Frame-Buffer geschrieben werden soll. OpenGL hat praktisch bereits ein 3D-Objekt im Frame-Buffer und zeichnet dann das neue 3D Objekt. Nun können wir OpenGL mitteilen, was in einer solchen Situation geschehen soll. Standard gemäß würde das obere Objekt, dass darunter liegende verdecken.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glBlendFunc(source,destination);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit dieser Anweisung können wir dieses Verhalten ändern. Wir geben zwei Faktoren an, den für die Quelle und den für das Ziel. Ich versuche das mal abstrakt zu erklären. Wie bereits gesagt, handelt es sich dabei um Faktoren. Nehmen wir mal an Source (Textur) wäre 1 und Destination (Framebuffer) 0, dann würde OpenGL beim zusammensetzen der Szene, das was bereits im Frame-Buffer ist total vernachlässigen und das neue, was darüber gezeichnet werden soll komplett drauf zeichnen. Je höher der Faktor einer dieser Beiden Werte ist, desto stärke werden die Farben beim Blending berücksichtigt.Das ganze funktioniert natürlich auch nur, wenn wir OpenGL dazu anhalten das Blending zu verwenden…&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glEnable(GL_BLEND);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das ganze klingt sicherlich sehr komplex, (ist es auch *sg*), aber an Hand einiger Beispiele sollte es einem klar werden. Nehmen wir folgende Bilder:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;center&amp;quot;&lt;br /&gt;
|[[Bild:Tutorial_lektion7_rock_tex.jpg]]&lt;br /&gt;
|[[Bild:Tutorial_lektion7_gras_tex.jpg]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese beiden Texturen zeichnen wir jeweils auf einem Quad und zwar exakt aufeinander. Wir zeichnen zunächst die steinige Fläche und erhalten:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_stone.jpg]]&lt;br /&gt;
&lt;br /&gt;
Soweit so gut… nun setzen wir folgenden Blend-Faktor:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glBlendFunc(GL_ONE,GL_ZERO);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und zeichnen die Gras-Textur darüber:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_gras.jpg]]&lt;br /&gt;
&lt;br /&gt;
Hmm… scheint nicht, als ob das Blending wirklich gut funktioniert hat, da wir nur die Textur sehen, die wir als letztes gezeichnet haben. Scheint soweit alles wie bisher zu sein. :-/&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glBlendFunc(GL_ONE,GL_ZERO);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Quatsch! Natürlich hat alles funktioniert wie wir es erwartet haben! Wir haben schließlich OpenGL dazu angewiesen von der neuen Textur &amp;quot;alles&amp;quot; zu nehmen und von dem, was im Frame-Buffer ist &amp;quot;nichts&amp;quot;. Auf neudeutsch, wir haben ihn angewiesen die alte Textur über die andere zu zeichnen. Würden wir hingegen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glBlendFunc(GL_ZERO,GL_ONE);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwenden, so würden wir OpenGL dazu anhalten vom Frame-Buffer &amp;quot;alles&amp;quot; zu übernehmen und von der neuen Textur &amp;quot;nichts&amp;quot;, das würde bedeuten dass wir ins leere Rendern (sehr gute Methode seine Engine zu optimieren… *sg*)&lt;br /&gt;
&lt;br /&gt;
Ach so noch etwas… nehmt das mit dem GL_ZERO und GL_ONE und meinem &amp;quot;nichts und alles&amp;quot; nicht ganz so wörtlich. Es ist mehr eine kleine Eselsbrücke, die einem am Anfang hilft nicht total zu verwirren. Technisch macht OpenGL nichts anderes als bei GL_ONE die Farbe der Texture auf (1,1,1,1) zu setzen bei GL_ZERO auf (0,0,0,0); Und sobald es &amp;quot;schwarz&amp;quot; ist, sieht man sie halt nicht mehr, klingt logisch oder?&lt;br /&gt;
&lt;br /&gt;
=== Claim for the World Order ===&lt;br /&gt;
&lt;br /&gt;
Wie alles hat auch das Blending einen kleinen Schönheitsfehler. Viele Leute denken immer wieder, dass auch beim Blending praktisch eine 3D-Welt existiert, d.h. man hinrendern kann wo man will. Das ist jedoch falsch, da die Quelle ja im Frame-Buffer bereits vorliegt. Praktisch bereits fertig gerendert. Es macht also für das Blending einen enormen Unterschied, in welcher Reihenfolge die Objekte zur Kamera hin gerendert werden.&lt;br /&gt;
&lt;br /&gt;
Würden wir zum Beispiel ein Haus mit einem Fenster render wollen und das Fensterglas mit Hilfe von Blending darstellen und wir würden zunächst das Glas rendern, so würde OpenGL dieses mit dem Hintergrund (vermutlich dem Himmel) blenden und dann anschließend das Haus drum herum zeichnen. Der ahnungslose Programmierer wird dann mit großen Augen vor dem Fenster stehen und sich wundern, wieso er durch die hintere Wand des Hauses ins Freie sehen kann. &lt;br /&gt;
&lt;br /&gt;
Kurz: Auch durchsichtige Objekte sind Objekte. Auch sie schreiben Werte in den [[Tiefenpuffer]]. Deshalb kann man nicht &amp;quot;hinter&amp;quot; durchsichtige Objekte etwas rendern.&lt;br /&gt;
&lt;br /&gt;
Ohne Blending, würde so etwas nicht weiter auffallen, mit Blending muss man Acht geben in welcher Reihenfolge wir die Objekte rendern.&lt;br /&gt;
&lt;br /&gt;
Darum gilt: '''alle undurchsichtigen Objekte rendern, bevor man zum Blending greift.''' Man sollte sich also durchaus Gedanken dazu machen, wie man seine Objekte rendert. Es gibt verschiedene Verfahren seine Objekte so ordnen zu lassen, dass das hier beschrieben Problem nicht auftritt, darauf möchte ich nicht näher eingehen, ihr seid gewarnt worden ;D Eigentlich ist es ein recht logischer Vorgang, allerdings erliegt man doch recht schnell dem kleinen Irrglauben hinter eine Scheibe rendern zu können. ;)&lt;br /&gt;
&lt;br /&gt;
=== Gleichheit für Alles! ===&lt;br /&gt;
&lt;br /&gt;
Das war ja nun auch ein interessanter Ausflug… gelernt haben wir immer noch nichts wirklich Neues, zumindest nichts, was man auch sehen kann. Üben wir uns doch nun einmal richtig ein und versuchen zwei Texturen zu gleichen Maßen zusammen zu blenden, so dass es aussieht, als hätten wir eine Textur. Sicherlich gibt es viele Möglichkeiten dies zu realisieren, allerdings möchte ich an dieser Stelle einen Alpha-Farbwert verwenden. Wie ihr sicherlich wisst, werden Farben meist im RGB-Werten gespeichert, sprich Rot, Grün und Blau! Gesetzt wird dieses mit glColor3f. Ebenfalls gibt es noch die Funktion glColor4f mit der wir einen RGBA-Wert vergeben können. Dieser wird uns vor allem beim Blending nützliche Dienste erweisen.&lt;br /&gt;
&lt;br /&gt;
Weil beide Texturen zu gleichen Prozentsätzen geblendet werden sollen ergibt sich daraus folglich ein Aufruf von &amp;quot;glColor4f(1,1,1,0.5)&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
Wie ihr sehen könnt setzen wir diesmal glBlendFunc(GL_SRC_ALPHA,GL_ONE); ein. Von der Quelle wird der Alpha-Wert als Blend-Faktor genommen, dass Ziel wird übernommen.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_stonegras.jpg]]&lt;br /&gt;
&lt;br /&gt;
Wenn das nicht aussieht wie eine Textur, die wie aus dem gleichen Guss kommt! Würde noch jemand erahnen, dass es sich dabei eigentlich um zwei Texturen handelt? Für alle, interessierten noch einmal kurz eine Tabelle zur Übersicht der Funktionen, die wir bisher eingesetzt haben und wir noch einsetzen werden, sowie der mathematischen Erklärung ihrer Bedeutung:&lt;br /&gt;
&lt;br /&gt;
{| {{Prettytable_B1}}&lt;br /&gt;
! Konstante&lt;br /&gt;
! Auf Parameter anwendbar&lt;br /&gt;
! Berechneter Blend-Faktor&lt;br /&gt;
|-&lt;br /&gt;
| GL_ZERO&lt;br /&gt;
| source or destination&lt;br /&gt;
| (0, 0, 0, 0)&lt;br /&gt;
|-&lt;br /&gt;
| GL_ONE&lt;br /&gt;
| source or destination&lt;br /&gt;
| (1, 1, 1, 1)&lt;br /&gt;
|-&lt;br /&gt;
| GL_DST_COLOR&lt;br /&gt;
| source&lt;br /&gt;
| (Rd, Gd, Bd, Ad)&lt;br /&gt;
|-&lt;br /&gt;
| GL_SRC_COLOR&lt;br /&gt;
| destination&lt;br /&gt;
| (Rs, Gs, Bs, As)&lt;br /&gt;
|-&lt;br /&gt;
| GL_ONE_MINUS_DST_COLOR&lt;br /&gt;
| source&lt;br /&gt;
| (1, 1, 1, 1)-(Rd, Gd, Bd, Ad)&lt;br /&gt;
|-&lt;br /&gt;
| GL_ONE_MINUS_SRC_COLOR&lt;br /&gt;
| destination&lt;br /&gt;
| (1, 1, 1, 1)-(Rs, Gs, Bs, As)&lt;br /&gt;
|-&lt;br /&gt;
| GL_SRC_ALPHA&lt;br /&gt;
| source or destination&lt;br /&gt;
| (As, As, As, As)&lt;br /&gt;
|-&lt;br /&gt;
| GL_ONE_MINUS_SRC_ALPH&lt;br /&gt;
| source or destination&lt;br /&gt;
| (1, 1, 1, 1)-(As, As, As, As)&lt;br /&gt;
|-&lt;br /&gt;
| GL_DST_ALPHA&lt;br /&gt;
| source or destination&lt;br /&gt;
| (Ad, Ad, Ad, Ad)&lt;br /&gt;
|-&lt;br /&gt;
| GL_ONE_MINUS_DST_ALPHA&lt;br /&gt;
| source or destination&lt;br /&gt;
| (1, 1, 1, 1)-(Ad, Ad, Ad, Ad)&lt;br /&gt;
|-&lt;br /&gt;
| GL_SRC_ALPHA_SATURATE&lt;br /&gt;
| source&lt;br /&gt;
| (f, f, f, 1); f=min(As, 1-Ad)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Sieht anfangs etwas verwirrend aus, aber der Mensch kann sich an vieles gewöhnen. Immerhin lest ihr bereits seit einiger Zeit Texte, die mit meinen Rechtschreibfehlern und meiner Sprache angereichert sind ;D&lt;br /&gt;
&lt;br /&gt;
Um sicher zu gehen, dass auch wirklich alles verstanden ist nehmen wir uns noch einmal kurz folgenden Aufruf zu herzen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glBlendFunc(GL_ONE, GL_SRC_COLOR); // Zur Info: GL_SRC_COLOR darf nur auf &lt;br /&gt;
                                     // den Paramter destination angewendet &lt;br /&gt;
                                     // werden, siehe dazu obenstehende Tabelle&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wieder lassen wir den Frame-Buffer wie er ist, verwenden diesmal jedoch keinen Alpha-Wert zu blenden, sondern jeweils die RGB-Werte der Textur. OpenGL macht also praktisch nichts anderes als die Farbwerte zu addieren und anschließend durch zwei zu teilen. Wie man der Tabelle entnehmen kann spielt bei einem solchen Aufruf nur die Destination eine Rolle. Folglich sieht das Ergebnis wie folgt aus:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_stonegras2.jpg]]&lt;br /&gt;
&lt;br /&gt;
Soweit nun zum einfachen Blending. Ihr könnt Euch vorstellen, dass wir damit bereits eine Menge sehr interessanter Effekte erzeugen können, aber nun wollen wir einige davon auch gleich einmal testen und einsetzen ;)&lt;br /&gt;
&lt;br /&gt;
== Blending-Verfahren ==&lt;br /&gt;
&lt;br /&gt;
=== Die Welt unter einer Maske ===&lt;br /&gt;
&lt;br /&gt;
Ein Verfahren, welches sehr häufig zum Einsatz kommt ist das &amp;quot;Masking-Verfahren&amp;quot;. Aus dem Namen kann man sich bereits grob vorstellen, worum es geht. Wenn der Mensch sich eine Maske aufsetzt, so versucht er etwas damit zu verdecken (zum Beispiel sein Gesicht). Genauso machen wir es auch mit OpenGL um etwas zu verdecken. In diesem Fall jedoch, das Objekt welches wir unter der Maske liegen haben. Es gibt einen Haufen von sinnvollen Verwendungs-Möglichkeiten. Beim Masking an sich geht es darum, nur einen bestimmten Bereich sichtbar zu machen. Nehmen wir also einmal wieder unsere schön Stein-Textur und erzeugen in einem Malprogramm unserer Wahl eine Maske. Diese wird in diesem Fall komplett weiß sein und die eigentliche Masken-Bereiche sind schwarz.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_dgl.gif]]&lt;br /&gt;
&lt;br /&gt;
Diese Maske zeichnen wir nun selbstredend über der Steintextur und verwenden dabei folgende Blending-Funktion:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glBlendFunc(GL_DST_COLOR, GL_ONE); // Zur Info: GL_DST_COLOR darf nur &lt;br /&gt;
                                     // auf den Parameter source angewendet&lt;br /&gt;
                                     // werden. Siehe dazu obenstehende Tabelle.&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Als Ergebnis erhalten wir folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_whitemask.jpg]]&lt;br /&gt;
&lt;br /&gt;
Alles was an der Quelle weiß ist, wird bei dem Source-Faktor enorm hoch bewertet also sehr &amp;quot;weiß&amp;quot; gemacht. Da Schwarz das Gegenteil von Weiß ist, wird dieses eben total vernachlässigt. Sprich wir können hindurch sehen. Da der Destination-Faktor auf die Farbe des &amp;quot;Ziel-Pixels&amp;quot; gesetzt ist sehen wir dann an den entsprechenden Stellen auch den Hintergrund. Wollen wir das Spielchen mal umdrehen, so dass man nur noch die schwarze Schrift sieht und ansonsten durch die zweite Textur komplett hindurch blicken kann? Kein Problem!&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_blackmask.jpg]]&lt;br /&gt;
&lt;br /&gt;
Und die dazugehörige Blend-Funktion :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
glBlendFunc(GL_ZERO,GL_SRC_COLOR);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besonders hoch bei der Quelle werten wir in diesem Fall das Schwarz. Bei der Zielfarbe bewerten wir die Farbe sehr hoch die die zweite Textur hat. D.h. alles was schön weiß ist wird entsprechend geblendet. Wer sich nun am Kopf kratzt sollte sich ein wenig Code schnappen und ein wenig damit herum spielen. Recht schnell merkt man, wie es funktioniert, denn Blending ist unter OpenGL gar nicht so schwer! Am besten auch gleich die obere Tabelle ausdrucken und daneben liegen haben ;)&lt;br /&gt;
&lt;br /&gt;
=== Der Überläufer ===&lt;br /&gt;
&lt;br /&gt;
Ich hoffe sehr, dass ich Euch jetzt hier nicht zu sehr langweile in dem ich andauernd die eine oder andere Funktion immer wieder und wieder in anderer Form zeige. Ich möchte schlicht und ergreifend damit erreichen, dass ihr ein wenig für die Möglichkeiten die man hat sensilibisiert werdet. Die Vielfalt an Effekten, die man erzielen kann ist nämlich wirklich groß, vor allem, wenn man einzelne Effekte miteinander kombiniert!&lt;br /&gt;
&lt;br /&gt;
Nehme wir uns noch einmal unsere Stein- und Gras-Textur zur Hand. Diesmal wollen wir versuchen, dass auf der linken Seite die Stein-Textur ist und auf der Rechten die Gras-Textur. Das ganze soll einen flüssigen Übergang ergeben.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_graswall.jpg]]&lt;br /&gt;
&lt;br /&gt;
Auch ein solcher Effekt ist nicht sonderlich schwer. Alles was wir hierfür brauchen sind unterschiedliche Alpha-Werte an den Eckpunkten der Quads. Beim ersten müssen diese links 1 sein und rechts 0, beim anderen entgegengesetzt. Setzen tun wir die Alpha-Werte mit glColor4f:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  Rock.SetTexture;&lt;br /&gt;
  glBegin(gl_Quads);&lt;br /&gt;
    glColor4f(1,1,1,1);&lt;br /&gt;
    glTexCoord2f(0,0);&lt;br /&gt;
    glVertex3f(-1,-1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor4f(1,1,1,0);&lt;br /&gt;
    glTexCoord2f(1,0);&lt;br /&gt;
    glVertex3f(1,-1,0);&lt;br /&gt;
    glTexCoord2f(1,1);&lt;br /&gt;
    glvertex3f(1,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor4f(1,1,1,1);&lt;br /&gt;
    glTexCoord2f(0,1);&lt;br /&gt;
    glvertex3f(-1,1,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&lt;br /&gt;
  glDisable(GL_DEPTH_TEST); // Wichtig! Da wir sonst das erste Quad verwerfen&lt;br /&gt;
                            // und damit auch die Textur, der Aufruf könnte auch &lt;br /&gt;
                            // in einer Init Funktion stehen.&lt;br /&gt;
  glEnable(GL_BLEND);&lt;br /&gt;
  glBlendFunc(GL_SRC_ALPHA,GL_DST_ALPHA);&lt;br /&gt;
&lt;br /&gt;
  gras.SetTexture;&lt;br /&gt;
  glBegin(gl_Quads);&lt;br /&gt;
    glColor4f(1,1,1,0);&lt;br /&gt;
    glTexCoord2f(0,0);&lt;br /&gt;
    glVertex3f(-1,-1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor4f(1,1,1,1);&lt;br /&gt;
    glTexCoord2f(1,0);&lt;br /&gt;
    glVertex3f(1,-1,0);&lt;br /&gt;
    glTexCoord2f(1,1);&lt;br /&gt;
    glvertex3f(1,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor4f(1,1,1,0);&lt;br /&gt;
    glTexCoord2f(0,1);&lt;br /&gt;
    glvertex3f(-1,1,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Während das erste Quad ohne Blending gezeichnet wird, wird das zweite geblendet, jeweils mit den Alpha-Werten der Quelle und des Zieles. Um dies zu verdeutlichen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_redbluealpha.jpg]]&lt;br /&gt;
&lt;br /&gt;
Je blauer die Farbe, desto weniger wird die Wand gefadet, je roter, desto mehr Gras wird angezeigt. In der Mitte verlaufen beide Texturen zusammen. Natürlich kann man nun anfangen zu spielen und gibt den Seiten andere Alpha-Werte. Genauso wäre es möglich drei Texturen ineinander einzublenden etc. Seit kreativ ;D&lt;br /&gt;
&lt;br /&gt;
=== Der Überläufer - Teil 2 ===&lt;br /&gt;
&lt;br /&gt;
Der eben beschriebene Effekt lässt sich auch noch sehr schön erweitern. Haben wir dort eine gerade Trennlinie zwischen den beiden Texturen gehabt, so werden wir uns nun mit einer Technik befassen, die es einem ermöglicht unregelmäßige Übergange zu erzeigen. Dieses Verfahren wird besondern oft bei Terrains angewandt, um zum Beispiel eine Schneelandschaft langsam ins Geröll überwandern zu lassen. Je nach Verfahren benötigen wir hierfür drei oder vier Texturen. Wir beschränken uns hierbei einfach auf drei.&lt;br /&gt;
&lt;br /&gt;
Würden wir zwei Texturen übereinander zeichnen, so würde die obere die untere Verdecken, würden wir bei einem Quad mit Alpha-Werten arbeiten würden die Übergange nicht flüssig sein. Würden wir die Farben addieren, würde so ziemlich alles dabei rauskommen, nur nicht einen Tetxuren-Verlauf, wie wir ihn erreichen wollen.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_blendmap.jpg]]&lt;br /&gt;
&lt;br /&gt;
Auf diesem Bild erkennt man sehr schön den Texturen-Verlauf, während im oberen Bereich herum bis nach links unten und rechts oben die steinige Felstextur ist, ist im unteren Bereich mehr die Gras-Textur, mit einer Ausnahme mitten in der Gras-Textur ist wiederum ein kleiner Abschnitt mit einer felsigen Textur (hier lila). Auch erkennt man auf dem zweiten Blick, dass an den Grenz-Bereichen ein wenig das Grüne auf dem Felsen durchschimmert, wir haben einen fließenden Übergang zu beiden Texturen erzeugt und können sogar im &amp;quot;Gebiet des Anderen&amp;quot; eine Textur durchscheinen lassen. Die Lösung ist wiederum eine Art von Masking:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_blendmap2.jpg]]&lt;br /&gt;
&lt;br /&gt;
Vergleicht man dieses Bild mit dem oberen wird man erkennen, dass die hellern Flecken &amp;quot;Stein-Textur&amp;quot; und die dunklen &amp;quot;Gras&amp;quot; bedeuten. Man redet von solchen Texturen meist von &amp;quot;Blend-Maps&amp;quot;. Technisch braucht man also nur eine Blend-Map für eine Textur und ein invertiertes Bild davon. An diesem Punkt können wir uns das invertieren bereits sparen, weil wir nur zwei Texturen blenden wollen und das Gegenteil von weiß, schwarz ist ;)&lt;br /&gt;
&lt;br /&gt;
== Lichterkarten ==&lt;br /&gt;
=== Und es ward Licht… ===&lt;br /&gt;
&lt;br /&gt;
Ein wirklich tolles Spielzeug, welches einen mit Blending in die Hand gelegt wird sind &amp;quot;Lightmaps&amp;quot;. Vielleicht hat jemand von Euch bereits einmal versucht in seinem Projekt so schöne Lichtquellen einzubauen, wie er es aus bekannten kommerziellen Spielen kennt und hat sofort anschließend ein langes Gesicht gezogen als die Augen auf die Framerate blickten. Dazu kommt, dass das OpenGL-Licht meist nicht einmal besonders ansehnlich ist. Ärgerlich…&lt;br /&gt;
&lt;br /&gt;
Doch es gibt natürlich eine Möglichkeit schnell und effizient mit wenig Aufwand genauso schöne Lichter in seinem Projekt einzubauen. Das Geheimnis ist schlicht und ergreifend, dass wir nicht auf OpenGL-Lichter aufbauen, sondern einen kleinen Trick auf unsere Benutzer spielen. Wir zeichnen vor dem Zielobjekt eine neue Texture, die nur aus Graustufen besteht. Je heller ein Punkt ist, desto mehr soll er anschließend leuchten.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;center&amp;quot;&lt;br /&gt;
|[[Bild:Tutorial_lektion7_lightmap.jpg]]&lt;br /&gt;
|[[Bild:Tutorial_lektion7_wall_tex.jpg]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Macht grafisch doch schon einiges mehr her als die ursprüngliche Textur oder? Wie bereits gesagt, dass Geheimnis sind die Lightmaps, die wir vor der Wand zeichnen:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;center&amp;quot;&lt;br /&gt;
|[[Bild:Tutorial_lektion7_light.jpg]]&lt;br /&gt;
|[[Bild:Tutorial_lektion7_light2.jpg]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit etwas Fantasie kann man sie sogar immer noch erkennen. Man beachte, dass die erste Lightmap vor allem im oberen Bereich besonders hell ist und vor allem einen sehr hellen Punkt im oberen Bereich hat. Blicken wir auf das fertige Bild, so sehen wir dort einen roten Punkt. Die zweite Lightmap ist eher gleichmäßig und matt, nicht sonderlich hell, zumindest nicht auffällig. Sie sorgt dafür, dass ein gleichmäßiger Grünton auf der Wand liegt. Doch wie kann aus der weißen Lightmap z.B. eine Rote werden? Das ist relativ leicht, wir zeichnen das Quad vor der Wand einfach rot mit Hilfe von glColor3f(1,0.7,0.7); Damit die Farbe auch richtig zur Geltung kommt müssen diese natürlich auch beim Blending entsprechend einarbeiten!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glBlendFunc(GL_DST_COLOR,GL_SRC_COLOR);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei der Quelle wird vor allem die Farbe berücksichtigt, bei der Destination die Zielfarbe. Jeweils zwei Quads werden nach diesem Verfahren über die Wand gezeichnet, um den Effekt zu erreichen. Bin mir durchaus bewusst, dass man schönere Lightmaps machen kann, auch ist es zum Beispiel sehr interessant bei der Zielfarbe GL_SRC_COLOR zu verwenden, wodurch die gesamte Wand verdunkelt wird und man nur noch um das rote leuchten oben etwas sehen könnte (in diesem Fall nur eine Lightmap!)&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_lightmap2.jpg]]&lt;br /&gt;
&lt;br /&gt;
Der Kreativität sind eigentlich keine Grenzen gesetzt. Alle modernen 3D-Engines berechnen bei einem Level an Hand der Lichtquellen solche Lightmaps und projizieren diese dann vor den Wänden, um sie aussehen zu lassen, wie sie aussehen sollen. Keiner dieses wunderschönen Effekts wird man mit einem OpenGL-Licht realisieren können. Wozu auch, wenn es so doch viel schneller geht ;)&lt;br /&gt;
&lt;br /&gt;
=== Die Kraft des Gemeinsamen! ===&lt;br /&gt;
&lt;br /&gt;
Wir haben uns hier sehr viel mit Blending auseinander gesetzt und sicherlich juckt es Euch bereits unter den Fingern Wände mit wunderschönen Lichteffekten an die Wand zu zaubern. So mindestens 300 Lichter sollten sicherlich zum Minimum zählen, oder ;D&lt;br /&gt;
&lt;br /&gt;
Nun schnell werdet ihr vor allem in größeren Projekten merken, dass Euch das auf die Rechenleistung schlägt. Das Problem ist logisch! Nicht das Blending an sich ist die größte CPU-Last, sondern das Transformieren der Quads, auf denen sich die Lightmaps befinden. Eigentlich sind es fast nur die Transformationen, die so richtig mit Leistung zu Buch schlagen. Es gilt also so wenig wie mögliche Quads hintereinander zu erzeugen und somit die Anzahl der Durchläufe zu minimieren (Renderpass). Das ließe sich zum Beispiel damit erreichen, dass man versucht möglichst viele Lichtinformationen auf einer Lightmap zu setzen. So würde es keinen Sinn machen zwei rote Lichter einmal oben und einmal unten.&lt;br /&gt;
&lt;br /&gt;
Dies sind jedoch nur logische Einsparungen, die jeder Programmierer in seinem Projekt selbst entscheiden muss. Seit einiger Zeit gibt es jedoch auch ein Hardware-Verfahren, welches uns helfen kann ein Spiel zu optimieren. Zu Beginn der 3D-Beschleunigung verlief das Rendering wie folgt:&lt;br /&gt;
&lt;br /&gt;
 # Texture setzen&lt;br /&gt;
 # Objekt transformieren&lt;br /&gt;
 # Texture setzen&lt;br /&gt;
 # Objekt transformieren&lt;br /&gt;
&lt;br /&gt;
Klingt soweit auch alles logisch! Wie bereits jedoch erwähnt frisst vor allem das transformieren die Rechenpower und in unserem Fall liegt das Objekt übereinander, es wäre eine Transformation der Vertizen eigentlich gar nicht nötig. Wir müssten also nur die beiden Texturen auf ein transformiertes Objekt bekommen. Würden wir einfach nur zwei Texturen hintereinander setzen, so würde OpenGL die zuletzt gewählte verwenden und die andere schlichtweg ignorieren. Die meisten heutigen Grafikkarten (z.B: GeForce8800 GTX) haben 64 Textur-Units. Das muss man sich vorstellen wie 64 Schubladen. In die ersten beiden legen wir eine Textur rein und wenn OpenGL rendert wird reingeschaut und beide werden auf das Ziel gepappt. Somit haben wir uns eine Transformation gespart, was vor allem wenn man sehr viel mit Lightmaps arbeitet schon ein beachtlicher Geschwindigkeitsgewinn von knapp 50% ist! Einige sehr moderne Grafikkarten haben bereits 128 Texturen-Units und die Zeit bis es welche mit 256 gibt ist auch schon bereits voraussehbar ;)&lt;br /&gt;
&lt;br /&gt;
Wie funktioniert das ganze nun für uns? OpenGL verwendet als Standard die Unit 0. Binden wir eine Textur oder definieren das Blending so beziehen sich alle diese Angaben immer auf die momentan selektierte Textur-Unit. Alles was wir machen müssen ist, die gewünschte auszuwählen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glActiveTextureARB(GL_TEXTURE0_ARB);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ich denke der Parameter erklärt sich praktisch von selbst! GL_TEXTURE1_ARB wäre entsprechend die zweite Textur-Unit. Nun setzen wir alle Einstellungen für diese Unit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glEnable(GL_TEXTURE_2D);&lt;br /&gt;
  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);&lt;br /&gt;
  wall.SetTexture;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zunächst teilen wir der Unit mit, dass sie bei der 2D-texturierung zur Verfügung stehen soll. Anschließend setzen wir die Textur-Envroiment auf die &amp;quot;Standard-Einstellung&amp;quot;. Wenn mich jetzt jemand fragt, wofür die Parameter gut sind, dann zitiere ich das RedBook &amp;quot;es muss so sein&amp;quot; ;D Wichtig ist eigentlich nur, dass hinten GL_MODULATE steht, damit die Textur ordnungsgemäß, so wie wir sie sehen auf das Quad wandern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glActiveTextureARB(GL_TEXTURE1_ARB);&lt;br /&gt;
  glEnable(GL_TEXTURE_2D);&lt;br /&gt;
  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);&lt;br /&gt;
  glEnable(gl_BLEND);&lt;br /&gt;
  glBlendFunc(GL_ONE,GL_ONE);&lt;br /&gt;
  light.SetTexture;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir wechseln danach zur zweiten Textur-Unit und machen ähnliche Einstellungen, aktivieren zusätzlich jedoch Blending für diese Funktion. Diesmal etwas sehr simples.&lt;br /&gt;
&lt;br /&gt;
Wer nun gehofft hat, dass dies alles gewesen wäre, was man für das MultiTexturing benötigt, der wird enttäuscht sein. Eine Kleinigkeit gibt es noch zu beachten. Da wir zwei Texturen verwenden kann es ja auch sein, dass die UV-Koordinaten unterschiedlich sein sollen. In unserem Fall ist dies nicht so, aber es kann durchaus passieren. Deswegen müssen wir auch für jeden Eckpunkt eine UV-Koordinate vergeben. Hierfür verwenden wir nicht glTexCoord2f, sondern die Funktion:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glMultiTexCoord2fARB(GL_TEXTURE0_ARB,0,0);&lt;br /&gt;
  glMultiTexCoord2fARB(GL_TEXTURE1_ARB,0,0);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zunächst geben wir bei der Funktion den Parameter ein gefolgt von der eigentlichen UV-Koordinate. So gehen wir dann bei jedem Punkt vor und vorausgesetzt, dass unsere Grafikkarte auch Multitexturing unterstütz sollten wir uns an folgendem Bild auf dem Bildschirm ergötzen können (natürlich nur, wenn man auch die gleichen Texturen verwendet! ;D)&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_multitex.jpg]]&lt;br /&gt;
&lt;br /&gt;
== Nachwort ==&lt;br /&gt;
&lt;br /&gt;
Oh je, ich weiß, ich habe inzwischen einen Ruf dafür, dass ich immer gleich so große Portionen liefere und nicht in der Lage bin, einmal kurz ein kleines Tutorial zu schreiben. Das liegt einfach daran, dass wenn ich einmal angefangen bin und mich für etwas begeistern konnte nicht mehr aufhören kann. Und gerade bei solchen Themen, wo man so richtig schön mit Effekten experimentieren kann fällt es einem besonders schwer einen Punkt zu finden.&lt;br /&gt;
&lt;br /&gt;
Ich denke spätestens nach diesem Tutorial sollte man gelernt haben wie leicht man mit OpenGL einige wunderschöne Effekte zaubern kann ohne wirklich großen Aufwand zu betreiben. Viel leichter wird es kaum möglich sein. Sollte jemand nun sich am Kopf kratzen und sich fragen, ob es einen Grund hat, warum die ganzen hier vorgestellten Beispiele 2D sind? Nein, eigentlich nicht, denn alle der hier vorgestellten Techniken lassen sich auch prima auf 3D-Objekte übertragen. So wäre es zum Beispiel auch Möglich einen 3D-Soldaten mit einer Lightmap und Multitexturing zu überziehen und somit unterschiedliche Helligkeit auf ihn zu projizieren. Aber darauf will ich nicht weiter eingehen, nun kommt wieder Euer Part. ;)&lt;br /&gt;
&lt;br /&gt;
Auch bin ich sehr froh zu sehen, dass die Community wächst und auch endlich einen Punkt erreicht an der man gezielt auf Probleme einwirken kann. Ich hoffe sehr, dass dies auch weiterhin so bleibt und bin auch sehr froh, dass zunehmend Feedback von Eurer Seite kommt, gar einige damit beginnen Kontent für uns zur Verfügung zu stellen oder uns auf kleinere Fehler aufmerksam macht oder aber auch einfach nur auf eine gute Seite aufmerksam machen, die wir bisher nicht gelinkt haben. Wenn jemand von Euch einmal ein kleines OpenGL-Projekt vorstellen möchte, etwas an dem er arbeitet, schreibt uns doch ruhig auch einmal an. Mehr als Ablehnen könnten wir auch nicht ;D&lt;br /&gt;
&lt;br /&gt;
In diesem Sinne viel Spaß und auf Wiedersehen.&lt;br /&gt;
&lt;br /&gt;
Euer [[Benutzer:Phobeus|Phobeus]]&lt;br /&gt;
&lt;br /&gt;
== Dateien == &lt;br /&gt;
* {{ArchivLink|file=tut_lektion_7_delphi_api|text=Alter Delphi-API-Quelltext zum Tutorial}}&lt;br /&gt;
* {{ArchivLink|file=tut_lektion_7_exe|text=Windows-Binary zum Tutorial}}&lt;br /&gt;
&lt;br /&gt;
{{TUTORIAL_NAVIGATION | [[Tutorial Lektion 5]] | [[Tutorial Lektion 8]]}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Tutorial|Lektion7]]&lt;/div&gt;</summary>
		<author><name>Nevermind10844</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_Lektion_7&amp;diff=25703</id>
		<title>Tutorial Lektion 7</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_Lektion_7&amp;diff=25703"/>
				<updated>2012-09-19T21:19:11Z</updated>
		
		<summary type="html">&lt;p&gt;Nevermind10844: /* Gleichheit für Alles! */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Verblendet! =&lt;br /&gt;
&lt;br /&gt;
== Vorwort ==&lt;br /&gt;
&lt;br /&gt;
Nachdem wir nun ein wenig Erfahrung in der Praxis gesammelt haben, wenden wir uns einen anderen Bereich in der OpenGL-Programmierung zu, der uns helfen wird einige wirklich tolle Spezial-Effekte zu realisieren. Die meisten unter Euch werden den Begriff bereits das eine oder andere Mal gehört haben - die Rede ist vom so genannten Blending. Das berühmteste davon wird sicherlich das &amp;quot;Alpha Blending&amp;quot; sein. Wer nun glaubt Blending ist, wenn man sich ne Halogen-Werfer nimmt und damit versucht seinen Nachbarn zu quälen, ist auf dem Holzweg ;)&lt;br /&gt;
&lt;br /&gt;
Blending weist OpenGL dazu an, zu entscheiden was passieren soll, wenn man zwei Objekte übereinander zeichnet. Im Normalfall wird das obere Objekt das untere überdecken, aber vielleicht wollen wir ja auch, dass das obere Objekt transparent ist und man hindurch blicken kann auf das untere? Wer denkt, dass hier bereits Schluss sei mit dem Blending, der ist schief gewickelt, denn hier beginnt erst die Welt des kreativen Geistes! Lasst Euch verzaubern von wunderschönen OpenGL-Effekten und heizt Euren Programmen grafisch ordentlich ein. Ich hoffe ihr habt Verständnis, dass ich nur auf Teilbereiche eingehen kann, denn die Palette von Möglichkeiten ist keine Grenze gesetzt ;)&lt;br /&gt;
&lt;br /&gt;
Ich hoffe dennoch, dass auch das Tutorial gefallen wird und es wenigstens halbwegs verständlich rüberkommt. Auch gibt es für viele Dinge besser Lösungsmöglichkeiten, vieles hängt von der benutzen Textur ab oder aber auch wie viele davon verwendet werden. Um ein ausgiebiges Testen der Funktionen wird man nicht herum kommen. Also ran an die Arbeit und viel Spaß dabei ;)&lt;br /&gt;
&lt;br /&gt;
== Die vorhandene Welt und was darüber liegt ==&lt;br /&gt;
&lt;br /&gt;
=== Was ist den nun blenden? ===&lt;br /&gt;
&lt;br /&gt;
Wichtig beim Blending ist es zu wissen, was genau eigentlich beim rendern geschieht. OpenGL erhält eine Anweisung etwas zu zeichnen. Dieses wird dann in den so genannten &amp;quot;Frame-Buffer&amp;quot; gespeichert, das ist der Buffer,den wir dann auch meistens bei jedem Rendering mit glClear &amp;quot;löschen&amp;quot;. (Wir erinnern uns, führen wir kein glClear aus, sehen wir nach jedem Rendering den alten Inhalt… habe versprochen, dass wir darauf zurück kommen ;) ). Das heißt eigentlich, dass wir bei jedem Rendern eines Objektes immer wieder etwas in den Frame-Buffer schreiben und dann wenn wir denken, dass alles fertig ist diesen Inhalt auf dem Bildschirm projizieren.&lt;br /&gt;
&lt;br /&gt;
Das Blending setzt nun genau an der Stelle an, an dem etwas in den Frame-Buffer geschrieben werden soll. OpenGL hat praktisch bereits ein 3D-Objekt im Frame-Buffer und zeichnet dann das neue 3D Objekt. Nun können wir OpenGL mitteilen, was in einer solchen Situation geschehen soll. Standard gemäß würde das obere Objekt, dass darunter liegende verdecken.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glBlendFunc(source,destination);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit dieser Anweisung können wir dieses Verhalten ändern. Wir geben zwei Faktoren an, den für die Quelle und den für das Ziel. Ich versuche das mal abstrakt zu erklären. Wie bereits gesagt, handelt es sich dabei um Faktoren. Nehmen wir mal an Source (Textur) wäre 1 und Destination (Framebuffer) 0, dann würde OpenGL beim zusammensetzen der Szene, das was bereits im Frame-Buffer ist total vernachlässigen und das neue, was darüber gezeichnet werden soll komplett drauf zeichnen. Je höher der Faktor einer dieser Beiden Werte ist, desto stärke werden die Farben beim Blending berücksichtigt.Das ganze funktioniert natürlich auch nur, wenn wir OpenGL dazu anhalten das Blending zu verwenden…&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glEnable(GL_BLEND);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das ganze klingt sicherlich sehr komplex, (ist es auch *sg*), aber an Hand einiger Beispiele sollte es einem klar werden. Nehmen wir folgende Bilder:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;center&amp;quot;&lt;br /&gt;
|[[Bild:Tutorial_lektion7_rock_tex.jpg]]&lt;br /&gt;
|[[Bild:Tutorial_lektion7_gras_tex.jpg]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese beiden Texturen zeichnen wir jeweils auf einem Quad und zwar exakt aufeinander. Wir zeichnen zunächst die steinige Fläche und erhalten:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_stone.jpg]]&lt;br /&gt;
&lt;br /&gt;
Soweit so gut… nun setzen wir folgenden Blend-Faktor:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glBlendFunc(GL_ONE,GL_ZERO);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und zeichnen die Gras-Textur darüber:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_gras.jpg]]&lt;br /&gt;
&lt;br /&gt;
Hmm… scheint nicht, als ob das Blending wirklich gut funktioniert hat, da wir nur die Textur sehen, die wir als letztes gezeichnet haben. Scheint soweit alles wie bisher zu sein. :-/&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glBlendFunc(GL_ONE,GL_ZERO);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Quatsch! Natürlich hat alles funktioniert wie wir es erwartet haben! Wir haben schließlich OpenGL dazu angewiesen von der neuen Textur &amp;quot;alles&amp;quot; zu nehmen und von dem, was im Frame-Buffer ist &amp;quot;nichts&amp;quot;. Auf neudeutsch, wir haben ihn angewiesen die alte Textur über die andere zu zeichnen. Würden wir hingegen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glBlendFunc(GL_ZERO,GL_ONE);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwenden, so würden wir OpenGL dazu anhalten vom Frame-Buffer &amp;quot;alles&amp;quot; zu übernehmen und von der neuen Textur &amp;quot;nichts&amp;quot;, das würde bedeuten dass wir ins leere Rendern (sehr gute Methode seine Engine zu optimieren… *sg*)&lt;br /&gt;
&lt;br /&gt;
Ach so noch etwas… nehmt das mit dem GL_ZERO und GL_ONE und meinem &amp;quot;nichts und alles&amp;quot; nicht ganz so wörtlich. Es ist mehr eine kleine Eselsbrücke, die einem am Anfang hilft nicht total zu verwirren. Technisch macht OpenGL nichts anderes als bei GL_ONE die Farbe der Texture auf (1,1,1,1) zu setzen bei GL_ZERO auf (0,0,0,0); Und sobald es &amp;quot;schwarz&amp;quot; ist, sieht man sie halt nicht mehr, klingt logisch oder?&lt;br /&gt;
&lt;br /&gt;
=== Claim for the World Order ===&lt;br /&gt;
&lt;br /&gt;
Wie alles hat auch das Blending einen kleinen Schönheitsfehler. Viele Leute denken immer wieder, dass auch beim Blending praktisch eine 3D-Welt existiert, d.h. man hinrendern kann wo man will. Das ist jedoch falsch, da die Quelle ja im Frame-Buffer bereits vorliegt. Praktisch bereits fertig gerendert. Es macht also für das Blending einen enormen Unterschied, in welcher Reihenfolge die Objekte zur Kamera hin gerendert werden.&lt;br /&gt;
&lt;br /&gt;
Würden wir zum Beispiel ein Haus mit einem Fenster render wollen und das Fensterglas mit Hilfe von Blending darstellen und wir würden zunächst das Glas rendern, so würde OpenGL dieses mit dem Hintergrund (vermutlich dem Himmel) blenden und dann anschließend das Haus drum herum zeichnen. Der ahnungslose Programmierer wird dann mit großen Augen vor dem Fenster stehen und sich wundern, wieso er durch die hintere Wand des Hauses ins Freie sehen kann. &lt;br /&gt;
&lt;br /&gt;
Kurz: Auch durchsichtige Objekte sind Objekte. Auch sie schreiben Werte in den [[Tiefenpuffer]]. Deshalb kann man nicht &amp;quot;hinter&amp;quot; durchsichtige Objekte etwas rendern.&lt;br /&gt;
&lt;br /&gt;
Ohne Blending, würde so etwas nicht weiter auffallen, mit Blending muss man Acht geben in welcher Reihenfolge wir die Objekte rendern.&lt;br /&gt;
&lt;br /&gt;
Darum gilt: '''alle undurchsichtigen Objekte rendern, bevor man zum Blending greift.''' Man sollte sich also durchaus Gedanken dazu machen, wie man seine Objekte rendert. Es gibt verschiedene Verfahren seine Objekte so ordnen zu lassen, dass das hier beschrieben Problem nicht auftritt, darauf möchte ich nicht näher eingehen, ihr seid gewarnt worden ;D Eigentlich ist es ein recht logischer Vorgang, allerdings erliegt man doch recht schnell dem kleinen Irrglauben hinter eine Scheibe rendern zu können. ;)&lt;br /&gt;
&lt;br /&gt;
=== Gleichheit für Alles! ===&lt;br /&gt;
&lt;br /&gt;
Das war ja nun auch ein interessanter Ausflug… gelernt haben wir immer noch nichts wirklich Neues, zumindest nichts, was man auch sehen kann. Üben wir uns doch nun einmal richtig ein und versuchen zwei Texturen zu gleichen Maßen zusammen zu blenden, so dass es aussieht, als hätten wir eine Textur. Sicherlich gibt es viele Möglichkeiten dies zu realisieren, allerdings möchte ich an dieser Stelle einen Alpha-Farbwert verwenden. Wie ihr sicherlich wisst, werden Farben meist im RGB-Werten gespeichert, sprich Rot, Grün und Blau! Gesetzt wird dieses mit glColor3f. Ebenfalls gibt es noch die Funktion glColor4f mit der wir einen RGBA-Wert vergeben können. Dieser wird uns vor allem beim Blending nützliche Dienste erweisen.&lt;br /&gt;
&lt;br /&gt;
Weil beide Texturen zu gleichen Prozentsätzen geblendet werden sollen ergibt sich daraus folglich ein Aufruf von &amp;quot;glColor4f(1,1,1,0.5)&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
Wie ihr sehen könnt setzen wir diesmal glBlendFunc(GL_SRC_ALPHA,GL_ONE); ein. Von der Quelle wird der Alpha-Wert als Blend-Faktor genommen, dass Ziel wird übernommen.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_stonegras.jpg]]&lt;br /&gt;
&lt;br /&gt;
Wenn das nicht aussieht wie eine Textur, die wie aus dem gleichen Guss kommt! Würde noch jemand erahnen, dass es sich dabei eigentlich um zwei Texturen handelt? Für alle, interessierten noch einmal kurz eine Tabelle zur Übersicht der Funktionen, die wir bisher eingesetzt haben und wir noch einsetzen werden, sowie der mathematischen Erklärung ihrer Bedeutung:&lt;br /&gt;
&lt;br /&gt;
{| {{Prettytable_B1}}&lt;br /&gt;
! Konstante&lt;br /&gt;
! Auf Parameter anwendbar&lt;br /&gt;
! Berechneter Blend-Faktor&lt;br /&gt;
|-&lt;br /&gt;
| GL_ZERO&lt;br /&gt;
| source or destination&lt;br /&gt;
| (0, 0, 0, 0)&lt;br /&gt;
|-&lt;br /&gt;
| GL_ONE&lt;br /&gt;
| source or destination&lt;br /&gt;
| (1, 1, 1, 1)&lt;br /&gt;
|-&lt;br /&gt;
| GL_DST_COLOR&lt;br /&gt;
| source&lt;br /&gt;
| (Rd, Gd, Bd, Ad)&lt;br /&gt;
|-&lt;br /&gt;
| GL_SRC_COLOR&lt;br /&gt;
| destination&lt;br /&gt;
| (Rs, Gs, Bs, As)&lt;br /&gt;
|-&lt;br /&gt;
| GL_ONE_MINUS_DST_COLOR&lt;br /&gt;
| source&lt;br /&gt;
| (1, 1, 1, 1)-(Rd, Gd, Bd, Ad)&lt;br /&gt;
|-&lt;br /&gt;
| GL_ONE_MINUS_SRC_COLOR&lt;br /&gt;
| destination&lt;br /&gt;
| (1, 1, 1, 1)-(Rs, Gs, Bs, As)&lt;br /&gt;
|-&lt;br /&gt;
| GL_SRC_ALPHA&lt;br /&gt;
| source or destination&lt;br /&gt;
| (As, As, As, As)&lt;br /&gt;
|-&lt;br /&gt;
| GL_ONE_MINUS_SRC_ALPH&lt;br /&gt;
| source or destination&lt;br /&gt;
| (1, 1, 1, 1)-(As, As, As, As)&lt;br /&gt;
|-&lt;br /&gt;
| GL_DST_ALPHA&lt;br /&gt;
| source or destination&lt;br /&gt;
| (Ad, Ad, Ad, Ad)&lt;br /&gt;
|-&lt;br /&gt;
| GL_ONE_MINUS_DST_ALPHA&lt;br /&gt;
| source or destination&lt;br /&gt;
| (1, 1, 1, 1)-(Ad, Ad, Ad, Ad)&lt;br /&gt;
|-&lt;br /&gt;
| GL_SRC_ALPHA_SATURATE&lt;br /&gt;
| source&lt;br /&gt;
| (f, f, f, 1); f=min(As, 1-Ad)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Sieht anfangs etwas verwirrend aus, aber der Mensch kann sich an vieles gewöhnen. Immerhin lest ihr bereits seit einiger Zeit Texte, die mit meinen Rechtschreibfehlern und meiner Sprache angereichert sind ;D&lt;br /&gt;
&lt;br /&gt;
Um sicher zu gehen, dass auch wirklich alles verstanden ist nehmen wir uns noch einmal kurz folgenden Aufruf zu herzen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glBlendFunc(GL_ONE, GL_SRC_COLOR); // Zur Info: GL_SRC_COLOR darf nur auf &lt;br /&gt;
                                     // den Paramter destination angewendet &lt;br /&gt;
                                     // werden, siehe dazu obenstehende Tabelle&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wieder lassen wir den Frame-Buffer wie er ist, verwenden diesmal jedoch keinen Alpha-Wert zu blenden, sondern jeweils die RGB-Werte der Textur. OpenGL macht also praktisch nichts anderes als die Farbwerte zu addieren und anschließend durch zwei zu teilen. Wie man der Tabelle entnehmen kann spielt bei einem solchen Aufruf nur die Destination eine Rolle. Folglich sieht das Ergebnis wie folgt aus:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_stonegras2.jpg]]&lt;br /&gt;
&lt;br /&gt;
Soweit nun zum einfachen Blending. Ihr könnt Euch vorstellen, dass wir damit bereits eine Menge sehr interessanter Effekte erzeugen können, aber nun wollen wir einige davon auch gleich einmal testen und einsetzen ;)&lt;br /&gt;
&lt;br /&gt;
== Blending-Verfahren ==&lt;br /&gt;
&lt;br /&gt;
=== Die Welt unter einer Maske ===&lt;br /&gt;
&lt;br /&gt;
Ein Verfahren, welches sehr häufig zum Einsatz kommt ist das &amp;quot;Masking-Verfahren&amp;quot;. Aus dem Namen kann man sich bereits grob vorstellen, worum es geht. Wenn der Mensch sich eine Maske aufsetzt, so versucht er etwas damit zu verdecken (zum Beispiel sein Gesicht). Genauso machen wir es auch mit OpenGL um etwas zu verdecken. In diesem Fall jedoch, das Objekt welches wir unter der Maske liegen haben. Es gibt einen Haufen von sinnvollen Verwendungs-Möglichkeiten. Beim Masking an sich geht es darum, nur einen bestimmten Bereich sichtbar zu machen. Nehmen wir also einmal wieder unsere schön Stein-Textur und erzeugen in einem Malprogramm unserer Wahl eine Maske. Diese wird in diesem Fall komplett weiß sein und die eigentliche Masken-Bereiche sind schwarz.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_dgl.gif]]&lt;br /&gt;
&lt;br /&gt;
Diese Maske zeichnen wir nun selbstredend über der Steintextur und verwenden dabei folgende Blending-Funktion:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glBlendFunc(GL_DST_COLOR, GL_ONE); // Zur Info: GL_DST_COLOR darf nur &lt;br /&gt;
                                     // auf den Parameter source angewendet&lt;br /&gt;
                                     // werden. Siehe dazu obenstehende Tabelle.&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Als Ergebnis erhalten wir folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_whitemask.jpg]]&lt;br /&gt;
&lt;br /&gt;
Alles was an der Quelle weiß ist, wird bei dem Source-Faktor enorm hoch bewertet also sehr &amp;quot;weiß&amp;quot; gemacht. Da Schwarz das Gegenteil von Weiß ist, wird dieses eben total vernachlässigt. Sprich wir können hindurch sehen. Da der Destination-Faktor auf die Farbe des &amp;quot;Ziel-Pixels&amp;quot; gesetzt ist sehen wir dann an den entsprechenden Stellen auch den Hintergrund. Wollen wir das Spielchen mal umdrehen, so dass man nur noch die schwarze Schrift sieht und ansonsten durch die zweite Textur komplett hindurch blicken kann? Kein Problem!&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_blackmask.jpg]]&lt;br /&gt;
&lt;br /&gt;
Und die dazugehörige Blend-Funktion :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
glBlendFunc(GL_ZERO,GL_SRC_COLOR);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besonders hoch bei der Quelle werten wir in diesem Fall das Schwarz. Bei der Zielfarbe bewerten wir die Farbe sehr hoch die die zweite Textur hat. D.h. alles was schön weiß ist wird entsprechend geblendet. Wer sich nun am Kopf kratzt sollte sich ein wenig Code schnappen und ein wenig damit herum spielen. Recht schnell merkt man, wie es funktioniert, den Blending ist unter OpenGL gar nicht so schwer! Am besten auch gleich die obere Tabelle ausdrucken und daneben liegen haben ;)&lt;br /&gt;
&lt;br /&gt;
=== Der Überläufer ===&lt;br /&gt;
&lt;br /&gt;
Ich hoffe sehr, dass ich Euch jetzt hier nicht zu sehr langweile in dem ich andauernd die eine oder andere Funktion immer wieder und wieder in anderer Form zeige. Ich möchte schlicht und ergreifend damit erreichen, dass ihr ein wenig für die Möglichkeiten die man hat sensilibisiert werdet. Die Vielfalt an Effekten, die man erzielen kann ist nämlich wirklich groß, vor allem, wenn man einzelne Effekte miteinander kombiniert!&lt;br /&gt;
&lt;br /&gt;
Nehme wir uns noch einmal unsere Stein- und Gras-Textur zur Hand. Diesmal wollen wir versuchen, dass auf der linken Seite die Stein-Textur ist und auf der Rechten die Gras-Textur. Das ganze soll einen flüssigen Übergang ergeben.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_graswall.jpg]]&lt;br /&gt;
&lt;br /&gt;
Auch ein solcher Effekt ist nicht sonderlich schwer. Alles was wir hierfür brauchen sind unterschiedliche Alpha-Werte an den Eckpunkten der Quads. Beim ersten müssen diese links 1 sein und rechts 0, beim anderen entgegengesetzt. Setzen tun wir die Alpha-Werte mit glColor4f:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  Rock.SetTexture;&lt;br /&gt;
  glBegin(gl_Quads);&lt;br /&gt;
    glColor4f(1,1,1,1);&lt;br /&gt;
    glTexCoord2f(0,0);&lt;br /&gt;
    glVertex3f(-1,-1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor4f(1,1,1,0);&lt;br /&gt;
    glTexCoord2f(1,0);&lt;br /&gt;
    glVertex3f(1,-1,0);&lt;br /&gt;
    glTexCoord2f(1,1);&lt;br /&gt;
    glvertex3f(1,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor4f(1,1,1,1);&lt;br /&gt;
    glTexCoord2f(0,1);&lt;br /&gt;
    glvertex3f(-1,1,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&lt;br /&gt;
  glDisable(GL_DEPTH_TEST); // Wichtig! Da wir sonst das erste Quad verwerfen&lt;br /&gt;
                            // und damit auch die Textur, der Aufruf könnte auch &lt;br /&gt;
                            // in einer Init Funktion stehen.&lt;br /&gt;
  glEnable(GL_BLEND);&lt;br /&gt;
  glBlendFunc(GL_SRC_ALPHA,GL_DST_ALPHA);&lt;br /&gt;
&lt;br /&gt;
  gras.SetTexture;&lt;br /&gt;
  glBegin(gl_Quads);&lt;br /&gt;
    glColor4f(1,1,1,0);&lt;br /&gt;
    glTexCoord2f(0,0);&lt;br /&gt;
    glVertex3f(-1,-1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor4f(1,1,1,1);&lt;br /&gt;
    glTexCoord2f(1,0);&lt;br /&gt;
    glVertex3f(1,-1,0);&lt;br /&gt;
    glTexCoord2f(1,1);&lt;br /&gt;
    glvertex3f(1,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor4f(1,1,1,0);&lt;br /&gt;
    glTexCoord2f(0,1);&lt;br /&gt;
    glvertex3f(-1,1,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Während das erste Quad ohne Blending gezeichnet wird, wird das zweite geblendet, jeweils mit den Alpha-Werten der Quelle und des Zieles. Um dies zu verdeutlichen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_redbluealpha.jpg]]&lt;br /&gt;
&lt;br /&gt;
Je blauer die Farbe, desto weniger wird die Wand gefadet, je roter, desto mehr Gras wird angezeigt. In der Mitte verlaufen beide Texturen zusammen. Natürlich kann man nun anfangen zu spielen und gibt den Seiten andere Alpha-Werte. Genauso wäre es möglich drei Texturen ineinander einzublenden etc. Seit kreativ ;D&lt;br /&gt;
&lt;br /&gt;
=== Der Überläufer - Teil 2 ===&lt;br /&gt;
&lt;br /&gt;
Der eben beschriebene Effekt lässt sich auch noch sehr schön erweitern. Haben wir dort eine gerade Trennlinie zwischen den beiden Texturen gehabt, so werden wir uns nun mit einer Technik befassen, die es einem ermöglicht unregelmäßige Übergange zu erzeigen. Dieses Verfahren wird besondern oft bei Terrains angewandt, um zum Beispiel eine Schneelandschaft langsam ins Geröll überwandern zu lassen. Je nach Verfahren benötigen wir hierfür drei oder vier Texturen. Wir beschränken uns hierbei einfach auf drei.&lt;br /&gt;
&lt;br /&gt;
Würden wir zwei Texturen übereinander zeichnen, so würde die obere die untere Verdecken, würden wir bei einem Quad mit Alpha-Werten arbeiten würden die Übergange nicht flüssig sein. Würden wir die Farben addieren, würde so ziemlich alles dabei rauskommen, nur nicht einen Tetxuren-Verlauf, wie wir ihn erreichen wollen.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_blendmap.jpg]]&lt;br /&gt;
&lt;br /&gt;
Auf diesem Bild erkennt man sehr schön den Texturen-Verlauf, während im oberen Bereich herum bis nach links unten und rechts oben die steinige Felstextur ist, ist im unteren Bereich mehr die Gras-Textur, mit einer Ausnahme mitten in der Gras-Textur ist wiederum ein kleiner Abschnitt mit einer felsigen Textur (hier lila). Auch erkennt man auf dem zweiten Blick, dass an den Grenz-Bereichen ein wenig das Grüne auf dem Felsen durchschimmert, wir haben einen fließenden Übergang zu beiden Texturen erzeugt und können sogar im &amp;quot;Gebiet des Anderen&amp;quot; eine Textur durchscheinen lassen. Die Lösung ist wiederum eine Art von Masking:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_blendmap2.jpg]]&lt;br /&gt;
&lt;br /&gt;
Vergleicht man dieses Bild mit dem oberen wird man erkennen, dass die hellern Flecken &amp;quot;Stein-Textur&amp;quot; und die dunklen &amp;quot;Gras&amp;quot; bedeuten. Man redet von solchen Texturen meist von &amp;quot;Blend-Maps&amp;quot;. Technisch braucht man also nur eine Blend-Map für eine Textur und ein invertiertes Bild davon. An diesem Punkt können wir uns das invertieren bereits sparen, weil wir nur zwei Texturen blenden wollen und das Gegenteil von weiß, schwarz ist ;)&lt;br /&gt;
&lt;br /&gt;
== Lichterkarten ==&lt;br /&gt;
=== Und es ward Licht… ===&lt;br /&gt;
&lt;br /&gt;
Ein wirklich tolles Spielzeug, welches einen mit Blending in die Hand gelegt wird sind &amp;quot;Lightmaps&amp;quot;. Vielleicht hat jemand von Euch bereits einmal versucht in seinem Projekt so schöne Lichtquellen einzubauen, wie er es aus bekannten kommerziellen Spielen kennt und hat sofort anschließend ein langes Gesicht gezogen als die Augen auf die Framerate blickten. Dazu kommt, dass das OpenGL-Licht meist nicht einmal besonders ansehnlich ist. Ärgerlich…&lt;br /&gt;
&lt;br /&gt;
Doch es gibt natürlich eine Möglichkeit schnell und effizient mit wenig Aufwand genauso schöne Lichter in seinem Projekt einzubauen. Das Geheimnis ist schlicht und ergreifend, dass wir nicht auf OpenGL-Lichter aufbauen, sondern einen kleinen Trick auf unsere Benutzer spielen. Wir zeichnen vor dem Zielobjekt eine neue Texture, die nur aus Graustufen besteht. Je heller ein Punkt ist, desto mehr soll er anschließend leuchten.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;center&amp;quot;&lt;br /&gt;
|[[Bild:Tutorial_lektion7_lightmap.jpg]]&lt;br /&gt;
|[[Bild:Tutorial_lektion7_wall_tex.jpg]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Macht grafisch doch schon einiges mehr her als die ursprüngliche Textur oder? Wie bereits gesagt, dass Geheimnis sind die Lightmaps, die wir vor der Wand zeichnen:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;center&amp;quot;&lt;br /&gt;
|[[Bild:Tutorial_lektion7_light.jpg]]&lt;br /&gt;
|[[Bild:Tutorial_lektion7_light2.jpg]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit etwas Fantasie kann man sie sogar immer noch erkennen. Man beachte, dass die erste Lightmap vor allem im oberen Bereich besonders hell ist und vor allem einen sehr hellen Punkt im oberen Bereich hat. Blicken wir auf das fertige Bild, so sehen wir dort einen roten Punkt. Die zweite Lightmap ist eher gleichmäßig und matt, nicht sonderlich hell, zumindest nicht auffällig. Sie sorgt dafür, dass ein gleichmäßiger Grünton auf der Wand liegt. Doch wie kann aus der weißen Lightmap z.B. eine Rote werden? Das ist relativ leicht, wir zeichnen das Quad vor der Wand einfach rot mit Hilfe von glColor3f(1,0.7,0.7); Damit die Farbe auch richtig zur Geltung kommt müssen diese natürlich auch beim Blending entsprechend einarbeiten!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glBlendFunc(GL_DST_COLOR,GL_SRC_COLOR);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei der Quelle wird vor allem die Farbe berücksichtigt, bei der Destination die Zielfarbe. Jeweils zwei Quads werden nach diesem Verfahren über die Wand gezeichnet, um den Effekt zu erreichen. Bin mir durchaus bewusst, dass man schönere Lightmaps machen kann, auch ist es zum Beispiel sehr interessant bei der Zielfarbe GL_SRC_COLOR zu verwenden, wodurch die gesamte Wand verdunkelt wird und man nur noch um das rote leuchten oben etwas sehen könnte (in diesem Fall nur eine Lightmap!)&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_lightmap2.jpg]]&lt;br /&gt;
&lt;br /&gt;
Der Kreativität sind eigentlich keine Grenzen gesetzt. Alle modernen 3D-Engines berechnen bei einem Level an Hand der Lichtquellen solche Lightmaps und projizieren diese dann vor den Wänden, um sie aussehen zu lassen, wie sie aussehen sollen. Keiner dieses wunderschönen Effekts wird man mit einem OpenGL-Licht realisieren können. Wozu auch, wenn es so doch viel schneller geht ;)&lt;br /&gt;
&lt;br /&gt;
=== Die Kraft des Gemeinsamen! ===&lt;br /&gt;
&lt;br /&gt;
Wir haben uns hier sehr viel mit Blending auseinander gesetzt und sicherlich juckt es Euch bereits unter den Fingern Wände mit wunderschönen Lichteffekten an die Wand zu zaubern. So mindestens 300 Lichter sollten sicherlich zum Minimum zählen, oder ;D&lt;br /&gt;
&lt;br /&gt;
Nun schnell werdet ihr vor allem in größeren Projekten merken, dass Euch das auf die Rechenleistung schlägt. Das Problem ist logisch! Nicht das Blending an sich ist die größte CPU-Last, sondern das Transformieren der Quads, auf denen sich die Lightmaps befinden. Eigentlich sind es fast nur die Transformationen, die so richtig mit Leistung zu Buch schlagen. Es gilt also so wenig wie mögliche Quads hintereinander zu erzeugen und somit die Anzahl der Durchläufe zu minimieren (Renderpass). Das ließe sich zum Beispiel damit erreichen, dass man versucht möglichst viele Lichtinformationen auf einer Lightmap zu setzen. So würde es keinen Sinn machen zwei rote Lichter einmal oben und einmal unten.&lt;br /&gt;
&lt;br /&gt;
Dies sind jedoch nur logische Einsparungen, die jeder Programmierer in seinem Projekt selbst entscheiden muss. Seit einiger Zeit gibt es jedoch auch ein Hardware-Verfahren, welches uns helfen kann ein Spiel zu optimieren. Zu Beginn der 3D-Beschleunigung verlief das Rendering wie folgt:&lt;br /&gt;
&lt;br /&gt;
 # Texture setzen&lt;br /&gt;
 # Objekt transformieren&lt;br /&gt;
 # Texture setzen&lt;br /&gt;
 # Objekt transformieren&lt;br /&gt;
&lt;br /&gt;
Klingt soweit auch alles logisch! Wie bereits jedoch erwähnt frisst vor allem das transformieren die Rechenpower und in unserem Fall liegt das Objekt übereinander, es wäre eine Transformation der Vertizen eigentlich gar nicht nötig. Wir müssten also nur die beiden Texturen auf ein transformiertes Objekt bekommen. Würden wir einfach nur zwei Texturen hintereinander setzen, so würde OpenGL die zuletzt gewählte verwenden und die andere schlichtweg ignorieren. Die meisten heutigen Grafikkarten (z.B: GeForce8800 GTX) haben 64 Textur-Units. Das muss man sich vorstellen wie 64 Schubladen. In die ersten beiden legen wir eine Textur rein und wenn OpenGL rendert wird reingeschaut und beide werden auf das Ziel gepappt. Somit haben wir uns eine Transformation gespart, was vor allem wenn man sehr viel mit Lightmaps arbeitet schon ein beachtlicher Geschwindigkeitsgewinn von knapp 50% ist! Einige sehr moderne Grafikkarten haben bereits 128 Texturen-Units und die Zeit bis es welche mit 256 gibt ist auch schon bereits voraussehbar ;)&lt;br /&gt;
&lt;br /&gt;
Wie funktioniert das ganze nun für uns? OpenGL verwendet als Standard die Unit 0. Binden wir eine Textur oder definieren das Blending so beziehen sich alle diese Angaben immer auf die momentan selektierte Textur-Unit. Alles was wir machen müssen ist, die gewünschte auszuwählen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glActiveTextureARB(GL_TEXTURE0_ARB);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ich denke der Parameter erklärt sich praktisch von selbst! GL_TEXTURE1_ARB wäre entsprechend die zweite Textur-Unit. Nun setzen wir alle Einstellungen für diese Unit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glEnable(GL_TEXTURE_2D);&lt;br /&gt;
  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);&lt;br /&gt;
  wall.SetTexture;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zunächst teilen wir der Unit mit, dass sie bei der 2D-texturierung zur Verfügung stehen soll. Anschließend setzen wir die Textur-Envroiment auf die &amp;quot;Standard-Einstellung&amp;quot;. Wenn mich jetzt jemand fragt, wofür die Parameter gut sind, dann zitiere ich das RedBook &amp;quot;es muss so sein&amp;quot; ;D Wichtig ist eigentlich nur, dass hinten GL_MODULATE steht, damit die Textur ordnungsgemäß, so wie wir sie sehen auf das Quad wandern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glActiveTextureARB(GL_TEXTURE1_ARB);&lt;br /&gt;
  glEnable(GL_TEXTURE_2D);&lt;br /&gt;
  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);&lt;br /&gt;
  glEnable(gl_BLEND);&lt;br /&gt;
  glBlendFunc(GL_ONE,GL_ONE);&lt;br /&gt;
  light.SetTexture;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir wechseln danach zur zweiten Textur-Unit und machen ähnliche Einstellungen, aktivieren zusätzlich jedoch Blending für diese Funktion. Diesmal etwas sehr simples.&lt;br /&gt;
&lt;br /&gt;
Wer nun gehofft hat, dass dies alles gewesen wäre, was man für das MultiTexturing benötigt, der wird enttäuscht sein. Eine Kleinigkeit gibt es noch zu beachten. Da wir zwei Texturen verwenden kann es ja auch sein, dass die UV-Koordinaten unterschiedlich sein sollen. In unserem Fall ist dies nicht so, aber es kann durchaus passieren. Deswegen müssen wir auch für jeden Eckpunkt eine UV-Koordinate vergeben. Hierfür verwenden wir nicht glTexCoord2f, sondern die Funktion:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glMultiTexCoord2fARB(GL_TEXTURE0_ARB,0,0);&lt;br /&gt;
  glMultiTexCoord2fARB(GL_TEXTURE1_ARB,0,0);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zunächst geben wir bei der Funktion den Parameter ein gefolgt von der eigentlichen UV-Koordinate. So gehen wir dann bei jedem Punkt vor und vorausgesetzt, dass unsere Grafikkarte auch Multitexturing unterstütz sollten wir uns an folgendem Bild auf dem Bildschirm ergötzen können (natürlich nur, wenn man auch die gleichen Texturen verwendet! ;D)&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_multitex.jpg]]&lt;br /&gt;
&lt;br /&gt;
== Nachwort ==&lt;br /&gt;
&lt;br /&gt;
Oh je, ich weiß, ich habe inzwischen einen Ruf dafür, dass ich immer gleich so große Portionen liefere und nicht in der Lage bin, einmal kurz ein kleines Tutorial zu schreiben. Das liegt einfach daran, dass wenn ich einmal angefangen bin und mich für etwas begeistern konnte nicht mehr aufhören kann. Und gerade bei solchen Themen, wo man so richtig schön mit Effekten experimentieren kann fällt es einem besonders schwer einen Punkt zu finden.&lt;br /&gt;
&lt;br /&gt;
Ich denke spätestens nach diesem Tutorial sollte man gelernt haben wie leicht man mit OpenGL einige wunderschöne Effekte zaubern kann ohne wirklich großen Aufwand zu betreiben. Viel leichter wird es kaum möglich sein. Sollte jemand nun sich am Kopf kratzen und sich fragen, ob es einen Grund hat, warum die ganzen hier vorgestellten Beispiele 2D sind? Nein, eigentlich nicht, denn alle der hier vorgestellten Techniken lassen sich auch prima auf 3D-Objekte übertragen. So wäre es zum Beispiel auch Möglich einen 3D-Soldaten mit einer Lightmap und Multitexturing zu überziehen und somit unterschiedliche Helligkeit auf ihn zu projizieren. Aber darauf will ich nicht weiter eingehen, nun kommt wieder Euer Part. ;)&lt;br /&gt;
&lt;br /&gt;
Auch bin ich sehr froh zu sehen, dass die Community wächst und auch endlich einen Punkt erreicht an der man gezielt auf Probleme einwirken kann. Ich hoffe sehr, dass dies auch weiterhin so bleibt und bin auch sehr froh, dass zunehmend Feedback von Eurer Seite kommt, gar einige damit beginnen Kontent für uns zur Verfügung zu stellen oder uns auf kleinere Fehler aufmerksam macht oder aber auch einfach nur auf eine gute Seite aufmerksam machen, die wir bisher nicht gelinkt haben. Wenn jemand von Euch einmal ein kleines OpenGL-Projekt vorstellen möchte, etwas an dem er arbeitet, schreibt uns doch ruhig auch einmal an. Mehr als Ablehnen könnten wir auch nicht ;D&lt;br /&gt;
&lt;br /&gt;
In diesem Sinne viel Spaß und auf Wiedersehen.&lt;br /&gt;
&lt;br /&gt;
Euer [[Benutzer:Phobeus|Phobeus]]&lt;br /&gt;
&lt;br /&gt;
== Dateien == &lt;br /&gt;
* {{ArchivLink|file=tut_lektion_7_delphi_api|text=Alter Delphi-API-Quelltext zum Tutorial}}&lt;br /&gt;
* {{ArchivLink|file=tut_lektion_7_exe|text=Windows-Binary zum Tutorial}}&lt;br /&gt;
&lt;br /&gt;
{{TUTORIAL_NAVIGATION | [[Tutorial Lektion 5]] | [[Tutorial Lektion 8]]}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Tutorial|Lektion7]]&lt;/div&gt;</summary>
		<author><name>Nevermind10844</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_Lektion_7&amp;diff=25702</id>
		<title>Tutorial Lektion 7</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_Lektion_7&amp;diff=25702"/>
				<updated>2012-09-19T21:16:17Z</updated>
		
		<summary type="html">&lt;p&gt;Nevermind10844: /* Claim for the World Order */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Verblendet! =&lt;br /&gt;
&lt;br /&gt;
== Vorwort ==&lt;br /&gt;
&lt;br /&gt;
Nachdem wir nun ein wenig Erfahrung in der Praxis gesammelt haben, wenden wir uns einen anderen Bereich in der OpenGL-Programmierung zu, der uns helfen wird einige wirklich tolle Spezial-Effekte zu realisieren. Die meisten unter Euch werden den Begriff bereits das eine oder andere Mal gehört haben - die Rede ist vom so genannten Blending. Das berühmteste davon wird sicherlich das &amp;quot;Alpha Blending&amp;quot; sein. Wer nun glaubt Blending ist, wenn man sich ne Halogen-Werfer nimmt und damit versucht seinen Nachbarn zu quälen, ist auf dem Holzweg ;)&lt;br /&gt;
&lt;br /&gt;
Blending weist OpenGL dazu an, zu entscheiden was passieren soll, wenn man zwei Objekte übereinander zeichnet. Im Normalfall wird das obere Objekt das untere überdecken, aber vielleicht wollen wir ja auch, dass das obere Objekt transparent ist und man hindurch blicken kann auf das untere? Wer denkt, dass hier bereits Schluss sei mit dem Blending, der ist schief gewickelt, denn hier beginnt erst die Welt des kreativen Geistes! Lasst Euch verzaubern von wunderschönen OpenGL-Effekten und heizt Euren Programmen grafisch ordentlich ein. Ich hoffe ihr habt Verständnis, dass ich nur auf Teilbereiche eingehen kann, denn die Palette von Möglichkeiten ist keine Grenze gesetzt ;)&lt;br /&gt;
&lt;br /&gt;
Ich hoffe dennoch, dass auch das Tutorial gefallen wird und es wenigstens halbwegs verständlich rüberkommt. Auch gibt es für viele Dinge besser Lösungsmöglichkeiten, vieles hängt von der benutzen Textur ab oder aber auch wie viele davon verwendet werden. Um ein ausgiebiges Testen der Funktionen wird man nicht herum kommen. Also ran an die Arbeit und viel Spaß dabei ;)&lt;br /&gt;
&lt;br /&gt;
== Die vorhandene Welt und was darüber liegt ==&lt;br /&gt;
&lt;br /&gt;
=== Was ist den nun blenden? ===&lt;br /&gt;
&lt;br /&gt;
Wichtig beim Blending ist es zu wissen, was genau eigentlich beim rendern geschieht. OpenGL erhält eine Anweisung etwas zu zeichnen. Dieses wird dann in den so genannten &amp;quot;Frame-Buffer&amp;quot; gespeichert, das ist der Buffer,den wir dann auch meistens bei jedem Rendering mit glClear &amp;quot;löschen&amp;quot;. (Wir erinnern uns, führen wir kein glClear aus, sehen wir nach jedem Rendering den alten Inhalt… habe versprochen, dass wir darauf zurück kommen ;) ). Das heißt eigentlich, dass wir bei jedem Rendern eines Objektes immer wieder etwas in den Frame-Buffer schreiben und dann wenn wir denken, dass alles fertig ist diesen Inhalt auf dem Bildschirm projizieren.&lt;br /&gt;
&lt;br /&gt;
Das Blending setzt nun genau an der Stelle an, an dem etwas in den Frame-Buffer geschrieben werden soll. OpenGL hat praktisch bereits ein 3D-Objekt im Frame-Buffer und zeichnet dann das neue 3D Objekt. Nun können wir OpenGL mitteilen, was in einer solchen Situation geschehen soll. Standard gemäß würde das obere Objekt, dass darunter liegende verdecken.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glBlendFunc(source,destination);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit dieser Anweisung können wir dieses Verhalten ändern. Wir geben zwei Faktoren an, den für die Quelle und den für das Ziel. Ich versuche das mal abstrakt zu erklären. Wie bereits gesagt, handelt es sich dabei um Faktoren. Nehmen wir mal an Source (Textur) wäre 1 und Destination (Framebuffer) 0, dann würde OpenGL beim zusammensetzen der Szene, das was bereits im Frame-Buffer ist total vernachlässigen und das neue, was darüber gezeichnet werden soll komplett drauf zeichnen. Je höher der Faktor einer dieser Beiden Werte ist, desto stärke werden die Farben beim Blending berücksichtigt.Das ganze funktioniert natürlich auch nur, wenn wir OpenGL dazu anhalten das Blending zu verwenden…&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glEnable(GL_BLEND);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das ganze klingt sicherlich sehr komplex, (ist es auch *sg*), aber an Hand einiger Beispiele sollte es einem klar werden. Nehmen wir folgende Bilder:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;center&amp;quot;&lt;br /&gt;
|[[Bild:Tutorial_lektion7_rock_tex.jpg]]&lt;br /&gt;
|[[Bild:Tutorial_lektion7_gras_tex.jpg]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese beiden Texturen zeichnen wir jeweils auf einem Quad und zwar exakt aufeinander. Wir zeichnen zunächst die steinige Fläche und erhalten:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_stone.jpg]]&lt;br /&gt;
&lt;br /&gt;
Soweit so gut… nun setzen wir folgenden Blend-Faktor:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glBlendFunc(GL_ONE,GL_ZERO);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und zeichnen die Gras-Textur darüber:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_gras.jpg]]&lt;br /&gt;
&lt;br /&gt;
Hmm… scheint nicht, als ob das Blending wirklich gut funktioniert hat, da wir nur die Textur sehen, die wir als letztes gezeichnet haben. Scheint soweit alles wie bisher zu sein. :-/&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glBlendFunc(GL_ONE,GL_ZERO);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Quatsch! Natürlich hat alles funktioniert wie wir es erwartet haben! Wir haben schließlich OpenGL dazu angewiesen von der neuen Textur &amp;quot;alles&amp;quot; zu nehmen und von dem, was im Frame-Buffer ist &amp;quot;nichts&amp;quot;. Auf neudeutsch, wir haben ihn angewiesen die alte Textur über die andere zu zeichnen. Würden wir hingegen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glBlendFunc(GL_ZERO,GL_ONE);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwenden, so würden wir OpenGL dazu anhalten vom Frame-Buffer &amp;quot;alles&amp;quot; zu übernehmen und von der neuen Textur &amp;quot;nichts&amp;quot;, das würde bedeuten dass wir ins leere Rendern (sehr gute Methode seine Engine zu optimieren… *sg*)&lt;br /&gt;
&lt;br /&gt;
Ach so noch etwas… nehmt das mit dem GL_ZERO und GL_ONE und meinem &amp;quot;nichts und alles&amp;quot; nicht ganz so wörtlich. Es ist mehr eine kleine Eselsbrücke, die einem am Anfang hilft nicht total zu verwirren. Technisch macht OpenGL nichts anderes als bei GL_ONE die Farbe der Texture auf (1,1,1,1) zu setzen bei GL_ZERO auf (0,0,0,0); Und sobald es &amp;quot;schwarz&amp;quot; ist, sieht man sie halt nicht mehr, klingt logisch oder?&lt;br /&gt;
&lt;br /&gt;
=== Claim for the World Order ===&lt;br /&gt;
&lt;br /&gt;
Wie alles hat auch das Blending einen kleinen Schönheitsfehler. Viele Leute denken immer wieder, dass auch beim Blending praktisch eine 3D-Welt existiert, d.h. man hinrendern kann wo man will. Das ist jedoch falsch, da die Quelle ja im Frame-Buffer bereits vorliegt. Praktisch bereits fertig gerendert. Es macht also für das Blending einen enormen Unterschied, in welcher Reihenfolge die Objekte zur Kamera hin gerendert werden.&lt;br /&gt;
&lt;br /&gt;
Würden wir zum Beispiel ein Haus mit einem Fenster render wollen und das Fensterglas mit Hilfe von Blending darstellen und wir würden zunächst das Glas rendern, so würde OpenGL dieses mit dem Hintergrund (vermutlich dem Himmel) blenden und dann anschließend das Haus drum herum zeichnen. Der ahnungslose Programmierer wird dann mit großen Augen vor dem Fenster stehen und sich wundern, wieso er durch die hintere Wand des Hauses ins Freie sehen kann. &lt;br /&gt;
&lt;br /&gt;
Kurz: Auch durchsichtige Objekte sind Objekte. Auch sie schreiben Werte in den [[Tiefenpuffer]]. Deshalb kann man nicht &amp;quot;hinter&amp;quot; durchsichtige Objekte etwas rendern.&lt;br /&gt;
&lt;br /&gt;
Ohne Blending, würde so etwas nicht weiter auffallen, mit Blending muss man Acht geben in welcher Reihenfolge wir die Objekte rendern.&lt;br /&gt;
&lt;br /&gt;
Darum gilt: '''alle undurchsichtigen Objekte rendern, bevor man zum Blending greift.''' Man sollte sich also durchaus Gedanken dazu machen, wie man seine Objekte rendert. Es gibt verschiedene Verfahren seine Objekte so ordnen zu lassen, dass das hier beschrieben Problem nicht auftritt, darauf möchte ich nicht näher eingehen, ihr seid gewarnt worden ;D Eigentlich ist es ein recht logischer Vorgang, allerdings erliegt man doch recht schnell dem kleinen Irrglauben hinter eine Scheibe rendern zu können. ;)&lt;br /&gt;
&lt;br /&gt;
=== Gleichheit für Alles! ===&lt;br /&gt;
&lt;br /&gt;
Das war ja nun auch ein interessanter Ausflug… gelernt haben wir immer noch nichts wirklich Neues, zumindest nichts, was man auch sehen kann. Üben wir uns doch nun einmal richtig ein und versuchen zwei Texturen zu gleichen Maßen zusammen zu blenden, so dass es aussieht, als hätten wir eine Textur. Sicherlich gibt es viele Möglichkeiten dies zu realisieren, allerdings möchte ich an dieser Stelle einen Alpha-Farbwert verwenden. Wie ihr sicherlich wisst, werden Farben meist im RGB-Werten gespeichert, sprich Rot, Grün und Blau! Gesetzt wird dieses mit glColor3f. Ebenfalls gibt es noch die Funktion glColor4f mit der wir einen RGBA-Wert vergeben können. Dieser wird uns vor allem beim Blending nützliche Dienste erweisen.&lt;br /&gt;
&lt;br /&gt;
Weil beide Texturen zu gleichen Prozentsätzen geblendet werden sollen ergibt sich daraus folglich ein Aufruf von &amp;quot;glColor4f(1,1,1,0.5)&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
Wie ihr sehen könnt setzen wir diesmal glBlendFunc(GL_SRC_ALPHA,GL_ONE); ein. Von der Quelle wird der Alpha-Wert als Blend-Faktor genommen, dass Ziel wird übernommen.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_stonegras.jpg]]&lt;br /&gt;
&lt;br /&gt;
Wenn das nicht aussieht wie eine Textur, die wie aus dem gleichen Guss kommt! Würde noch jemand erahnen, dass es sich dabei eigentlich um zwei Texturen handelt? Für alle, interessierten noch einmal kurz eine Tabelle zur Übersicht der Funktionen, die wir bisher eingesetzt haben und wir noch einsetzen werden, sowie der mathematischen Erklärung ihrer Bedeutung:&lt;br /&gt;
&lt;br /&gt;
{| {{Prettytable_B1}}&lt;br /&gt;
! Konstante&lt;br /&gt;
! Auf Parameter anwendbar&lt;br /&gt;
! Berechneter Blend-Faktor&lt;br /&gt;
|-&lt;br /&gt;
| GL_ZERO&lt;br /&gt;
| source or destination&lt;br /&gt;
| (0, 0, 0, 0)&lt;br /&gt;
|-&lt;br /&gt;
| GL_ONE&lt;br /&gt;
| source or destination&lt;br /&gt;
| (1, 1, 1, 1)&lt;br /&gt;
|-&lt;br /&gt;
| GL_DST_COLOR&lt;br /&gt;
| source&lt;br /&gt;
| (Rd, Gd, Bd, Ad)&lt;br /&gt;
|-&lt;br /&gt;
| GL_SRC_COLOR&lt;br /&gt;
| destination&lt;br /&gt;
| (Rs, Gs, Bs, As)&lt;br /&gt;
|-&lt;br /&gt;
| GL_ONE_MINUS_DST_COLOR&lt;br /&gt;
| source&lt;br /&gt;
| (1, 1, 1, 1)-(Rd, Gd, Bd, Ad)&lt;br /&gt;
|-&lt;br /&gt;
| GL_ONE_MINUS_SRC_COLOR&lt;br /&gt;
| destination&lt;br /&gt;
| (1, 1, 1, 1)-(Rs, Gs, Bs, As)&lt;br /&gt;
|-&lt;br /&gt;
| GL_SRC_ALPHA&lt;br /&gt;
| source or destination&lt;br /&gt;
| (As, As, As, As)&lt;br /&gt;
|-&lt;br /&gt;
| GL_ONE_MINUS_SRC_ALPH&lt;br /&gt;
| source or destination&lt;br /&gt;
| (1, 1, 1, 1)-(As, As, As, As)&lt;br /&gt;
|-&lt;br /&gt;
| GL_DST_ALPHA&lt;br /&gt;
| source or destination&lt;br /&gt;
| (Ad, Ad, Ad, Ad)&lt;br /&gt;
|-&lt;br /&gt;
| GL_ONE_MINUS_DST_ALPHA&lt;br /&gt;
| source or destination&lt;br /&gt;
| (1, 1, 1, 1)-(Ad, Ad, Ad, Ad)&lt;br /&gt;
|-&lt;br /&gt;
| GL_SRC_ALPHA_SATURATE&lt;br /&gt;
| source&lt;br /&gt;
| (f, f, f, 1); f=min(As, 1-Ad)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Sieht anfangs etwas verwirrend aus, aber der Mensch kann sich an vieles gewöhnen. Immerhin lest ihr bereits seit einiger Zeit Texte, die mit meinem Rechtschreibfehlern und meiner Sprache angereichert sind ;D&lt;br /&gt;
&lt;br /&gt;
Um sicher zu gehen, dass auch wirklich alles verstanden ist nehmen wir uns noch einmal kurz folgenden Aufruf zu herzen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glBlendFunc(GL_ONE, GL_SRC_COLOR); // Zur Info: GL_SRC_COLOR darf nur auf &lt;br /&gt;
                                     // den Paramter destination angewendet &lt;br /&gt;
                                     // werden, siehe dazu obenstehende Tabelle&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wieder lassen wir den Frame-Buffer wie er ist, verwenden diesmal jedoch keinen Alpha-Wert zu blenden, sondern jeweils die RGB-Werte der Textur. OpenGL macht also praktisch nichts anderes als die Farbwerte zu addieren und anschließend durch zwei zu teilen. Wie man der Tabelle entnehmen kann spielt bei einem solchen Aufruf nur die Destination eine Rolle. Folglich sieht das Ergebnis wie folgt aus:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_stonegras2.jpg]]&lt;br /&gt;
&lt;br /&gt;
Soweit nun zum einfachen Blending. Ihr könnt Euch vorstellen, dass wir damit bereits eine Menge sehr interessanter Effekte erzeugen können, aber nun wollen wir einige davon auch gleich einmal testen und einsetzen ;)&lt;br /&gt;
&lt;br /&gt;
== Blending-Verfahren ==&lt;br /&gt;
&lt;br /&gt;
=== Die Welt unter einer Maske ===&lt;br /&gt;
&lt;br /&gt;
Ein Verfahren, welches sehr häufig zum Einsatz kommt ist das &amp;quot;Masking-Verfahren&amp;quot;. Aus dem Namen kann man sich bereits grob vorstellen, worum es geht. Wenn der Mensch sich eine Maske aufsetzt, so versucht er etwas damit zu verdecken (zum Beispiel sein Gesicht). Genauso machen wir es auch mit OpenGL um etwas zu verdecken. In diesem Fall jedoch, das Objekt welches wir unter der Maske liegen haben. Es gibt einen Haufen von sinnvollen Verwendungs-Möglichkeiten. Beim Masking an sich geht es darum, nur einen bestimmten Bereich sichtbar zu machen. Nehmen wir also einmal wieder unsere schön Stein-Textur und erzeugen in einem Malprogramm unserer Wahl eine Maske. Diese wird in diesem Fall komplett weiß sein und die eigentliche Masken-Bereiche sind schwarz.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_dgl.gif]]&lt;br /&gt;
&lt;br /&gt;
Diese Maske zeichnen wir nun selbstredend über der Steintextur und verwenden dabei folgende Blending-Funktion:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glBlendFunc(GL_DST_COLOR, GL_ONE); // Zur Info: GL_DST_COLOR darf nur &lt;br /&gt;
                                     // auf den Parameter source angewendet&lt;br /&gt;
                                     // werden. Siehe dazu obenstehende Tabelle.&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Als Ergebnis erhalten wir folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_whitemask.jpg]]&lt;br /&gt;
&lt;br /&gt;
Alles was an der Quelle weiß ist, wird bei dem Source-Faktor enorm hoch bewertet also sehr &amp;quot;weiß&amp;quot; gemacht. Da Schwarz das Gegenteil von Weiß ist, wird dieses eben total vernachlässigt. Sprich wir können hindurch sehen. Da der Destination-Faktor auf die Farbe des &amp;quot;Ziel-Pixels&amp;quot; gesetzt ist sehen wir dann an den entsprechenden Stellen auch den Hintergrund. Wollen wir das Spielchen mal umdrehen, so dass man nur noch die schwarze Schrift sieht und ansonsten durch die zweite Textur komplett hindurch blicken kann? Kein Problem!&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_blackmask.jpg]]&lt;br /&gt;
&lt;br /&gt;
Und die dazugehörige Blend-Funktion :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
glBlendFunc(GL_ZERO,GL_SRC_COLOR);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besonders hoch bei der Quelle werten wir in diesem Fall das Schwarz. Bei der Zielfarbe bewerten wir die Farbe sehr hoch die die zweite Textur hat. D.h. alles was schön weiß ist wird entsprechend geblendet. Wer sich nun am Kopf kratzt sollte sich ein wenig Code schnappen und ein wenig damit herum spielen. Recht schnell merkt man, wie es funktioniert, den Blending ist unter OpenGL gar nicht so schwer! Am besten auch gleich die obere Tabelle ausdrucken und daneben liegen haben ;)&lt;br /&gt;
&lt;br /&gt;
=== Der Überläufer ===&lt;br /&gt;
&lt;br /&gt;
Ich hoffe sehr, dass ich Euch jetzt hier nicht zu sehr langweile in dem ich andauernd die eine oder andere Funktion immer wieder und wieder in anderer Form zeige. Ich möchte schlicht und ergreifend damit erreichen, dass ihr ein wenig für die Möglichkeiten die man hat sensilibisiert werdet. Die Vielfalt an Effekten, die man erzielen kann ist nämlich wirklich groß, vor allem, wenn man einzelne Effekte miteinander kombiniert!&lt;br /&gt;
&lt;br /&gt;
Nehme wir uns noch einmal unsere Stein- und Gras-Textur zur Hand. Diesmal wollen wir versuchen, dass auf der linken Seite die Stein-Textur ist und auf der Rechten die Gras-Textur. Das ganze soll einen flüssigen Übergang ergeben.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_graswall.jpg]]&lt;br /&gt;
&lt;br /&gt;
Auch ein solcher Effekt ist nicht sonderlich schwer. Alles was wir hierfür brauchen sind unterschiedliche Alpha-Werte an den Eckpunkten der Quads. Beim ersten müssen diese links 1 sein und rechts 0, beim anderen entgegengesetzt. Setzen tun wir die Alpha-Werte mit glColor4f:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  Rock.SetTexture;&lt;br /&gt;
  glBegin(gl_Quads);&lt;br /&gt;
    glColor4f(1,1,1,1);&lt;br /&gt;
    glTexCoord2f(0,0);&lt;br /&gt;
    glVertex3f(-1,-1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor4f(1,1,1,0);&lt;br /&gt;
    glTexCoord2f(1,0);&lt;br /&gt;
    glVertex3f(1,-1,0);&lt;br /&gt;
    glTexCoord2f(1,1);&lt;br /&gt;
    glvertex3f(1,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor4f(1,1,1,1);&lt;br /&gt;
    glTexCoord2f(0,1);&lt;br /&gt;
    glvertex3f(-1,1,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&lt;br /&gt;
  glDisable(GL_DEPTH_TEST); // Wichtig! Da wir sonst das erste Quad verwerfen&lt;br /&gt;
                            // und damit auch die Textur, der Aufruf könnte auch &lt;br /&gt;
                            // in einer Init Funktion stehen.&lt;br /&gt;
  glEnable(GL_BLEND);&lt;br /&gt;
  glBlendFunc(GL_SRC_ALPHA,GL_DST_ALPHA);&lt;br /&gt;
&lt;br /&gt;
  gras.SetTexture;&lt;br /&gt;
  glBegin(gl_Quads);&lt;br /&gt;
    glColor4f(1,1,1,0);&lt;br /&gt;
    glTexCoord2f(0,0);&lt;br /&gt;
    glVertex3f(-1,-1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor4f(1,1,1,1);&lt;br /&gt;
    glTexCoord2f(1,0);&lt;br /&gt;
    glVertex3f(1,-1,0);&lt;br /&gt;
    glTexCoord2f(1,1);&lt;br /&gt;
    glvertex3f(1,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor4f(1,1,1,0);&lt;br /&gt;
    glTexCoord2f(0,1);&lt;br /&gt;
    glvertex3f(-1,1,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Während das erste Quad ohne Blending gezeichnet wird, wird das zweite geblendet, jeweils mit den Alpha-Werten der Quelle und des Zieles. Um dies zu verdeutlichen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_redbluealpha.jpg]]&lt;br /&gt;
&lt;br /&gt;
Je blauer die Farbe, desto weniger wird die Wand gefadet, je roter, desto mehr Gras wird angezeigt. In der Mitte verlaufen beide Texturen zusammen. Natürlich kann man nun anfangen zu spielen und gibt den Seiten andere Alpha-Werte. Genauso wäre es möglich drei Texturen ineinander einzublenden etc. Seit kreativ ;D&lt;br /&gt;
&lt;br /&gt;
=== Der Überläufer - Teil 2 ===&lt;br /&gt;
&lt;br /&gt;
Der eben beschriebene Effekt lässt sich auch noch sehr schön erweitern. Haben wir dort eine gerade Trennlinie zwischen den beiden Texturen gehabt, so werden wir uns nun mit einer Technik befassen, die es einem ermöglicht unregelmäßige Übergange zu erzeigen. Dieses Verfahren wird besondern oft bei Terrains angewandt, um zum Beispiel eine Schneelandschaft langsam ins Geröll überwandern zu lassen. Je nach Verfahren benötigen wir hierfür drei oder vier Texturen. Wir beschränken uns hierbei einfach auf drei.&lt;br /&gt;
&lt;br /&gt;
Würden wir zwei Texturen übereinander zeichnen, so würde die obere die untere Verdecken, würden wir bei einem Quad mit Alpha-Werten arbeiten würden die Übergange nicht flüssig sein. Würden wir die Farben addieren, würde so ziemlich alles dabei rauskommen, nur nicht einen Tetxuren-Verlauf, wie wir ihn erreichen wollen.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_blendmap.jpg]]&lt;br /&gt;
&lt;br /&gt;
Auf diesem Bild erkennt man sehr schön den Texturen-Verlauf, während im oberen Bereich herum bis nach links unten und rechts oben die steinige Felstextur ist, ist im unteren Bereich mehr die Gras-Textur, mit einer Ausnahme mitten in der Gras-Textur ist wiederum ein kleiner Abschnitt mit einer felsigen Textur (hier lila). Auch erkennt man auf dem zweiten Blick, dass an den Grenz-Bereichen ein wenig das Grüne auf dem Felsen durchschimmert, wir haben einen fließenden Übergang zu beiden Texturen erzeugt und können sogar im &amp;quot;Gebiet des Anderen&amp;quot; eine Textur durchscheinen lassen. Die Lösung ist wiederum eine Art von Masking:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_blendmap2.jpg]]&lt;br /&gt;
&lt;br /&gt;
Vergleicht man dieses Bild mit dem oberen wird man erkennen, dass die hellern Flecken &amp;quot;Stein-Textur&amp;quot; und die dunklen &amp;quot;Gras&amp;quot; bedeuten. Man redet von solchen Texturen meist von &amp;quot;Blend-Maps&amp;quot;. Technisch braucht man also nur eine Blend-Map für eine Textur und ein invertiertes Bild davon. An diesem Punkt können wir uns das invertieren bereits sparen, weil wir nur zwei Texturen blenden wollen und das Gegenteil von weiß, schwarz ist ;)&lt;br /&gt;
&lt;br /&gt;
== Lichterkarten ==&lt;br /&gt;
=== Und es ward Licht… ===&lt;br /&gt;
&lt;br /&gt;
Ein wirklich tolles Spielzeug, welches einen mit Blending in die Hand gelegt wird sind &amp;quot;Lightmaps&amp;quot;. Vielleicht hat jemand von Euch bereits einmal versucht in seinem Projekt so schöne Lichtquellen einzubauen, wie er es aus bekannten kommerziellen Spielen kennt und hat sofort anschließend ein langes Gesicht gezogen als die Augen auf die Framerate blickten. Dazu kommt, dass das OpenGL-Licht meist nicht einmal besonders ansehnlich ist. Ärgerlich…&lt;br /&gt;
&lt;br /&gt;
Doch es gibt natürlich eine Möglichkeit schnell und effizient mit wenig Aufwand genauso schöne Lichter in seinem Projekt einzubauen. Das Geheimnis ist schlicht und ergreifend, dass wir nicht auf OpenGL-Lichter aufbauen, sondern einen kleinen Trick auf unsere Benutzer spielen. Wir zeichnen vor dem Zielobjekt eine neue Texture, die nur aus Graustufen besteht. Je heller ein Punkt ist, desto mehr soll er anschließend leuchten.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;center&amp;quot;&lt;br /&gt;
|[[Bild:Tutorial_lektion7_lightmap.jpg]]&lt;br /&gt;
|[[Bild:Tutorial_lektion7_wall_tex.jpg]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Macht grafisch doch schon einiges mehr her als die ursprüngliche Textur oder? Wie bereits gesagt, dass Geheimnis sind die Lightmaps, die wir vor der Wand zeichnen:&lt;br /&gt;
&lt;br /&gt;
{| align=&amp;quot;center&amp;quot;&lt;br /&gt;
|[[Bild:Tutorial_lektion7_light.jpg]]&lt;br /&gt;
|[[Bild:Tutorial_lektion7_light2.jpg]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit etwas Fantasie kann man sie sogar immer noch erkennen. Man beachte, dass die erste Lightmap vor allem im oberen Bereich besonders hell ist und vor allem einen sehr hellen Punkt im oberen Bereich hat. Blicken wir auf das fertige Bild, so sehen wir dort einen roten Punkt. Die zweite Lightmap ist eher gleichmäßig und matt, nicht sonderlich hell, zumindest nicht auffällig. Sie sorgt dafür, dass ein gleichmäßiger Grünton auf der Wand liegt. Doch wie kann aus der weißen Lightmap z.B. eine Rote werden? Das ist relativ leicht, wir zeichnen das Quad vor der Wand einfach rot mit Hilfe von glColor3f(1,0.7,0.7); Damit die Farbe auch richtig zur Geltung kommt müssen diese natürlich auch beim Blending entsprechend einarbeiten!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glBlendFunc(GL_DST_COLOR,GL_SRC_COLOR);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei der Quelle wird vor allem die Farbe berücksichtigt, bei der Destination die Zielfarbe. Jeweils zwei Quads werden nach diesem Verfahren über die Wand gezeichnet, um den Effekt zu erreichen. Bin mir durchaus bewusst, dass man schönere Lightmaps machen kann, auch ist es zum Beispiel sehr interessant bei der Zielfarbe GL_SRC_COLOR zu verwenden, wodurch die gesamte Wand verdunkelt wird und man nur noch um das rote leuchten oben etwas sehen könnte (in diesem Fall nur eine Lightmap!)&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_lightmap2.jpg]]&lt;br /&gt;
&lt;br /&gt;
Der Kreativität sind eigentlich keine Grenzen gesetzt. Alle modernen 3D-Engines berechnen bei einem Level an Hand der Lichtquellen solche Lightmaps und projizieren diese dann vor den Wänden, um sie aussehen zu lassen, wie sie aussehen sollen. Keiner dieses wunderschönen Effekts wird man mit einem OpenGL-Licht realisieren können. Wozu auch, wenn es so doch viel schneller geht ;)&lt;br /&gt;
&lt;br /&gt;
=== Die Kraft des Gemeinsamen! ===&lt;br /&gt;
&lt;br /&gt;
Wir haben uns hier sehr viel mit Blending auseinander gesetzt und sicherlich juckt es Euch bereits unter den Fingern Wände mit wunderschönen Lichteffekten an die Wand zu zaubern. So mindestens 300 Lichter sollten sicherlich zum Minimum zählen, oder ;D&lt;br /&gt;
&lt;br /&gt;
Nun schnell werdet ihr vor allem in größeren Projekten merken, dass Euch das auf die Rechenleistung schlägt. Das Problem ist logisch! Nicht das Blending an sich ist die größte CPU-Last, sondern das Transformieren der Quads, auf denen sich die Lightmaps befinden. Eigentlich sind es fast nur die Transformationen, die so richtig mit Leistung zu Buch schlagen. Es gilt also so wenig wie mögliche Quads hintereinander zu erzeugen und somit die Anzahl der Durchläufe zu minimieren (Renderpass). Das ließe sich zum Beispiel damit erreichen, dass man versucht möglichst viele Lichtinformationen auf einer Lightmap zu setzen. So würde es keinen Sinn machen zwei rote Lichter einmal oben und einmal unten.&lt;br /&gt;
&lt;br /&gt;
Dies sind jedoch nur logische Einsparungen, die jeder Programmierer in seinem Projekt selbst entscheiden muss. Seit einiger Zeit gibt es jedoch auch ein Hardware-Verfahren, welches uns helfen kann ein Spiel zu optimieren. Zu Beginn der 3D-Beschleunigung verlief das Rendering wie folgt:&lt;br /&gt;
&lt;br /&gt;
 # Texture setzen&lt;br /&gt;
 # Objekt transformieren&lt;br /&gt;
 # Texture setzen&lt;br /&gt;
 # Objekt transformieren&lt;br /&gt;
&lt;br /&gt;
Klingt soweit auch alles logisch! Wie bereits jedoch erwähnt frisst vor allem das transformieren die Rechenpower und in unserem Fall liegt das Objekt übereinander, es wäre eine Transformation der Vertizen eigentlich gar nicht nötig. Wir müssten also nur die beiden Texturen auf ein transformiertes Objekt bekommen. Würden wir einfach nur zwei Texturen hintereinander setzen, so würde OpenGL die zuletzt gewählte verwenden und die andere schlichtweg ignorieren. Die meisten heutigen Grafikkarten (z.B: GeForce8800 GTX) haben 64 Textur-Units. Das muss man sich vorstellen wie 64 Schubladen. In die ersten beiden legen wir eine Textur rein und wenn OpenGL rendert wird reingeschaut und beide werden auf das Ziel gepappt. Somit haben wir uns eine Transformation gespart, was vor allem wenn man sehr viel mit Lightmaps arbeitet schon ein beachtlicher Geschwindigkeitsgewinn von knapp 50% ist! Einige sehr moderne Grafikkarten haben bereits 128 Texturen-Units und die Zeit bis es welche mit 256 gibt ist auch schon bereits voraussehbar ;)&lt;br /&gt;
&lt;br /&gt;
Wie funktioniert das ganze nun für uns? OpenGL verwendet als Standard die Unit 0. Binden wir eine Textur oder definieren das Blending so beziehen sich alle diese Angaben immer auf die momentan selektierte Textur-Unit. Alles was wir machen müssen ist, die gewünschte auszuwählen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glActiveTextureARB(GL_TEXTURE0_ARB);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ich denke der Parameter erklärt sich praktisch von selbst! GL_TEXTURE1_ARB wäre entsprechend die zweite Textur-Unit. Nun setzen wir alle Einstellungen für diese Unit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glEnable(GL_TEXTURE_2D);&lt;br /&gt;
  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);&lt;br /&gt;
  wall.SetTexture;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zunächst teilen wir der Unit mit, dass sie bei der 2D-texturierung zur Verfügung stehen soll. Anschließend setzen wir die Textur-Envroiment auf die &amp;quot;Standard-Einstellung&amp;quot;. Wenn mich jetzt jemand fragt, wofür die Parameter gut sind, dann zitiere ich das RedBook &amp;quot;es muss so sein&amp;quot; ;D Wichtig ist eigentlich nur, dass hinten GL_MODULATE steht, damit die Textur ordnungsgemäß, so wie wir sie sehen auf das Quad wandern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glActiveTextureARB(GL_TEXTURE1_ARB);&lt;br /&gt;
  glEnable(GL_TEXTURE_2D);&lt;br /&gt;
  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);&lt;br /&gt;
  glEnable(gl_BLEND);&lt;br /&gt;
  glBlendFunc(GL_ONE,GL_ONE);&lt;br /&gt;
  light.SetTexture;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir wechseln danach zur zweiten Textur-Unit und machen ähnliche Einstellungen, aktivieren zusätzlich jedoch Blending für diese Funktion. Diesmal etwas sehr simples.&lt;br /&gt;
&lt;br /&gt;
Wer nun gehofft hat, dass dies alles gewesen wäre, was man für das MultiTexturing benötigt, der wird enttäuscht sein. Eine Kleinigkeit gibt es noch zu beachten. Da wir zwei Texturen verwenden kann es ja auch sein, dass die UV-Koordinaten unterschiedlich sein sollen. In unserem Fall ist dies nicht so, aber es kann durchaus passieren. Deswegen müssen wir auch für jeden Eckpunkt eine UV-Koordinate vergeben. Hierfür verwenden wir nicht glTexCoord2f, sondern die Funktion:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
  glMultiTexCoord2fARB(GL_TEXTURE0_ARB,0,0);&lt;br /&gt;
  glMultiTexCoord2fARB(GL_TEXTURE1_ARB,0,0);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zunächst geben wir bei der Funktion den Parameter ein gefolgt von der eigentlichen UV-Koordinate. So gehen wir dann bei jedem Punkt vor und vorausgesetzt, dass unsere Grafikkarte auch Multitexturing unterstütz sollten wir uns an folgendem Bild auf dem Bildschirm ergötzen können (natürlich nur, wenn man auch die gleichen Texturen verwendet! ;D)&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion7_multitex.jpg]]&lt;br /&gt;
&lt;br /&gt;
== Nachwort ==&lt;br /&gt;
&lt;br /&gt;
Oh je, ich weiß, ich habe inzwischen einen Ruf dafür, dass ich immer gleich so große Portionen liefere und nicht in der Lage bin, einmal kurz ein kleines Tutorial zu schreiben. Das liegt einfach daran, dass wenn ich einmal angefangen bin und mich für etwas begeistern konnte nicht mehr aufhören kann. Und gerade bei solchen Themen, wo man so richtig schön mit Effekten experimentieren kann fällt es einem besonders schwer einen Punkt zu finden.&lt;br /&gt;
&lt;br /&gt;
Ich denke spätestens nach diesem Tutorial sollte man gelernt haben wie leicht man mit OpenGL einige wunderschöne Effekte zaubern kann ohne wirklich großen Aufwand zu betreiben. Viel leichter wird es kaum möglich sein. Sollte jemand nun sich am Kopf kratzen und sich fragen, ob es einen Grund hat, warum die ganzen hier vorgestellten Beispiele 2D sind? Nein, eigentlich nicht, denn alle der hier vorgestellten Techniken lassen sich auch prima auf 3D-Objekte übertragen. So wäre es zum Beispiel auch Möglich einen 3D-Soldaten mit einer Lightmap und Multitexturing zu überziehen und somit unterschiedliche Helligkeit auf ihn zu projizieren. Aber darauf will ich nicht weiter eingehen, nun kommt wieder Euer Part. ;)&lt;br /&gt;
&lt;br /&gt;
Auch bin ich sehr froh zu sehen, dass die Community wächst und auch endlich einen Punkt erreicht an der man gezielt auf Probleme einwirken kann. Ich hoffe sehr, dass dies auch weiterhin so bleibt und bin auch sehr froh, dass zunehmend Feedback von Eurer Seite kommt, gar einige damit beginnen Kontent für uns zur Verfügung zu stellen oder uns auf kleinere Fehler aufmerksam macht oder aber auch einfach nur auf eine gute Seite aufmerksam machen, die wir bisher nicht gelinkt haben. Wenn jemand von Euch einmal ein kleines OpenGL-Projekt vorstellen möchte, etwas an dem er arbeitet, schreibt uns doch ruhig auch einmal an. Mehr als Ablehnen könnten wir auch nicht ;D&lt;br /&gt;
&lt;br /&gt;
In diesem Sinne viel Spaß und auf Wiedersehen.&lt;br /&gt;
&lt;br /&gt;
Euer [[Benutzer:Phobeus|Phobeus]]&lt;br /&gt;
&lt;br /&gt;
== Dateien == &lt;br /&gt;
* {{ArchivLink|file=tut_lektion_7_delphi_api|text=Alter Delphi-API-Quelltext zum Tutorial}}&lt;br /&gt;
* {{ArchivLink|file=tut_lektion_7_exe|text=Windows-Binary zum Tutorial}}&lt;br /&gt;
&lt;br /&gt;
{{TUTORIAL_NAVIGATION | [[Tutorial Lektion 5]] | [[Tutorial Lektion 8]]}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Tutorial|Lektion7]]&lt;/div&gt;</summary>
		<author><name>Nevermind10844</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_StereoSehen&amp;diff=25699</id>
		<title>Tutorial StereoSehen</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_StereoSehen&amp;diff=25699"/>
				<updated>2012-09-10T14:24:03Z</updated>
		
		<summary type="html">&lt;p&gt;Nevermind10844: /* Einleitung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Stereo einmal sehen statt hören=&lt;br /&gt;
[[Bild:Tutorial_Stereo_anaglyph.jpg|center]]&lt;br /&gt;
==Einleitung==&lt;br /&gt;
Viele Tiere haben mit dem Menschen eins gemein: Zwei Augen. Dies kann unterschiedliche Vorteile haben, etwa in zwei Richtungen gleichzeitig sehen zu können, oder, was viel häufiger ist, um räumlich sehen zu können. Die Welt aus zwei leicht unterschiedlichen Blickwinkeln zu sehen ermöglicht es dem Gehirn Abstände und Geschwindigkeiten besser einzuschätzen, als mit einem Auge. Das es uns auch mit einem Auge gelingt, erkennt man dran, dass das in begrenztem Umfang auch bei Fotos gelingt und wenn wir mit einem geschlossenem Auge durch ein Zimmer laufen trotzdem nicht überall anstossen. Das funktioniert sogar so gut, dass Menschen, die nur stark eingeschränkt oder gar nicht räumlich sehen können, dies gar nicht so selten erst bei Routineuntersuchungen von Augenärzten oder im Verlauf ihrer Musterung erfahren.&lt;br /&gt;
&lt;br /&gt;
An einer Stelle ist es jedoch egal, ob wir räumlich sehen können oder nicht - beim Spielen und Arbeiten am Computer. Aber 3D Spiele wollen doch gerade eines: uns die Welt möglichst realistisch vor Augen führen. Leider hat der Computer meist nur einen Monitor, wir aber zwei Augen... Und damit Good Bye räumliches Sehen? Nein, nein, so einfach dürfen wir uns nicht geschlagen geben. Tatsächlich fallen mir gleich eine Reihe von Möglichkeiten ein: Stereogramme, wie sie eine Zeit lang modern waren, und in vielen Büchern vorkommen: langes Stieren auf stylisch eingefärbte Buchseiten. Bei 3D-Spielen sind 3D-Shutterbrillen beliebt, bei denen jeweils ein Auge abgedunkelt wird, auf dem Monitor für das Auge ein Bild angezeigt und dann gewechselt wird. Zeigt man für jedes Auge ein leicht versetztes Bild an und wechselt die Bilder häufig genug, entsteht für den Betrachter ein 3-Dimensionales Bild der Szenerie. Für diese Technik ist jedoch ein schnell schaltender Monitor von Nöten - Entweder ein Röhrenmonitor oder ein neuer TFT-Bildschirm mit 120Hz Bildwiederholrate.&lt;br /&gt;
&lt;br /&gt;
Bei der im Jahr 2009 neu aufgelebten 3D-Technik in den Kinos kommen entweder die oben beschriebenen Shutterbrillen oder Brillen mit Polarisationsfiltern zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
Eine äußerst alte aber gut funktionierende Technik ist die der Anaglyphe: Dabei muss der Betrachter eine 3D-Brille aufsetzen, die vor jedes Auge einen anderen Farbfilter setzt, etwa rot und grün. Bei der Filmaufnahme werden dann zwei überkreuzt filmende Kameras verwendet.&lt;br /&gt;
&lt;br /&gt;
Der 3D Effekt stellt sich bei den meisten Menschen schnell ein, bei Anderen dauert es ein wenig oder erfordert etwas Übung. Den Effekt gar nicht genießen können üblicherweise nur stark Schielende, Einäugige und Blinde. Eine wesentliche Einschränkung bei, Anaglyphenverfahren ist jedoch, dass das entstehende Bild meist einfarbig, ähnlich einem s/w Film, ist. Der entstehende 3D Effekt gleicht dieses Manko meiner Ansicht nach auf alle Fälle aus - und da wir möglichst Niemanden ausschliessen möchten, unsere Programme zu bestaunen, werden wir unsere Programme sowohl für &amp;quot;normale&amp;quot; Grafikausgabe als auch für 3D-Brillen vorbereiten.&lt;br /&gt;
&lt;br /&gt;
==Die 3D-Brille==&lt;br /&gt;
Jeder der weitermachen möchte, sollte sich spätestens hier eine 3D-Brille besorgen. Diese gibts z.B. beim Optiker und sollte möglichst einen roten und einen grünen Filter besitzen. Zur Not tuts auch rot-blau, ist jedoch wegen den stark auseinanderliegenden Wellenlängen des roten und blauen Lichtes weniger gut geeignet. Einigen Büchern zu optischen Täuschungen liegen ebenfalls Brillen bei. Solltet ihr keine passende, vorgefertigte Brille finden, könnt ihr sie auch mit etwas Pappe oder Karton und Farbfolien aus dem Bastelladen selbst bauen - oder ganz nobel: mit Filtern aus dem Fotoladen statt ordinären Farbfolien. &lt;br /&gt;
[[Bild:Tutorial_Stereo_brille.png|center]]&lt;br /&gt;
&lt;br /&gt;
==Projektionen==&lt;br /&gt;
Bei der üblichen, perspektivischen Projektion ist die Sache einfach: Man hat ein Auge, einen Öffnungswinkel für die Kamera und die Entfernung für die nahe Clipping-Ebene: &lt;br /&gt;
[[Bild:Tutorial_Stereo_perspektivisch.png|center]]&lt;br /&gt;
&lt;br /&gt;
Beim Stereo-Sehen wird die Sache ein Wenig komplizierter. Da man mit Beiden Augen auf die Projektionsebene Bildschirm schaut, ist diese für beide Augen identisch, jedoch sind die Blickkegel nicht mehr gerade, sondern schief:&lt;br /&gt;
[[Bild:Tutorial_Stereo_stereo.png|center]]&lt;br /&gt;
&lt;br /&gt;
Zu allem überfluss können wir unsere Augen auch noch auf eine bestimmte Entfernung ausrichten, d.h. die Ebene, auf die die Augen eingestellt sind, muss nicht der Entfernung der nahen Clippling-Plane entsprechend - das Problem können wir jedoch ganz einfach mithilfe der Strahlensätze lösen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;...&lt;br /&gt;
var&lt;br /&gt;
  zNear, zFar, Oeffnungswinkel, Augenabstand, Zielweite : Single;&lt;br /&gt;
...&lt;br /&gt;
const&lt;br /&gt;
  PBufSize = 1024; //Seitenläne des PBuffers&lt;br /&gt;
var&lt;br /&gt;
  SVerh, ROeffnung : Single;&lt;br /&gt;
  Breitenhaelfte, NeardZielweite : Single;&lt;br /&gt;
  left, right, top, bottom : Single;&lt;br /&gt;
&lt;br /&gt;
  procedure CalcValues;&lt;br /&gt;
  begin&lt;br /&gt;
    ROeffnung := DegToRad(Oeffnungswinkel / 2); //Halber Öffnungswinkel in RAD&lt;br /&gt;
    Breitenhaelfte := zNear * Tan(ROeffnung); //Halbe Breite der Proj. Ebene&lt;br /&gt;
    NeardZielweite := zNear / Zielweite; &lt;br /&gt;
  end;&lt;br /&gt;
&lt;br /&gt;
begin&lt;br /&gt;
  if Opt.Stereo then //Stereo Modus&lt;br /&gt;
  begin&lt;br /&gt;
    glViewport(0, 0, ClientWidth, ClientHeight);&lt;br /&gt;
    //Projektionsmatrix resetten&lt;br /&gt;
    glMatrixMode(GL_PROJECTION);&lt;br /&gt;
    glLoadIdentity();&lt;br /&gt;
&lt;br /&gt;
    SVerh := ClientWidth/ClientHeight; //Breite zu Höhe&lt;br /&gt;
    CalcValues;&lt;br /&gt;
    &lt;br /&gt;
    //Ränder der Projektionsebene für glFrustum&lt;br /&gt;
    left := - SVerh * Breitenhaelfte - 0.5 *Augenabstand*NeardZielweite;&lt;br /&gt;
    right := SVerh * Breitenhaelfte - 0.5 *Augenabstand*NeardZielweite;&lt;br /&gt;
    top := Breitenhaelfte;&lt;br /&gt;
    bottom := -Breitenhaelfte;&lt;br /&gt;
    glFrustum(left, right, bottom, top, zNear, zFar);&lt;br /&gt;
&lt;br /&gt;
    glMatrixMode(GL_MODELVIEW);&lt;br /&gt;
    glLoadIdentity;&lt;br /&gt;
&lt;br /&gt;
    //PBuffer erzeugen und aktivieren, wenn noch nicht geschehen&lt;br /&gt;
    if not Assigned(PBuffer) then&lt;br /&gt;
    begin&lt;br /&gt;
      PBuffer := TPixelBuffer.Create(PBufSize, PBufSize, DC, RC, Self);&lt;br /&gt;
      wglShareLists(RC, PBuffer.RC);&lt;br /&gt;
      PBuffer.Enable;&lt;br /&gt;
      InitGl;&lt;br /&gt;
    end&lt;br /&gt;
    else&lt;br /&gt;
      PBuffer.Enable;&lt;br /&gt;
&lt;br /&gt;
    glViewport(0, 0, PBufSize, PBufSize);&lt;br /&gt;
    //Projektionsmatrix resetten&lt;br /&gt;
    glMatrixMode(GL_PROJECTION);&lt;br /&gt;
    glLoadIdentity();&lt;br /&gt;
&lt;br /&gt;
    //Diesmal das Frustum in die andere Richtung schiefstellen(+ statt -)&lt;br /&gt;
    left := - SVerh * Breitenhaelfte + 0.5 *Augenabstand*NeardZielweite;&lt;br /&gt;
    right := SVerh * Breitenhaelfte + 0.5 *Augenabstand*NeardZielweite;&lt;br /&gt;
    top := Breitenhaelfte;&lt;br /&gt;
    bottom := -Breitenhaelfte;&lt;br /&gt;
    glFrustum(left, right, bottom, top, zNear, zFar);&lt;br /&gt;
&lt;br /&gt;
    glMatrixMode(GL_MODELVIEW);&lt;br /&gt;
    glLoadIdentity;&lt;br /&gt;
&lt;br /&gt;
    PBuffer.Disable;&lt;br /&gt;
  end&lt;br /&gt;
  else&lt;br /&gt;
  begin&lt;br /&gt;
    //Normale Ansicht&lt;br /&gt;
    glViewport(0, 0, ClientWidth, ClientHeight);&lt;br /&gt;
    //Projektionsmatrix resetten&lt;br /&gt;
    glMatrixMode(GL_PROJECTION);&lt;br /&gt;
    glLoadIdentity();&lt;br /&gt;
    //Perspektivische Darstellung&lt;br /&gt;
    SVerh := ClientWidth/ClientHeight;&lt;br /&gt;
    CalcValues;&lt;br /&gt;
    //Hier wird nichts verschoben&lt;br /&gt;
    left := - SVerh * Breitenhaelfte;&lt;br /&gt;
    right := SVerh * Breitenhaelfte;&lt;br /&gt;
    top := Breitenhaelfte;&lt;br /&gt;
    bottom := -Breitenhaelfte;&lt;br /&gt;
    glFrustum(left, right, bottom, top, zNear, zFar);&lt;br /&gt;
&lt;br /&gt;
    glMatrixMode(GL_MODELVIEW);&lt;br /&gt;
    glLoadIdentity;&lt;br /&gt;
&lt;br /&gt;
  end;&lt;br /&gt;
end;&lt;br /&gt;
...&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Und Rendern?==&lt;br /&gt;
Nun müssen wir nur noch zwei Bilder anzeigen. Die einfachste Möglichkeit dürfte sein, ein Bild für das linke Auge in einen PBuffer oder sonstige Textur zu rendern, dann das Bild für das rechte Auge in den Hintergrundpuffer. Der Inhalt des PBuffers wird dann mittels Blending auch in den Hintergrundpuffer gezeichnet und voila. Wir haben zwei verschiedene Bilder auf dem Schirm. Zu beachten ist noch, dass die Farben mittels [[glColorMask]] beim Rendern maskiert werden müssen - wir wollen ja für die beiden Augen unterschiedliche Farben anzeigen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;procedure TStereoForm.Render;&lt;br /&gt;
  procedure RenderScene;&lt;br /&gt;
  begin&lt;br /&gt;
    //kein glLoadIdentity am Anfang! Das muss vorher gemacht worden sein -&lt;br /&gt;
    //für die Augen muss ja bereits verschoben worden sein. Also Achtung.&lt;br /&gt;
    //Brav mit Push und Pop Matrix arbeiten.&lt;br /&gt;
    ...&lt;br /&gt;
  end;&lt;br /&gt;
&lt;br /&gt;
var&lt;br /&gt;
  Error : glUInt;&lt;br /&gt;
begin&lt;br /&gt;
  if (Opt.Stereo) and (Assigned(PBuffer)) then&lt;br /&gt;
  begin&lt;br /&gt;
    //Stereo Modus&lt;br /&gt;
    PBuffer.Enable;&lt;br /&gt;
    glClearColor(0.0, 0.0, 0.0, 0.0);&lt;br /&gt;
    glClear(GL_DEPTH_BUFFER_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
&lt;br /&gt;
    glLoadIdentity;&lt;br /&gt;
    glTranslatef(Augenabstand/2, 0, 0);&lt;br /&gt;
&lt;br /&gt;
    //linkes Auge bekommt grün und blau - das sollte über Optionen wählbar sein&lt;br /&gt;
    glColorMask(false, true, true, true);&lt;br /&gt;
    RenderScene;&lt;br /&gt;
    PBuffer.Disable;&lt;br /&gt;
&lt;br /&gt;
    glClearColor(0.0, 0.0, 0.0, 0.0);&lt;br /&gt;
    glClear(GL_DEPTH_BUFFER_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
&lt;br /&gt;
    glLoadIdentity;&lt;br /&gt;
    glTranslatef(-Augenabstand/2, 0, 0);&lt;br /&gt;
&lt;br /&gt;
    //rechtes Auge bekommt nuir rot&lt;br /&gt;
    glColorMask(true, false, false, true);&lt;br /&gt;
    RenderScene;&lt;br /&gt;
&lt;br /&gt;
    //Und zusammenblenden&lt;br /&gt;
    glColorMask(true, true, true, true);&lt;br /&gt;
    glDepthFunc(GL_ALWAYS);&lt;br /&gt;
    glMatrixMode(GL_PROJECTION);&lt;br /&gt;
      glPushMatrix;&lt;br /&gt;
      glLoadIdentity;&lt;br /&gt;
      glOrtho(0, 1, 1, 0, -1.0, 1.0);&lt;br /&gt;
&lt;br /&gt;
      glMatrixMode(GL_MODELVIEW);&lt;br /&gt;
      glLoadIdentity;&lt;br /&gt;
&lt;br /&gt;
      glDisable(GL_LIGHTING);&lt;br /&gt;
      glEnable(GL_TEXTURE_2D);&lt;br /&gt;
      PBuffer.Bind;&lt;br /&gt;
      glEnable(GL_BLEND);&lt;br /&gt;
      glBlendFunc(GL_ONE, GL_ONE);&lt;br /&gt;
      glBegin(GL_QUADS);&lt;br /&gt;
        glTexCoord2f(1, 0); glVertex2f(1,1);&lt;br /&gt;
        glTexCoord2f(1, 1); glVertex2f(1,0);&lt;br /&gt;
        glTexCoord2f(0, 1); glVertex2f(0,0);&lt;br /&gt;
        glTexCoord2f(0, 0); glVertex2f(0,1);&lt;br /&gt;
      glEnd();&lt;br /&gt;
      glDisable(GL_BLEND);&lt;br /&gt;
&lt;br /&gt;
      glEnable(GL_LIGHTING);&lt;br /&gt;
      glDisable(GL_TEXTURE_2D);&lt;br /&gt;
      PBuffer.Release;&lt;br /&gt;
&lt;br /&gt;
      glMatrixMode(GL_PROJECTION);&lt;br /&gt;
      glPopMatrix;&lt;br /&gt;
    glMatrixMode(GL_MODELVIEW);&lt;br /&gt;
    glDepthFunc(GL_LESS);&lt;br /&gt;
  end&lt;br /&gt;
  else&lt;br /&gt;
  begin&lt;br /&gt;
    //&amp;quot;Mono&amp;quot; Modus&lt;br /&gt;
    glClearColor(0.0, 0.0, 0.0, 0.0);&lt;br /&gt;
    glClear(GL_DEPTH_BUFFER_BIT or GL_COLOR_BUFFER_BIT);&lt;br /&gt;
&lt;br /&gt;
    glLoadIdentity;&lt;br /&gt;
    RenderScene;&lt;br /&gt;
  end;&lt;br /&gt;
  ...&lt;br /&gt;
end;&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Und 3D-Shutterbrillen?==&lt;br /&gt;
Wer eine 3D-Sutterbrille und entsprechendes Equipment zuhause hat, kann seine Anwendungen auch für diese anpassen. Einige Grafikkarten haben bereits passende Treiber, die die Ausgabe der Programme automatisch anpassen. Wer das nicht will, sollte sich einmal mit Stereo-OpenGl auseinandersetzen. Bei der dglOpenGl genügt es, den Rendering Context mit der Option opStereo zu erstellen. Mit [[glDrawBuffer]]('''GL_BACK_LEFT'''/'''GL_BACK_RIGHT''') kann dann für jedes Auge in einen anderen Puffer gerendert werden.&lt;br /&gt;
&lt;br /&gt;
==Abschluss==&lt;br /&gt;
Ich hoffe euch hat dieser kleine Exkurs gefallen. Vielleicht läuft einem ja mal das eine oder andere Programm über den Weg, wo man die Wahl zwischen Tiefensehen und normaler Ansicht hat - mich würde es jedenfalls freuen, zumal es ja nicht wirklich schwer ist, seine Programme entsprechend anzupassen und es doch ein wesentlich anderes Gefühl der Szene erzeugt, als üblicherweise vor dem Computer. Ich bin jedenfalls gespannt, erwarte mir aber nicht allzuviel - bislang war das Feedback und eure Demos ja sehr bescheiden. Wo ich da nur immer die Motivation hernehme, euch mit ein paar neuen Ideen zu versorgen? ;-D&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Euer Delphic&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Kleine Anmerkung am Rande==&lt;br /&gt;
Nach Fertigstellung des Tutorials wurde ich darauf aufmerksam gemacht, dass man um den Einsatz des P-Buffers herumkommt - was Geschwindigkeit und Kompatibilität erhöht. Man muss nur zwischen den glColorMask Befehlen einmal den Tiefenpuffer leeren. In etwa läuft das ganze dann wie folgt:&lt;br /&gt;
&lt;br /&gt;
         private void RenderMono()&lt;br /&gt;
         {&lt;br /&gt;
             RenderSettings rs = new RenderSettings();&lt;br /&gt;
             rs.Mono(RenderPanel.ClientRectangle.Width, RenderPanel.ClientRectangle.Height);&lt;br /&gt;
 &lt;br /&gt;
             GL.Clear(GL.COLOR_BUFFER_BIT | GL.DEPTH_BUFFER_BIT);&lt;br /&gt;
             //Szene Rendern&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         private void RenderStereo()&lt;br /&gt;
         {&lt;br /&gt;
             GL.Clear(GL.COLOR_BUFFER_BIT | GL.DEPTH_BUFFER_BIT);&lt;br /&gt;
 &lt;br /&gt;
             RenderSettings rs = new RenderSettings();&lt;br /&gt;
             rs.Stereo(true, RenderPanel.ClientRectangle.Width, RenderPanel.ClientRectangle.Height);&lt;br /&gt;
             GL.ColorMask(1, 0, 0,1);&lt;br /&gt;
             //Szene Rendern&lt;br /&gt;
 &lt;br /&gt;
             rs.Stereo(false, RenderPanel.ClientRectangle.Width, RenderPanel.ClientRectangle.Height);&lt;br /&gt;
             GL.Clear(GL.DEPTH_BUFFER_BIT);&lt;br /&gt;
             GL.ColorMask(0, 1, 1,1);&lt;br /&gt;
             //Szene Rendern&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
             if (GL.GetError() != GL.NO_ERROR)&lt;br /&gt;
             {&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         private void Render()&lt;br /&gt;
         {&lt;br /&gt;
             if (rc != null)&lt;br /&gt;
             {&lt;br /&gt;
                 GL.PushAttrib(GL.ALL_ATTRIB_BITS);&lt;br /&gt;
                 GL.ClearColor(0.0f, 0f, 0f, 1f);&lt;br /&gt;
                 if (stereo)&lt;br /&gt;
                     RenderStereo();&lt;br /&gt;
                 else&lt;br /&gt;
                     RenderMono();&lt;br /&gt;
                 rc.SwapBuffers();&lt;br /&gt;
                 GL.PopAttrib();&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
&lt;br /&gt;
Die rs.stereo(true/false, ...) Befehle laden die benötigten Matrizen für das linke bzw. rechte Auge.&lt;br /&gt;
&lt;br /&gt;
== Dateien ==&lt;br /&gt;
* {{ArchivLink|file=tut_stereo_delphi_vcl|text=Delphi-VCL-Quelltext zum Tutorial}}&lt;br /&gt;
&lt;br /&gt;
{{TUTORIAL_NAVIGATION|[[Tutorial_StencilSpiegel]]|[[Tutorial_Alphamasking]]}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Tutorial|StereoSehen]]&lt;/div&gt;</summary>
		<author><name>Nevermind10844</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_Partikel1&amp;diff=25698</id>
		<title>Tutorial Partikel1</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_Partikel1&amp;diff=25698"/>
				<updated>2012-08-29T11:38:00Z</updated>
		
		<summary type="html">&lt;p&gt;Nevermind10844: /* Container auf - Partikel rein - Container zu */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Partikel Systeme I=&lt;br /&gt;
&lt;br /&gt;
==Übersicht==&lt;br /&gt;
Dies ist das erste von zwei Tutorials zum Thema Partikel Systeme. Zuerst werde ich den Begriff Partikel Systeme und deren Funktionsweise erklären.Anhand des beiliegenden Beispiel-Programms erläutere ich die Programmierung eines einfachen Partikel-Systems. Dieses Tutorial wird hauptsächlich auf die verwendeten Konzepte eingehen, die Umsetzung ist dem Beispiel zu entnehmen. Es empfiehlt sich deshalb, das Gelesene sofort anhand des Source-Codes nachzuvollziehen.&lt;br /&gt;
&lt;br /&gt;
Das zweite Tutorial baut auf dem ersten auf und erweitert das System um Effekte wie Gravitation oder Luftwiderstand. Außerdem werde ich noch ein paar Probleme und deren Lösung ansprechen, damit der eigenen Partikel-Engine nichts mehr im Wege steht!&lt;br /&gt;
&lt;br /&gt;
==Von Chaos, Ordnung und Explosionen==&lt;br /&gt;
Wer sich mit Grafikprogrammierung beschäftigt, hat meist das Ziel, Szenen oder Grafiken zu erschaffen, die realen Vorbildern möglichst exakt nachempfunden sind. In Computerspielen zum Beispiel ist die Atmosphäre und damit auch der Unterhaltungswert abhängig von Detailgrad und Stimmigkeit der simulierten virtuellen Welt.&lt;br /&gt;
&lt;br /&gt;
Es ist also notwendig Methoden zu finden, um reale Objekte und Phänomene möglichst genau nachzubilden. Während Dinge wie Personen oder Gebäude von festlegbarer Form sind und sich sehr gut aus Polygonen und Texturen nachbilden lassen, dürfte auch der beste Modeller bei Effekten wie Feuer, Dampf oder Wasser das Handtuch werfen. Sieht man sich zum Beispiel eine Explosion genauer an, so lassen sich keine festen Formen erkennen, die man nachbauen könnte. Stattdessen fliegen hier Funken, Feuerzungen blähen sich auf und fallen zusammen, Teile fliegen auseinander und Rauch steigt auf. Tausende kleiner Partikel verhalten sich scheinbar chaotisch und bilden doch im Zusammenspiel ein stimmiges Bild.&lt;br /&gt;
&lt;br /&gt;
Um solche Effekte nachbilden zu können, braucht man ein anderes Konzept, nämlich das der Partikel-Systeme. So komplex eine Explosion auch aussehen mag, das Verhalten der einzelnen Komponenten lässt sich durch physikalische Gesetze erklären. Sind die Zusammenhänge erst einmal erkannt, so lassen sie sich auch auf virtuelle Systeme übertragen. Ein Partikel-System besteht also aus einer Reihe von Regeln, die das Verhalten von unzähligen dynamischen Partikeln beeinflussen. Um ein möglichst natürliches, chaotisches Resultat zu erhalten, werden zusätzlich zufällige Elemente eingeflochten.&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis sind chaotische, dynamische Gebilde, die trotzdem kontrollierbar bleiben. Durch die Kombination verschiedenster Regeln/Effekte mit variierten Parametern wird der Kreativität kaum Grenzen gesetzt.&lt;br /&gt;
&lt;br /&gt;
==Am Anfang war nur Staub==&lt;br /&gt;
Nachdem jetzt klar ist, was Partikel Systeme sind und wofür sie gebraucht werden, zeige ich an einem einfachen Beispiel, woraus solch ein System aufgebaut sein muss und wie es sich implementieren lässt.&lt;br /&gt;
&lt;br /&gt;
Eins jedoch noch vorweg: Ich werde hier keine ultraschnelle und flexible Partikel-Engine mit dutzenden von Effekt-Templates und Schießmichtot-Features entwickeln. Am Ende der Tutorials sollte der Grundstein für die Entwicklung einer echten Engine gelegt sein, aber diese Arbeit nehme ich euch nicht ab! ;)&lt;br /&gt;
&lt;br /&gt;
Das allerwichtigste an einem Partikel System sind natürlich die Partikel (siehe Unit pfxCore). Deshalb sollten wir uns zunächst Gedanken darüber machen, welche Attribute diese ausmachen.Zuerst fallen einem da die physikalischen Grundeigenschaften ein, die jeder Körper besitzt: Position, Geschwindigkeit (und Richtung), Masse, Dichte (des Materials) und natürlich eine bestimmte Größe. Eigenschaften wie Masse oder Dichte scheinen auf den ersten Blick unwichtig zu sein, sind aber für bestimmte Effekte wie Gravitation oder Wind unverzichtbar.Außerdem ordnen wir jedem Partikel eine individuelle Lebensspanne zu, die jeden Frame nach unten korrigieren wird. Von ähnlicher Bedeutung ist das Alter, das in Millisekunden angegeben wird.&lt;br /&gt;
&lt;br /&gt;
Zuletzt spendieren wir unseren Partikeln noch eine Farbe, die Zeiten der Schwarz-Weiß Effekte sind schließlich vorbei!Nun dürfte auch die Implementation keine Fragen mehr aufwerfen - na ja, fasst keine! Man könnte sich fragen, ob man lieber ein Partikel-Record oder eine Partikel-Klasse macht, aber wenn man bedenkt, dass eine Klassen-Instanz wesentlich mehr Speicher belegt als ein schlichtes Record und wir mit hunderten von Partikel hantieren wollen, sollte die Entscheidung klar sein.&lt;br /&gt;
&lt;br /&gt;
Ein Partikel-Typ könnte also folgendermaßen definiert sein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;TPfxParticle = record&lt;br /&gt;
  Position : TPfxVector;&lt;br /&gt;
  Velocity : TPfxVector;&lt;br /&gt;
  Density  : single;&lt;br /&gt;
  Mass     : single;&lt;br /&gt;
  Size     : single;&lt;br /&gt;
  Color    : TPfxColor;&lt;br /&gt;
  LiveSpan : integer;&lt;br /&gt;
  Age      : integer;&lt;br /&gt;
end;&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Typen TPfxVector und TPfxColor müssen wir natürlich auch noch definieren.TPfxVektor enthält eine x, y und z Koordinate und eignet sich damit prächtig zum speichern von Orts- oder Richtungsvektoren.&lt;br /&gt;
&lt;br /&gt;
TPfxColor definiert beliebige Farben, indem es die Farbintensität der Grundfarben angibt. Üblicherweise verwendet man dazu drei Byte, so dass jede Grundfarbe eine Intensität von 0 bis 255 haben kann. OpenGL benutzt jedoch Fließkommazahlen (Single), bei denen die Intensität zwischen 0 und 1 angegeben wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;TPfxVector = record&lt;br /&gt;
  x     : single;&lt;br /&gt;
  y     : single;&lt;br /&gt;
  z     : single;&lt;br /&gt;
end;&lt;br /&gt;
&lt;br /&gt;
TPfxColor = record&lt;br /&gt;
  r     : single;&lt;br /&gt;
  g     : single;&lt;br /&gt;
  b     : single;&lt;br /&gt;
end;&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Container auf - Partikel rein - Container zu==&lt;br /&gt;
Nun haben wir festgelegt, wie ein Partikel auszusehen hat, aber ein Partikel allein macht noch keinen eindrucksvollen Effekt. Für etwas Aufwendiges wie z.B. eine Explosion sollten es schon einige hundert Partikel sein mit gänzlich verschiedenen Eigenschaften. So müssen sich Rauch-Partikel anders verhalten als ihre Kollegen bei den sprühenden Funken, oder züngelnden Flammen.&lt;br /&gt;
&lt;br /&gt;
Um da nicht die Übersicht zu verlieren, fassen wir gleiche Partikel-Typen zu Gruppen zusammen, die über eine Container-Klasse verwaltet werden.Unsere Container-Klasse hat ein Interface, über das sich Partikel hinzufügen und löschen lassen. Diese Partikel werden in einem dynamischen Array gespeichert, das dem Benutzer Zugriff auf die Attribute aller Partikel gewährt. Außerdem aktualisiert die Container-Klasse die Partikel in Bezug auf Position und Alter und gibt bei abgelaufener Lebensspanne den Speicher frei.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Container Interface könnte z.B. so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;TPfxContainer = class(TObject)&lt;br /&gt;
protected&lt;br /&gt;
  FnumParticles : word;&lt;br /&gt;
  function GetSize : word;&lt;br /&gt;
public&lt;br /&gt;
  Particles  : array of TPfxParticle;&lt;br /&gt;
  property numParticles : word read FnumParticles;&lt;br /&gt;
  property Size : word read GetSize;&lt;br /&gt;
  constructor Create(aSize : word); overload;&lt;br /&gt;
  function Add(var aParticle : TPfxParticle) : integer;&lt;br /&gt;
  procedure Delete(aIndex : integer);&lt;br /&gt;
  procedure Advance(aTime : integer);&lt;br /&gt;
  function Clean : word;&lt;br /&gt;
  procedure Clear;&lt;br /&gt;
end;&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wie gesagt, dieses Interface ist einfach - und genauso einfach ist die Implementation. Ein Blick auf den Quellcode in der Unit pfxCore sollte die meisten Fragen beantworten. Die Speicherverwaltung ist zwar effektiv, aber noch lange nicht perfekt. Wenn die Instanz eines Containers erstellt wird, muss die Anzahl der zur Verfügung stehenden Plätze mit angegeben werden. Danach ändert sich daran nichts mehr, denn das Zuweisen und Freigeben von Speicher ist sehr rechenintensiv.&lt;br /&gt;
&lt;br /&gt;
numParticles gibt an, wie viele Plätze belegt sind. Dabei ist es egal, ob es sich um aktive Partikel handelt oder &amp;quot;tote&amp;quot;, bei denen LiveSpan bereits negativ ist. Für ein neues Partikel wird numParticels einfach um eins erhöht und dient gleichzeitig als Index für das dynamische Array.&lt;br /&gt;
&lt;br /&gt;
Und was machen wir, wenn alle Plätze belegt sind (numParticles = Size-1)? Wir rufen die Funktion Clean auf, die das gesamte Array einmal durch geht und in sich selbst kopiert, dabei aber Partikel mit negativer LiveSpan außen vor lässt. Die Anzahl der kopierten Partikel ist damit auch die Anzahl der belegten Plätze und wird in numParticles geschrieben.&lt;br /&gt;
&lt;br /&gt;
Die Funktion Advance geht alle Partikel im Array durch und aktualisiert sie. aTime gibt dabei die Zeit seit der letzten Aktualisierung an - dadurch wird die Bewegung von der Framerate unabhängig. Die Position wird anhand der alten Position und des Bewegungsvektors neu festgelegt und die Lebensspanne wird um aTime gesenkt, während das Alter entsprechend erhöht wird.&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, sind Partikel mit LiveSpan kleiner eins nicht mehr aktiv (und werden damit nicht mehr gerendert), auch wenn sie noch bewegt werden. Um ein Partikel zu löschen, reicht es demnach, LiveSpan auf Null zu setzen.&lt;br /&gt;
&lt;br /&gt;
==Klappe  und Action!==&lt;br /&gt;
Es wird Zeit für etwas Farbe auf dem Bildschirm, und deshalb werden wir uns jetzt einen kleinen Beispiel-Effekt basteln, der Feuer nachempfunden ist.Dazu erstellen wir in der Unit PfxImp eine Effekt-Klasse, um im praktischen Einsatz möglichst wenig Aufwand mit dem Effekt zu haben. Diese sollte über eine Methode zum Rendern des Effekts und eine für die Aktualisierung verfügen. Außerdem brauchen wir einen Container für die Partikel, eine Partikel-Schablone, eine Textur und eine Hilfsvariable, die die Emission steuert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;TExampleFX = class(Tobject)&lt;br /&gt;
protected&lt;br /&gt;
  Container : TPfxContainer;&lt;br /&gt;
  Particle : TPfxParticle;&lt;br /&gt;
  EmissionTime : integer;&lt;br /&gt;
  FireTex : GluInt;&lt;br /&gt;
public&lt;br /&gt;
  constructor Create;&lt;br /&gt;
  destructor Destroy; override;&lt;br /&gt;
  procedure Render;&lt;br /&gt;
  procedure Advance(aTime : integer);&lt;br /&gt;
end;&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu Constructor und Destructor gibt es nicht viel zu sagen interessant ist vor allem die Methode Advance, die das eigentliche Herz des Effekts ist. Sie fügt neue Partikel in den Container ein und sorgt dafür, dass der Inhalt des Containers per Advance-Methode aktualisiert wird. Da Anzahl und Eigenschaften der erstellten Partikel nicht von der Framerate abhängen sollen, brauchen wir wieder die Zeit seit der letzten Aktualisierung. Advance macht zunächst einmal nichts anderes, als die Zeit seit der letzten Aktualisierung (aTime) zu der Zeit seit der letzten Partikel Emission (EmissionTime) hinzuzuaddieren. Erst wenn ein bestimmtes Limit überschritten ist, werden Partikel erstellt. Dieses Limit ist übrigens über die Konstante EMISSION_RATE festgelegt. Ist es Zeit für eine Emission, so werden neue Partikel erstellt, und zwar so viele, wie der Wert von PARTICLES_PER_EMISSION vorschreibt.&lt;br /&gt;
&lt;br /&gt;
Um ein chaotisches Aussehen zu erreichen, werden die Attribute der zu erstellenden Partikel innerhalb bestimmter Grenzen vom Zufall ausgewählt und in der Schablone gespeichert. Da sich alle Partikel einer Emission von den Attributen ähnlich sein sollen - denn das sorgt für einen realistischeren Flammen-Effekt -, wird die Schablone nur einmal pro Emission generiert und dann für jedes Partikel der Emission leicht variiert. Mit der Add-Methode des Containers fügen wir eine Kopie der Schablone den Partikeln im Container hinzu.&lt;br /&gt;
&lt;br /&gt;
Da der Container die Partikel von nun an selbstständig verwaltet, brauchen wir uns nur noch um das Zeichnen der Partikel zu kümmern. Dies geschieht in der Prozedur Render. Wie ein Partikel gerendert wird, hängt natürlich vor allem davon ab, um was für ein Partikel es sich handelt. Die einfachste Möglichkeit einen Partikel zu zeichnen wäre, mit [[GlBegin#Beschreibung|GL_POINTS]] an die Position des Partikels einen Punkt zu setzen. Meistens bedient man sich jedoch des so genannten Billboards, wodurch sich wesentlich vielseitigere Effekte realisieren lassen. Jeder Partikel wird als Quad gerendert, das genau senkrecht zur Blickrichtung der Kamera positioniert ist. Durch den Einsatz unterschiedlicher Texturen, Farben, Größen, Formen und natürlich Blending lassen sich so mit relativ wenigen Partikeln eindrucksvolle und vielseitige Effekte schaffen.&lt;br /&gt;
&lt;br /&gt;
In unserem Bespiel Effekt werden alle Quads gleichgroß sein und dieselbe Textur (eine Feuerzunge in Schwarz-Weiß) haben - lediglich Farbe und Transparenz der Partikel unterscheiden sich. Da es sich bei Feuer im Wesentlichen um Licht handelt, nutzen wir, wie bei fast allen Lichteffekten, additives Blending, bei dem die Farbwerte des Quads zu den Farbwerten im Buffer addiert werden. Außerdem errechnen wir aus dem Alter der Partikel einen Alphawert, der die nötige Transparenz angibt.&lt;br /&gt;
&lt;br /&gt;
Bevor wir aber das erste Mal Rendern können, müssen gewisse Vorbereitungen getroffen werden. Natürlich muss Blending aktiviert und die richtige Blendfunktion gesetzt werden, aber auch der Tiefen-Puffer muss deaktiviert sein, denn wir wollen ja auch mehrere Quads übereinanderzeichnen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;glEnable(GL_BLEND);&lt;br /&gt;
glDisable(GL_DEPTH_TEST);&lt;br /&gt;
glBlendFunc(GL_SRC_ALPHA, GL_ONE);&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt können wir uns der eigentlichen Render-Methode der Effekt-Klasse zuwenden: In einer Schleife werden alle Partikel des Containers durchlaufen. Ist die Lebensspanne größer 0, so ist das Partikel noch aktiv und muss gerendert werden. Dazu wird als erstes die Farbstärke (sat von Saturation = Sättigung) berechnet. In den ersten 100ms steigt sat auf 1 an, um dann bis zum Ablauf der Lebensspanne wieder auf 0 zu sinken. Nun können wir die Farbe des aktuellen Partikels setzen. Als Alphawert setzen wir sat ein, wobei das Partikel transparenter wird, je kleiner sat ist.&lt;br /&gt;
&lt;br /&gt;
Bevor wir das Partikel rendern, müssen wir mit glTranslate die Modelview-Matrix auf die aktuelle Position des Partikels setzen. Davor sichern wir jedoch die Matrix, denn die Transformationen sind für jeden Partikel unterschiedlich. Nach der Transformation entfernen wir mit FXBillboardBegin alle Rotationen aus der Matrix d.h. wir ersetzen die ersten 3 Felder der ersten drei Zeilen durch eine 3x3 Einheitsmatrix. Das texturierte Quad, das wir nun zeichnen, steht damit parallel zum Bildschirm.&lt;br /&gt;
&lt;br /&gt;
Am Ende jeden Schleifendurchlaufs wird die im [[Stack]] gesicherte Matrix wieder hergestellt, die Matrix wird also nicht dauerhaft verändert.&lt;br /&gt;
&lt;br /&gt;
Jetzt brauchen wir die Partikel-Klasse nur noch irgendwo einzubauen. (Für die Demo habe ich das OpenGL-Template genommen) Nachdem die Klasseninstanz erstellt ist, müssen wir der Prozedur glDraw nur noch folgende Methoden-Aufrufe hinzufügen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;Effect.Advance(round(1000/FPS));&lt;br /&gt;
glLoadIdentity;&lt;br /&gt;
glTranslate(0,-1,-3);&lt;br /&gt;
Effect.Render;&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und schon haben wir eine lustig flackernde Flamme auf unserem geliebten Bildschirm!&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_Partikel1_fire.gif|center]]&lt;br /&gt;
&lt;br /&gt;
==Das Wort zum Sonntag==&lt;br /&gt;
Tja, es ist Sonntag und ich habe mein erstes Tutorial für die DGL fertig! Ich hoffe, es hat dem ein oder anderen gefallen. Die Materie ist teilweise nicht ganz einfach, aber dafür umso spannender! ^^ Ich schlage vor, ihr nehmt das soeben Gelesene als Grundstock für eigene Versuche auf dem Gebiet der Partikel-Systeme. Wie eingangs erwähnt, werde ich im zweiten Tutorial das heute entwickelte System erweitern und auf besonders quälende Fragen und häufige Probleme eingehen. Das setzt natürlich voraus, dass ich von den Problemen und Fragen erfahre. Also her mit dem Feedback - sonst gibt's kein zweites Tut! *fg*&lt;br /&gt;
&lt;br /&gt;
Happy Coding!&lt;br /&gt;
&lt;br /&gt;
'''Thomas aka Lithander'''&lt;br /&gt;
&lt;br /&gt;
== Dateien ==&lt;br /&gt;
* {{ArchivLink|file=tut_particle_delphi_api|text=Delphi-API-Quelltext zum Tutorial}}&lt;br /&gt;
* {{ArchivLink|file=tut_particle1_exe|text=Windows-Binary zum Tutorial}}&lt;br /&gt;
&lt;br /&gt;
{{TUTORIAL_NAVIGATION|[[Tutorial_Nebel]]|[[Tutorial_BumpMap]]}}&lt;br /&gt;
[[Kategorie:Tutorial|Partikel1]]&lt;/div&gt;</summary>
		<author><name>Nevermind10844</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_glsl&amp;diff=25697</id>
		<title>Tutorial glsl</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_glsl&amp;diff=25697"/>
				<updated>2012-08-27T22:56:17Z</updated>
		
		<summary type="html">&lt;p&gt;Nevermind10844: /* Einleitung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Einleitung=&lt;br /&gt;
Hallo und willkommen bei meiner &amp;quot;Einführung&amp;quot; in GLSL (kurz für &amp;quot;Open'''GL''' '''S'''hading '''L'''anguage&amp;quot;), der offiziellen Hochlevel-Shadersprache von OpenGL. 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 GLSL 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 einfache Shader zu schreiben und auch in ihren Programmen zu nutzen. Außerdem soll dieses Dokument gleichzeitig als ein deutsches &amp;quot;Pendant&amp;quot; zu den von Khronoes veröffentlichten Shaderspezifikationen, und damit als alltägliches Nachschlagewerk, dienen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Vorkenntnisse==&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=Was ist GLSL?=&lt;br /&gt;
Wie Eingangs kurz angesprochen handelt es sich bei GLSL 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.&lt;br /&gt;
&lt;br /&gt;
Die in GLSL geschriebenen Programme nennen sich, angepasst an die Terminologie von RenderMan und DirectX, [[Shader]] (im Gegensatz zu &amp;quot;Programme&amp;quot; bei ARB_VP/FP) und werden entweder auf Eckpunkte (VertexShader), Fragmente (FragmentShader) angewendet, oder (neuerdings, ab Shadermodell 4.0) auch genutzt um Geometrie zu erstellen (Geometryshader). Andere Teile der Renderpipeline (z.B. die Rasterisierung) können momentan noch nicht durch Shader beeinflusst werden, was allerdings in Zukunft noch kommen kann.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Voraussetzungen==&lt;br /&gt;
&lt;br /&gt;
GLSL wurde 2005 mit OpenGL 1.5 eingeführt. Während es in Sachen Treiber- und Hardwareunterstützung anfänglich noch dürftig aussah, wird man inzwischen keine Grafikkarte mehr kaufen können die nicht zumindest Vertex- und Fragmentshader beherscht. Geometrieshader hingegen sind relativ neu und wurden erst mit Shadermodell 4.0 eingeführt, hier ist es also unter Umständen noch möglich dass selbst aktuelle Treiber/Karten keine Geometrieshader beherrschen.&lt;br /&gt;
&lt;br /&gt;
Natürlich benötigt man auch einen passenden OpenGL-Header der die für GLSL nötigen Funktionen exportiert. Ich verweise dazu auf unseren eigenen OpenGL-Header [[DGLOpenGL.pas]], der peermanent auf dem aktuellsten Stand gehalten wird und auch Support für Geometrieshader mitbringt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Extensions==&lt;br /&gt;
&lt;br /&gt;
Die GL-Shadersprache &amp;quot;besteht&amp;quot; in ihrer aktuellen Version aus folgenden Extensions, fürs Verständnis wäre es nicht schlecht, wenn ihr euch zumindest die Einleitungen dazu durchlest :&lt;br /&gt;
* [[GL_ARB_shader_objects]] ([http://oss.sgi.com/projects/ogl-sample/registry/ARB/shader_objects.txt Originalspezifikation])&lt;br /&gt;
: Definiert die API-Aufrufe, die zum Erstellen, Kompilieren, Linken, Anhängen und Aktivieren von Shader- und Programmobjekten nötig sind. &lt;br /&gt;
* [[GL_ARB_vertex_shader]] ([http://oss.sgi.com/projects/ogl-sample/registry/ARB/vertex_shader.txt Originalspezifikation])&lt;br /&gt;
: Fügt der OpenGL Programmierbarkeit auf Vertexebene hinzu. &lt;br /&gt;
* [[GL_ARB_fragment_shader]] ([http://oss.sgi.com/projects/ogl-sample/registry/ARB/fragment_shader.txt Originalspezifikation])&lt;br /&gt;
: Fügt der OpenGL Programmierbarkeit auf Fragmentebene hinzu. &lt;br /&gt;
* [[GL_ARB_shading_language_100]] ([http://oss.sgi.com/projects/ogl-sample/registry/ARB/shading_language_100.txt Originalspezifikation])&lt;br /&gt;
: Gibt die unterstützte Version von glSlang an, momentan 1.00.&lt;br /&gt;
&lt;br /&gt;
'''Hinweis''' : Seit OpenGL 2.0 ist GLSL Teil des Kerns. Wenn die Karte also OpenGL 2.0 unterstützt, dann unterstützt sie auch (zumindest in Software) Vertex- und Fragmentshader.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Sprachversionen==&lt;br /&gt;
&lt;br /&gt;
Neben der OpenGL-Version und dem vorhandenen Shadermodell (das eher an DirectX ausgerichtet ist), bietet auch GLSL verschiedene Versionen, die entsprechend erweiterte Funktionalität bieten.&lt;br /&gt;
&lt;br /&gt;
Auslesen kann man die verfürgbare GLSL-Version wie folgt:&lt;br /&gt;
&lt;br /&gt;
 glGetString(GL_SHADING_LANGUAGE_VERSION)&lt;br /&gt;
&lt;br /&gt;
Erst ab Version 1.4. kann man davon ausgehen dass GLSL alle Features des Shadermodells 4.0 liefert, ab 1.3 grob gesagt Shadermodell 3.0 (bei GLSL lässt sich das leider nicht so leicht unterteilen).&lt;br /&gt;
&lt;br /&gt;
Außerdem kann man seinem Shader eine Versionsnummer verpassen. Sollte der Shadercompiler (also Treiber bzw. Hardware) diese Version nicht unterstützen, gibt dieser eine Fehlermeldung heraus:&lt;br /&gt;
&lt;br /&gt;
 #version 1.50 &lt;br /&gt;
&lt;br /&gt;
''(Hinweis: Muss am Anfang des Shaders stehen)''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Objekte==&lt;br /&gt;
&lt;br /&gt;
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 dass 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 dazu). Mit glSlang wurden dann im Zuge dieser Aktion zwei neue Objekte eingeführt, deren Definition ihr euch unbedingt einprägen solltet:&lt;br /&gt;
&lt;br /&gt;
* '''Programmobjekt'''&lt;br /&gt;
: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.&lt;br /&gt;
&lt;br /&gt;
* '''Shaderobjekt'''&lt;br /&gt;
:Dieses Objekt verwaltet den Quellcodestring eines Shaders und ist entweder vom Typ '''GL_VERTEX_SHADER''', '''GL_FRAGMENT_SHADER_ARB''' oder '''GL_GEOMETRY_SHADER'''.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Ressourcen==&lt;br /&gt;
&lt;br /&gt;
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, dass auch Grafikkarten der nächsten und übernächsten Generation voll ausgenutzt werden können. Damit einher geht die Tatsache, dass sich die Spezifikationen in Zukunft ändern/erweitern werden, weshalb man da immer einen Blick hineinwerfen sollte. Die Anlaufstelle dafür ist die [http://www.opengl.org/documentation/specs/ Spezifikationenliste auf OpenGL.org].&lt;br /&gt;
&lt;br /&gt;
=GLSL im Programm=&lt;br /&gt;
Bevor wir uns mit der Syntax von glSlang beschäftigen, zeige ich euch erstmal, wie ihr Shader in euer Programm einbindet und nutzt. 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.&lt;br /&gt;
&lt;br /&gt;
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 &amp;quot;Datentyp&amp;quot; {{INLINE_CODE|glHandle}} eingeführt, der ein Objekthandle repräsentiert. Wir deklarieren also wie folgt :&lt;br /&gt;
&lt;br /&gt;
 ProgramObject        : GLhandle;&lt;br /&gt;
 VertexShaderObject   : GLhandle;&lt;br /&gt;
 FragmentShaderObject : GLhandle;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nach dieser Deklaration können wir dann damit beginnen unsere Objekte zu erstellen. Den Anfang macht das Programmobjekt :&lt;br /&gt;
&lt;br /&gt;
 ProgramObject        := glCreateProgram;&lt;br /&gt;
&lt;br /&gt;
Die Funktion [[glCreateProgram]] erstellt uns oben ein leeres Programmobjekt und gibt ein gültiges Handle darauf zurück.&lt;br /&gt;
&lt;br /&gt;
Weiter gehts mit der Erstellung unseres Vertex bzw. Fragment Shaders :&lt;br /&gt;
&lt;br /&gt;
 VertexShaderObject   := glCreateShader(GL_VERTEX_SHADER);&lt;br /&gt;
 FragmentShaderObject := glCreateShader(GL_FRAGMENT_SHADER);&lt;br /&gt;
&lt;br /&gt;
[[glCreateShader]] dient zur Generierung eines leeren Shaderobjektes. Momentan unterstützt diese Funktion VertexShader und FragmentShader.&lt;br /&gt;
&lt;br /&gt;
Nachdem wir nun also zwei gültige Shaderobjekte haben, wollen wir diese auch mit entsprechendem Quellcode versorgen :&lt;br /&gt;
&lt;br /&gt;
 glShaderSource(VertexShaderObject, 1, @ShaderText, @ShaderLength);&lt;br /&gt;
 glShaderSource(FragmentShaderObject, 1, @ShaderText, @ShaderLength);&lt;br /&gt;
&lt;br /&gt;
Via [[glShaderSource]] setzen wir den Quellcode eines Shaderobjektes ''komplett'' neu. Zum Laden des Quellcodes bietet sich unter Delphi übrigens eine TStringList geradezu an. Es sollte beachtet werden, dass der Quellcode zu diesem Zeitpunkt ''nicht geparst'' wird, also keine Fehleruntersuchung stattfindet.&lt;br /&gt;
&lt;br /&gt;
Der Quellcode wurde jetzt also an unsere Shaderobjekte gebunden und sollte dann natürlich auch noch kompiliert werden :&lt;br /&gt;
&lt;br /&gt;
 glCompileShader(VertexShaderObject);&lt;br /&gt;
 glCompileShader(FragmentShaderObject);&lt;br /&gt;
&lt;br /&gt;
Der glSlang-Compiler des Treibers wird bei einem Aufruf von [[glCompileShader]] versuchen, unsere Shader zu kompilieren. Sofern diese keine Fehler aufweisen, sollte dies auch erfolgreich sein. Wenn nicht, dann spuckt uns der Shadercompiler (je nach Treiber) recht detaillierte Infos aus. Wie man an diese Infos kommt könnt ihr gleich nachlesen.&lt;br /&gt;
&lt;br /&gt;
Wenn unsere Shader dann kompiliert werden konnten, ist es Zeit, diese an unser anfangs erstelltes Programmobjekt anzuhängen :&lt;br /&gt;
&lt;br /&gt;
 glAttachShader(ProgramObject, VertexShaderObject);&lt;br /&gt;
 glAttachShader(ProgramObject, FragmentShaderObject);&lt;br /&gt;
&lt;br /&gt;
Nachdem die Shaderobjekte nun an das Programmobjekt angehängt wurden, werden diese nicht mehr benötigt und ihre Resourcen können freigegeben werden :&lt;br /&gt;
&lt;br /&gt;
 glDeleteShader(VertexShaderObject);&lt;br /&gt;
 glDeleteShader(FragmentShaderObject);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Am Schluß müssen wir dann noch unsere ans Programmobjekt gebundenen Shader linken :&lt;br /&gt;
&lt;br /&gt;
 glLinkProgram(ProgramObject);&lt;br /&gt;
&lt;br /&gt;
Während [[glCompileShader]] unsere Shader auf syntaktische Fehler innerhalb ihres lokalen Raums geprüft hat, werden beim Linken durch [[glLinkProgram]] die angehangenen Shader zu einem ausführbaren Shader gelinkt. Folgende Bedingungen führen zu einem '''Linkerfehler''':&lt;br /&gt;
&lt;br /&gt;
* Die Zahl der von der Implementation unterstützten Attributvariablen wurde überschritten&lt;br /&gt;
* Der Speicherplatz für Uniformvariablen wurde überschritten&lt;br /&gt;
* Die Zahl der von der Implementation angebotenen Sampler wurde überschritten&lt;br /&gt;
* Die main-Funktion fehlt&lt;br /&gt;
* Die Liste der Varying-Variablen des Vertexshaders stimmt nicht mit der des Fragmentshaders überein&lt;br /&gt;
* Funktions- oder Variablenname nicht gefunden&lt;br /&gt;
* Eine gemeinsame Globale ist mit unterschiedlichen Werten oder Typen initialisiert worden&lt;br /&gt;
* Zwei Sampler unterschiedlichen Typs zeigen auf die selbe Textureneinheit&lt;br /&gt;
* Ein oder mehrere angehangene(r) Shader wurden nicht erfolgreich kompiliert&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Noch eine kleine Notiz zum Löschen der Shader mittel [[glDeleteShader]] : Da Shader(objekte) einen Referenzzähler besitzen und erst gelöscht werden wenn diese nirgendwo mehr benötigt werden, ist es nicht falsch diese vor dem Linkvorgang zu löschen. Allerdings spielt es letztendlich keine Rolle ob die Löschanweisung vorher der nachher ausgeführt wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Fehlererkennung==&lt;br /&gt;
Natürlich wird es ohne Fehlerausgabe recht schwer, etwaige Probleme in einem Vertex- oder Fragmentshader 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 [[glGetShaderInfoLog]] und [[glGetShader]] mit dem Argument {{INLINE_CODE|GL_OBJECT_INFO_LOG_LENGTH}}. 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.&lt;br /&gt;
&lt;br /&gt;
Um die Ausgabe dieses Logs so einfach wie möglich zu machen, bietet es sich an beide in einer einfach Funktion unterzubringen :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;function glSlang_GetInfoLog(pShader: GLHandleARB): String;&lt;br /&gt;
var&lt;br /&gt;
  blen, slen: GLInt;&lt;br /&gt;
  InfoLog: PGLCharARB;&lt;br /&gt;
&lt;br /&gt;
begin&lt;br /&gt;
  glGetShaderiv(glObject, GL_INFO_LOG_LENGTH , @blen);&lt;br /&gt;
  if blen &amp;gt; 1 then&lt;br /&gt;
  begin&lt;br /&gt;
    GetMem(InfoLog, blen * SizeOf(GLCharARB));&lt;br /&gt;
    glGetShaderInfoLog(pShader, blen, slen, InfoLog);&lt;br /&gt;
    Result := PChar(InfoLog);&lt;br /&gt;
    Dispose(InfoLog);&lt;br /&gt;
  end;&lt;br /&gt;
end;&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Funktion ist recht leicht erklärt : Zuerst lassen wir uns über {{INLINE_CODE|glGetShaderiv}} mitteilen wie lang der aktuelle Infolog ist. Sollte dort tatsächlich etwas drinstehen (blen &amp;gt; 1), dann lassen wir uns dessen Inhalt via {{INLINE_CODE|glGetShaderInfoLog}} in {{INLINE_CODE|InfoLog}} ausgeben und liefern diesen als Ergebnis zurück.&lt;br /&gt;
&lt;br /&gt;
Wie bereits gesagt wird nur nach dem Kompilieren eines Shaders bzw. dem Linken eines Programmobjektes ein Infolog erstellt. Es bietet sich dadurch an, direkt danach einen solchen Aufruf zu machen :&lt;br /&gt;
&lt;br /&gt;
 glCompileShader(VertexShaderObject);&lt;br /&gt;
 ShowMessage(glSlang_GetInfoLog(VertexShaderObject));&lt;br /&gt;
&lt;br /&gt;
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 &amp;quot;belohnt&amp;quot;, z.B. so :&lt;br /&gt;
&lt;br /&gt;
[[Bild:GLSL_error_vshader.jpg]]&lt;br /&gt;
&lt;br /&gt;
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 :&lt;br /&gt;
&lt;br /&gt;
[[Bild:GLSL info programobject.jpg]]&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
==Shader benutzen==&lt;br /&gt;
Um den Shader auch für die nächsten Polygone zu benutzen oder Uniformparameter übergeben zu können, ruft man die Funktion&lt;br /&gt;
 glUseProgramt(ProgramObject);&lt;br /&gt;
um alle Shader zu deaktivieren, ruft man dieselbe Funktion mit dem Parameter 0.&lt;br /&gt;
&lt;br /&gt;
==Parameterübergabe==&lt;br /&gt;
Uniformparameter (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 [[glUniform]] sind. Während mit {{INLINE_CODE|glUniform4f}} z.B. ein Vier-Komponentenvektor an das Programmobjekt übergeben wird, kann man mittels {{INLINE_CODE|glUniformMatrix4fv}} ganze Matrizen schnell und einfach übergeben. Außerdem 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 [[glGetUniformLocationARB]] gibt anhand des übergebenen Parameternamens dessen Position zurück. Man kann also ganz einfach über den Namen drauf zugreifen :&lt;br /&gt;
&lt;br /&gt;
 glUniform3f(glGetUniformLocation(ProgramObject, PGLCharARB('LightPosition')), LPos[0], LPos[1], LPos[2]);&lt;br /&gt;
 glUniform1i(glGetUniformLocation(ProgramObject, PGLCharARB('texSamplerTMU3')), 3);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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 muß.&lt;br /&gt;
&lt;br /&gt;
=Die Shadersprache=&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
'''Obligatorische Hinweise für verwöhnte Delphi-Nutzer : '''&lt;br /&gt;
*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.&lt;br /&gt;
*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).&lt;br /&gt;
&lt;br /&gt;
'''Kleine Programmstrukturkunde für C-Unkundige :'''&amp;lt;br&amp;gt;&lt;br /&gt;
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 :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
uniform vec4 VariableA;&lt;br /&gt;
float VariableB;&lt;br /&gt;
vec3  VariableC;&lt;br /&gt;
const float KonstanteA = 256.0;&lt;br /&gt;
&lt;br /&gt;
float MyFunction(vec4 ArgumentA)&lt;br /&gt;
{&lt;br /&gt;
    float FunktionsVariableA = float(5.0);&lt;br /&gt;
&lt;br /&gt;
    return float(ArgumentA * (FunktionsVariableA + KonstanteA));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Ich bin ein Kommentar&lt;br /&gt;
/* Und ich auch */&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;&lt;br /&gt;
    gl_TexCoord[0] = gl_MultiTexCoord0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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 der Compiler einen Fehler aus.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Datentypen==&lt;br /&gt;
&lt;br /&gt;
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 :&lt;br /&gt;
&lt;br /&gt;
{|{{Prettytable_B1}}&lt;br /&gt;
!Datentyp  	&lt;br /&gt;
!Erklärung&lt;br /&gt;
|-&lt;br /&gt;
|void 	&lt;br /&gt;
|Für Funktionen die keinen Wert zurückgeben&lt;br /&gt;
|-&lt;br /&gt;
|bool 	&lt;br /&gt;
|Konditionaler Typ, entweder true (wahr) oder false (falsch)&lt;br /&gt;
|-&lt;br /&gt;
|int 	&lt;br /&gt;
|Vorzeichenbehafteter vorzeichenbehafteter Integerwert&lt;br /&gt;
|-&lt;br /&gt;
|uint 	&lt;br /&gt;
|Vorzeichenbehafteter vorzeichenloser Integerwert&lt;br /&gt;
|-&lt;br /&gt;
|float 	&lt;br /&gt;
|Fließkommaskalar mit Singlegenauigkeit (32 Bit)&lt;br /&gt;
|-&lt;br /&gt;
|vec2 	&lt;br /&gt;
|2-Komponenten Fließkommavektor&lt;br /&gt;
|-&lt;br /&gt;
|vec3 	&lt;br /&gt;
|3-Komponenten Fließkommavektor&lt;br /&gt;
|-&lt;br /&gt;
|vec4 	&lt;br /&gt;
|4-Komponenten Fließkommavektor&lt;br /&gt;
|-&lt;br /&gt;
|bvec2 	&lt;br /&gt;
|2-Komponenten Booleanvektor&lt;br /&gt;
|-&lt;br /&gt;
|bvec3 	&lt;br /&gt;
|3-Komponenten Booleanvektor&lt;br /&gt;
|-&lt;br /&gt;
|bvec4 	&lt;br /&gt;
|4-Komponenten Booleanvektor&lt;br /&gt;
|-&lt;br /&gt;
|ivec2 	&lt;br /&gt;
|2-Komponenten vorzeichenbehafteter Integervektor&lt;br /&gt;
|-&lt;br /&gt;
|ivec3 	&lt;br /&gt;
|3-Komponenten vorzeichenbehafteter Integervektor&lt;br /&gt;
|-&lt;br /&gt;
|ivec4 	&lt;br /&gt;
|4-Komponenten vorzeichenbehafteter Integervektor&lt;br /&gt;
|-&lt;br /&gt;
|uvec2 	&lt;br /&gt;
|2-Komponenten vorzeichenloser Integervektor&lt;br /&gt;
|-&lt;br /&gt;
|uvec3 	&lt;br /&gt;
|3-Komponenten vorzeichenloser Integervektor&lt;br /&gt;
|-&lt;br /&gt;
|uvec4 	&lt;br /&gt;
|4-Komponenten vorzeichenloser Integervektor&lt;br /&gt;
|-&lt;br /&gt;
|mat2 	&lt;br /&gt;
|2x2 Fließkommamatrix&lt;br /&gt;
|-&lt;br /&gt;
|mat3 	&lt;br /&gt;
|3x3 Fließkommamatrix&lt;br /&gt;
|-&lt;br /&gt;
|mat4 	&lt;br /&gt;
|4x4 Fließkommamatrix&lt;br /&gt;
|-&lt;br /&gt;
|matMxN 	&lt;br /&gt;
|Matrix mit M Spalten und N Zeilen&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die sampler-Typen stellen eine besondere Klasse zum Zugriff auf Texturen dar, und werden im Kapitel 6.7 genauer erklärt, inklusive einiger Anwendungsbeispiele.&lt;br /&gt;
&lt;br /&gt;
{|{{Prettytable_B1}}&lt;br /&gt;
!Datentyp  	&lt;br /&gt;
!Erklärung&lt;br /&gt;
|-&lt;br /&gt;
|sampler1D 	&lt;br /&gt;
|Zugriff auf 1D-Textur&lt;br /&gt;
|-&lt;br /&gt;
|sampler2D 	&lt;br /&gt;
|Zugriff auf 2D-Textur&lt;br /&gt;
|-&lt;br /&gt;
|sampler3D 	&lt;br /&gt;
|Zugriff auf 3D-Textur&lt;br /&gt;
|-&lt;br /&gt;
|samplerCube 	&lt;br /&gt;
|Zugriff auf Cubemap&lt;br /&gt;
|-&lt;br /&gt;
|sampler2DRect 	&lt;br /&gt;
|Zugriff auf Texturen die nicht 2^n * 2^n entsprechen (&amp;quot;non power-of-two&amp;quot;, NPOT)&lt;br /&gt;
|-&lt;br /&gt;
|sampler1DShadow 	&lt;br /&gt;
|Zugriff auf 1D-Tiefentextur mit Vergleichsoperation&lt;br /&gt;
|-&lt;br /&gt;
|sampler2DShadow 	&lt;br /&gt;
|Zugriff auf 2D-Tiefentextur mit Vergleichsoperation&lt;br /&gt;
|-&lt;br /&gt;
|samplerCubeShadow&lt;br /&gt;
|Zugriff auf Tiefentextur in einer Cubemap (z.b. für omni-diretionale Lichtquellen)&lt;br /&gt;
|-&lt;br /&gt;
|sampler2DRectShadow&lt;br /&gt;
|Zugriff auf 2D-NPOT-Tiefentextur &lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|sampler1DArray&lt;br /&gt;
|Zugriff auf ein array aus 1D-Texturen &lt;br /&gt;
|-&lt;br /&gt;
|sampler2DArray&lt;br /&gt;
|Zugriff auf ein array aus 2D-Texturen &lt;br /&gt;
|-&lt;br /&gt;
|sampler1DArrayShadow&lt;br /&gt;
|Zugriff auf ein array aus 1D-Tiefentexturen &lt;br /&gt;
|-&lt;br /&gt;
|sampler2DArrayShadow&lt;br /&gt;
|Zugriff auf ein array aus 2D-Tiefentexturen &lt;br /&gt;
|-&lt;br /&gt;
|samplerBuffer&lt;br /&gt;
|Zugriff auf eine Puffertextur (1D-Texutr zum Speichern von Pufferobjekten)&lt;br /&gt;
|-&lt;br /&gt;
|sampler2DMS&lt;br /&gt;
|Zugriff auf eine 2D-Textur mit mehreren Samplepunkten (z.b. für Multisampling)&lt;br /&gt;
|-&lt;br /&gt;
|sampler2DMSArray&lt;br /&gt;
|Zugriff auf einarray aus 2D-Textur mit mehreren Samplepunkten (z.b. für Multisampling)&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Arrays===&lt;br /&gt;
&lt;br /&gt;
Natürlich unterstützt glSlang auch Arrays, die wie in C deklariert werden und deren Index bei 0 beginnt. Folgendes Array im Shader :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
float temp[3];&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Strukturen===&lt;br /&gt;
&lt;br /&gt;
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 {{INLINE_CODE|struct}} eingeleitet und können dann zur Typisierung von Variablen genutzt werden. Folgendes Beispiel dürfte die Nutzung verdeutlichen :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
struct light&lt;br /&gt;
{&lt;br /&gt;
    bool active;&lt;br /&gt;
    float intensity;&lt;br /&gt;
    vec3 position;&lt;br /&gt;
    vec3 color;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Im Shader können dann neue Variablen von diesem Typ ganz einfach deklariert werden :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
 light LightSource[3];&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Der Zugriff auf die Elemente der Struktur erfolgt dann wie gewohnt über den Punkt :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
LightSource[i].position = vec3(1.0, 1.0, 5.0);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Typenqualifzierer==&lt;br /&gt;
&lt;br /&gt;
Zusätzlich zur Typendeklaration kann eine Variable noch einen Typenqualifizerer vorangestellt bekommen, der an den Anfang der Deklaration gehört.&lt;br /&gt;
&lt;br /&gt;
* '''const'''&lt;br /&gt;
: Festgelegte (nur lesen) Konstante bzw. nur lesbarer Funktionsparameter.&lt;br /&gt;
&lt;br /&gt;
* '''uniform'''&lt;br /&gt;
: 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.&lt;br /&gt;
&lt;br /&gt;
* '''attribute'''&lt;br /&gt;
: 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.&lt;br /&gt;
&lt;br /&gt;
* '''varying'''&lt;br /&gt;
: 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.&lt;br /&gt;
&lt;br /&gt;
* '''in'''&lt;br /&gt;
: Für Variablen die an eine Funktion übergeben und dort ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
* '''out'''&lt;br /&gt;
: Für Variablen die von einer Funktion nach außen zurückgegeben werden.&lt;br /&gt;
&lt;br /&gt;
* '''inout'''&lt;br /&gt;
: Für Variablen die sowohl an eine Funktion übergeben als auch von dieser zurückgegeben werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Um obige Auflistung nicht leer im Raum stehen zu lassen zeige ich ein paar Beispiele die hoffentlich zum Verständnis beitragen :&lt;br /&gt;
&lt;br /&gt;
===Beispiel A=== &lt;br /&gt;
Vertexnormale soll an einen FragmenShader (interpoliert) übergeben werden :&lt;br /&gt;
&lt;br /&gt;
:Im VertexShader :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
varying vec3 VertexNormal;&lt;br /&gt;
...&lt;br /&gt;
VertexNormal = normalize(MV_IT * gl_Normal);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
:Im FragmentShader :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
varying vec3 VertexNormal;&lt;br /&gt;
...&lt;br /&gt;
TempVector = VertexNormal*...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Beispiel B=== &lt;br /&gt;
Uniformparameter zur nachträglichen Farbänderung der Szene wird im Programm übergeben :&lt;br /&gt;
&lt;br /&gt;
:Im VertexShader :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
uniform vec4 GlobalColor;&lt;br /&gt;
...&lt;br /&gt;
gl_FrontColor = GlobalColor * gl_Color;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
:Im Programm :&lt;br /&gt;
&lt;br /&gt;
 glUniform4fARB(glSlang_GetUniLoc(ProgramObject, 'GlobalColor'), Col[0], Col[1], Col[2], Col[3]);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Beispiel C=== &lt;br /&gt;
Konstante zur festen Farbänderung :&lt;br /&gt;
&lt;br /&gt;
:Im VertexShader :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
const vec4 ColorBias = vec4(0.2, 0.3, 0.0, 0.0);&lt;br /&gt;
...&lt;br /&gt;
gl_FrontColor = ColorBias * gl_Color;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
==Konstruktoren==&lt;br /&gt;
&lt;br /&gt;
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 {{INLINE_CODE|vec4}} mit dem Konstruktor {{INLINE_CODE|vec4(float, float, float, float)}} initialisieren.&lt;br /&gt;
&lt;br /&gt;
Allerdings hat man sich recht viel Mühe bei dieser Konstruktorgeschichte gemacht, so dass man einen vec4 nicht unbedingt mit einem {{INLINE_CODE|vec4}}-Konstruktor vorbelegen muss, sondern es vielseitige Möglichkeiten gibt. Um dies zu verdeutlichen gibts ein paar Beispiele :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
vec4 Color = vec4(1.0, 0.0, 0.0, 0.0);&lt;br /&gt;
vec4 Color = vec4(MyVec3, 1.0);&lt;br /&gt;
vec4 Color = vec4(MyVec2_A, MyVec2_B);&lt;br /&gt;
&lt;br /&gt;
vec3 LVec  = vec3(MyVec4);&lt;br /&gt;
vec2 Tmp   = vec2(MyVec3);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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 {{INLINE_CODE|vec3.x, vec3.y, vec3.z}} aus MyVec4 übernommen werden und MyVec4.w einfach ignoriert wird.&lt;br /&gt;
&lt;br /&gt;
Das Umkehrbeispiel, also&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
vec4 Color = vec4(MyVec3)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
funktioniert allerdings nicht, da hier die Zahl der benötigten Argumente nicht erreicht wird. In diesem Falle müsste es dann&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt; &lt;br /&gt;
vec4 Color = vec4(MyVec3, 0.0)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
heissen.&lt;br /&gt;
&lt;br /&gt;
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'' :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
mat4 MyMatrix = mat4(MyVec4, MyVec4, MyVec4, MyVec4);&lt;br /&gt;
mat2 MyMatrix = mat4(1.0, 0.0, 0.0, 0.0,&lt;br /&gt;
                     0.0, 1.0, 0.0, 0.0,&lt;br /&gt;
                     0.0, 0.0, 1.0, 0.0,&lt;br /&gt;
                     0.0, 0.0, 0.0, 1.0);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Vektor- und Matrixkomponenten==&lt;br /&gt;
&lt;br /&gt;
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 :&lt;br /&gt;
&lt;br /&gt;
* {x, y, z, w}&lt;br /&gt;
:Für den Zugriff auf Vektoren die Punkte, Normale oder sonstige Vertexdaten repräsentieren.&lt;br /&gt;
&lt;br /&gt;
* {r, g, b, a}&lt;br /&gt;
:Für den Zugriff auf Vektoren die Farbwerte repräsentieren.&lt;br /&gt;
&lt;br /&gt;
* {s, t, p, q}&lt;br /&gt;
:Für den Zugriff auf Vektoren die Texturkoordinaten repräsentieren.&lt;br /&gt;
&lt;br /&gt;
Ein paar Beispiele zur Unterstreichung des oben gesagten :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
v4.rgba = vec4(1.0, 0.0, 0.0, 0.0);  // gültig&lt;br /&gt;
v4.rgzw = vec4(1.0, 1.0, 1.0, 2.0);  // Ungültig, da verschiedenen Namensets&lt;br /&gt;
v2.rgb  = vec3(1.0, 2.0, 1.0);       // Ungültig, da vec2 nur r+g besitzt&lt;br /&gt;
v2.xx   = vec2(5.0, 3.0);            // Ungültig, da 2 mal gleiche Komponente&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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 :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
MyMat4[2]    = vec4(1.0); // Setzt die 3.Zeile der Matrix komplett auf 1.0&lt;br /&gt;
MyMat4[3][3] = 3.5;       // Setzt das Element unren rechts auf 3.5&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Vektor- und Matrixoperationen==&lt;br /&gt;
&lt;br /&gt;
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über hinaus 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 :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
vec3  dest;&lt;br /&gt;
vec3  source;&lt;br /&gt;
float factor;&lt;br /&gt;
&lt;br /&gt;
vec3 dest = source + factor; &lt;br /&gt;
&lt;br /&gt;
// Ist gleich&lt;br /&gt;
dest.x = source.x + factor;&lt;br /&gt;
dest.y = source.y + factor;&lt;br /&gt;
dest.z = source.z + factor;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Matrix * Vektor ist auch ohne manuelle Konvertierung möglich :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
vec3  dest;&lt;br /&gt;
vec3  source;&lt;br /&gt;
mat3  MyMat;&lt;br /&gt;
 &lt;br /&gt;
dest = source * MyMat; &lt;br /&gt;
 &lt;br /&gt;
// Ist gleich&lt;br /&gt;
dest.x = dot(source, MyMat[0]);&lt;br /&gt;
dest.y = dot(source, MyMat[1]);&lt;br /&gt;
dest.z = dot(source, MyMat[2]);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch hier sind die Möglichkeiten fast unbeschränkt und zeigen wieder wie flexibel glSlang ausgelegt ist. &lt;br /&gt;
&lt;br /&gt;
==Operatoren==&lt;br /&gt;
&lt;br /&gt;
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 :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div  align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{|{{Prettytable_B1}}&lt;br /&gt;
!Operatorklasse  	&lt;br /&gt;
!Operatoren  	&lt;br /&gt;
!Assoziation&lt;br /&gt;
|-&lt;br /&gt;
|Gruppering 	&lt;br /&gt;
|() 	&lt;br /&gt;
| -&lt;br /&gt;
|-&lt;br /&gt;
|Arrayindizierung&amp;lt;br&amp;gt;Funktionsaufrufe und Konstruktoren&amp;lt;br&amp;gt;Strukturfeldwahl und Swizzle&amp;lt;br&amp;gt;Postinkrement und -dekrement&amp;lt;br&amp;gt; 	&lt;br /&gt;
|[]&amp;lt;br&amp;gt;()&amp;lt;br&amp;gt;.&amp;lt;br&amp;gt;++ -- 	&lt;br /&gt;
|Links n. Rechts&lt;br /&gt;
|-&lt;br /&gt;
|Prefixinkrement- und dekrement&amp;lt;br&amp;gt;Einheitlich (~ reserviert) 	&lt;br /&gt;
| ++ --&amp;lt;br&amp;gt; + - ~ ! 	&lt;br /&gt;
|Rechts n. Links&lt;br /&gt;
|-&lt;br /&gt;
|Mulitplikation (% reserviert) 	&lt;br /&gt;
|* / % 	&lt;br /&gt;
|Links n. Rechts&lt;br /&gt;
|-&lt;br /&gt;
|Additiv 	&lt;br /&gt;
| + - 	&lt;br /&gt;
|Links n. Rechts&lt;br /&gt;
|-&lt;br /&gt;
|Bitweises Verschieben (reserviert) 	&lt;br /&gt;
|&amp;lt;&amp;lt;  &amp;gt;&amp;gt; 	&lt;br /&gt;
|Links n. Rechts&lt;br /&gt;
|-&lt;br /&gt;
|Relation 	&lt;br /&gt;
|&amp;lt;  &amp;gt;  &amp;lt;=  &amp;gt;= 	&lt;br /&gt;
|Links n. Rechts&lt;br /&gt;
|-&lt;br /&gt;
|Vergleich 	&lt;br /&gt;
|==  != 	&lt;br /&gt;
|Links n. Rechts&lt;br /&gt;
|-&lt;br /&gt;
|Bitweises AND (reserviert) 	&lt;br /&gt;
|&amp;amp; 	&lt;br /&gt;
|Links n. Rechts&lt;br /&gt;
|-&lt;br /&gt;
|Bitweises XOR (reserviert) 	&lt;br /&gt;
|^ 	&lt;br /&gt;
|Links n. Rechts&lt;br /&gt;
|-&lt;br /&gt;
|Bitweises OR (reserviert) 	&lt;br /&gt;
| &amp;lt;nowiki&amp;gt;|&amp;lt;/nowiki&amp;gt; 	&lt;br /&gt;
|Links n. Rechts&lt;br /&gt;
|-&lt;br /&gt;
|Logisches AND 	&lt;br /&gt;
|&amp;amp;&amp;amp; 	&lt;br /&gt;
|Links n. Rechts&lt;br /&gt;
|-&lt;br /&gt;
|Logisches XOR 	&lt;br /&gt;
|^^ 	&lt;br /&gt;
|Links n. Rechts&lt;br /&gt;
|-&lt;br /&gt;
|Logisches OR 	&lt;br /&gt;
| &amp;lt;nowiki&amp;gt;||&amp;lt;/nowiki&amp;gt; 	&lt;br /&gt;
|Links n. Rechts&lt;br /&gt;
|-&lt;br /&gt;
|Auswahl 	&lt;br /&gt;
|?: 	&lt;br /&gt;
|Rechts n. Links&lt;br /&gt;
|-&lt;br /&gt;
|Zuweisung&amp;lt;br&amp;gt;Arithmetrische Zuweisung&amp;lt;br&amp;gt;(Modulis, Shift und bitweise Op. reserviert) 	&lt;br /&gt;
|&amp;lt;nowiki&amp;gt;=&amp;lt;/nowiki&amp;gt;&amp;lt;br&amp;gt; &amp;lt;nowiki&amp;gt;+= -=  *=  /=  %=&amp;lt;/nowiki&amp;gt; &amp;lt;br&amp;gt; &amp;lt;nowiki&amp;gt;&amp;lt;&amp;lt;=  &amp;gt;&amp;gt;= &amp;amp;=  ^=  |=&amp;lt;/nowiki&amp;gt; 	&lt;br /&gt;
|Rechts n. Links&lt;br /&gt;
|-&lt;br /&gt;
|Aufzählung 	&lt;br /&gt;
|, 	&lt;br /&gt;
|Links n. Rechts&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Funktionen==&lt;br /&gt;
&lt;br /&gt;
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 :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
RückgabeTyp FunktionsName(Typ0 Argument0, Typ1, Argument1, ... , TypN, ArgumentN)&lt;br /&gt;
 {&lt;br /&gt;
 return RückgabeWert;&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Funktionen die ''nichts zurückgeben'' müssen mit dem RückgabeTyp {{INLINE_CODE|void}} deklariert werden, außerdem entfällt dann logischerweise das {{INLINE_CODE|return}}. Falls die Funktion eines ihrere Argumente nach außen ü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.&lt;br /&gt;
Ein paar Beispiele :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
void MeineFunktion(float EingabeWert; out float AusgabeWert)&lt;br /&gt;
 {&lt;br /&gt;
 AusgabeWert = EingabeWert*MyConstValue;&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion gibt ''nichts'' zurück, aber gibt EingabeWert*MyConstValue im Ausgabeargument AusgabeWert nach außen.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
float MeineFunktion(float EingabeWert)&lt;br /&gt;
 {&lt;br /&gt;
 return EingabeWert*MyConstValue;&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bietet genau die selbe Funktionalität wie das Beispiel darüber. Allerdings wird hier der berechnete Wert als Ergebnis der Funktion zurückgeliefert.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
float VektorSumme(float v[])&lt;br /&gt;
 {&lt;br /&gt;
 return v[0]+v[1]+v[2]+v[3];&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==if-Anweisung==&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
'''Hinweis : ''' &lt;br /&gt;
Grafikkarten auf dem Stand des Shadermodells 2.0 (Radeon 9x00, Radeon X8x0, GeForceFX 5x00) unterstüzten im Fragmentshader kein Early-Out, was zur Folge hat das bei einer If-Anweisung immer alle Zweige ausgeführt werden. Am Ende wird dann aber nur ein Ergebnis geschrieben, die anderen verworfen. Auf solchen Karten bringen If-Anweisungen also im Normalfall keine Geschwindigkeitssteigerung, sondern oft eher das Gegenteil.&lt;br /&gt;
Neuere SM3.0-Karten (Radeon X1x00, GeForce6x00 und höher) ist dass nicht mehr der Fall, da hier dynamische Verzweigungen und auch Early-Out von der Hardware implementiert werden.&lt;br /&gt;
&lt;br /&gt;
==Schleifen==&lt;br /&gt;
&lt;br /&gt;
Auch Schleifen, ein wichtiges Konzept jeder Hochsprache haben ihren Weg in glSlang gefunden. Unterstützt werden folgende Schleifentypen :&lt;br /&gt;
&lt;br /&gt;
* '''for'''-Schleife&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
for (Startausdruck; Durchlaufbedingung; Wiederholungsausdruck)&lt;br /&gt;
  {&lt;br /&gt;
   statement&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* '''while'''-Schleife&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
while (Durchlaufbedingung)&lt;br /&gt;
 {&lt;br /&gt;
  statement&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* '''do'''-while-Schleife&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
do&lt;br /&gt;
 {&lt;br /&gt;
  statement&lt;br /&gt;
 }&lt;br /&gt;
 while (Durchlaufbedingung)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Hinweis :''' Grafikkarten auf dem Stand des Shadermodells 2.0 (Radeon 9x00, Radeon X8x0, GeForceFX 5x00) unterstüzten Schleifen nicht in Hardware. Schleifen werden dann beim Kompilieren vom Treiber entrollt, wodurch natürlich Shader mit weitaus mehr Instruktionen als erwartet generiert werden. Von daher sollte man auf solchen Karten möglichst auf Schleifen verzichten, oder diese nur recht kurz halten. Bei SM3.0-Karten (Radeon X1x00, GeForce6x00 und höher) ist dass nicht mehr der Fall.&lt;br /&gt;
&lt;br /&gt;
=Eingebaute Variablen, Attribute und Konstanten=&lt;br /&gt;
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).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Variablen im Vertex Shader==&lt;br /&gt;
Exklusiv im Vertex Shader stehen die folgenden Variablen zur Verfügung :&lt;br /&gt;
&lt;br /&gt;
* vec4 gl_Position    muss geschrieben werden&lt;br /&gt;
: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 (mehrfach) geschrieben und ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
* float gl_PointSize    kann geschrieben werden&lt;br /&gt;
:Diese Variable wurde dazu vorgesehen um dort im VertexShader die Punktgröße in Pixeln hineinzuschreiben.&lt;br /&gt;
&lt;br /&gt;
* vec4 gl_ClipVertex    kann geschrieben werden&lt;br /&gt;
: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.&lt;br /&gt;
&lt;br /&gt;
==Attribute im Vertex Shader==&lt;br /&gt;
&lt;br /&gt;
Folgende Attribute stehen nur im Vertex Shader zur Verfügung und '''können nur gelesen werden''' :&lt;br /&gt;
&lt;br /&gt;
* vec4 gl_Color&lt;br /&gt;
: Farbwert des Vertex.&lt;br /&gt;
* vec4 gl_SecondaryColor&lt;br /&gt;
:Sekundärer Farbwert des Vertex.&lt;br /&gt;
* vec4 gl_Normal&lt;br /&gt;
:Normale des Vertex.&lt;br /&gt;
* vec4 gl_Vertex&lt;br /&gt;
:Koordinaten des Vertex;&lt;br /&gt;
* vec4 gl_MultiTexCoord0..7&lt;br /&gt;
:Texturkoordinaten auf Textureinheit 0..7.&lt;br /&gt;
* float gl_FogCoord&lt;br /&gt;
:Nebelkoordinate des Vertex. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Variablen im Fragment Shader==&lt;br /&gt;
&lt;br /&gt;
Im Fragment Shader sind folgende Variablen exklusiv nutzbar :&lt;br /&gt;
&lt;br /&gt;
* vec4 gl_FragColor&lt;br /&gt;
: 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.&lt;br /&gt;
&lt;br /&gt;
* vec4 gl_FragData[0..15]&lt;br /&gt;
: Ersetzt gl_FragColor bei der Verwendung von multiplen Rendertargets. &lt;br /&gt;
&lt;br /&gt;
* float gl_FragDepth&lt;br /&gt;
: 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.&lt;br /&gt;
&lt;br /&gt;
* vec4 gl_FragCoord    nur lesen&lt;br /&gt;
: 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.&lt;br /&gt;
&lt;br /&gt;
* bool gl_FrontFacing    nur lesen&lt;br /&gt;
: Gibt an ob das Fragment zu einer nach vorne zeigenden Primitive gehört (=true). &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
==Eingebaute Varyings==&lt;br /&gt;
&lt;br /&gt;
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 :&lt;br /&gt;
&lt;br /&gt;
* vec4 gl_FrontColor&lt;br /&gt;
: Farbe der Vorderseite des Vertex.&lt;br /&gt;
&lt;br /&gt;
* vec4 gl_BackColor&lt;br /&gt;
: Farbe der Rückseite des Vertex.&lt;br /&gt;
&lt;br /&gt;
* vec4 gl_FrontSecondaryColor&lt;br /&gt;
: Sekundäre Farbe der Vorderseite des Vertex.&lt;br /&gt;
&lt;br /&gt;
* vec4 gl_BackSecondaryColor&lt;br /&gt;
: Sekundäre Farbe der Rückseite des Vertex.&lt;br /&gt;
&lt;br /&gt;
* vec4 gl_TexCoord[x]&lt;br /&gt;
: Texturkoordinaten des Vertex auf Textureinheit x, wobei x die von der Hardware zur Verfügung gestellte Zahl der Textureinheiten-1 nicht überschreiten darf.&lt;br /&gt;
&lt;br /&gt;
* float gl_FogFragCoord&lt;br /&gt;
: Nebelkoordinate des Fragmentes. &lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Eingebaute Konstanten==&lt;br /&gt;
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 :&lt;br /&gt;
&lt;br /&gt;
: OpenGL 1.0/1.2 :&lt;br /&gt;
* int gl_MaxLights (8)&lt;br /&gt;
* int gl_MaxClipPlanes (6)&lt;br /&gt;
* int gl_MaxTextureUnits (2)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: ARB_Fragment_Program :&lt;br /&gt;
* int gl_MaxTextureCoordsARB (2)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: Vertex_Shader :&lt;br /&gt;
* int gl_MaxVertexAttributesGL2 (16)&lt;br /&gt;
* int gl_MaxVertexUniformFloatsGL2 (512)&lt;br /&gt;
* int gl_MaxVaryingFloatsGL2 (32)&lt;br /&gt;
* int gl_MaxVertexTextureUnitsGL2 (1)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: Fragment_Shader :&lt;br /&gt;
* int gl_MaxFragmentTextureUnitsGL2 (2)&lt;br /&gt;
* int gl_MaxFragmentUniformFloatsGL2 (64)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Eingebaute Uniformvariablen==&lt;br /&gt;
&lt;br /&gt;
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 :&lt;br /&gt;
&lt;br /&gt;
* mat4 gl_ModelViewMatrix&lt;br /&gt;
* mat4 gl_ProjectionMatrix&lt;br /&gt;
* mat4 gl_ModelViewProjectionMatrix&lt;br /&gt;
* mat3 gl_NormalMatrix&lt;br /&gt;
:{{INLINE_CODE|gl_NormalMatrix}} repräsentiert die invertierten und anschließend transponierten oberen 3x3 Werte der {{INLINE_CODE|gl_ModelViewMatrix}}.&lt;br /&gt;
* mat4 gl_TextureMatrix[gl_MaxTextureCoordsARB]&lt;br /&gt;
&lt;br /&gt;
* float gl_NormalScale&lt;br /&gt;
: Gibt den unter OpenGL festgelegten Faktor zur Skalierung der Normalen zurück.&lt;br /&gt;
&lt;br /&gt;
* struct gl_DepthRangeParameters&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
struct gl_DepthRangeParameters&lt;br /&gt;
{&lt;br /&gt;
 float near;&lt;br /&gt;
 float far;&lt;br /&gt;
 float diff;&lt;br /&gt;
};&lt;br /&gt;
gl_DepthRangeParameters gl_DepthRange;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
: Clippingplanes : &lt;br /&gt;
* vec4 gl_ClipPlane[gl_MaxClipPlanes]&lt;br /&gt;
  &lt;br /&gt;
*struct gl_PointParameters&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
struct gl_PointParameters&lt;br /&gt;
{&lt;br /&gt;
 float size;&lt;br /&gt;
 float sizeMin;&lt;br /&gt;
 float sizeMax;&lt;br /&gt;
 float fadeThresholdSize;&lt;br /&gt;
 float distanceConstantAttenuation;&lt;br /&gt;
 float distanceLinearAttenuation;&lt;br /&gt;
 float distanceQuadraticAttenuation;&lt;br /&gt;
};&lt;br /&gt;
gl_PointParameters gl_Point;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*struct gl_MaterialParameters&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
struct gl_MaterialParameters&lt;br /&gt;
{&lt;br /&gt;
 vec4 emission;&lt;br /&gt;
 vec4 ambient;&lt;br /&gt;
 vec4 diffuse;&lt;br /&gt;
 vec4 specular;&lt;br /&gt;
 float shininess;&lt;br /&gt;
};&lt;br /&gt;
gl_MaterialParameters gl_FrontMaterial;&lt;br /&gt;
gl_MaterialParameters gl_BackMaterial;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*struct gl_LightSourceParameters&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
struct gl_LightSourceParameters&lt;br /&gt;
{&lt;br /&gt;
 vec4 ambient;&lt;br /&gt;
 vec4 diffuse;&lt;br /&gt;
 vec4 specular;&lt;br /&gt;
 vec4 position;&lt;br /&gt;
 vec4 halfVector;&lt;br /&gt;
 vec3 spotDirection;&lt;br /&gt;
 float spotExponent;&lt;br /&gt;
 float spotCutoff;&lt;br /&gt;
 float spotCosCutoff;&lt;br /&gt;
 float constantAttenuation;&lt;br /&gt;
 float linearAttenuation;&lt;br /&gt;
 float quadraticAttenuation;&lt;br /&gt;
};&lt;br /&gt;
gl_LightSourceParameters gl_LightSource[gl_MaxLights];&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*struct gl_LightModelParameters&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
struct gl_LightModelParameters&lt;br /&gt;
{&lt;br /&gt;
 vec4 ambient;&lt;br /&gt;
};&lt;br /&gt;
gl_LightModelParameters gl_LightModel;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*struct gl_LightModelProducts&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
struct gl_LightModelProducts&lt;br /&gt;
{&lt;br /&gt;
 vec4 sceneColor;&lt;br /&gt;
};&lt;br /&gt;
gl_LightModelProducts gl_FrontLightModelProduct;&lt;br /&gt;
gl_LightModelProducts gl_BackLightModelProduct;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*struct gl_LightProducts&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
struct gl_LightProducts&lt;br /&gt;
{&lt;br /&gt;
 vec4 ambient;&lt;br /&gt;
 vec4 diffuse;&lt;br /&gt;
 vec4 specular;&lt;br /&gt;
};&lt;br /&gt;
gl_LightProducts gl_FrontLightProduct[gl_MaxLights];&lt;br /&gt;
gl_LightProducts gl_BackLightProduct[gl_MaxLights];&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* vec4 gl_TextureEnvColor[gl_MaxFragmentTextureUnitsGL2]&lt;br /&gt;
* vec4 gl_EyePlaneS[gl_MaxTextureCoordsARB]&lt;br /&gt;
* vec4 gl_EyePlaneT[gl_MaxTextureCoordsARB]&lt;br /&gt;
* vec4 gl_EyePlaneR[gl_MaxTextureCoordsARB]&lt;br /&gt;
* vec4 gl_EyePlaneQ[gl_MaxTextureCoordsARB]&lt;br /&gt;
* vec4 gl_ObjectPlaneS[gl_MaxTextureCoordsARB]&lt;br /&gt;
* vec4 gl_ObjectPlaneT[gl_MaxTextureCoordsARB]&lt;br /&gt;
* vec4 gl_ObjectPlaneR[gl_MaxTextureCoordsARB]&lt;br /&gt;
* vec4 gl_ObjectPlaneQ[gl_MaxTextureCoordsARB]&lt;br /&gt;
&lt;br /&gt;
*struct gl_FogParameters&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
struct gl_FogParameters&lt;br /&gt;
{&lt;br /&gt;
 vec4 color;&lt;br /&gt;
 float density;&lt;br /&gt;
 float start;&lt;br /&gt;
 float end;&lt;br /&gt;
 float scale;&lt;br /&gt;
};&lt;br /&gt;
gl_FogParameters gl_Fog;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=Eingebaute Funktionen=&lt;br /&gt;
glSlang ist mit diversen Skalar- und Vektorfunktionen ausgestattet, die teilweise (idealerweise) sogar direkt in der Hardware ausgeführt werden, weshalb einer fertigen Funktion ggü. gleichwertigen eigenen Berechnungen immer der Vorzug zu geben ist.&lt;br /&gt;
{{Hinweis| ''genType'' kann vom Type float, vec2, vec3 oder vec4 sein, ''mat'' vom Typ mat2, mat3 oder mat4.}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Trigonometrie und Winkel==&lt;br /&gt;
Alle übergebenen Winkel sollten, soweit nicht anders vermerkt, in Radien angegeben werden.&lt;br /&gt;
&lt;br /&gt;
* genType radians (genType degrees)&lt;br /&gt;
: Wandelt von Grad nach Radien. &lt;br /&gt;
* genType degrees (genType radians)&lt;br /&gt;
: Wandelt von Radien nach Grad.&lt;br /&gt;
* genType sin (genType x)&lt;br /&gt;
: Gibt den Sinus von x zurück, wobei x in Radien angegeben wird.&lt;br /&gt;
* genType cos (genType x)&lt;br /&gt;
: Gibt den Kosinus von x zurück, wobei x in Radien angegeben wird.&lt;br /&gt;
* genType tan (genType x)&lt;br /&gt;
: Gibt den Tangens von x zurück, wobei x in Radien angegeben wird.&lt;br /&gt;
* genType asin (genType x)&lt;br /&gt;
: Liefert den Arcsinus von x zurück, also den Winkel dessen Sinus x ergeben würde.&lt;br /&gt;
* genType acos (genType x)&lt;br /&gt;
: Liefert den Arckosinus von x zurück, also den Winkel dessen Cosinus x ergeben würde.&lt;br /&gt;
* genType atan (genType y, genType x)&lt;br /&gt;
: Liefert den Winkel zurück, dessen Tangens x/y ergeben würde.&lt;br /&gt;
* genType atan (genType y_over_x)&lt;br /&gt;
: Liefert den Winkel zurück, dessen Tangens x über y ergeben würde.&lt;br /&gt;
&lt;br /&gt;
==Hyperbolisch==&lt;br /&gt;
* genType sinh (genType x)&lt;br /&gt;
: Gibt den Sinus Hyperbolicus von x zurück. (return = (exp(x) - exp(-x)) * 0.5; )&lt;br /&gt;
* genType cosh (genType x)&lt;br /&gt;
: Gibt den Kosinus Hyperbolicus von x zurück. (return = (exp(x) + exp(-x)) * 0.5; )&lt;br /&gt;
* genType tanh (genType x)&lt;br /&gt;
: Gibt den Tangens Hyperbolicus von x zurück. (return = (exp(x) - exp(-x)) / (exp(x) + exp(-x)); )&lt;br /&gt;
* genType asinh (genType angle)&lt;br /&gt;
: Gibt den Areasinus Hyperbolicus von x zurück. (return = log(x + sqrt(x * x + 1.0)); )&lt;br /&gt;
* genType acosh (genType angle)&lt;br /&gt;
: Gibt den Areakosinus Hyperbolicus von x zurück. (return = log(x + sqrt(x * x - 1.0)); )&lt;br /&gt;
* genType atanh (genType x)&lt;br /&gt;
: Gibt den Areatangens Hyperbolicus von x zurück. (return = log((1.0 + x) / (1.0 - x)) * 0.5; )&lt;br /&gt;
&lt;br /&gt;
==Exponentiell==&lt;br /&gt;
* genType pow (genType x, genType y)&lt;br /&gt;
: Gibt x hoch y zurück.&lt;br /&gt;
* genType exp (genType x)&lt;br /&gt;
: Gibt e(Eulerischezahl ≈ 2.7182) hoch x zurück.&lt;br /&gt;
* genType log (genType x)&lt;br /&gt;
: Gibt den Logarithmus zur Basis e(Eulerischezahl ≈ 2.7182) von x zurück.&lt;br /&gt;
* genType exp2 (genType x)&lt;br /&gt;
: Gibt 2 hoch x zurück.&lt;br /&gt;
* genType log2 (genType x)&lt;br /&gt;
: Gibt den Logarithmus zur Basis 2 von x zurück.&lt;br /&gt;
* genType sqrt (genType x)&lt;br /&gt;
: Gibt die Wurzel von x zurück.&lt;br /&gt;
* genType inversesqrt (genType x)&lt;br /&gt;
: Gibt die umgekehrte Wurzel von x zurück.&lt;br /&gt;
&lt;br /&gt;
==Standardfunktionen==&lt;br /&gt;
* genType abs (genType x)&lt;br /&gt;
: Liefert den absoluten Wert von x zurück.&lt;br /&gt;
* genType sign (genType x)&lt;br /&gt;
: Gibt -1.0 zurück, wenn x &amp;lt; 0.0, 0.0 wenn x = 0.0 und 1.0 wenn x &amp;gt; 0.0.&lt;br /&gt;
* genType roundeven(genType x)&lt;br /&gt;
: Rundet auf den nächsten Integerwert und gibt diesen zuück. Bei &amp;quot;.5&amp;quot; Werten wird zur nächsten geraden Zahl gerundet.&lt;br /&gt;
* genType round(genType x)&lt;br /&gt;
: Rundet auf den nächsten Integerwert und gibt diesen zuück. &amp;quot;.5&amp;quot; Werte werden je nach Implementation anders behandelt.&lt;br /&gt;
* genType trunc(genType x)&lt;br /&gt;
: Rundet auf den nächsten Integerwert dessen Absoluterwert nicht größer ist als der Absolutewert von x&lt;br /&gt;
* genType floor (genType x)&lt;br /&gt;
: Gibt denn größten Integerwert zurück, der kleiner oder gleich x ist.&lt;br /&gt;
* genType ceil (genType x)&lt;br /&gt;
: Gibt den kleinsten Integerwert zurück, der größer oder gleich x ist.&lt;br /&gt;
* genType fract (genType x)&lt;br /&gt;
: Gibt den Nachkommateil von x zurück.&lt;br /&gt;
* genType mod (genType x, float y) &lt;br /&gt;
* genType mod (genType x, genType y)&lt;br /&gt;
: Gibt den Modulus zurück. (result = x - y * floor(x / y)) &lt;br /&gt;
* genType min (genType x, genType y) &lt;br /&gt;
* genType min (genType x, float y)&lt;br /&gt;
: Liefert y zurück wenn y &amp;lt; x, ansonsten x. &lt;br /&gt;
* genType max (genType x, genType y) &lt;br /&gt;
* genType max (genType x, float y)&lt;br /&gt;
: Liefert y zurück wenn x &amp;lt; y, ansonsten x. &lt;br /&gt;
* genType clamp (genType x, genType minVal, genType maxVal) &lt;br /&gt;
* genType clamp (genType x, float minVal, float maxVal)&lt;br /&gt;
: Zwängt x in den Bereich minVal ... maxVal .  (return = min(minVal, max(maxVal, x)))&lt;br /&gt;
* genType mix (genType x, genType y, genType a)&lt;br /&gt;
* genType mix (genType x, genType y, float a)&lt;br /&gt;
: Liefert den linearen Blend zwischen x und y zurück. (result = x * (1 - a) + y * a) &lt;br /&gt;
* genType step (genType edge, genType x)&lt;br /&gt;
* genType step (float edge, genType x)&lt;br /&gt;
: Liefert 0.0 zurück, wenn x &amp;lt; edge, ansonsten 1.0. &lt;br /&gt;
* genType smoothstep (genType edge0, genType edge1, genType x)&lt;br /&gt;
* genType smoothstep (float edge0, float edge1, genType x)&lt;br /&gt;
: Liefert 0.0 zurück, wenn x &amp;lt;= edge0 und 1.0 wenn x &amp;gt;= edge1. Dazwischen wird eine weiche Hermite Interpolation durchgeführt.&lt;br /&gt;
::float t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);&lt;br /&gt;
::result = t * t * (3.0 - 2.0 * t)&lt;br /&gt;
&lt;br /&gt;
==Geometrie==&lt;br /&gt;
* float length (genType x)&lt;br /&gt;
: Gibt die Länge des Vektors x (= sqrt(x[0]² + x[1]² + ... + x[n]²) zurück. &lt;br /&gt;
* float distance (genType p0, genType p1)&lt;br /&gt;
: Gibt die Distanz zwischen den zwei Vektoren p0 un p1 (= length(p0-p1)) zurück. &lt;br /&gt;
* float dot (genType x, genType y)&lt;br /&gt;
: Gibt das Punktprodukt von x und y zurück (=x[0]*y[0] + x[1]*y[1] + ... + x[n]*y[n]). &lt;br /&gt;
* vec3 cross (vec3 x, vec3 y)&lt;br /&gt;
: Gibt das Kreuzprodukt von x und y zurück. &lt;br /&gt;
* genType normalize (genType x)&lt;br /&gt;
: Normalisiert den Vektor x auf die Länge 1. &lt;br /&gt;
* vec4 ftransform()&lt;br /&gt;
: Nur im Vertex Shader. Die Funktion stellt sicher, das das eingehende Vertex haargenau so transformiert wird wie in der festen Funktionspipeline. gl_Position = ftransform() wird dann also gebraucht, wenn in mehreren Durchgängen sowohl im Shader als auch in der festen Pipeline gerendert wird, um sicherzustellen das in beiden Fällen die gleiche Vertexposition herauskommt. &lt;br /&gt;
* genType faceforward (genType N, genType I, genType Nref)&lt;br /&gt;
: Gibt einen nach vorne zeigenden Vektor N zurück. (If dot(NRef, I) &amp;lt; 0 return N else return -N) &lt;br /&gt;
* genType reflect (genType l, genType N)&lt;br /&gt;
: Gibt den an der Flächenausrichtung N reflektierten Vektor I zurück. (result = l - 2.0 * dot(N,I) * N; )&lt;br /&gt;
* genType refract (genType l, genType N, float eta)&lt;br /&gt;
: Gibt den an der Flächenausrichtung N mit dem Brechungsindex eta gebrochenen Vektor l zurück. &lt;br /&gt;
::gentype k = 1.0 - eta * eta * (1.0 - dot(N, l) * dot(N, l))&lt;br /&gt;
::if (k &amp;lt; 0.0)&lt;br /&gt;
:::result = 0.0&lt;br /&gt;
::else &lt;br /&gt;
:::result = eta * l - (eta * dot(N, l) * sqrt(k)) * N&lt;br /&gt;
&lt;br /&gt;
==Matrixfunktionen==&lt;br /&gt;
* mat matrixCompMult (mat x, mat y)&lt;br /&gt;
: Multipliziert Matrix X mit Matrix Y komponentenweise. Um eine normale lineare Matrixmultiplikation durchzuführen, sollte der &amp;quot;*&amp;quot;-Operator genutzt werden.&lt;br /&gt;
* mat transpose (mat m)&lt;br /&gt;
: Gibt die transponierte Matrix von m zurück.&lt;br /&gt;
* mat invert (mat m)&lt;br /&gt;
: Gibt die invertierte Matrix von m zurück. Also die Matrix, die mit m multipliziert die Normalmatrix ergeben würde.&lt;br /&gt;
* mat outerProduct(vec c, vec r)&lt;br /&gt;
: Gibt eine eine Matrix als Ergebnis der linearen (return[m, n] = c[m] * r[n])-Operation zurück.&lt;br /&gt;
&lt;br /&gt;
==Vektorvergleiche==&lt;br /&gt;
Die meisten Vektorvergleichsfunktionen liefern als Ergebnis einen boolvektor zurück, da die Vergleiche per Komponente stattfinden. Wenn man also x = vec4(1.0, 3.0, 0.0, 0.0) mit y = vec4(2.0, 1.5, 1.5, 0.0) via lessThan(x, y) vergleicht, erhält man als Ergebnis bvec(true, false, true, false).&lt;br /&gt;
&lt;br /&gt;
* bvec lessThan (vec x, vec y)&lt;br /&gt;
* bvec lessThan (ivec x, ivec y)&lt;br /&gt;
: Gibt den komponentenweisen Vergleich x &amp;lt; y zurück. &lt;br /&gt;
* bvec lessThanEqual (vec x, vec y)&lt;br /&gt;
* bvec lessThanEqual (ivec x, ivec y)&lt;br /&gt;
: Gibt den komponentenweisen Vergleich x &amp;lt;= y zurück. &lt;br /&gt;
* bvec greaterThan (vec x, vec y)&lt;br /&gt;
* bvec greaterThan (ivec x, ivec y)&lt;br /&gt;
: Gibt den komponentenweisen Vergleich x &amp;gt; y zurück. &lt;br /&gt;
* bvec greaterThanEqual (vec x, vec y)&lt;br /&gt;
* bvec greaterThanEqual (ivec x, ivec y)&lt;br /&gt;
: Gibt den komponentenweisen Vergleich x &amp;gt;= y zurück. &lt;br /&gt;
* bvec equal (vec x, vec y)&lt;br /&gt;
* bvec equal (ivec x, ivec y)&lt;br /&gt;
* bvec equal (bvec x, bvec y)&lt;br /&gt;
: Gibt den komponentenweisen Vergleich x == y zurück. &lt;br /&gt;
* bvec notEqual (vec x, vec y)&lt;br /&gt;
* bvec notEqual (ivec x, ivec y)&lt;br /&gt;
* bvec notEqual (bvec x, bvec y)&lt;br /&gt;
: Gibt den komponentenweisen Vergleich x != y zurück. &lt;br /&gt;
* bool any (bvec x)&lt;br /&gt;
: Liefert true zurück, wenn mindestens eine der Komponenten von x true ist.&lt;br /&gt;
* bool all (bvec x)&lt;br /&gt;
: Liefert true zurück, wenn alle Komponenten von x true sind. &lt;br /&gt;
* bvec not (bvec x)&lt;br /&gt;
: Liefert die logische Negation von x zurück. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Texturenzugriffe==&lt;br /&gt;
&lt;br /&gt;
Diese wichtige Funktionskategorie dient dazu, Werte aus einer an eine Textureinheit gebundenen Textur zu ermitteln. Die Texturenzugriffe können sowohl im Vertex (!) als auch im Fragment Shader ausgeführt werden, wobei der optionale Parameter bias im Vertex Shader ignoriert wird. Allerdings gibt es zusätzlich Funktionen die auf &amp;quot;Lod&amp;quot; enden und nur im Vertex Shader genutzt werden dürfen um eben dieses Manko zu umgehen. Funktionen mit dem Suffix &amp;quot;Proj&amp;quot; geben einen projizierten Texturenwert zurück.&lt;br /&gt;
&lt;br /&gt;
: '''1D-Texturen :'''&lt;br /&gt;
* vec4 texture1D (sampler1D sampler, float coord [, float bias])&lt;br /&gt;
* vec4 texture1DProj (sampler1D sampler, vec2 coord [, float bias])&lt;br /&gt;
* vec4 texture1DProj (sampler1D sampler, vec4 coord [, float bias])&lt;br /&gt;
: Nur im Vertex Shader :&lt;br /&gt;
* vec4 texture1DLod (sampler1D sampler, float coord, float lod)&lt;br /&gt;
* vec4 texture1DProjLod (sampler1D sampler, vec2 coord, float lod)&lt;br /&gt;
* vec4 texture1DProjLod (sampler1D sampler, vec4 coord, float lod)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: '''2D-Texturen :'''&lt;br /&gt;
* vec4 texture2D (sampler2D sampler, vec2 coord [, float bias])&lt;br /&gt;
* vec4 texture2DProj (sampler2D sampler, vec3 coord [, float bias])&lt;br /&gt;
* vec4 texture2DProj (sampler2D sampler, vec4 coord [, float bias])&lt;br /&gt;
: Nur im Vertex Shader : &lt;br /&gt;
* vec4 texture2DLod (sampler2D sampler, vec2 coord, float lod)&lt;br /&gt;
* vec4 texture2DProjLod (sampler2D sampler, vec3 coord, float lod)&lt;br /&gt;
* vec4 texture2DProjLod (sampler2D sampler, vec4 coord, float lod)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: '''3D-Texturen :'''&lt;br /&gt;
* vec4 texture3D (sampler3D sampler, vec3 coord [, float bias])&lt;br /&gt;
* vec4 texture3DProj (sampler3D sampler, vec4 coord [, float bias])&lt;br /&gt;
: Nur im Vertex Shader : &lt;br /&gt;
* vec4 texture3DLod (sampler3D sampler, vec3 coord, float lod)&lt;br /&gt;
* vec4 texture3DProjLod (sampler3D sampler, vec4 coord, float lod)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: '''Cubemap :'''&lt;br /&gt;
* vec4 textureCube (samplerCube sampler, vec3 coord [, float bias])&lt;br /&gt;
: Nur im Vertex Shader : &lt;br /&gt;
*vec4 textureCubeLod (samplerCube sampler, vec3 coord, float lod)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: '''Tiefentextur (Shadowmap) :'''&lt;br /&gt;
* vec4 shadow1D (sampler1DShadow sampler, vec3 coord [, float bias])&lt;br /&gt;
* vec4 shadow2D (sampler2DShadow sampler, vec3 coord [, float bias])&lt;br /&gt;
* vec4 shadow1DProj (sampler1DShadow sampler, vec4 coord [, float bias])&lt;br /&gt;
* vec4 shadow2DProj (sampler2DShadow sampler, vec4 coord [, float bias])&lt;br /&gt;
: Nur im Vertex Shader :&lt;br /&gt;
* vec4 shadow1DLod (sampler1DShadow sampler, vec3 coord, float lod)&lt;br /&gt;
* vec4 shadow2DLod (sampler2DShadow sampler, vec3 coord, float lod)&lt;br /&gt;
* vec4 shadow1DProjLod (sampler1DShadow sampler, vec4 coord, float lod)&lt;br /&gt;
* vec4 shadow2DProjLod (sampler2DShadow sampler, vec4 coord, float lod)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
: '''Texturegröße :'''&lt;br /&gt;
Mit den textureSize-Befehlen kann die Größe einer Textur bestimmt werden, wobei lod das Mipmaplevel angibt:&lt;br /&gt;
*int textureSize(sampler1D sampler, int lod)&lt;br /&gt;
*ivec2 textureSize(sampler2D sampler, int lod)&lt;br /&gt;
*ivec3 textureSize(sampler3D sampler, int lod)&lt;br /&gt;
*ivec2 textureSize(samplerCube sampler, int lod)&lt;br /&gt;
*int textureSize(sampler1DShadow sampler, int lod)&lt;br /&gt;
*ivec2 textureSize(sampler2DShadow sampler, int lod)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Wie bereits eingangs gesagt ist dieses Kapitel ein sehr wichtiges, denn eine 3D-Szene ohne Texturen ist heute kaum denkbar. Darüber hinaus lassen sich durch Texturenzugriffe recht viele interessante Sachen machen, z.B. ein einfacher Blurfilter oder das freie überblenden bestimmter Texturenteile. Deshalb führe ich hier kurz ein paar Beispiele an, welche die Nutzung dieser Funktionen verdeutlichen sollen :&lt;br /&gt;
&lt;br /&gt;
===Beispiel A=== &lt;br /&gt;
Eine Textur gebunden die einfach ausgegeben werden soll&lt;br /&gt;
&lt;br /&gt;
''Im Vertex Shader'' :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
 gl_Position     = gl_ModelViewProjectionMatrix * gl_Vertex;&lt;br /&gt;
 gl_TexCoord[0]  = gl_MultiTexCoord0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt; &lt;br /&gt;
Der Vertex Shader ist recht minimal. Neben der homogenen Vertexposition leiten wir hier nur die im OpenGL-Programm angegebenen Texturkoordinaten weiter. ''Dies ist aber unbedingt nötig!'' Ohne die letzte Zeile hätten wir im Fragment Shader keine gültigen Texturkoordinaten auf TMU0, was in einer Fehldarstellung enden würde.&lt;br /&gt;
&lt;br /&gt;
''im Fragment Shader'' :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
uniform sampler2D texSampler;&lt;br /&gt;
&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
 gl_FragColor = texture2D(texSampler, vec2(gl_TexCoord[0]));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Zuerst deklarieren wir hier einen 2D-Texturensampler, wichtig : '''Texturensampler müssen IMMER als uniform deklariert werden!''' In der Hauptfunktion weisen wir dann einfach den über die Funktion texture2D aus unserer gebundenen Textur ausgelesenen Farbwert, anhand der vom Vertex Shader übergebenen Texturkoordinaten, zu.&lt;br /&gt;
{{Vorlage:Hinweis|Der Uniform-Integer für den Sampler referenziert '''nicht''' den Namen (die ID) des Texturobjektes, sondern die Nummer der Texturunit.}}&lt;br /&gt;
&lt;br /&gt;
===Beispiel B=== &lt;br /&gt;
Zwei Texturen, jeweils auf TMU0 und TMU1. Fragmentfarbe soll eine Multiplikation der beiden Texturen darstellen.&lt;br /&gt;
&lt;br /&gt;
In diesem Beispielfall (der recht häufig vorkommt) müssen wir im Programm festlegen, ''welcher Sampler welche Textureinheit adressiert'', genau deshalb müssen die Texturensampler auch als uniform deklariert werden. Die Standardtextureneinheit eines Samplers ist TMU0, was in unserem Falle natürlich nicht brauchbar ist. Also müssen wir unserem zweiten Textursampler im Programm mitteilen das er seine Daten aus TMU1 beziehen soll :&lt;br /&gt;
&lt;br /&gt;
 glUniform1iARB(glSlang_GetUniLoc(ProgramObject, 'texSamplerTMU1'), 1);&lt;br /&gt;
&lt;br /&gt;
Dies ist also unbedingt zu machen, sobald ein Texturensampler eine Textureinheit &amp;gt; GL_TEXTURE_0 adressieren will. Die Textureneinheit des Samplers lässt sich also nicht im Shader selbst festlegen. Der Fragment Shader ist nun allerdings schnell hergeleitet (Vertex Shader verändert sich nicht, da TMU1 die Texturkoordinaten auch von TMU0 bezieht) :&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
im Fragment Shader :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
uniform sampler2D texSamplerTMU0;&lt;br /&gt;
uniform sampler2D texSamplerTMU1;&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
    gl_FragColor = texture2D(texSamplerTMU0, vec2(gl_TexCoord[0])) *&lt;br /&gt;
                   texture2D(texSamplerTMU1, vec2(gl_TexCoord[0]));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Noisefunktionen==&lt;br /&gt;
Sowohl im Vertex als auch im Fragment Shader lassen sich [[GLSL noise|Noisefunktionen]] nutzen, mit deren Hilfe sich eine gewisse &amp;quot;Zufälligkeit&amp;quot; simulieren lässt (wirklich zufällige Werte sind es natürlich nicht). Ein zurückgegebener Wert liegt dabei immer im Bereich [-1..1] und ist immer bei gleichem Eigabewert auch immer gleich. Die Verwendung empfiehlt sich derzeit allerdings eher nicht, da nur die 3DLabs Treiber die Funktionen unterstützen und eine Noisetextur wahrscheinlich performanter ist.&lt;br /&gt;
&lt;br /&gt;
* float noise1 (genType x)&lt;br /&gt;
* vec2 noise2 (genType x)&lt;br /&gt;
* vec3 noise3 (genType x)&lt;br /&gt;
* vec4 noise4 (genType x)&lt;br /&gt;
&lt;br /&gt;
==Discard==&lt;br /&gt;
Eigentlich keine Funktion, sondern eine Abbruchbedingung '''nur im Fragment Shader'''. Das Schlüsselwort {{INLINE_CODE|discard}} verwirft das aktuell bearbeitete Fragment und beendet gleichzeitig den Shader. Es kann z.B. genutzt werden um Alphamasking manuell durchzuführen.&lt;br /&gt;
Man sollte dabei jedoch beachten dass ein Großteil der aktuellen Hardware kein &amp;quot;early-out&amp;quot; (frühes Beenden) im Fragmentshader unterstützt. Wenn dort also ein {{INLINE_CODE|discard}} auftaucht, wird trotzdem auch der Code danach ausgeführt und einfach verworfen. Einen Geschwindigkeitsvorteil durch diesen Befehl wird man also erst auf neueren Karten feststellen, die dieses Faeature auch so unterstützen wie es angedacht war. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Beispielshader=&lt;br /&gt;
Wen bis hierhin nicht der Mut verlassen hat, und wer aufmerksam gelesen hat, dürfte jetzt also zumindest in der Lage sein kleinere Shader in glSlang zu schreiben und diese auch im Programm zu nutzen. Ich habe im Themenbereich &amp;quot;glSlang&amp;quot; versucht alle Bereiche der Shadersprache selbst anzusprechen und hoffe das auch brauchbar rübergebracht zu haben. Um oben erlerntes (hoffe ich doch mal) nochmal zu vertiefen werde ich jetzt (wie ich das bereits bei meinem ARB_VP-Tutorial getan habe) einen simplen Beispielshader (Vertex und Fragment Shader) auseinanderpflücken um so u.a. auch die Programmstruktur für alle die in C nicht so bewandert sind zu erörtern.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Der Vertex Shader==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
uniform vec4 GlobalColor;&lt;br /&gt;
&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
 gl_Position     = gl_ModelViewProjectionMatrix * gl_Vertex;&lt;br /&gt;
 gl_FrontColor   = gl_Color * GlobalColor;&lt;br /&gt;
 gl_TexCoord[0]  = gl_MultiTexCoord0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wie gesagt recht simpel. Angefangen wird mit der Deklaration einer globalen Uniformvariable namens {{INLINE_CODE|GlobalColor}}. Wie wir uns erinnern gibt der Typenqualifizierer uniform an, das wir den Wert dieser Variable (ein 4-Komponentenvektor, da Farbwerte aus R,G,B und A bestehen) in unserem Programm an den Shader übermitteln.&lt;br /&gt;
&lt;br /&gt;
Danach gehts ohne Umwege direkt in unsere Hauptfunktion, da wir im Vertex Shader keine anderen Funktionen benötigen. Dort berechnen wir zuerst die homogene Position unseres Vertex, die sich aus der eingehenden Vertexposition multipliziert mit der Modelansichtsmatrix ergibt. Wie schonmal gesagt '''muss diesem Wert etwas zugewiesen werden''', da sonst alle darauf aufbauenden Funktionen unvorhersehbare Ergebnisse liefern.&lt;br /&gt;
Außerdem wollen wir die Frontfarbe unseres Vertex jedesmal mit der im Programm übergebenen GlobalColor multiplizieren, so dass wir den Farbwert der gesamten Szene aus unserem Programm heraus manipulieren können. Zu guterletzt geben wir dann noch unsere aus der festen Funktionspipeline erhaltenen Texturkoordinaten auf Textureinheit 0 weiter. Wenn im Fragmentshader Texturkoordinaten verwendet werden, '''muss das getan werden'''. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Der Fragment Shader==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;glsl&amp;quot;&amp;gt;&lt;br /&gt;
uniform sampler2D Texture0;&lt;br /&gt;
uniform sampler2D Texture1;&lt;br /&gt;
uniform sampler2D Texture2;&lt;br /&gt;
uniform sampler2D Texture3;&lt;br /&gt;
&lt;br /&gt;
void main(void)&lt;br /&gt;
{&lt;br /&gt;
 vec2 TexCoord = vec2( gl_TexCoord[0] );&lt;br /&gt;
 vec4 RGB      = texture2D( Texture0, TexCoord );&lt;br /&gt;
&lt;br /&gt;
 gl_FragColor  = texture2D(Texture1, TexCoord) * RGB.r +&lt;br /&gt;
                 texture2D(Texture2, TexCoord) * RGB.g +&lt;br /&gt;
                 texture2D(Texture3, TexCoord) * RGB.b;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch hier passiert nicht wirklich viel Großartiges. Wir deklarieren beim Shaderanfang zuerst vier Texturensampler, da wir insgesamt vier verschiedene Texturen im Shader auslesen wollen, eine Verlaufstextur und drei Oberflächentexturen. Auch hier sei wieder gesagt das man Sampler '''immer als uniform deklarieren muss'''. In der Hauptfunktion deklarieren wir dann einen Farbvektor, der auch direkt einen Farbwert aus Textureinheit 0 zugewiesen bekommt. Auf Textureinheit 0 haben wir ihm Hauptprogramm eine Verlaufstextur gebunden, die angibt wie die drei folgenden Texturen ineinander geblendet werden.&lt;br /&gt;
Danach schreiben wir dann den Farbwert des Fragmentes, der '''im Fragment Shader ausgegeben werden muss'''. Der besteht wie einfach zu erkennen aus Farbwert von Textureinheit 1 * Rotwert von Textureinheit 0 + Farbwert von Textureinheit 2 * Grünwert von Textureinheit 0 + Farbwert von Textureinheit 3 * Blauwert von Textureinheit 0. So ist z.B. an Stellen an denen in der Verlaufstextur reines blau liegt nur die dritte Textur sichtbar.&lt;br /&gt;
&lt;br /&gt;
So viel also zu unserem kleinen Beispielshader. Er ist weder besonders toll noch besonders sinnvoll, sollte aber auch eher dazu dienen euch glSlang ein wenig zu veranschaulichen, was mir hoffentlich gelungen ist.&lt;br /&gt;
&lt;br /&gt;
Wenn ihr in den vorangegangenen Kapiteln zumindest ein wenig aufgepasst habt, dann könnt ihr euch vor eurem inneren Auge hoffentlich vortstellen was der Shader macht : Er blendet drei Texturen weich anhand der Verlaufstextur ineinander über. Sowas kann man z.B. für ein Terrain nutzen, um dieses anhand einer Farbtextur zu texturieren. Für alle, die damit Probleme haben hier zwei Bilder die den Shader veranschaulichen. Links die Verlaufstextur, die angibt wo welche Textur wie stark gewichtet wird und rechts dann das Ergebnis :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt; [[BILD:GLSL_sample_shader_a.jpg]] [[BILD:GLSL_sample_shader_b.jpg]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Post Mortem=&lt;br /&gt;
Das wars also, meine &amp;quot;Einführung&amp;quot; in die OpenGL Shader Sprache. Ich hoffe es hat euch nicht gelangweilt und auch die von mir zur Verfügung gestellten Informationen haben euch hoffentlich ausgereicht. Mit der Veröffentlichung dieser Einführung geht übrigens auch die Eröffnung eines Shaderforums hier auf der DGL einher, in der ihr dann also fleissig Fragen zum Thema stellen oder eure Shader präsentieren könnt. In diesem Post Mortem gehe ich jetzt noch kurz auf die Zukunft von glSlang ein und zeige ein paar Screenshots (damit die Augen entspannen können), bevor ihr euch dann selbst in die Shaderwelt stürzen könnt. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Beispiele=&lt;br /&gt;
&lt;br /&gt;
Anbei ein paar exemplarische Screenshots. Da man mit GLSL aber alle möglichen Effekte berechnen kann (u.a. auch 1:1 die feste Funktionspipeline) ist es hier unmöglich einen Überblick aller möglichen Techniken zu geben.&lt;br /&gt;
&lt;br /&gt;
[[Datei:tut_glsl_eigenershader_01.png]] [[Datei:tut_glsl_eigenershader_02.png]] [[Datei:tut_glsl_eigenershader_03.png]]&lt;br /&gt;
&lt;br /&gt;
Wie im ersten (und dritten) Screenshot zu sehen ist es natürlich auch möglich mehrere Techniken innerhalb einer Szene zu nutzen. Hier sind letztendlich bis auf Hardwarelimitationen keine Grenzen gesetzt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Die Zukunft=&lt;br /&gt;
Als dieses Tutorial geschrieben wurde, war noch nicht ganz abzusehen dass bzw. ob sich GLSL auch durchsetzen würde. ARB-Shader und NVidias cG waren damals die Platzhirsche, aber inzwischen werden ARB-Shader nicht mehr genutzt (und auch schon länger nicht mehr weiterentwickelt) und auch NVidia setzt primär auf GLSL. GLSL wird permanent weiterenwtickelt und Hersteller können dank des flexiblen Extensionsystems auch in GLSL eigene Extensions offenlegen um die aktuellsten Features (wie z.b. den Tesselator auf aktuellen ATI-Karten) nutzen zu können. GLSL gilt inzwischen auch offizielle die Shadersprache für OpenGL und wird permanent an die neusten technischen Entwicklungen im Grafikkartenbereich angepasst.&lt;br /&gt;
&lt;br /&gt;
Wer also unter OpenGL etwas mit Shadern machen möchte, kommt an GLSL nicht vorbei!&lt;br /&gt;
&lt;br /&gt;
Also viel Spaß beim Experimentieren und Shaderschreiben! Und nicht vergessen : Wir wollen sehen was ihr so treibt,&lt;br /&gt;
&lt;br /&gt;
Euer&lt;br /&gt;
:Sascha Willems&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{TUTORIAL_NAVIGATION|-|[[Tutorial_glsl2]]}}&lt;br /&gt;
[[Kategorie:Tutorial|GLSL]]&lt;/div&gt;</summary>
		<author><name>Nevermind10844</name></author>	</entry>

	</feed>