<?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=Gucky</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=Gucky"/>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php/Spezial:Beitr%C3%A4ge/Gucky"/>
		<updated>2026-04-29T18:01:17Z</updated>
		<subtitle>Benutzerbeiträge</subtitle>
		<generator>MediaWiki 1.27.4</generator>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_Abseits_eckiger_Welten&amp;diff=19931</id>
		<title>Tutorial Abseits eckiger Welten</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_Abseits_eckiger_Welten&amp;diff=19931"/>
				<updated>2006-12-06T17:09:10Z</updated>
		
		<summary type="html">&lt;p&gt;Gucky: /* Erster Versuch: Evaluator Kurve */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Abseits eckiger Welten =&lt;br /&gt;
== Einführung ==&lt;br /&gt;
&lt;br /&gt;
Unsere Welt ist, schlecht, eintönig und eckig. Wirklich? Eckig? Schauen wir uns noch einmal um. An vielen Stellen ja, aber nicht überall. Rundungen sind sogar recht häufig anzutreffen. Ein Blick um mich herum offenbart es. Maus, Lampe, Joystick,... Vieles hat Rundungen. Ein Blick auf das was bisher bei DGL zu sehen war, zeigt: alles eckig :-(. Das muss geändert werden. Runden wir unsere Virtuellen Welten ein wenig ab - unser Hilfsmittel: Curved Surfaces&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Von Linien und Kurven ==&lt;br /&gt;
&lt;br /&gt;
Dies Kaptitel basiert auf [http://www.gamasutra.com/features/19990611/bezier_01.htm Curved Surfaces Using Bézier Patches von Gabe Kruger]. Mit freundlicher Genehmigung übersetzt ins deutsche und natürlich mit der ein oder anderen Änderung.&lt;br /&gt;
&lt;br /&gt;
=== Prozedurale Linien ===&lt;br /&gt;
&lt;br /&gt;
Gerade Strecken lassen sich allein durch die Angabe eines Start und Endpunktes beschreiben. Mit einer Liniengleichung lassen sich beliebig viele Punkte auf dieser Strecke berechnen. Eine solche Gleichung ist z.B.:&lt;br /&gt;
&lt;br /&gt;
    Q(t) = (1 - t)*P&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; + t*P&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Parameter t hat dabei Werte von 0 bis 1. P&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; und P&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; sind die Endpunkte der Strecke. Setzt man den Parameter t = 0 ein, so ist das Ergebnis P&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt;. Setzt man 1 ein ist das Ergebnis P&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt;.&lt;br /&gt;
Die Gleichung könnte man etwas umschreiben:&lt;br /&gt;
&lt;br /&gt;
    Q(t) = B&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt;(t)*P&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; + B&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt;(t)*P&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wobei gilt:&lt;br /&gt;
&lt;br /&gt;
    B&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt;(t) = 1 - t&lt;br /&gt;
    B&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt;(t) = t&lt;br /&gt;
&lt;br /&gt;
Man nennt B&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt;(t) und B&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt;(t) die Basis Funktionen der Liniengleichung. Ändern wir diese Gleichung nochmals ein wenig ab:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_Abseits_eckiger_Welten_linearsum.gif]]&lt;br /&gt;
&lt;br /&gt;
Dies ist die analytisch bearbeitete Fassung der Liniengleichung, die meisten Beschreibungen parametrischer '''Kurven''' werden in dieser Form gezeigt. Kurven? Klingt so als wären wir bereits am Ziel? Fast.&lt;br /&gt;
&lt;br /&gt;
=== Evolution zu Bezier Kurven ===&lt;br /&gt;
&lt;br /&gt;
Wie erzeugen wir nun eine Kurve in parametrischer Form, ählich der obigen? Wir wollen eine Kurve beschreiben, die durch die Endpunkte geht und dazwischen sich einem anderen Punkt annähert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_Abseits_eckiger_Welten_linesconnected.gif]]&lt;br /&gt;
&lt;br /&gt;
P&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; und P&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; sind die Endpunkte, P&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; ist der Kontrollpunkt, dem wir uns annähern wollen. Man könnte nun einen Punkt P&amp;lt;sub&amp;gt;a&amp;lt;/sub&amp;gt;(t) zwischen P&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; und P&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; interpolieren und das gleiche mit einem Punkt P&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt;(t) zwischen P&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; und P&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt;. Der gewünschte Punkt Q(t) auf der Kurve kann dann errechnet werden, durch interpolieren zwischen den Punkten P&amp;lt;sub&amp;gt;a&amp;lt;/sub&amp;gt; und P&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_Abseits_eckiger_Welten_interpolated.gif]]&lt;br /&gt;
&lt;br /&gt;
In mathematischen Formeln ausgedrückt:&lt;br /&gt;
&lt;br /&gt;
    P&amp;lt;sub&amp;gt;a&amp;lt;/sub&amp;gt;(t) = (1 - t)P&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; + t * P&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt;&lt;br /&gt;
    P&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt;(t) = (1 - t)P&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; + t * P&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
    Q(t)  = (1 - t)P&amp;lt;sub&amp;gt;a&amp;lt;/sub&amp;gt;(t) + tP&amp;lt;sub&amp;gt;b&amp;lt;/sub&amp;gt;(t) =&lt;br /&gt;
          = (1 - t)²P&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; + 2(1 - t)tP&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; + t²P&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Lohn der Mühen:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_Abseits_eckiger_Welten_quadraticbezier.gif]]&lt;br /&gt;
&lt;br /&gt;
Wir können nun die Gleichung von oben umschreiben:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_Abseits_eckiger_Welten_quadraticsum.gif]]&lt;br /&gt;
&lt;br /&gt;
Nun haben wir eine Definition einer quadratischen Bézier Kurve entwickelt. Es gibt eine einfache Formel für die Basis Funktionen von Bezier Kurven für einen beliebigen Grad. n ist der Grad, i beziffert die Basis Funktion und reicht damit von 0 bis n:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_Abseits_eckiger_Welten_basisfunctions.gif]]&lt;br /&gt;
&lt;br /&gt;
Ein Beispiel einer solchen Kurve mit dem Grad 3:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_Abseits_eckiger_Welten_kubikbezier.gif]]&lt;br /&gt;
&lt;br /&gt;
== Evaluators ==&lt;br /&gt;
&lt;br /&gt;
Wir können jetzt Bezier Kurven von Hand berechnen und, mit etwas Umsicht das ganze von 2D auf 3D transformieren. Das kann man vielseitig einsetzen und man kann gleichzeitig auf Detaileinstellungen in einem Programm reagieren - je höher der Detailgrad, desto feiner werden die Kurven aufgelöst. Ebenso kann man sie schön für Kamerafahrten verwenden.&lt;br /&gt;
&lt;br /&gt;
Interessant ist jedoch, dass OpenGl auch die Möglichkeit besitzt, solche Kurven selber zu rendern. In reinem OpenGl kann man hier Evaluators verwenden. &lt;br /&gt;
&lt;br /&gt;
=== Erster Versuch: Evaluator Kurve ===&lt;br /&gt;
&lt;br /&gt;
Als erstes wollen wir versuchen einen kubischen Bézier mit OpenGL Evaluators zu zeichnen. Definieren wir zuerst einmal 4 Kontrollpunkte:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
    const&lt;br /&gt;
      ctrlpoints : Array[0..3] of Array[0..2] of TGlFloat =&lt;br /&gt;
       ((-4.0, 2.0, 0.0),(-2.0, 4.0, 0.0),(2.0, -4.0, 0.0),(4.0, 2.0, 0.0));&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der nächste Schritt ist das Übergeben der Punkte an OpenGL:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
    glMap1f(GL_MAP1_VERTEX_3, 0, 1.0, 3, 4, @ctrlpoints[0,0]);&lt;br /&gt;
    glEnable(GL_MAP1_VERTEX_3);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kleine Erklärung von '''[[glMap1]]f''':&lt;br /&gt;
&lt;br /&gt;
Die 1 steht dafür, dass wir eine Linie beschreiben wollen. Eine 2, also '''[[glMap2]]f''' stünde für eine Fläche. Der erste Parameter beschreibt den Typ an Daten, den wir übergeben - in unserem Fall also dreidimensionale Vertexkoordinaten. Die nächsten 2 Parameter u&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; und u&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; beschreiben den Start- und Endwert unserer Kurve. Sie beschreiben sozusagen die Werte, die der Parameter t aus dem vorigen Kapitel haben muss, um eine komplette Kurve zu beschreiben. Der Parameter stride sagt aus, wie viele Floats vom Startfloat des einen Kontroll- bis zum Startfloat des nächsten Kontrollpunkts liegen. In unserem Fall also 3(Wegen Array[0..2] - hat 3 Elemente). Der vorletzte Parameter order sagt aus, wie viele Kontrollpunkte wir übergeben wollen. Zu guter Letzt noch ein Pointer auf die Kontrollpunkte, und wir haben unsere Schuldigkeit getan. &lt;br /&gt;
&lt;br /&gt;
Wir haben nun zwei Möglichkeiten unseren Evaluator zu zeichnen. Die erste:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
    var&lt;br /&gt;
      i : Integer;&lt;br /&gt;
      ...&lt;br /&gt;
      glBegin(GL_LINE_STRIP);&lt;br /&gt;
        for i := 0 to 30 do&lt;br /&gt;
          glEvalCoord1f(i/30);&lt;br /&gt;
      glEnd;&lt;br /&gt;
      ...&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''[[glEvalCoord]]1f''' erzeugt die Vertexdaten, die wir mittels [[glMap1]]f genauer definiert haben (diesmal also nur Vertexpositionen, keine Farben, keine Texturkoordinaten, etc.). Als Parameter werden Werte zwischen u&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; und u&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; eingesetzt.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_Abseits_eckiger_Welten_openglcurve.gif]]&lt;br /&gt;
&lt;br /&gt;
Es gibt noch eine andere Methode, mit der man die Daten in einem Rutsch an OpenGL weitergeben kann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
    glMapGrid1f(30, 0.0, 1.0);&lt;br /&gt;
    //...&lt;br /&gt;
    glEvalMesh1(GL_LINE, 0, 30);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Mit [[glMapGrid]]1f stellen wir ein, in wieviele Segmente wir unsere Linie unterteilen wollen und übergeben gleichzeitig den Bereich in der Kurve u&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; und u&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[glEvalMesh]]1 zeichnet nun unser gutes Stück als Linie. Gezeichnet werden alle Segmente von 0 bis 30.&lt;br /&gt;
&lt;br /&gt;
=== Von Linien zu Flächen ===&lt;br /&gt;
&lt;br /&gt;
Die bisherigen Ergebnisse sind noch nicht wirklich überzeugend. Unser Ziel war es immerhin Oberflächen zu zeichnen und keine Linien. Aber weit entfernt sind wir nicht mehr. Beginnen wir damit, wieder unsere Kontrollpunkte zu definieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  var&lt;br /&gt;
    CtrlPoints2D : Array[0..3] of Array[0..3] of Array[0..2] of TGlFloat;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diesmal eine Variable? Ist er krank? Seit wann benutzt er Variablen? Nun ich kanns euch erklären: Ich habe mit mehreren unterschiedlichen Flächen herumgespielt und wollte nicht ständig dabei am Code herumbasteln, also habe ich mir fix eine Routine zum Laden der Punkte geschrieben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
    procedure LoadCtrlPoints;&lt;br /&gt;
    var&lt;br /&gt;
      u, v : Integer;&lt;br /&gt;
      F : Text;&lt;br /&gt;
    begin&lt;br /&gt;
      AssignFile(F, 'surface.txt');&lt;br /&gt;
      Reset(F);&lt;br /&gt;
      FillChar(CtrlPoints2D[0,0,0], SizeOf(CtrlPoints2D), 0);&lt;br /&gt;
      for u := 0 to 3 do&lt;br /&gt;
        for v := 0 to 3 do&lt;br /&gt;
          ReadLn(F, CtrlPoints2D[u,v,0], CtrlPoints2D[u,v,1], CtrlPoints2D[u,v,2]);&lt;br /&gt;
      CloseFile(F)&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist die billigste Variante, ohne jeglichen Fehlerschutz, aber fürs herumspielen sollte das genügen. Sobald wir unsere Kontrollpunkte geladen haben, sollten wir sie an OpenGL übergeben:  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
    LoadCtrlPoints;&lt;br /&gt;
    glMap2f(GL_MAP2_VERTEX_3, 0.0, 1.0, 3, 4, 0.0, 1.0, 12, 4, @CtrlPoints2D[0,0,0]);&lt;br /&gt;
    glEnable(GL_MAP2_VERTEX_3);&lt;br /&gt;
    glEnable(GL_AUTO_NORMAL);&lt;br /&gt;
    glEnable(GL_NORMALIZE);&lt;br /&gt;
    glMapGrid2f(40, 0.0, 1.0, 40, 0.0, 1.0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die [[glMap2]]f Funktionen haben jetzt ein paar neue Parameter erhalten. Dies sind an sich die gleichen Parameter wie bei den [[glMap1]]f Funktionen, nur dass sie nun doppelt vorhanden sind, für die v Richtung.&lt;br /&gt;
&lt;br /&gt;
Schlussendlich wollen wir noch unsere Oberflächen anzeigen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
    glEvalMesh2(GL_FILL, 0, 40, 0, 40);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hat die Datei &amp;quot;surface.txt&amp;quot; nun folgenden Inhalt:&lt;br /&gt;
&lt;br /&gt;
    -1.5 2.0 -1.5&lt;br /&gt;
    -0.5 -1.0 -1.5&lt;br /&gt;
    0.5 -1.0 -1.5&lt;br /&gt;
    1.5 -2.0 -1.5&lt;br /&gt;
    -1.5 2.0 -0.5&lt;br /&gt;
    -0.5 1.0 -0.5&lt;br /&gt;
    0.5 -2.0 -0.5&lt;br /&gt;
    1.5 0.0 -0.5&lt;br /&gt;
    -1.5 0.0 0.5&lt;br /&gt;
    -0.5 0.0 0.5&lt;br /&gt;
    0.5 0.0 0.5&lt;br /&gt;
    1.5 0.0 0.5&lt;br /&gt;
    -1.5 1.0 1.5&lt;br /&gt;
    -0.5 0.0 1.5&lt;br /&gt;
    0.5 -1.0 1.5&lt;br /&gt;
    3.0 -2.0 1.5&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
dann könnte das enstehende Bild so aussehen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_Abseits_eckiger_Welten_owncurve.gif]]&lt;br /&gt;
&lt;br /&gt;
=== Texturen und anderes Feintuning ===&lt;br /&gt;
&lt;br /&gt;
Es ist schon bemerkenswert, wie leicht man Curved Surfaces in OpenGL zeichnen kann. Was fehlt, ist die Möglichkeit, die Flächen, die man erzeugt hat, auch noch zu texturieren. Jedem dürfte klar sein, was das für Vorteile hat.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_Abseits_eckiger_Welten_texturedcurve.gif]]&lt;br /&gt;
&lt;br /&gt;
Das schöne ist, dass man die Texturierung mit den gleichen Mitteln lösen kann, wie die Oberflächen selbst - mit Evaluators. Definieren wir uns also zuerst einmal ein paar Kontrollpunkte:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
    const&lt;br /&gt;
      TexCoords : Array[0..1, 0..1, 0..1] of TGlFloat =&lt;br /&gt;
        (((0.0, 0.0),(0.0, 1.0)), ((1.0, 0.0),(1.0, 1.0)));&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und übergeben die Koordinaten an OpenGL:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
    LoadCtrlPoints;&lt;br /&gt;
    glMap2f(GL_MAP2_VERTEX_3, 0.0, 1.0, 3, 4, 0.0, 1.0, 12, 4, @CtrlPoints2D[0,0,0]);&lt;br /&gt;
&lt;br /&gt;
    LoadDevilTexture('flowers256.tga');&lt;br /&gt;
    glMap2f(GL_MAP2_TEXTURE_COORD_2, 0.0, 1.0, 2, 2, 0.0, 1.0, 4, 2, @TexCoords[0,0,0]);&lt;br /&gt;
    glEnable(GL_MAP2_TEXTURE_COORD_2);&lt;br /&gt;
&lt;br /&gt;
    glEnable(GL_MAP2_VERTEX_3);&lt;br /&gt;
    glEnable(GL_AUTO_NORMAL);&lt;br /&gt;
    glEnable(GL_NORMALIZE);&lt;br /&gt;
    glMapGrid2f(40, 0.0, 1.0, 40, 0.0, 1.0);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir jetzt mittels glEvalMesh2(GL_FILL, 0, 40, 0, 40) die Szene rendern, bekommen wir das obige Bild. Simpel, nicht?&lt;br /&gt;
&lt;br /&gt;
Noch viel schöner wirds, wenn man feststellt, dass das gleiche nicht nur mit Texturkoordinaten funktioniert. Hier mal eine kleine Übersicht über die Parameter von glMapxf:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
! Konstante:&lt;br /&gt;
! Parameter:&lt;br /&gt;
|-&lt;br /&gt;
| GL_MAPx_VERTEX_3 || x,y,z Vertex-Koordinaten&lt;br /&gt;
|-&lt;br /&gt;
| GL_MAPx_VERTEX_4 || x,y,z,w Vertex-Koordinaten&lt;br /&gt;
|-&lt;br /&gt;
| GL_MAPx_INDEX || Farb-Index Werte&lt;br /&gt;
|-&lt;br /&gt;
| GL_MAPx_COLOR_4 || R,G,B,A Farbwerte&lt;br /&gt;
|-&lt;br /&gt;
| GL_MAPx_NORMAL || Normalen-Richtungen&lt;br /&gt;
|-&lt;br /&gt;
| GL_MAPx_TEXTURE_COORD_1 || s Textur-Koordinaten&lt;br /&gt;
|-&lt;br /&gt;
| GL_MAPx_TEXTURE_COORD_2 || s,t Textur-Koordinaten&lt;br /&gt;
|-&lt;br /&gt;
| GL_MAPx_TEXTURE_COORD_3 || s,t,r Textur-Koordinaten&lt;br /&gt;
|-&lt;br /&gt;
| GL_MAPx_TEXTURE_COORD_4 || s,t,r,q Textur-Koordinaten&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== NURBs ==&lt;br /&gt;
&lt;br /&gt;
Die OpenGL NURBs sind Teil der GL Utilities und damit bei jeder OpenGL-Implementation dabei. Im Redbook finden sich einige sehr schöne Beispiele, die die Verwendung von Nurbs zeigen. Eine Sache, die die NURBs interessant macht, ist die Möglichkeit sie trimmen zu können. Darunter fällt z.B. das Schneiden von Löchern mitten in die Fläche. Eine genaue Besprechung von NURBs würde den Rahmen dieses kleinen Tutorials sicher bei weitem sprengen, so dass ich beschließe hier meine kleine Einführung in das Thema Curved Surfaces zu beenden.&lt;br /&gt;
&lt;br /&gt;
[[Benutzer:Nico Michaelis|Delphic]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{TUTORIAL_NAVIGATION| - | [[Tutorial_Renderpass]] }}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Tutorial|Abseits eckiger Welten]]&lt;/div&gt;</summary>
		<author><name>Gucky</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_Lektion_7&amp;diff=19877</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=19877"/>
				<updated>2006-11-20T23:01:58Z</updated>
		
		<summary type="html">&lt;p&gt;Gucky: /* Nachwort */&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;pascal&amp;gt;&lt;br /&gt;
  glBlendFunc(source,destination);&lt;br /&gt;
&amp;lt;/pascal&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 wäre 0 und Destination 1, 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;pascal&amp;gt;&lt;br /&gt;
  glEnable(GL_BLEND);&lt;br /&gt;
&amp;lt;/pascal&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;pascal&amp;gt;&lt;br /&gt;
  glBlendFunc(GL_ONE,GL_ZERO);&lt;br /&gt;
&amp;lt;/pascal&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;pascal&amp;gt;&lt;br /&gt;
  glBlendFunc(GL_ONE,GL_ZERO);&lt;br /&gt;
&amp;lt;/pascal&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;pascal&amp;gt;&lt;br /&gt;
  glBlendFunc(GL_ZERO,GL_ONE);&lt;br /&gt;
&amp;lt;/pascal&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. 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 ne 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 das Fenster so merkwürdig aussieht und man praktisch durch das Glas ins Freie sehen kann. Würden wir kein Blending haben, so würde es nicht weiter auffallen in welcher Reihenfolge wir die Objekte gerendert haben.&lt;br /&gt;
&lt;br /&gt;
Darum gilt, alle 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 seit gewarnt worden ;D Eigentlich ist es ein recht logischer Vorgang, allerdings erliegt man doch recht schnell diesem kleinen Irrglauben ;)&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;
! Berücksichtige Faktoren&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;pascal&amp;gt;&lt;br /&gt;
  glBlendFunc(GL_SRC_COLOR,GL_ONE);&lt;br /&gt;
&amp;lt;/pascal&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;pascal&amp;gt;&lt;br /&gt;
  glBlendFunc(GL_ONE,GL_DST_COLOR);&lt;br /&gt;
&amp;lt;/pascal&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;pascal&amp;gt;&lt;br /&gt;
glBlendFunc(GL_ZERO,GL_SRC_COLOR);&lt;br /&gt;
&amp;lt;/pascal&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;pascal&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;
  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;/pascal&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;pascal&amp;gt;&lt;br /&gt;
  glBlendFunc(GL_SRC_COLOR,GL_DST_COLOR);&lt;br /&gt;
&amp;lt;/pascal&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: GeForce2 MX) haben zwei Textur-Units. Das muss man sich vorstellen wie zwei Schubladen. In beiden legen wir eine Textur rein und wenn OpenGL rendert werden reingeschaut und beide 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 4 Texturen-Units und die Zeit bis es welche mit 8 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;pascal&amp;gt;&lt;br /&gt;
  glActiveTextureARB(GL_TEXTURE0_ARB);&lt;br /&gt;
&amp;lt;/pascal&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;pascal&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;/pascal&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;pascal&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;/pascal&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;pascal&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;/pascal&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;
{{TUTORIAL_NAVIGATION | [[Tutorial Lektion 5]] | [[Tutorial Lektion 8]]}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Tutorial|Lektion7]]&lt;/div&gt;</summary>
		<author><name>Gucky</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_Lektion_7&amp;diff=19876</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=19876"/>
				<updated>2006-11-20T22:38:08Z</updated>
		
		<summary type="html">&lt;p&gt;Gucky: /* Vorwort */&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;pascal&amp;gt;&lt;br /&gt;
  glBlendFunc(source,destination);&lt;br /&gt;
&amp;lt;/pascal&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 wäre 0 und Destination 1, 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;pascal&amp;gt;&lt;br /&gt;
  glEnable(GL_BLEND);&lt;br /&gt;
&amp;lt;/pascal&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;pascal&amp;gt;&lt;br /&gt;
  glBlendFunc(GL_ONE,GL_ZERO);&lt;br /&gt;
&amp;lt;/pascal&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;pascal&amp;gt;&lt;br /&gt;
  glBlendFunc(GL_ONE,GL_ZERO);&lt;br /&gt;
&amp;lt;/pascal&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;pascal&amp;gt;&lt;br /&gt;
  glBlendFunc(GL_ZERO,GL_ONE);&lt;br /&gt;
&amp;lt;/pascal&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. 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 ne 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 das Fenster so merkwürdig aussieht und man praktisch durch das Glas ins Freie sehen kann. Würden wir kein Blending haben, so würde es nicht weiter auffallen in welcher Reihenfolge wir die Objekte gerendert haben.&lt;br /&gt;
&lt;br /&gt;
Darum gilt, alle 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 seit gewarnt worden ;D Eigentlich ist es ein recht logischer Vorgang, allerdings erliegt man doch recht schnell diesem kleinen Irrglauben ;)&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;
! Berücksichtige Faktoren&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;pascal&amp;gt;&lt;br /&gt;
  glBlendFunc(GL_SRC_COLOR,GL_ONE);&lt;br /&gt;
&amp;lt;/pascal&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;pascal&amp;gt;&lt;br /&gt;
  glBlendFunc(GL_ONE,GL_DST_COLOR);&lt;br /&gt;
&amp;lt;/pascal&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;pascal&amp;gt;&lt;br /&gt;
glBlendFunc(GL_ZERO,GL_SRC_COLOR);&lt;br /&gt;
&amp;lt;/pascal&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;pascal&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;
  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;/pascal&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;pascal&amp;gt;&lt;br /&gt;
  glBlendFunc(GL_SRC_COLOR,GL_DST_COLOR);&lt;br /&gt;
&amp;lt;/pascal&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: GeForce2 MX) haben zwei Textur-Units. Das muss man sich vorstellen wie zwei Schubladen. In beiden legen wir eine Textur rein und wenn OpenGL rendert werden reingeschaut und beide 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 4 Texturen-Units und die Zeit bis es welche mit 8 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;pascal&amp;gt;&lt;br /&gt;
  glActiveTextureARB(GL_TEXTURE0_ARB);&lt;br /&gt;
&amp;lt;/pascal&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;pascal&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;/pascal&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;pascal&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;/pascal&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;pascal&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;/pascal&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, den 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;
{{TUTORIAL_NAVIGATION | [[Tutorial Lektion 5]] | [[Tutorial Lektion 8]]}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Tutorial|Lektion7]]&lt;/div&gt;</summary>
		<author><name>Gucky</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_Lektion_5&amp;diff=19875</id>
		<title>Tutorial Lektion 5</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_Lektion_5&amp;diff=19875"/>
				<updated>2006-11-16T22:38:18Z</updated>
		
		<summary type="html">&lt;p&gt;Gucky: /* Und Cut! */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Artenvielfalten und Ihre Folgen =&lt;br /&gt;
&lt;br /&gt;
== Vorwort ==&lt;br /&gt;
&lt;br /&gt;
Und wieder einmal aufraffen und etwas tippen. Es ist wirklich nicht immer leicht solche Tutorials zu schreiben, vor allem wenn man mal wieder eine Null-Bock-Phase hat. Ich hoffe doch sehr, dass auch Ihr dafür Verständnis habt, den… auch ich bin ein Mensch… egal wie viele Augen und Beine ich habe *sg* Gut… genug &amp;quot;gefaxt&amp;quot;, es geht wieder um den Ernst im Leben: Delphi&lt;br /&gt;
&lt;br /&gt;
Ich habe wirklich eine zeitlang überlegt, was wir machen sollten. Einige haben sich Lichter gewünscht, aber ich konnte mich nicht dazu aufraffen… Stattdessen habe ich irgendwie Lust gehabt, mal was anderes zu machen, etwas wo man etwas kreativ sein kann und was auch mir Spaß macht! :-D Und deswegen, habe ich mir gedacht, dass wir das eine oder andere Wissen, welches zwischendurch behandelt wurde noch etwas vertiefen sollten… einfach in dem wir andere Möglichkeiten aufzeigen, elegant ein Problem zu umschiffen und auch den einen oder anderen Effekt erzielen!&lt;br /&gt;
&lt;br /&gt;
Und genau darum geht es auch diesmal! Ich denke, dass sich dieses Tutorial weitestgehend nur an die richten wird, die bereits ein solides Wissen in OpenGL und Delphi haben, ansonsten wird es wohl schwer sein mir zu folgen, ich werde einiges an Wissen woraus setzen! Wer folgen kann, wird dann mit einem Wissen belohnt werden, dass ihm das eine oder andere Probleme sehr elegant umschiffen lässt in dem man einfach seine Software entsprechend mit OpenGL optimiert! Den nur wer sein Handwerk bis ins Detail beherrscht, darf sich Meister nennen ;)&lt;br /&gt;
&lt;br /&gt;
== Unbekannte Zeichen-Arten ==&lt;br /&gt;
&lt;br /&gt;
=== OpenGL-Maxime ===&lt;br /&gt;
&lt;br /&gt;
Wer nicht gerade erst jetzt hier eingestiegen ist, wird sicherlich bereits bemerkt haben, dass OpenGL streng genommen nach einem sehr einfachen Prinzip arbeitet. Ständig wird an unserer &amp;quot;Zustand-Maschine&amp;quot; etwas manipuliert und mit Matrizen setzen wir die Positionen fest. Jedoch nur an einer einzigen Stelle kommen alle diese Werte zusammen. Nämlich dann, wenn etwas gerendert wird. Genau in diesem Moment werden alle Werte &amp;quot;zusammengerechnet&amp;quot; und erzeugen etwas Sichtbares auf dem Bildschirm. In den meisten Fällen wird dies Eben zwischen glBegin und glEnd geschehen. Und genau diese beiden Funktionen wollen wir nun näher betrachten. Interessant hierbei ist nämlich der Parameter von glBegin…&lt;br /&gt;
&lt;br /&gt;
Streng genommen definieren wir nur eine Anzahl von Punkten zwischen glBegin und glEnd, der Parameter bei glBegin bestimmt aber letztendlich wie diese Verstanden werden sollen. Nur als kleine Übersicht alle verfügbaren Parameter:&lt;br /&gt;
&lt;br /&gt;
 GL_POINTS&lt;br /&gt;
 GL_LINES&lt;br /&gt;
 GL_LINE_STRIP&lt;br /&gt;
 GL_LINE_LOOP&lt;br /&gt;
 GL_TRIANGLES&lt;br /&gt;
 GL_TRIANGLE_STRIP&lt;br /&gt;
 GL_TRIANGLE_FAN&lt;br /&gt;
 GL_QUADS&lt;br /&gt;
 GL_QUAD_STRIP&lt;br /&gt;
 GL_POLYGON&lt;br /&gt;
&lt;br /&gt;
Ich denke aus ethischen Gründen werde ich nun darauf verzichten Euch erneut zu erklären wofür, die bisher für Euch bekannten Parameter (gl_Triangle und gl_Quad) gut sind… jeder wird sich denken können, dass das erste immer per 3er Punkte ein Dreieck bildet, dass zweiter aus 4 Punkten ein Quadrat erzeugt… (mist… *g*) ;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Zunächst war der Strich ===&lt;br /&gt;
&lt;br /&gt;
Interessanter, da neu für uns, sind die restlichen Parameter, auch wenn diese sich weitestgehend selbst erklären. Gl_Points z.B. … könnte es vielleicht bedeuten, dass OpenGL alle mit [[glVertex3f]] definierten Punkte auch nur als Punkte zeichnet?&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gl_point.gif]]&lt;br /&gt;
&lt;br /&gt;
Scheint zu stimmen… ;) Es ist nun auch nicht besonders schwer herzuleiten, was OpenGL mit Vertex-Definitionen macht, die mit gl_Lines beginnen. Versuchen wir doch mal ein Dreieck damit zu zeichnen! Ergo brauchen wir 4 Punkte (der letzte muss, auf den ersten verweisen)…&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gl_lines.gif]]&lt;br /&gt;
&lt;br /&gt;
Doch was ist das? Dies ist kein böswilliger Trick, den ich auf Euch spielen will, sondern folgender Code, ist dafür verantwortlich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBegin(GL_LINES);&lt;br /&gt;
    glVertex3f(-1,0,0);&lt;br /&gt;
    glVertex3f(0,1,0);&lt;br /&gt;
    glVertex3f(1,0,0);&lt;br /&gt;
    glVertex3f(-1,0,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sieht soweit alles in Ordnung aus - ist es auch! Der Fehler liegt nämlich nicht am Code, sondern an uns selbst. Gl_Lines bewirkt nämlich nicht, dass alle Punkte miteinander verbunden werden, sondern nur, dass jeweils Punkte 1 mit Punkte2, Punkte 3 mit Punkt 4 etc. verbunden werden, d.h. immer zweiter Pärchen. Um das Ergebnis zu erreichen, welches wir angestrebt haben (und zwar ohne 6 Punkte zu definieren), wäre in diesem Fall nämlich gl_Line_Strip gewesen, was nämlich bewirkt, dass der &amp;quot;Zeichenstift&amp;quot; immer von seiner letzten Position zum nächst definierten Punkte einen Strich zieht. Dementsprechend sieht dann unser Dreieck mit praktisch dem gleichen Source wie folgt aus:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gl_line_strip.gif]]&lt;br /&gt;
&lt;br /&gt;
hr merkt bereits jetzt wie viele Möglichkeiten einen OpenGL bietet mit nur wenigen Zeilen eine Menge zu verändern. Man bedenkt, dass wir hier nur sehr wenige Punkte haben, allerdings kann man eine Menge Leistung rausschlagen, wenn man statt gl_Lines, gl_Line_Strip verwendet, da einfach weniger Punkte abgearbeitet werden müssen. Logisch oder?&lt;br /&gt;
&lt;br /&gt;
 GL_LINE_LOOP&lt;br /&gt;
&lt;br /&gt;
Der Parameter entspricht praktisch gesehen GL_LINE_STRIP, nur das der letzte Punkt mit dem ersten verbunden wird. Man bräuchte mit diesem Parameter also nur 3 Punkte… das Optimum für unser Dreieck! ;)&lt;br /&gt;
&lt;br /&gt;
== Artenvielfalt ==&lt;br /&gt;
&lt;br /&gt;
Natürlich lassen sich solche Zeichenoperationen auch durch eine Menge anderer Faktoren beeinflussen. Vielleicht hat sich der eine oder andere ja bereits gefragt, wie man es schafft, dass der Rahmen des Dreiecks dicker gezeichnet wird. Sicherlich könnte man nun beginnen und ganz leicht versetzt daneben noch ein Dreieck zu zeichnen. Dies würde dann natürlich einen kleinen Tick größer wirken.&lt;br /&gt;
&lt;br /&gt;
Die Lösung liegt allerdings viel näher - den OpenGL bietet hierfür eine hauseigene Lösung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glLineWidth(3);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir stellen einfach ein, wie OpenGL die Linien Rastern soll. Wir nehmen in diesem Fall die dreifache Dicke von der normalen Einstellung:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gllinewidth.gif]]&lt;br /&gt;
&lt;br /&gt;
Auch werden einige von Euch sicherlich die unschönen Treppchen kennen, die vor allem bei solch einfache Konstruktionen wie diesm dickeren Dreieck auftreten können. Die Lösung dagegen heißt bekanntlich &amp;quot;AntiAliasing&amp;quot; … verschlägt es Euch die Sprache, dass ich gleich mit solch derben Geschützen auffahre?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glEnable(GL_LINE_SMOOTH);&lt;br /&gt;
  glDisable(GL_LINE_SMOOTH);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da verschlägt es einem die Sprache oder? Das war es nämlich auch bereits wieder. Wir schalten einfach einen Zustand um und fertig war die Geschichte! Der Zustands-Maschine von OpenGL sei dank, müssen wir nur noch sagen, was gemacht werden soll, ein lästiges &amp;quot;Wie?&amp;quot; entfällt komplett&lt;br /&gt;
&lt;br /&gt;
Und noch ein kleines Beispiel dafür, wie einfach OpenGL nicht nur Informationen schreiben lässt, sondern diese auch wieder preisgibt. Haben wir vergessen wie Dick wir die Linien eingestellt haben? Kein Problem, den OpenGL bietet folgende Funktionen um zu ermitteln, auf welchen Wert ein &amp;quot;Zustand&amp;quot; geschaltet ist:&lt;br /&gt;
&lt;br /&gt;
[[glGetBooleanv]], [[glGetDoublev]], [[glGetFloatv]], [[glGetIntegerv]]&lt;br /&gt;
&lt;br /&gt;
Ich glaube ich brauche nicht wieder damit zu beginnen darauf hinzuweisen, dass man bei jeder Abfrage auch den richtigen Variablen-Typen verwenden sollte *g*&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glGetIntegerv(gl_line_width,@myint);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und schon haben wir in myint die Stärke mit der OpenGL momentan alle Linien zeichnen soll. Das geht mit fast allen Zuständen, die OpenGL haben kann. Ich denke, das Grundprinzip ist recht leicht verständlich. Beachtet auch, dass OpenGL bei solchen Funktionen immer einen Pointer auf eine Variable erwartet (sprich besser: eine Adresse) und nicht die Variable selbst!&lt;br /&gt;
&lt;br /&gt;
Achso… vergaß ich zu erwähnen, dass wir auch weitere Funktionen auf Striche anwenden können? Zum Beispiel glColor, um den Strich einzufärben? Auch der Z-Buffer lasst sich darauf anwenden, eben alles, was man auch bei einem Dreieck tun könnte (wovon man aber zwingend absehen sollte eine Linie zu texturieren, um keine unnötigen Berechnungen durchzuführen)&lt;br /&gt;
&lt;br /&gt;
=== Ein Dreieck, hat drei Ecken… ===&lt;br /&gt;
&lt;br /&gt;
Nun dreht sich erstmal alles um Dreiecke. Den auch hierfür bietet OpenGL mehre Möglichkeiten, wie die Reihenfolge der Punkte verstanden wird:&lt;br /&gt;
&lt;br /&gt;
 GL_TRIANGLES&lt;br /&gt;
 GL_TRIANGLE_STRIP&lt;br /&gt;
 GL_TRIANGLE_FAN&lt;br /&gt;
&lt;br /&gt;
Zunächst widmen wir uns GL_TRIANGLES! Dieser Parameter sollte uns allen noch geläufig sein. Jeweils 3 Punkte werden zusammen zu einem eigenständigen Dreieck verbunden. Nicht neues für uns, bereits im ersten Tutorial könnt ihr den Beweis finden, dass es klappt :-D&lt;br /&gt;
&lt;br /&gt;
Sicherlich werdet ihr Euch denken können, dass dies sehr hilfreich ist, wenn man nur ein Dreieck zeichnen möchte, nicht jedoch, sobald man mehre in einem Rutsch auf dem Bildschirm bringen möchte. Hierzu bietet sich dann eher GL_TRIANGLE_STRIP an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBegin(GL_TRIANGLE_STRIP);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(-1,0,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(1,1,0);&lt;br /&gt;
    glVertex3f(0,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(1,1,1);&lt;br /&gt;
    glVertex3f(1,0,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(0,1,1);&lt;br /&gt;
    glVertex3f(2,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(0,1,0);&lt;br /&gt;
    glVertex3f(3,0,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wem das ganze zu schwer erscheint, sollte er sich die glColor3f weglassen, die habe ich reingesetzt, damit das ganze auch schön aussieht ;) (Programmierer lieben sinnlosen Spielkram der glänzt und bunt ist). Wer sich nur auf die Punkte konzentriert und sich die Position im Kopfe vorstellt, wird ein Gebilde wie folgt vorstellen können:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gl_triangle_strip.gif]]&lt;br /&gt;
&lt;br /&gt;
Streng genommen macht OpenGL nichts anderes, als die ersten drei Eckpunkte zu nehmen und ein Dreieck daraus zu rendern. Anschließend fällt der erste Punkt weg und es wird das Dreieck zwischen 2,3,4 gerendert usw. Im Anschluss dieses Kapitels werden wir nochmals hieraus zurückkommen und das Culling erklären!&lt;br /&gt;
&lt;br /&gt;
=== Ventilatoren und OpenGL ===&lt;br /&gt;
&lt;br /&gt;
Tja, und nur die wenigstens wissen, dass auch ein Ventilator ein Dreieck sein kann *hust* Okay, begnügen wir uns hier lieber mit dem englischen Begriff &amp;quot;Fan&amp;quot;. (Das ist nicht der Kerl, der vorm Fenster steht, einem gierig anstarrt und laut ruft &amp;quot;Ich will ein Kind von Dir…&amp;quot; … nein, sicher nicht ;)&lt;br /&gt;
&lt;br /&gt;
Vielmehr sollten wir uns wirklich mal bildlich einen Fahrrad-Reifen vorstellen. Von den Außenseiten verlaufen die einzelnen Speichen alle zu einem Mittelpunkt. Nach einem ähnlichen Render-Prinzip funktioniert auch GL_TRIANGLE_FAN. Der einfachheitshalber werden wir hier jedoch kein komplexes Objekt anfertigen, sondern nur eine Möglichkeit zeigen, wie man mit dieser Einstellung sinnvoll ein Objekt zeichnen kann! In unserem Fall nehmen wir einfach einen Drachen (nein… nicht das Fabelwesen), denn auch dieser ist ein Ventilator (Chaos… perfekt….) ;)&lt;br /&gt;
&lt;br /&gt;
Bevor nun jeder abdreht, schauen wir uns doch mal das Objekt an, von dem ich sprach:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gl_triangle_fan.gif]]&lt;br /&gt;
&lt;br /&gt;
In diesem Fall benötigen wir nur 6 (!) Punkte, um dieses Gebilde zu erzeugen. Zentraler Ausgangspunkt ist hierbei die Nummer 1. Wie man erkennen kann besteht das erste Dreieck aus 1,2,3. Das zweite aus 1,3,4… für die abstrakt denkenden Menschen lässt sich daraus folgern, dass OpenGL die Punkte wie folgt abarbeitet 1, n+1, n+2. Oder um es neudeutsch zu sagen, der erste Punkte wird immer mit zwei weiteren verbunden. Im Gegensatz zum Strip fällt allerdings nicht der erste Punkt raus, sondern immer der zweite.&lt;br /&gt;
&lt;br /&gt;
Gl_Triangle_Fans und Gl_Triangle_Strips sind wohl die besten Möglichkeiten, um die Anzahl der im Video-Speicher befindlichen Daten zu begrenzen. Allerdings lässt sich schnell erahnen, dass nicht jeder dieser Methoden wirklich komplexe Objekte zulässt. Man sollte sie jedoch nicht total ignorieren, sondern mit Verstand einsetzen. Ach so, bevor ich es vergesse, der Code dafür:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBegin(GL_TRIANGLE_FAN);&lt;br /&gt;
    glColor3f(1,1,0);&lt;br /&gt;
    glVertex3f(0,0,0);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(0,1,0);&lt;br /&gt;
    glVertex3f(1,0,0);&lt;br /&gt;
    glColor3f(0,0,1);&lt;br /&gt;
    glVertex3f(0,-3,0);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(-1,0,0);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(0,1,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass Punkte 2 und Punkte 6 identisch sind, da er sonst das letzte Dreieck weglassen würde ;)&lt;br /&gt;
&lt;br /&gt;
=== Quadratisch, praktisch, gut! ===&lt;br /&gt;
&lt;br /&gt;
Das ist ne wahrer Marathon geworden. Schlimm, wenn man bedenkt, dass wir uns hier nur mit einem Parameter für eine Funktion beschäftigen. Ihr versteht, warum ich so oft geschrieben haben &amp;quot;lassen wir es lieber und klären nicht jeden Parameter&amp;quot; ;) Aber gut, ich halte es für sehr wichtig, solche Dinge zu beherrschen, weil es einfach zu den Grundlagen dazugehört! Immerhin bleiben nicht mehr sonderlich viele übrig, als ran an die letzten drei!&lt;br /&gt;
&lt;br /&gt;
 GL_QUADS&lt;br /&gt;
&lt;br /&gt;
Sollte auch niemanden von uns mehr sonderlich schockieren können. Jeweils vier übergebende Punkte werden zusammengefasst zu einem Quadrat… nichts weiter Aufregendes. Eigentlich sollten inzwischen auch die GL_QUAD_STRIP nichts wirklich erstaunliches mehr liefern. Wir übergeben zunächst vier Punkte und bei jedem Durchgang entfallen die beiden ersten und werden durch die nächsten zwei ersetzt. OpenGL erstellt dann daraus jedes Mal ein Viereck. Bitte nicht verwechseln mit der Punkt-Reihenfolge von GL_QUADS. Folgendes Beispiel sollte die Problematik verdeutlichen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
glBegin(GL_QUAD_STRIP);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(0,0,0);&lt;br /&gt;
    glVertex3f(0,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(0,0,1);&lt;br /&gt;
    glVertex3f(1,0,0);&lt;br /&gt;
    glVertex3f(1,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(0,1,0);&lt;br /&gt;
    glVertex3f(2,0,0);&lt;br /&gt;
    glVertex3f(2,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(0,1,1);&lt;br /&gt;
    glVertex3f(3,0,0);&lt;br /&gt;
    glVertex3f(3,1,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gl_quad_strip.gif]]&lt;br /&gt;
&lt;br /&gt;
Unser erstes Quadrat besteht aus den Punkten 1,2,3,4, dass zweite aus 3,4,5,6 etc. Ich denke, dass das Prinzip dahinter leicht verständlich ist. Anbieten tut sich diese Lösung meist in Schleifen, wenn man längere solcher Quad-Strukturen braucht, die aneinander gereiht sind&lt;br /&gt;
&lt;br /&gt;
=== Vieleckerei ===&lt;br /&gt;
&lt;br /&gt;
Und zu guter letzt GL_POLYGON, was wohl am einfachsten nachzuvollziehen ist ;) Es wird einfach eine Liste von Punkten übergeben und daraus wird schlicht und ergreifend dann ein Vieleck gemacht. Wie variantenreich dies werden kann, könnt ihr Euch vorstellen. Vor allem komplexere und exotischere Formen lassen sich damit darstellen, auch wenn es in diesem Fall eher Leitung kostet als wirklich einbringt. Zeichnet also niemals ein Quadrat mit GL_POLYGON.&lt;br /&gt;
&lt;br /&gt;
Eine mögliche Form wäre zum Beispiel diese:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gl_polygon.png]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBegin(GL_POLYGON);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(0,0,0);&lt;br /&gt;
    glColor3f(0,1,0);&lt;br /&gt;
    glVertex3f(3,0,0);&lt;br /&gt;
    glColor3f(0,0,1);&lt;br /&gt;
    glVertex3f(4,1,0);&lt;br /&gt;
    glColor3f(1,1,0);&lt;br /&gt;
    glVertex3f(2,2,0);&lt;br /&gt;
    glColor3f(0,1,1);&lt;br /&gt;
    glVertex3f(-2,3,0);&lt;br /&gt;
    glColor3f(1,1,1);&lt;br /&gt;
    glVertex3f(-1,1,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ich denke zusammen mit dem Code sollte es keine weiteren Fragen mehr dazu geben. Am besten setzt Ihr Euch nun alle einmal hin und wendet das Wissen Testweise an, denn es muss sitzen und stellt vor allem, wenn es um optimierte Programmierung geht ein absolutes Grundwissen da! Ihr werdet mit etwas praktischer Erfahrung schnell die Grenzen finden, die die einzelnen Parametern mit sich bringen z.B. bei der Texturierung von Objekten!&lt;br /&gt;
&lt;br /&gt;
Ein Quadrat besteht aus einem Face (Fläche), ein Quadrat zusammengesetzt aus zwei Triangle aus zwei. Man könnte zum Beispiel letzteres mit zwei Texturen versehen, ersteres nur mit einer!&lt;br /&gt;
&lt;br /&gt;
== Das Culling-Verfahren ==&lt;br /&gt;
&lt;br /&gt;
=== Wahrheiten und Wirklichkeiten ===&lt;br /&gt;
&lt;br /&gt;
;Dramatik:&lt;br /&gt;
Bevor wir uns mit der eigentlichen Technik des Cullings befassen, möchte ich eine radikale Aufklärung betreiben, die Euer Weltbild für immer verändern wird. Seit gewarnt, dass Ihr nach dem lesen der folgenden Zeilen, Eure Quake3-Gegner mit ganz anderen Augen sehen werdet und eventuell nie wieder zu Eurer alten Denkweise zurückkehren werden könnt!&lt;br /&gt;
&lt;br /&gt;
Habt Ihr Euch bereits gefragt wie die kommerziellen Spiele es schaffen ohne Ruckler (*hust), super Grafiken auf den Bildschirm zu bringen, die auch nach der Optimierung immer noch durch grafische Qualität überzeugen können? Nun, es gibt viele Möglichkeiten seine Szenen zu optimieren, dass wohl einfachste und auch mit am effizientesten ist das so genannte Culling-Verfahren.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_trooper1.gif]]&lt;br /&gt;
&lt;br /&gt;
So sieht unser Protagonist wie gewohnt aus. Wir sehen ihn wie er liebt und lebt. (ja…) Um es philosophisch auszudrücken, sehen wir hier jedoch nur die halbe Wahrheit… ich stelle die wage Behauptung aus, dass wir der Rückseite von ihm gar nicht sehen können und daher auch nicht sagen können, ob sie existiert! Jedes Mal, wenn wir uns um ihn herum bewegen sehen wir nie seine Rückseite. Nun… das wäre auch nicht weiter fatal, wenn ich nicht sofort eine weitere These aufstellen würde: &amp;quot;Der Kerl hat gar keine Rückseite&amp;quot; :-O&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Aber! Wenn wir um ihn herum gehen, dann sehen wir doch seine Rückseite, also ist sie da!&amp;quot;, könnte man mir nun skeptisch zurufen. Tja… beweist mir das Gegenteil und schickt einen Wetteinsatz an mich, ich werde Euch beweisen, dass er immer nur eine Seite hat und seine Rückseite erst erzeugt wird, wenn sie auch benötigt wird. Es würde dann aussage gegen Aussage stehen und ihr könntet mir nicht beweisen, dass Ihr Recht habt, denn wenn wir um ihn herum gehen, würden wir ja wieder seine Rückseite nicht sehen. Ich hingegen würde den Beweis antreten und Euch mitteilen, dass wir ja einfach mal uns die Rückseite ansehen, dann allerdings auf die Forderseite verzichten müssen:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_trooper2.gif]]&lt;br /&gt;
&lt;br /&gt;
Was auf den ersten Blick wie ein verschwommendes Etwas aussieht entpuppt sich auf den zweiten Blick bereits als unser Soldat vom ersten Bild. Skeptisch wird man sich sicherlich fragen, was geschehen ist. Und wenn man genau durch ihn durch sieht, erkennen wir, dass wir hier z.B. seine Hose sehen… und seinen Kopf auch irgendwie von … hinten! Was ist geschehen? Wir betrachten Ihn von vorne und sehen, was hinten ist! (Oh Gott… eine ganze Wirklichkeit stürzt in sich ein *sg* ).&lt;br /&gt;
&lt;br /&gt;
Natürlich ist das verschwommende Ding da oben nicht ein grafisches Ziel, was wir erreichen wollen, sondern nur der Beweis dafür, dass jede Figur zwei Seiten hat (Erkenntnis, aufschreiben!) und wir in OpenGL bestimmen können, welche Seite wir von Vorne oder Hinten betrachten können.&lt;br /&gt;
&lt;br /&gt;
Ich gebe ja auch zu, dass ich ein wenig getrickst habe und im oberen Bild, die Figur zweimal genredert habe, davon einmal die Vorderseite mit Alpha Blending, weil auf einem unbewegten Bild es schwer zu erkennen wäre, welches die Vorder und welches die Rückseite ist, da wir die Texturen eben gespiegelt auf dem Modell sehen würden.&lt;br /&gt;
&lt;br /&gt;
=== Eine abstrakte Wahrheit… ===&lt;br /&gt;
&lt;br /&gt;
An sich klingt bisher doch auch noch alles recht logisch oder? Den wieso sollte OpenGL die Rückseite von Objekten rendern, wenn man sie gar nicht sehen kann. Grob würde dies eben die doppelte Arbeit sein, die sinnlos getätigt wird. Der Laie wird nun vor Freude an die Decke springen, der erfahrene Programmierer skeptisch die Falten runzeln. &amp;quot;Wie erkennt OpenGL, den das es sich um die Rückseite handelt!&amp;quot;. Gute Frage oder? Des Lösung-Rätsel sollte ein Blick auf die Uhr zeigen… (&amp;lt;== nein, er spinnt nicht (Anm. v. Flo2))&lt;br /&gt;
&lt;br /&gt;
Aber was meine ich damit? Was haben unsere Objekte mit einer Uhr gemeinsam? Was zunächst einem komisch vorkommt ist eigentlich logisch: Es ist die Laufrichtung! Unsere Uhr sollte im Normalfall im Uhrzeigersinn laufen (… scheiß Übergang!). Die Entgegengesetzte Richtung nennen wir dann entgegen des Uhrzeigersinnes. Das wird nun vielleicht den einen oder anderen dazu gebracht haben ein leises Aua von sich zu geben und sich mit der Hand an die Rübe zu schlagen. Diese Erkenntnis ist jedoch grundlegend ^__-&lt;br /&gt;
&lt;br /&gt;
Denn auch unsere Objekte haben eine gewissen Definitionsreihenfolge. Wir werden die Problematik im Weiteren an Hand eines Quadrates verfolgen ;) Folgender Code ist nicht gleich…&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBegin(GL_QUADS);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(0,0,0);&lt;br /&gt;
    glColor3f(0,1,0);&lt;br /&gt;
    glVertex3f(1,0,0);&lt;br /&gt;
    glColor3f(1,1,1);&lt;br /&gt;
    glVertex3f(1,1,0);&lt;br /&gt;
    glColor3f(0,0,1);&lt;br /&gt;
    glVertex3f(0,1,0);&lt;br /&gt;
    glEnd;&lt;br /&gt;
&lt;br /&gt;
  glBegin(GL_QUADS);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(0,0,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(1,1,0);&lt;br /&gt;
    glVertex3f(0,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(1,1,1);&lt;br /&gt;
    glVertex3f(1,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(0,0,1);&lt;br /&gt;
    glVertex3f(1,0,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wer das Culling nicht verstanden hat, wird sich fragen, wo der unterschied liegt, den beide Stücke beschreiben ein und das gleiche Dreieck. Der Unterschied wird erst deutlich, wenn wir uns die Definitionsreihenfolge vor Augen führen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_order.png]]&lt;br /&gt;
&lt;br /&gt;
Und genau hier ist auch für OpenGL die Definition von &amp;quot;Rückseite&amp;quot;. Normalerweise erwartet OpenGL nämlich, dass alle Punkte gegen den Uhrzeigersinn definiert werden, also so wie beim ersten Quadrat. OpenGL würde in seinem Normalzustand dies dann als die Vorderseite ansehen. Mit einer einzigen Zeile können wir dieses Verhalten jedoch auch verändern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glFrontFace(GL_CW);        //Clock-Wise&lt;br /&gt;
  glFrontFace(GL_CCW);       // Counter Clock-Wise&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es ist jedoch zu empfehlen diese Reihenfolge beizubehalten, da andere Programmierer, die Euren Source lesen, ebenfalls davon ausgehen werden, dass ihr die Punkte gegen den Uhrzeigersinn definieren werdet. Nur bei einigen Optimierungsverfahren macht ein umschalten wirklich Sinn.&lt;br /&gt;
&lt;br /&gt;
=== … und eine verlogene Wirklichkeit ===&lt;br /&gt;
&lt;br /&gt;
Vielleicht springt mal wieder jemand auf und wird mich beschuldigen totalen Mist erzählt zu haben… immerhin zeichnet er vielleicht bei Euch mit beiden Code-Schnipseln die Quadrate? Das liegt einfach daran, dass OpenGL das Culling standardgemäß deaktiviert hat und somit auch beide Seiten rendert. Wie man Culling aktivieren kann, ist schon fast ratbar:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glEnable(GL_CULL_FACE);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit glDisable dementsprechend können wir es wieder deaktivieren. Sollten wir beide Quadrate nebeneinander gerendert haben und eben an den Grundeinstellungen nichts verändert haben, sollte mit der Aktivierung dieser Seite nur noch das linke Dreieck angezeigt werden, weil OpenGL es als Vorderseite ansieht. Wollen wir, dass er nur die &amp;quot;Rückseiten&amp;quot; rendert, so lässt sich dies erfolgreich mit einem weiteren Funktionsauruf realisieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glCullface(GL_FRONT);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit glCullface können wir immer die Seite angeben, die OpenGL vernachlässigen soll. In diesem Fall wäre dies dann die Vorderseite, die nicht gezeichnet werden würde (oder eben das rechte Dreieck).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glCullface(GL_BACK);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Würde wieder den ursprünglichen Status herstellen. Zusammengefasst : glCullFace definiert, ob Vorder oder Rückseite weggelassen werden sollen, glFrontFace definiert, wie die Vorderseite definiert ist (im oder gegen Uhrzeigersinn!) und mit glEnable müssen wir das Culling zunächst aktivieren (und das sollten wir auch immer tun, wenn es möglich ist!).&lt;br /&gt;
&lt;br /&gt;
Ich hoffe, ich habe es halbwegsverständlich erklärt. Wenn jemand sich jetzt fragt, wie man nun die Rückseite sehen kann, wenn wir ein Quadrat haben, dass im Uhrzeigersinn definiert ist… Wenn wir von vorne drauf sehen, werden wir es nicht sehen, da die Punkte in der falschen Reihenfolge definiert sind. Bewegen wir uns durch das Dreieck hin durch und drehen uns um, so werden wir feststellen, dass die Punkte wieder in der &amp;quot;richtigen&amp;quot; Reihenfolge definiert sind und OpenGL sie rendern wird. Wir haben dies hier nur bei sehr simplen Gebilden betrachtet, aber auch bei komplexen ist das Prinzip gleich!&lt;br /&gt;
&lt;br /&gt;
Auch bei möchte ich noch einmal ein gutes Beispiel sehen, wo man bei komplexeren Objekten das Culling gut erkennen kann. Nämlich unserer Landschaft aus dem vierten Tutorial:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_culledland.jpg]]&lt;br /&gt;
&lt;br /&gt;
Dies sieht zunächst nach einem schweren Grafikfehler aus… ist es jedoch nicht. Wir betrachten unsere Landschaft hier nur von einer Seite, die der Spieler nicht sehen würde, nämlich von der Unterseite. Wir sehen die Landschaft von unten bei aktivieren Culling (Anm.: Beim vierten Tutorial ist KEIN Culling von mir aktiviert worden!) und da ich die Quadrate alle richtig definiert habe sehen wir auch nur noch die, die dem Spieler zu gewannt sind. Etwas Fantasie benötigt man schon dazu, um dies wieder zu erkennen, allerdings sieht man es oben rechts doch noch recht gut. Ich hoffe, dass spätestens dieses kleine Beispiel es noch verständlicher gemacht hat, wenn nicht… schaut Euch die Samples an und fragt dann im Forum nach ;)&lt;br /&gt;
&lt;br /&gt;
BTW: Für psychische Folgen, die beim Lesen dieses Tutorials entstanden sind, übernimmt der Autor keine Haftung. Auch er hat bisher keinen glaubwürdigen Beweis dafür gefunden, dass seine Mitmenschen eine Rückseite haben. Auch die Betrachtung dieser mit Hilfe eines Spiegels kann eine zusätzlich Render-Routine der Engine sein, in der wir leben. Es ist jedoch höchst wahrscheinlich, dass sie keine haben, weil … wer sollte die ganze Rechenleistung aufbringen, um die Vorder- und Rückseite aller Menschen zu rendern! :-D&lt;br /&gt;
&lt;br /&gt;
== Für das Protokoll ==&lt;br /&gt;
&lt;br /&gt;
=== Grundgedanken zu Display-Listen ===&lt;br /&gt;
&lt;br /&gt;
Bisher sind wir sicherlich noch nicht in die Verlegenheit gekommen, komplexere Programme zu schreiben. Hat es doch jemand bereits versucht, so wird er schnell gemerkt haben, dass man ohne eine solide Organisation keine Chance hat, ein größeres Projekt zu verwirklichen. Die goldene Regel für eine gelungene Organisation ist sehr einfach! Redundanzen vermeiden um jeden Preis. Man sollte nicht wenn man z.B. ein Quad zeichnen will, diesen 20.000 Mal hintereinander erzeugt. Vielleicht tut es ja auch eine Schleife? Genauso, wie man nicht mehrfach ein Modell in den Speicher laden sollte, wenn es bereits einmal geladen wurde.&lt;br /&gt;
&lt;br /&gt;
Das klingt nun sicherlich wie eine große Verarschung meinerseits. Aber wer jetzt hier geschmunzelt hat, sollte aufpassen, dass ihm nicht gleich das Grinsen im Gesichte stecken bleibt. Würdest Ihr Euer Programm richtig aufbauen? Versuchen eben doppelte Daten zu vermeiden, wo man es nur kann? Wer halbwegs geschickt vorgeht, wird mit Prozeduren, die er immer wieder aufrufen kann eine Menge sinnlosen Code vermeiden. Zum Beispiel wenn wir einen Würfel zeichnen wollen! Wir würden diesen in einer Funktion zeichnen lassen und dann einfach diese Prozedur aufrufen, wenn er gerendert werden soll.&lt;br /&gt;
&lt;br /&gt;
Wünschen wir ihn an einer anderen Stelle in der Welt, so rufen wir einfach die entsprechende Matrix vor dem rendern auf und schon haben wir zwei Würfel in einer Welt mit nur einer Prozedur erzeugt… und die Hälfte an Code gespart. Das lässt sich doch sehen! Auch können wir nun zahlreiche andere Dinge mir in diese Funktion setzen, z.B. eine andere Farbe. Das klingt doch sehr nach einem richtige Ansatz!&lt;br /&gt;
&lt;br /&gt;
=== Vorsicht! Aufnahme ===&lt;br /&gt;
&lt;br /&gt;
OpenGL bietet für ein solches vorgehen ein eigenes System und eine Anzahl von Funktionen, die man unter den Namen &amp;quot;Display-Listen&amp;quot; zusammenfassen kann. Dies kann man sich so vorstellen, dass man einen Namen für eine Liste vergibt (nicht irritieren lassen, ein Name ist in diesem Fall eine Nummer!) und sagt OpenGL, dass alles, was jetzt geschieht mitprotokolliert werden soll. Man kann nun anfangen Objekte zu zeichnen, Farben, Texturen, Materialen, einfach alles was einem in den Sinn kommt zu rendern und OpenGL wird dann all dies in einer Liste zusammen fassen. Es steht dann im fortan für uns auf Abruf zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technisch gesehen ließen sich wohl diese Listen auch mit Prozeduren realisieren, allerdings gibt es auch einen weiteren Vorteil (angeblich… ich konnte ihn nicht nachweisen, evtl. nur einen Sinn in seiner Frühzeit, wo es keine 3D-Karten gab oder nur bei extrem vielen Objekten spürbar…). OpenGL kann diese Befehle nämlich schneller durchführen, weil es die Operationen praktisch vormerkt. Wie auch immer man es sehen mag, Display-Listen sind unglaublich praktisch bei der Programmierung und meist weicht die anfängliche Skepsis durch Begeisterung (und wenn nicht, gehört es zum guten Ton es zu wissen, weil man es häufiger sehen wird)&lt;br /&gt;
&lt;br /&gt;
Zunächst holen wir uns von OpenGL eine Nummer ab, unter welcher wir die Display-Liste später erreichen werden. Diese wird im Idealfall von Typ Integer sein. Der Aufruf ist kinderleicht:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  Displaylist := glGenLists(1);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
glGenLists liefert und eben den gewünschten Wert zurück. Wir können auch stattdessen ein Array of GLUint nehmen und gleich mehre anfordern.&lt;br /&gt;
&lt;br /&gt;
Der Rest ist dann praktisch aufgebaut wie ein glBegin und glEnd, nur dass die entsprechenden Funktionen diesmal glNewList und glEndList heißen und wie folgt verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
glNewList(DisplayList,GL_COMPILE);&lt;br /&gt;
  […]&lt;br /&gt;
glEndList;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das GL_COMPILE bewirkt schlicht und ergreifend, dass sich OpenGL die verwendeten Funktionen merkt. Alternativ könnten wie auch noch GL_COMPILE_AND_EXECUTE einsetzen, wenn wir wollen, dass die Liste auch gleich ausgeführt wird. Praktisch alles was zwischen diesen beiden Funktionen steht wird auch mitgeschrieben. Ausnahmen gibt es wir immer und ich werde dies Mal aufführen, ich denke, dass man erahnen kann, warum diese nicht mit unterstützt werden. Es gibt praktisch keinen Sinn diese mit aufzuzeichnen:&lt;br /&gt;
&lt;br /&gt;
[[glColorPointer]], [[glDeleteLists]], [[glDisableClientState]], [[glEdgeFlagPointer]],&lt;br /&gt;
[[glEnableClientState]], [[glFeedbackBuffer]], [[glFinish]], [[glFlush]], [[glGenLists]],&lt;br /&gt;
[[glIndexPointer]],[[glInterleavedArrays]], [[glIsEnabled]], [[glIsList]], [[glNormalPointer]],&lt;br /&gt;
[[glPopClientAttrib]],[[glPixelStore]], [[glPushClientAttrib]], [[glReadPixels]],&lt;br /&gt;
[[glRenderMode]], [[glSelectBuffer]], [[glTexCoordPointer]], [[glVertexPointer]] und&lt;br /&gt;
alle  [[glGet]] Routinen&lt;br /&gt;
&lt;br /&gt;
=== Und Cut! ===&lt;br /&gt;
&lt;br /&gt;
Idealerweise erzeugen wir diese Listen nicht bei jedem Render-Vorgang, sondern nur einmal im Init und führen diese dann nur noch beim Rendern aus. Dies geschieht mit der Funktion glCallList und sollte genauso leicht zu beherrschen sein, wie die bisherigen auch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glCallList(displaylist);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir übergeben einfach den Namen der Liste und schon wird alles was wir drinnen aufgezeichnet haben auch ausgeführt… Perfekt, oder? Das ganze ist doch recht leicht hand zu haben und sollte niemanden vor einem großen Problem stellen.&lt;br /&gt;
&lt;br /&gt;
Im Sample haben wir ein kleines Astroiden-Feld nachgebildet, eben mit Hilfe dieser Display-Listen. Ich denke, dass dort anschaulich gezeigt wird, wie man sie sinnvoll einsetzen kann. Auch die Demo von Jan Horn &amp;quot;Biohazzard&amp;quot; zeigt eindrucksvoll, wie man es am Geschicktesten machen kann ;)&lt;br /&gt;
&lt;br /&gt;
*g* Ach ja… einen kleinen Nachteil haben die Display-Listen doch noch! (ich weiß, dass ist jetzt so, als würde ich euch ins ein tiefes Becken tauchen lassen und am Boden steht: &amp;quot;Du hast die Sauerstoff-Falsche vergessen…&amp;quot;) Sie verbrauchen relativ viel Speicher! Alle Vorgänge werden nämlich direkt im Arbeitsspeicher geschoben. Eine gesunde Mischung zwischen normalen Rendering und Display-Listen ist also anzuraten, denn wenn der Speicher voll ist, hilft keine Optimierung mehr, dass Programm vorm ruckeln zu schützen ;)&lt;br /&gt;
&lt;br /&gt;
Auch sollte man bedenken, dass ein Aufbau z.B. eines Quads sich nicht mehr sehr optimieren lässt. Richtig bringen werden Euch die Display-Listen nur dann etwas, wenn ihr mit komplexen Gebilden arbeitet, die sich immer wiederholen. Würdet ihr z.B. bei jedem Render-Durchgang ein Model verändern, also seine Form (nicht seine Farbe), so würde eine Display-Liste eher hinderlich als nützlich sein!&lt;br /&gt;
&lt;br /&gt;
== Nachwort ==&lt;br /&gt;
&lt;br /&gt;
Bitte nicht traurig sein, wenn hier bereits wieder Ende ist ;) Ich weiß, dass ich in diesem Tutorial mehr versprochen habe, als letztendlich drin ist, aber ich habe vermehrt zu hören bekommen, dass ich dazu neige, wahre Monster-Tutorials zu machen und wollte dem hier mal entgegen wirken, da ich noch rund das doppelte an Stoff gehabt hätte. Drum ist hier nun erstmal Schluss! Es ist nicht ganz das geworden, was zunächst geplant war, aber ich denke, dass das Ziel, nämlich das Grundwissen zu vertiefen erfüllt werden konnte!&lt;br /&gt;
&lt;br /&gt;
Das schreiben dieses Tutorials hat mir sehr viel Spaß gemacht, da ich mich teilweise auch selbst momentan damit beschäftige und es auch unglaublich interessant finde. In diesem Sinne möchte ich dann auch beim sechsten Tutorial weiter machen und mal eine kleine Exkursion unternehmen, nämlich Möglichkeiten aufzuzeigen, Daten aus einem Modellierer in unsere Software zu bekommen. Die Direct3D-Welt hat gesagt, dass sie bereit ist für einen Kampf ist - also sollen sie ihn auch bekommen ^__-&lt;br /&gt;
&lt;br /&gt;
Den mit OpenGL lässt sich weitaus mehr machen als man zunächst erahnt und da die meisten Samples immer nur Dreiecke zeigen wollen wir doch mal etwas Komplexeres machen! Ihr dürft also gespannt sein und wie immer gilt… Ideen und Vorschläge sind herzlich willkommen ;) Im Gegensatz zu D3D hat man bei OpenGL erkannt, dass eine Grafik-API keine Model- und Texture-Loader bieten sollte. Hier ist dann eine Hardcodierung notwendig, die wir allerdings nicht als Nachteil ansehen sollten, sondern vielmehr die Chancen nutzen, unsere Programme mit eigenen Routinen zu optimieren. ;)&lt;br /&gt;
&lt;br /&gt;
Have Fun!&lt;br /&gt;
&lt;br /&gt;
Euer [[Benutzer:Phobeus|Phobeus]]&lt;br /&gt;
&lt;br /&gt;
{{TUTORIAL_NAVIGATION | [[Tutorial Lektion 4]] | [[Tutorial Lektion 7]]}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Tutorial|Lektion5]]&lt;/div&gt;</summary>
		<author><name>Gucky</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_Lektion_5&amp;diff=19874</id>
		<title>Tutorial Lektion 5</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_Lektion_5&amp;diff=19874"/>
				<updated>2006-11-16T22:16:05Z</updated>
		
		<summary type="html">&lt;p&gt;Gucky: /* Eine abstrakte Wahrheit… */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Artenvielfalten und Ihre Folgen =&lt;br /&gt;
&lt;br /&gt;
== Vorwort ==&lt;br /&gt;
&lt;br /&gt;
Und wieder einmal aufraffen und etwas tippen. Es ist wirklich nicht immer leicht solche Tutorials zu schreiben, vor allem wenn man mal wieder eine Null-Bock-Phase hat. Ich hoffe doch sehr, dass auch Ihr dafür Verständnis habt, den… auch ich bin ein Mensch… egal wie viele Augen und Beine ich habe *sg* Gut… genug &amp;quot;gefaxt&amp;quot;, es geht wieder um den Ernst im Leben: Delphi&lt;br /&gt;
&lt;br /&gt;
Ich habe wirklich eine zeitlang überlegt, was wir machen sollten. Einige haben sich Lichter gewünscht, aber ich konnte mich nicht dazu aufraffen… Stattdessen habe ich irgendwie Lust gehabt, mal was anderes zu machen, etwas wo man etwas kreativ sein kann und was auch mir Spaß macht! :-D Und deswegen, habe ich mir gedacht, dass wir das eine oder andere Wissen, welches zwischendurch behandelt wurde noch etwas vertiefen sollten… einfach in dem wir andere Möglichkeiten aufzeigen, elegant ein Problem zu umschiffen und auch den einen oder anderen Effekt erzielen!&lt;br /&gt;
&lt;br /&gt;
Und genau darum geht es auch diesmal! Ich denke, dass sich dieses Tutorial weitestgehend nur an die richten wird, die bereits ein solides Wissen in OpenGL und Delphi haben, ansonsten wird es wohl schwer sein mir zu folgen, ich werde einiges an Wissen woraus setzen! Wer folgen kann, wird dann mit einem Wissen belohnt werden, dass ihm das eine oder andere Probleme sehr elegant umschiffen lässt in dem man einfach seine Software entsprechend mit OpenGL optimiert! Den nur wer sein Handwerk bis ins Detail beherrscht, darf sich Meister nennen ;)&lt;br /&gt;
&lt;br /&gt;
== Unbekannte Zeichen-Arten ==&lt;br /&gt;
&lt;br /&gt;
=== OpenGL-Maxime ===&lt;br /&gt;
&lt;br /&gt;
Wer nicht gerade erst jetzt hier eingestiegen ist, wird sicherlich bereits bemerkt haben, dass OpenGL streng genommen nach einem sehr einfachen Prinzip arbeitet. Ständig wird an unserer &amp;quot;Zustand-Maschine&amp;quot; etwas manipuliert und mit Matrizen setzen wir die Positionen fest. Jedoch nur an einer einzigen Stelle kommen alle diese Werte zusammen. Nämlich dann, wenn etwas gerendert wird. Genau in diesem Moment werden alle Werte &amp;quot;zusammengerechnet&amp;quot; und erzeugen etwas Sichtbares auf dem Bildschirm. In den meisten Fällen wird dies Eben zwischen glBegin und glEnd geschehen. Und genau diese beiden Funktionen wollen wir nun näher betrachten. Interessant hierbei ist nämlich der Parameter von glBegin…&lt;br /&gt;
&lt;br /&gt;
Streng genommen definieren wir nur eine Anzahl von Punkten zwischen glBegin und glEnd, der Parameter bei glBegin bestimmt aber letztendlich wie diese Verstanden werden sollen. Nur als kleine Übersicht alle verfügbaren Parameter:&lt;br /&gt;
&lt;br /&gt;
 GL_POINTS&lt;br /&gt;
 GL_LINES&lt;br /&gt;
 GL_LINE_STRIP&lt;br /&gt;
 GL_LINE_LOOP&lt;br /&gt;
 GL_TRIANGLES&lt;br /&gt;
 GL_TRIANGLE_STRIP&lt;br /&gt;
 GL_TRIANGLE_FAN&lt;br /&gt;
 GL_QUADS&lt;br /&gt;
 GL_QUAD_STRIP&lt;br /&gt;
 GL_POLYGON&lt;br /&gt;
&lt;br /&gt;
Ich denke aus ethischen Gründen werde ich nun darauf verzichten Euch erneut zu erklären wofür, die bisher für Euch bekannten Parameter (gl_Triangle und gl_Quad) gut sind… jeder wird sich denken können, dass das erste immer per 3er Punkte ein Dreieck bildet, dass zweiter aus 4 Punkten ein Quadrat erzeugt… (mist… *g*) ;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Zunächst war der Strich ===&lt;br /&gt;
&lt;br /&gt;
Interessanter, da neu für uns, sind die restlichen Parameter, auch wenn diese sich weitestgehend selbst erklären. Gl_Points z.B. … könnte es vielleicht bedeuten, dass OpenGL alle mit [[glVertex3f]] definierten Punkte auch nur als Punkte zeichnet?&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gl_point.gif]]&lt;br /&gt;
&lt;br /&gt;
Scheint zu stimmen… ;) Es ist nun auch nicht besonders schwer herzuleiten, was OpenGL mit Vertex-Definitionen macht, die mit gl_Lines beginnen. Versuchen wir doch mal ein Dreieck damit zu zeichnen! Ergo brauchen wir 4 Punkte (der letzte muss, auf den ersten verweisen)…&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gl_lines.gif]]&lt;br /&gt;
&lt;br /&gt;
Doch was ist das? Dies ist kein böswilliger Trick, den ich auf Euch spielen will, sondern folgender Code, ist dafür verantwortlich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBegin(GL_LINES);&lt;br /&gt;
    glVertex3f(-1,0,0);&lt;br /&gt;
    glVertex3f(0,1,0);&lt;br /&gt;
    glVertex3f(1,0,0);&lt;br /&gt;
    glVertex3f(-1,0,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sieht soweit alles in Ordnung aus - ist es auch! Der Fehler liegt nämlich nicht am Code, sondern an uns selbst. Gl_Lines bewirkt nämlich nicht, dass alle Punkte miteinander verbunden werden, sondern nur, dass jeweils Punkte 1 mit Punkte2, Punkte 3 mit Punkt 4 etc. verbunden werden, d.h. immer zweiter Pärchen. Um das Ergebnis zu erreichen, welches wir angestrebt haben (und zwar ohne 6 Punkte zu definieren), wäre in diesem Fall nämlich gl_Line_Strip gewesen, was nämlich bewirkt, dass der &amp;quot;Zeichenstift&amp;quot; immer von seiner letzten Position zum nächst definierten Punkte einen Strich zieht. Dementsprechend sieht dann unser Dreieck mit praktisch dem gleichen Source wie folgt aus:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gl_line_strip.gif]]&lt;br /&gt;
&lt;br /&gt;
hr merkt bereits jetzt wie viele Möglichkeiten einen OpenGL bietet mit nur wenigen Zeilen eine Menge zu verändern. Man bedenkt, dass wir hier nur sehr wenige Punkte haben, allerdings kann man eine Menge Leistung rausschlagen, wenn man statt gl_Lines, gl_Line_Strip verwendet, da einfach weniger Punkte abgearbeitet werden müssen. Logisch oder?&lt;br /&gt;
&lt;br /&gt;
 GL_LINE_LOOP&lt;br /&gt;
&lt;br /&gt;
Der Parameter entspricht praktisch gesehen GL_LINE_STRIP, nur das der letzte Punkt mit dem ersten verbunden wird. Man bräuchte mit diesem Parameter also nur 3 Punkte… das Optimum für unser Dreieck! ;)&lt;br /&gt;
&lt;br /&gt;
== Artenvielfalt ==&lt;br /&gt;
&lt;br /&gt;
Natürlich lassen sich solche Zeichenoperationen auch durch eine Menge anderer Faktoren beeinflussen. Vielleicht hat sich der eine oder andere ja bereits gefragt, wie man es schafft, dass der Rahmen des Dreiecks dicker gezeichnet wird. Sicherlich könnte man nun beginnen und ganz leicht versetzt daneben noch ein Dreieck zu zeichnen. Dies würde dann natürlich einen kleinen Tick größer wirken.&lt;br /&gt;
&lt;br /&gt;
Die Lösung liegt allerdings viel näher - den OpenGL bietet hierfür eine hauseigene Lösung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glLineWidth(3);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir stellen einfach ein, wie OpenGL die Linien Rastern soll. Wir nehmen in diesem Fall die dreifache Dicke von der normalen Einstellung:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gllinewidth.gif]]&lt;br /&gt;
&lt;br /&gt;
Auch werden einige von Euch sicherlich die unschönen Treppchen kennen, die vor allem bei solch einfache Konstruktionen wie diesm dickeren Dreieck auftreten können. Die Lösung dagegen heißt bekanntlich &amp;quot;AntiAliasing&amp;quot; … verschlägt es Euch die Sprache, dass ich gleich mit solch derben Geschützen auffahre?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glEnable(GL_LINE_SMOOTH);&lt;br /&gt;
  glDisable(GL_LINE_SMOOTH);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da verschlägt es einem die Sprache oder? Das war es nämlich auch bereits wieder. Wir schalten einfach einen Zustand um und fertig war die Geschichte! Der Zustands-Maschine von OpenGL sei dank, müssen wir nur noch sagen, was gemacht werden soll, ein lästiges &amp;quot;Wie?&amp;quot; entfällt komplett&lt;br /&gt;
&lt;br /&gt;
Und noch ein kleines Beispiel dafür, wie einfach OpenGL nicht nur Informationen schreiben lässt, sondern diese auch wieder preisgibt. Haben wir vergessen wie Dick wir die Linien eingestellt haben? Kein Problem, den OpenGL bietet folgende Funktionen um zu ermitteln, auf welchen Wert ein &amp;quot;Zustand&amp;quot; geschaltet ist:&lt;br /&gt;
&lt;br /&gt;
[[glGetBooleanv]], [[glGetDoublev]], [[glGetFloatv]], [[glGetIntegerv]]&lt;br /&gt;
&lt;br /&gt;
Ich glaube ich brauche nicht wieder damit zu beginnen darauf hinzuweisen, dass man bei jeder Abfrage auch den richtigen Variablen-Typen verwenden sollte *g*&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glGetIntegerv(gl_line_width,@myint);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und schon haben wir in myint die Stärke mit der OpenGL momentan alle Linien zeichnen soll. Das geht mit fast allen Zuständen, die OpenGL haben kann. Ich denke, das Grundprinzip ist recht leicht verständlich. Beachtet auch, dass OpenGL bei solchen Funktionen immer einen Pointer auf eine Variable erwartet (sprich besser: eine Adresse) und nicht die Variable selbst!&lt;br /&gt;
&lt;br /&gt;
Achso… vergaß ich zu erwähnen, dass wir auch weitere Funktionen auf Striche anwenden können? Zum Beispiel glColor, um den Strich einzufärben? Auch der Z-Buffer lasst sich darauf anwenden, eben alles, was man auch bei einem Dreieck tun könnte (wovon man aber zwingend absehen sollte eine Linie zu texturieren, um keine unnötigen Berechnungen durchzuführen)&lt;br /&gt;
&lt;br /&gt;
=== Ein Dreieck, hat drei Ecken… ===&lt;br /&gt;
&lt;br /&gt;
Nun dreht sich erstmal alles um Dreiecke. Den auch hierfür bietet OpenGL mehre Möglichkeiten, wie die Reihenfolge der Punkte verstanden wird:&lt;br /&gt;
&lt;br /&gt;
 GL_TRIANGLES&lt;br /&gt;
 GL_TRIANGLE_STRIP&lt;br /&gt;
 GL_TRIANGLE_FAN&lt;br /&gt;
&lt;br /&gt;
Zunächst widmen wir uns GL_TRIANGLES! Dieser Parameter sollte uns allen noch geläufig sein. Jeweils 3 Punkte werden zusammen zu einem eigenständigen Dreieck verbunden. Nicht neues für uns, bereits im ersten Tutorial könnt ihr den Beweis finden, dass es klappt :-D&lt;br /&gt;
&lt;br /&gt;
Sicherlich werdet ihr Euch denken können, dass dies sehr hilfreich ist, wenn man nur ein Dreieck zeichnen möchte, nicht jedoch, sobald man mehre in einem Rutsch auf dem Bildschirm bringen möchte. Hierzu bietet sich dann eher GL_TRIANGLE_STRIP an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBegin(GL_TRIANGLE_STRIP);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(-1,0,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(1,1,0);&lt;br /&gt;
    glVertex3f(0,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(1,1,1);&lt;br /&gt;
    glVertex3f(1,0,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(0,1,1);&lt;br /&gt;
    glVertex3f(2,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(0,1,0);&lt;br /&gt;
    glVertex3f(3,0,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wem das ganze zu schwer erscheint, sollte er sich die glColor3f weglassen, die habe ich reingesetzt, damit das ganze auch schön aussieht ;) (Programmierer lieben sinnlosen Spielkram der glänzt und bunt ist). Wer sich nur auf die Punkte konzentriert und sich die Position im Kopfe vorstellt, wird ein Gebilde wie folgt vorstellen können:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gl_triangle_strip.gif]]&lt;br /&gt;
&lt;br /&gt;
Streng genommen macht OpenGL nichts anderes, als die ersten drei Eckpunkte zu nehmen und ein Dreieck daraus zu rendern. Anschließend fällt der erste Punkt weg und es wird das Dreieck zwischen 2,3,4 gerendert usw. Im Anschluss dieses Kapitels werden wir nochmals hieraus zurückkommen und das Culling erklären!&lt;br /&gt;
&lt;br /&gt;
=== Ventilatoren und OpenGL ===&lt;br /&gt;
&lt;br /&gt;
Tja, und nur die wenigstens wissen, dass auch ein Ventilator ein Dreieck sein kann *hust* Okay, begnügen wir uns hier lieber mit dem englischen Begriff &amp;quot;Fan&amp;quot;. (Das ist nicht der Kerl, der vorm Fenster steht, einem gierig anstarrt und laut ruft &amp;quot;Ich will ein Kind von Dir…&amp;quot; … nein, sicher nicht ;)&lt;br /&gt;
&lt;br /&gt;
Vielmehr sollten wir uns wirklich mal bildlich einen Fahrrad-Reifen vorstellen. Von den Außenseiten verlaufen die einzelnen Speichen alle zu einem Mittelpunkt. Nach einem ähnlichen Render-Prinzip funktioniert auch GL_TRIANGLE_FAN. Der einfachheitshalber werden wir hier jedoch kein komplexes Objekt anfertigen, sondern nur eine Möglichkeit zeigen, wie man mit dieser Einstellung sinnvoll ein Objekt zeichnen kann! In unserem Fall nehmen wir einfach einen Drachen (nein… nicht das Fabelwesen), denn auch dieser ist ein Ventilator (Chaos… perfekt….) ;)&lt;br /&gt;
&lt;br /&gt;
Bevor nun jeder abdreht, schauen wir uns doch mal das Objekt an, von dem ich sprach:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gl_triangle_fan.gif]]&lt;br /&gt;
&lt;br /&gt;
In diesem Fall benötigen wir nur 6 (!) Punkte, um dieses Gebilde zu erzeugen. Zentraler Ausgangspunkt ist hierbei die Nummer 1. Wie man erkennen kann besteht das erste Dreieck aus 1,2,3. Das zweite aus 1,3,4… für die abstrakt denkenden Menschen lässt sich daraus folgern, dass OpenGL die Punkte wie folgt abarbeitet 1, n+1, n+2. Oder um es neudeutsch zu sagen, der erste Punkte wird immer mit zwei weiteren verbunden. Im Gegensatz zum Strip fällt allerdings nicht der erste Punkt raus, sondern immer der zweite.&lt;br /&gt;
&lt;br /&gt;
Gl_Triangle_Fans und Gl_Triangle_Strips sind wohl die besten Möglichkeiten, um die Anzahl der im Video-Speicher befindlichen Daten zu begrenzen. Allerdings lässt sich schnell erahnen, dass nicht jeder dieser Methoden wirklich komplexe Objekte zulässt. Man sollte sie jedoch nicht total ignorieren, sondern mit Verstand einsetzen. Ach so, bevor ich es vergesse, der Code dafür:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBegin(GL_TRIANGLE_FAN);&lt;br /&gt;
    glColor3f(1,1,0);&lt;br /&gt;
    glVertex3f(0,0,0);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(0,1,0);&lt;br /&gt;
    glVertex3f(1,0,0);&lt;br /&gt;
    glColor3f(0,0,1);&lt;br /&gt;
    glVertex3f(0,-3,0);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(-1,0,0);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(0,1,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass Punkte 2 und Punkte 6 identisch sind, da er sonst das letzte Dreieck weglassen würde ;)&lt;br /&gt;
&lt;br /&gt;
=== Quadratisch, praktisch, gut! ===&lt;br /&gt;
&lt;br /&gt;
Das ist ne wahrer Marathon geworden. Schlimm, wenn man bedenkt, dass wir uns hier nur mit einem Parameter für eine Funktion beschäftigen. Ihr versteht, warum ich so oft geschrieben haben &amp;quot;lassen wir es lieber und klären nicht jeden Parameter&amp;quot; ;) Aber gut, ich halte es für sehr wichtig, solche Dinge zu beherrschen, weil es einfach zu den Grundlagen dazugehört! Immerhin bleiben nicht mehr sonderlich viele übrig, als ran an die letzten drei!&lt;br /&gt;
&lt;br /&gt;
 GL_QUADS&lt;br /&gt;
&lt;br /&gt;
Sollte auch niemanden von uns mehr sonderlich schockieren können. Jeweils vier übergebende Punkte werden zusammengefasst zu einem Quadrat… nichts weiter Aufregendes. Eigentlich sollten inzwischen auch die GL_QUAD_STRIP nichts wirklich erstaunliches mehr liefern. Wir übergeben zunächst vier Punkte und bei jedem Durchgang entfallen die beiden ersten und werden durch die nächsten zwei ersetzt. OpenGL erstellt dann daraus jedes Mal ein Viereck. Bitte nicht verwechseln mit der Punkt-Reihenfolge von GL_QUADS. Folgendes Beispiel sollte die Problematik verdeutlichen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
glBegin(GL_QUAD_STRIP);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(0,0,0);&lt;br /&gt;
    glVertex3f(0,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(0,0,1);&lt;br /&gt;
    glVertex3f(1,0,0);&lt;br /&gt;
    glVertex3f(1,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(0,1,0);&lt;br /&gt;
    glVertex3f(2,0,0);&lt;br /&gt;
    glVertex3f(2,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(0,1,1);&lt;br /&gt;
    glVertex3f(3,0,0);&lt;br /&gt;
    glVertex3f(3,1,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gl_quad_strip.gif]]&lt;br /&gt;
&lt;br /&gt;
Unser erstes Quadrat besteht aus den Punkten 1,2,3,4, dass zweite aus 3,4,5,6 etc. Ich denke, dass das Prinzip dahinter leicht verständlich ist. Anbieten tut sich diese Lösung meist in Schleifen, wenn man längere solcher Quad-Strukturen braucht, die aneinander gereiht sind&lt;br /&gt;
&lt;br /&gt;
=== Vieleckerei ===&lt;br /&gt;
&lt;br /&gt;
Und zu guter letzt GL_POLYGON, was wohl am einfachsten nachzuvollziehen ist ;) Es wird einfach eine Liste von Punkten übergeben und daraus wird schlicht und ergreifend dann ein Vieleck gemacht. Wie variantenreich dies werden kann, könnt ihr Euch vorstellen. Vor allem komplexere und exotischere Formen lassen sich damit darstellen, auch wenn es in diesem Fall eher Leitung kostet als wirklich einbringt. Zeichnet also niemals ein Quadrat mit GL_POLYGON.&lt;br /&gt;
&lt;br /&gt;
Eine mögliche Form wäre zum Beispiel diese:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gl_polygon.png]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBegin(GL_POLYGON);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(0,0,0);&lt;br /&gt;
    glColor3f(0,1,0);&lt;br /&gt;
    glVertex3f(3,0,0);&lt;br /&gt;
    glColor3f(0,0,1);&lt;br /&gt;
    glVertex3f(4,1,0);&lt;br /&gt;
    glColor3f(1,1,0);&lt;br /&gt;
    glVertex3f(2,2,0);&lt;br /&gt;
    glColor3f(0,1,1);&lt;br /&gt;
    glVertex3f(-2,3,0);&lt;br /&gt;
    glColor3f(1,1,1);&lt;br /&gt;
    glVertex3f(-1,1,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ich denke zusammen mit dem Code sollte es keine weiteren Fragen mehr dazu geben. Am besten setzt Ihr Euch nun alle einmal hin und wendet das Wissen Testweise an, denn es muss sitzen und stellt vor allem, wenn es um optimierte Programmierung geht ein absolutes Grundwissen da! Ihr werdet mit etwas praktischer Erfahrung schnell die Grenzen finden, die die einzelnen Parametern mit sich bringen z.B. bei der Texturierung von Objekten!&lt;br /&gt;
&lt;br /&gt;
Ein Quadrat besteht aus einem Face (Fläche), ein Quadrat zusammengesetzt aus zwei Triangle aus zwei. Man könnte zum Beispiel letzteres mit zwei Texturen versehen, ersteres nur mit einer!&lt;br /&gt;
&lt;br /&gt;
== Das Culling-Verfahren ==&lt;br /&gt;
&lt;br /&gt;
=== Wahrheiten und Wirklichkeiten ===&lt;br /&gt;
&lt;br /&gt;
;Dramatik:&lt;br /&gt;
Bevor wir uns mit der eigentlichen Technik des Cullings befassen, möchte ich eine radikale Aufklärung betreiben, die Euer Weltbild für immer verändern wird. Seit gewarnt, dass Ihr nach dem lesen der folgenden Zeilen, Eure Quake3-Gegner mit ganz anderen Augen sehen werdet und eventuell nie wieder zu Eurer alten Denkweise zurückkehren werden könnt!&lt;br /&gt;
&lt;br /&gt;
Habt Ihr Euch bereits gefragt wie die kommerziellen Spiele es schaffen ohne Ruckler (*hust), super Grafiken auf den Bildschirm zu bringen, die auch nach der Optimierung immer noch durch grafische Qualität überzeugen können? Nun, es gibt viele Möglichkeiten seine Szenen zu optimieren, dass wohl einfachste und auch mit am effizientesten ist das so genannte Culling-Verfahren.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_trooper1.gif]]&lt;br /&gt;
&lt;br /&gt;
So sieht unser Protagonist wie gewohnt aus. Wir sehen ihn wie er liebt und lebt. (ja…) Um es philosophisch auszudrücken, sehen wir hier jedoch nur die halbe Wahrheit… ich stelle die wage Behauptung aus, dass wir der Rückseite von ihm gar nicht sehen können und daher auch nicht sagen können, ob sie existiert! Jedes Mal, wenn wir uns um ihn herum bewegen sehen wir nie seine Rückseite. Nun… das wäre auch nicht weiter fatal, wenn ich nicht sofort eine weitere These aufstellen würde: &amp;quot;Der Kerl hat gar keine Rückseite&amp;quot; :-O&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Aber! Wenn wir um ihn herum gehen, dann sehen wir doch seine Rückseite, also ist sie da!&amp;quot;, könnte man mir nun skeptisch zurufen. Tja… beweist mir das Gegenteil und schickt einen Wetteinsatz an mich, ich werde Euch beweisen, dass er immer nur eine Seite hat und seine Rückseite erst erzeugt wird, wenn sie auch benötigt wird. Es würde dann aussage gegen Aussage stehen und ihr könntet mir nicht beweisen, dass Ihr Recht habt, denn wenn wir um ihn herum gehen, würden wir ja wieder seine Rückseite nicht sehen. Ich hingegen würde den Beweis antreten und Euch mitteilen, dass wir ja einfach mal uns die Rückseite ansehen, dann allerdings auf die Forderseite verzichten müssen:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_trooper2.gif]]&lt;br /&gt;
&lt;br /&gt;
Was auf den ersten Blick wie ein verschwommendes Etwas aussieht entpuppt sich auf den zweiten Blick bereits als unser Soldat vom ersten Bild. Skeptisch wird man sich sicherlich fragen, was geschehen ist. Und wenn man genau durch ihn durch sieht, erkennen wir, dass wir hier z.B. seine Hose sehen… und seinen Kopf auch irgendwie von … hinten! Was ist geschehen? Wir betrachten Ihn von vorne und sehen, was hinten ist! (Oh Gott… eine ganze Wirklichkeit stürzt in sich ein *sg* ).&lt;br /&gt;
&lt;br /&gt;
Natürlich ist das verschwommende Ding da oben nicht ein grafisches Ziel, was wir erreichen wollen, sondern nur der Beweis dafür, dass jede Figur zwei Seiten hat (Erkenntnis, aufschreiben!) und wir in OpenGL bestimmen können, welche Seite wir von Vorne oder Hinten betrachten können.&lt;br /&gt;
&lt;br /&gt;
Ich gebe ja auch zu, dass ich ein wenig getrickst habe und im oberen Bild, die Figur zweimal genredert habe, davon einmal die Vorderseite mit Alpha Blending, weil auf einem unbewegten Bild es schwer zu erkennen wäre, welches die Vorder und welches die Rückseite ist, da wir die Texturen eben gespiegelt auf dem Modell sehen würden.&lt;br /&gt;
&lt;br /&gt;
=== Eine abstrakte Wahrheit… ===&lt;br /&gt;
&lt;br /&gt;
An sich klingt bisher doch auch noch alles recht logisch oder? Den wieso sollte OpenGL die Rückseite von Objekten rendern, wenn man sie gar nicht sehen kann. Grob würde dies eben die doppelte Arbeit sein, die sinnlos getätigt wird. Der Laie wird nun vor Freude an die Decke springen, der erfahrene Programmierer skeptisch die Falten runzeln. &amp;quot;Wie erkennt OpenGL, den das es sich um die Rückseite handelt!&amp;quot;. Gute Frage oder? Des Lösung-Rätsel sollte ein Blick auf die Uhr zeigen… (&amp;lt;== nein, er spinnt nicht (Anm. v. Flo2))&lt;br /&gt;
&lt;br /&gt;
Aber was meine ich damit? Was haben unsere Objekte mit einer Uhr gemeinsam? Was zunächst einem komisch vorkommt ist eigentlich logisch: Es ist die Laufrichtung! Unsere Uhr sollte im Normalfall im Uhrzeigersinn laufen (… scheiß Übergang!). Die Entgegengesetzte Richtung nennen wir dann entgegen des Uhrzeigersinnes. Das wird nun vielleicht den einen oder anderen dazu gebracht haben ein leises Aua von sich zu geben und sich mit der Hand an die Rübe zu schlagen. Diese Erkenntnis ist jedoch grundlegend ^__-&lt;br /&gt;
&lt;br /&gt;
Denn auch unsere Objekte haben eine gewissen Definitionsreihenfolge. Wir werden die Problematik im Weiteren an Hand eines Quadrates verfolgen ;) Folgender Code ist nicht gleich…&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBegin(GL_QUADS);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(0,0,0);&lt;br /&gt;
    glColor3f(0,1,0);&lt;br /&gt;
    glVertex3f(1,0,0);&lt;br /&gt;
    glColor3f(1,1,1);&lt;br /&gt;
    glVertex3f(1,1,0);&lt;br /&gt;
    glColor3f(0,0,1);&lt;br /&gt;
    glVertex3f(0,1,0);&lt;br /&gt;
    glEnd;&lt;br /&gt;
&lt;br /&gt;
  glBegin(GL_QUADS);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(0,0,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(1,1,0);&lt;br /&gt;
    glVertex3f(0,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(1,1,1);&lt;br /&gt;
    glVertex3f(1,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(0,0,1);&lt;br /&gt;
    glVertex3f(1,0,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wer das Culling nicht verstanden hat, wird sich fragen, wo der unterschied liegt, den beide Stücke beschreiben ein und das gleiche Dreieck. Der Unterschied wird erst deutlich, wenn wir uns die Definitionsreihenfolge vor Augen führen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_order.png]]&lt;br /&gt;
&lt;br /&gt;
Und genau hier ist auch für OpenGL die Definition von &amp;quot;Rückseite&amp;quot;. Normalerweise erwartet OpenGL nämlich, dass alle Punkte gegen den Uhrzeigersinn definiert werden, also so wie beim ersten Quadrat. OpenGL würde in seinem Normalzustand dies dann als die Vorderseite ansehen. Mit einer einzigen Zeile können wir dieses Verhalten jedoch auch verändern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glFrontFace(GL_CW);        //Clock-Wise&lt;br /&gt;
  glFrontFace(GL_CCW);       // Counter Clock-Wise&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es ist jedoch zu empfehlen diese Reihenfolge beizubehalten, da andere Programmierer, die Euren Source lesen, ebenfalls davon ausgehen werden, dass ihr die Punkte gegen den Uhrzeigersinn definieren werdet. Nur bei einigen Optimierungsverfahren macht ein umschalten wirklich Sinn.&lt;br /&gt;
&lt;br /&gt;
=== … und eine verlogene Wirklichkeit ===&lt;br /&gt;
&lt;br /&gt;
Vielleicht springt mal wieder jemand auf und wird mich beschuldigen totalen Mist erzählt zu haben… immerhin zeichnet er vielleicht bei Euch mit beiden Code-Schnipseln die Quadrate? Das liegt einfach daran, dass OpenGL das Culling standardgemäß deaktiviert hat und somit auch beide Seiten rendert. Wie man Culling aktivieren kann, ist schon fast ratbar:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glEnable(GL_CULL_FACE);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit glDisable dementsprechend können wir es wieder deaktivieren. Sollten wir beide Quadrate nebeneinander gerendert haben und eben an den Grundeinstellungen nichts verändert haben, sollte mit der Aktivierung dieser Seite nur noch das linke Dreieck angezeigt werden, weil OpenGL es als Vorderseite ansieht. Wollen wir, dass er nur die &amp;quot;Rückseiten&amp;quot; rendert, so lässt sich dies erfolgreich mit einem weiteren Funktionsauruf realisieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glCullface(GL_FRONT);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit glCullface können wir immer die Seite angeben, die OpenGL vernachlässigen soll. In diesem Fall wäre dies dann die Vorderseite, die nicht gezeichnet werden würde (oder eben das rechte Dreieck).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glCullface(GL_BACK);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Würde wieder den ursprünglichen Status herstellen. Zusammengefasst : glCullFace definiert, ob Vorder oder Rückseite weggelassen werden sollen, glFrontFace definiert, wie die Vorderseite definiert ist (im oder gegen Uhrzeigersinn!) und mit glEnable müssen wir das Culling zunächst aktivieren (und das sollten wir auch immer tun, wenn es möglich ist!).&lt;br /&gt;
&lt;br /&gt;
Ich hoffe, ich habe es halbwegsverständlich erklärt. Wenn jemand sich jetzt fragt, wie man nun die Rückseite sehen kann, wenn wir ein Quadrat haben, dass im Uhrzeigersinn definiert ist… Wenn wir von vorne drauf sehen, werden wir es nicht sehen, da die Punkte in der falschen Reihenfolge definiert sind. Bewegen wir uns durch das Dreieck hin durch und drehen uns um, so werden wir feststellen, dass die Punkte wieder in der &amp;quot;richtigen&amp;quot; Reihenfolge definiert sind und OpenGL sie rendern wird. Wir haben dies hier nur bei sehr simplen Gebilden betrachtet, aber auch bei komplexen ist das Prinzip gleich!&lt;br /&gt;
&lt;br /&gt;
Auch bei möchte ich noch einmal ein gutes Beispiel sehen, wo man bei komplexeren Objekten das Culling gut erkennen kann. Nämlich unserer Landschaft aus dem vierten Tutorial:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_culledland.jpg]]&lt;br /&gt;
&lt;br /&gt;
Dies sieht zunächst nach einem schweren Grafikfehler aus… ist es jedoch nicht. Wir betrachten unsere Landschaft hier nur von einer Seite, die der Spieler nicht sehen würde, nämlich von der Unterseite. Wir sehen die Landschaft von unten bei aktivieren Culling (Anm.: Beim vierten Tutorial ist KEIN Culling von mir aktiviert worden!) und da ich die Quadrate alle richtig definiert habe sehen wir auch nur noch die, die dem Spieler zu gewannt sind. Etwas Fantasie benötigt man schon dazu, um dies wieder zu erkennen, allerdings sieht man es oben rechts doch noch recht gut. Ich hoffe, dass spätestens dieses kleine Beispiel es noch verständlicher gemacht hat, wenn nicht… schaut Euch die Samples an und fragt dann im Forum nach ;)&lt;br /&gt;
&lt;br /&gt;
BTW: Für psychische Folgen, die beim Lesen dieses Tutorials entstanden sind, übernimmt der Autor keine Haftung. Auch er hat bisher keinen glaubwürdigen Beweis dafür gefunden, dass seine Mitmenschen eine Rückseite haben. Auch die Betrachtung dieser mit Hilfe eines Spiegels kann eine zusätzlich Render-Routine der Engine sein, in der wir leben. Es ist jedoch höchst wahrscheinlich, dass sie keine haben, weil … wer sollte die ganze Rechenleistung aufbringen, um die Vorder- und Rückseite aller Menschen zu rendern! :-D&lt;br /&gt;
&lt;br /&gt;
== Für das Protokoll ==&lt;br /&gt;
&lt;br /&gt;
=== Grundgedanken zu Display-Listen ===&lt;br /&gt;
&lt;br /&gt;
Bisher sind wir sicherlich noch nicht in die Verlegenheit gekommen, komplexere Programme zu schreiben. Hat es doch jemand bereits versucht, so wird er schnell gemerkt haben, dass man ohne eine solide Organisation keine Chance hat, ein größeres Projekt zu verwirklichen. Die goldene Regel für eine gelungene Organisation ist sehr einfach! Redundanzen vermeiden um jeden Preis. Man sollte nicht wenn man z.B. ein Quad zeichnen will, diesen 20.000 Mal hintereinander erzeugt. Vielleicht tut es ja auch eine Schleife? Genauso, wie man nicht mehrfach ein Modell in den Speicher laden sollte, wenn es bereits einmal geladen wurde.&lt;br /&gt;
&lt;br /&gt;
Das klingt nun sicherlich wie eine große Verarschung meinerseits. Aber wer jetzt hier geschmunzelt hat, sollte aufpassen, dass ihm nicht gleich das Grinsen im Gesichte stecken bleibt. Würdest Ihr Euer Programm richtig aufbauen? Versuchen eben doppelte Daten zu vermeiden, wo man es nur kann? Wer halbwegs geschickt vorgeht, wird mit Prozeduren, die er immer wieder aufrufen kann eine Menge sinnlosen Code vermeiden. Zum Beispiel wenn wir einen Würfel zeichnen wollen! Wir würden diesen in einer Funktion zeichnen lassen und dann einfach diese Prozedur aufrufen, wenn er gerendert werden soll.&lt;br /&gt;
&lt;br /&gt;
Wünschen wir ihn an einer anderen Stelle in der Welt, so rufen wir einfach die entsprechende Matrix vor dem rendern auf und schon haben wir zwei Würfel in einer Welt mit nur einer Prozedur erzeugt… und die Hälfte an Code gespart. Das lässt sich doch sehen! Auch können wir nun zahlreiche andere Dinge mir in diese Funktion setzen, z.B. eine andere Farbe. Das klingt doch sehr nach einem richtige Ansatz!&lt;br /&gt;
&lt;br /&gt;
=== Vorsicht! Aufnahme ===&lt;br /&gt;
&lt;br /&gt;
OpenGL bietet für ein solches vorgehen ein eigenes System und eine Anzahl von Funktionen, die man unter den Namen &amp;quot;Display-Listen&amp;quot; zusammenfassen kann. Dies kann man sich so vorstellen, dass man einen Namen für eine Liste vergibt (nicht irritieren lassen, ein Name ist in diesem Fall eine Nummer!) und sagt OpenGL, dass alles, was jetzt geschieht mitprotokolliert werden soll. Man kann nun anfangen Objekte zu zeichnen, Farben, Texturen, Materialen, einfach alles was einem in den Sinn kommt zu rendern und OpenGL wird dann all dies in einer Liste zusammen fassen. Es steht dann im fortan für uns auf Abruf zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technisch gesehen ließen sich wohl diese Listen auch mit Prozeduren realisieren, allerdings gibt es auch einen weiteren Vorteil (angeblich… ich konnte ihn nicht nachweisen, evtl. nur einen Sinn in seiner Frühzeit, wo es keine 3D-Karten gab oder nur bei extrem vielen Objekten spürbar…). OpenGL kann diese Befehle nämlich schneller durchführen, weil es die Operationen praktisch vormerkt. Wie auch immer man es sehen mag, Display-Listen sind unglaublich praktisch bei der Programmierung und meist weicht die anfängliche Skepsis durch Begeisterung (und wenn nicht, gehört es zum guten Ton es zu wissen, weil man es häufiger sehen wird)&lt;br /&gt;
&lt;br /&gt;
Zunächst holen wir uns von OpenGL eine Nummer ab, unter welcher wir die Display-Liste später erreichen werden. Diese wird im Idealfall von Typ Integer sein. Der Aufruf ist kinderleicht:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  Displaylist := glGenLists(1);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
glGenLists liefert und eben den gewünschten Wert zurück. Wir können auch stattdessen ein Array of GLUint nehmen und gleich mehre anfordern.&lt;br /&gt;
&lt;br /&gt;
Der Rest ist dann praktisch aufgebaut wie ein glBegin und glEnd, nur dass die entsprechenden Funktionen diesmal glNewList und glEndList heißen und wie folgt verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
glNewList(DisplayList,GL_COMPILE);&lt;br /&gt;
  […]&lt;br /&gt;
glEndList;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das GL_COMPILE bewirkt schlicht und ergreifend, dass sich OpenGL die verwendeten Funktionen merkt. Alternativ könnten wie auch noch GL_COMPILE_AND_EXECUTE einsetzen, wenn wir wollen, dass die Liste auch gleich ausgeführt wird. Praktisch alles was zwischen diesen beiden Funktionen steht wird auch mitgeschrieben. Ausnahmen gibt es wir immer und ich werde dies Mal aufführen, ich denke, dass man erahnen kann, warum diese nicht mit unterstützt werden. Es gibt praktisch keinen Sinn diese mit aufzuzeichnen:&lt;br /&gt;
&lt;br /&gt;
[[glColorPointer]], [[glDeleteLists]], [[glDisableClientState]], [[glEdgeFlagPointer]],&lt;br /&gt;
[[glEnableClientState]], [[glFeedbackBuffer]], [[glFinish]], [[glFlush]], [[glGenLists]],&lt;br /&gt;
[[glIndexPointer]],[[glInterleavedArrays]], [[glIsEnabled]], [[glIsList]], [[glNormalPointer]],&lt;br /&gt;
[[glPopClientAttrib]],[[glPixelStore]], [[glPushClientAttrib]], [[glReadPixels]],&lt;br /&gt;
[[glRenderMode]], [[glSelectBuffer]], [[glTexCoordPointer]], [[glVertexPointer]] und&lt;br /&gt;
alle  [[glGet]] Routinen&lt;br /&gt;
&lt;br /&gt;
=== Und Cut! ===&lt;br /&gt;
&lt;br /&gt;
Idealerweise erzeugen wir diese Listen nicht bei jedem Render-Vorgang, sondern nur einmal im Init und führen diese dann nur noch beim Rendern aus. Dies geschieht mit der Funktion glCallList und sollte genauso leicht zu beherrschen sein, wie die bisherigen auch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glCallList(displaylist);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir übergeben einfach den Namen der Liste und schon wird alles was wir drinnen aufgezeichnet haben auch ausgeführt… Perfekt, oder? Das ganze ist doch recht leicht hand zu haben und sollte niemanden vor einem großen Problem stellen.&lt;br /&gt;
&lt;br /&gt;
Im Sample haben wir ein kleines Astroiden-Feld nachgebildet, eben mit Hilfe dieser Display-Listen. Ich denke, dass dort anschaulich gezeigt wird, wie man sie sinnvoll einsetzen kann. Auch die Demo von Jan Horn &amp;quot;Biohazzard&amp;quot; zeigt eindrucksvoll, wie man es am Geschicktesten machen kann ;)&lt;br /&gt;
&lt;br /&gt;
*g* Ach ja… einen kleinen Nachteil haben die Display-Listen doch noch! (ich weiß, dass ist jetzt so, als würde ich euch ins ein tiefes Becken tauchen lassen und am Boden steht: &amp;quot;Du hast die Sauerstoff-Falsche vergessen…&amp;quot;) Sie verbrauchen relativ viel Speicher! Alle Vorgänge werden nämlich direkt im Arbeitsspeicher geschoben. Eine gesunde Mischung zwischen normalen Rendering und Display-Listen ist also anzuraten, den wenn der Speicher voll ist, hilft keine Optimierung mehr, dass Programm vorm ruckeln zu schützen ;)&lt;br /&gt;
&lt;br /&gt;
Auch sollte man bedenken, dass ein Aufbau z.B. eines Quads sich nicht mehr sehr optimieren lässt. Richtig bringen werden Euch die Display-Listen nur dann etwas, wenn ihr mit komplexen Gebilden arbeitet, die sich immer wieder holen. Würdest ihr z.B. bei jedem Render-Durchgang ein Model verändern, also seine Form (nicht seine Farbe), so würde eine Display-Liste eher hinderlich als nützlich sein!&lt;br /&gt;
&lt;br /&gt;
== Nachwort ==&lt;br /&gt;
&lt;br /&gt;
Bitte nicht traurig sein, wenn hier bereits wieder Ende ist ;) Ich weiß, dass ich in diesem Tutorial mehr versprochen habe, als letztendlich drin ist, aber ich habe vermehrt zu hören bekommen, dass ich dazu neige, wahre Monster-Tutorials zu machen und wollte dem hier mal entgegen wirken, da ich noch rund das doppelte an Stoff gehabt hätte. Drum ist hier nun erstmal Schluss! Es ist nicht ganz das geworden, was zunächst geplant war, aber ich denke, dass das Ziel, nämlich das Grundwissen zu vertiefen erfüllt werden konnte!&lt;br /&gt;
&lt;br /&gt;
Das schreiben dieses Tutorials hat mir sehr viel Spaß gemacht, da ich mich teilweise auch selbst momentan damit beschäftige und es auch unglaublich interessant finde. In diesem Sinne möchte ich dann auch beim sechsten Tutorial weiter machen und mal eine kleine Exkursion unternehmen, nämlich Möglichkeiten aufzuzeigen, Daten aus einem Modellierer in unsere Software zu bekommen. Die Direct3D-Welt hat gesagt, dass sie bereit ist für einen Kampf ist - also sollen sie ihn auch bekommen ^__-&lt;br /&gt;
&lt;br /&gt;
Den mit OpenGL lässt sich weitaus mehr machen als man zunächst erahnt und da die meisten Samples immer nur Dreiecke zeigen wollen wir doch mal etwas Komplexeres machen! Ihr dürft also gespannt sein und wie immer gilt… Ideen und Vorschläge sind herzlich willkommen ;) Im Gegensatz zu D3D hat man bei OpenGL erkannt, dass eine Grafik-API keine Model- und Texture-Loader bieten sollte. Hier ist dann eine Hardcodierung notwendig, die wir allerdings nicht als Nachteil ansehen sollten, sondern vielmehr die Chancen nutzen, unsere Programme mit eigenen Routinen zu optimieren. ;)&lt;br /&gt;
&lt;br /&gt;
Have Fun!&lt;br /&gt;
&lt;br /&gt;
Euer [[Benutzer:Phobeus|Phobeus]]&lt;br /&gt;
&lt;br /&gt;
{{TUTORIAL_NAVIGATION | [[Tutorial Lektion 4]] | [[Tutorial Lektion 7]]}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Tutorial|Lektion5]]&lt;/div&gt;</summary>
		<author><name>Gucky</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_Lektion_5&amp;diff=19873</id>
		<title>Tutorial Lektion 5</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_Lektion_5&amp;diff=19873"/>
				<updated>2006-11-16T22:12:59Z</updated>
		
		<summary type="html">&lt;p&gt;Gucky: /* Wahrheiten und Wirklichkeiten */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Artenvielfalten und Ihre Folgen =&lt;br /&gt;
&lt;br /&gt;
== Vorwort ==&lt;br /&gt;
&lt;br /&gt;
Und wieder einmal aufraffen und etwas tippen. Es ist wirklich nicht immer leicht solche Tutorials zu schreiben, vor allem wenn man mal wieder eine Null-Bock-Phase hat. Ich hoffe doch sehr, dass auch Ihr dafür Verständnis habt, den… auch ich bin ein Mensch… egal wie viele Augen und Beine ich habe *sg* Gut… genug &amp;quot;gefaxt&amp;quot;, es geht wieder um den Ernst im Leben: Delphi&lt;br /&gt;
&lt;br /&gt;
Ich habe wirklich eine zeitlang überlegt, was wir machen sollten. Einige haben sich Lichter gewünscht, aber ich konnte mich nicht dazu aufraffen… Stattdessen habe ich irgendwie Lust gehabt, mal was anderes zu machen, etwas wo man etwas kreativ sein kann und was auch mir Spaß macht! :-D Und deswegen, habe ich mir gedacht, dass wir das eine oder andere Wissen, welches zwischendurch behandelt wurde noch etwas vertiefen sollten… einfach in dem wir andere Möglichkeiten aufzeigen, elegant ein Problem zu umschiffen und auch den einen oder anderen Effekt erzielen!&lt;br /&gt;
&lt;br /&gt;
Und genau darum geht es auch diesmal! Ich denke, dass sich dieses Tutorial weitestgehend nur an die richten wird, die bereits ein solides Wissen in OpenGL und Delphi haben, ansonsten wird es wohl schwer sein mir zu folgen, ich werde einiges an Wissen woraus setzen! Wer folgen kann, wird dann mit einem Wissen belohnt werden, dass ihm das eine oder andere Probleme sehr elegant umschiffen lässt in dem man einfach seine Software entsprechend mit OpenGL optimiert! Den nur wer sein Handwerk bis ins Detail beherrscht, darf sich Meister nennen ;)&lt;br /&gt;
&lt;br /&gt;
== Unbekannte Zeichen-Arten ==&lt;br /&gt;
&lt;br /&gt;
=== OpenGL-Maxime ===&lt;br /&gt;
&lt;br /&gt;
Wer nicht gerade erst jetzt hier eingestiegen ist, wird sicherlich bereits bemerkt haben, dass OpenGL streng genommen nach einem sehr einfachen Prinzip arbeitet. Ständig wird an unserer &amp;quot;Zustand-Maschine&amp;quot; etwas manipuliert und mit Matrizen setzen wir die Positionen fest. Jedoch nur an einer einzigen Stelle kommen alle diese Werte zusammen. Nämlich dann, wenn etwas gerendert wird. Genau in diesem Moment werden alle Werte &amp;quot;zusammengerechnet&amp;quot; und erzeugen etwas Sichtbares auf dem Bildschirm. In den meisten Fällen wird dies Eben zwischen glBegin und glEnd geschehen. Und genau diese beiden Funktionen wollen wir nun näher betrachten. Interessant hierbei ist nämlich der Parameter von glBegin…&lt;br /&gt;
&lt;br /&gt;
Streng genommen definieren wir nur eine Anzahl von Punkten zwischen glBegin und glEnd, der Parameter bei glBegin bestimmt aber letztendlich wie diese Verstanden werden sollen. Nur als kleine Übersicht alle verfügbaren Parameter:&lt;br /&gt;
&lt;br /&gt;
 GL_POINTS&lt;br /&gt;
 GL_LINES&lt;br /&gt;
 GL_LINE_STRIP&lt;br /&gt;
 GL_LINE_LOOP&lt;br /&gt;
 GL_TRIANGLES&lt;br /&gt;
 GL_TRIANGLE_STRIP&lt;br /&gt;
 GL_TRIANGLE_FAN&lt;br /&gt;
 GL_QUADS&lt;br /&gt;
 GL_QUAD_STRIP&lt;br /&gt;
 GL_POLYGON&lt;br /&gt;
&lt;br /&gt;
Ich denke aus ethischen Gründen werde ich nun darauf verzichten Euch erneut zu erklären wofür, die bisher für Euch bekannten Parameter (gl_Triangle und gl_Quad) gut sind… jeder wird sich denken können, dass das erste immer per 3er Punkte ein Dreieck bildet, dass zweiter aus 4 Punkten ein Quadrat erzeugt… (mist… *g*) ;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Zunächst war der Strich ===&lt;br /&gt;
&lt;br /&gt;
Interessanter, da neu für uns, sind die restlichen Parameter, auch wenn diese sich weitestgehend selbst erklären. Gl_Points z.B. … könnte es vielleicht bedeuten, dass OpenGL alle mit [[glVertex3f]] definierten Punkte auch nur als Punkte zeichnet?&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gl_point.gif]]&lt;br /&gt;
&lt;br /&gt;
Scheint zu stimmen… ;) Es ist nun auch nicht besonders schwer herzuleiten, was OpenGL mit Vertex-Definitionen macht, die mit gl_Lines beginnen. Versuchen wir doch mal ein Dreieck damit zu zeichnen! Ergo brauchen wir 4 Punkte (der letzte muss, auf den ersten verweisen)…&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gl_lines.gif]]&lt;br /&gt;
&lt;br /&gt;
Doch was ist das? Dies ist kein böswilliger Trick, den ich auf Euch spielen will, sondern folgender Code, ist dafür verantwortlich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBegin(GL_LINES);&lt;br /&gt;
    glVertex3f(-1,0,0);&lt;br /&gt;
    glVertex3f(0,1,0);&lt;br /&gt;
    glVertex3f(1,0,0);&lt;br /&gt;
    glVertex3f(-1,0,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sieht soweit alles in Ordnung aus - ist es auch! Der Fehler liegt nämlich nicht am Code, sondern an uns selbst. Gl_Lines bewirkt nämlich nicht, dass alle Punkte miteinander verbunden werden, sondern nur, dass jeweils Punkte 1 mit Punkte2, Punkte 3 mit Punkt 4 etc. verbunden werden, d.h. immer zweiter Pärchen. Um das Ergebnis zu erreichen, welches wir angestrebt haben (und zwar ohne 6 Punkte zu definieren), wäre in diesem Fall nämlich gl_Line_Strip gewesen, was nämlich bewirkt, dass der &amp;quot;Zeichenstift&amp;quot; immer von seiner letzten Position zum nächst definierten Punkte einen Strich zieht. Dementsprechend sieht dann unser Dreieck mit praktisch dem gleichen Source wie folgt aus:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gl_line_strip.gif]]&lt;br /&gt;
&lt;br /&gt;
hr merkt bereits jetzt wie viele Möglichkeiten einen OpenGL bietet mit nur wenigen Zeilen eine Menge zu verändern. Man bedenkt, dass wir hier nur sehr wenige Punkte haben, allerdings kann man eine Menge Leistung rausschlagen, wenn man statt gl_Lines, gl_Line_Strip verwendet, da einfach weniger Punkte abgearbeitet werden müssen. Logisch oder?&lt;br /&gt;
&lt;br /&gt;
 GL_LINE_LOOP&lt;br /&gt;
&lt;br /&gt;
Der Parameter entspricht praktisch gesehen GL_LINE_STRIP, nur das der letzte Punkt mit dem ersten verbunden wird. Man bräuchte mit diesem Parameter also nur 3 Punkte… das Optimum für unser Dreieck! ;)&lt;br /&gt;
&lt;br /&gt;
== Artenvielfalt ==&lt;br /&gt;
&lt;br /&gt;
Natürlich lassen sich solche Zeichenoperationen auch durch eine Menge anderer Faktoren beeinflussen. Vielleicht hat sich der eine oder andere ja bereits gefragt, wie man es schafft, dass der Rahmen des Dreiecks dicker gezeichnet wird. Sicherlich könnte man nun beginnen und ganz leicht versetzt daneben noch ein Dreieck zu zeichnen. Dies würde dann natürlich einen kleinen Tick größer wirken.&lt;br /&gt;
&lt;br /&gt;
Die Lösung liegt allerdings viel näher - den OpenGL bietet hierfür eine hauseigene Lösung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glLineWidth(3);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir stellen einfach ein, wie OpenGL die Linien Rastern soll. Wir nehmen in diesem Fall die dreifache Dicke von der normalen Einstellung:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gllinewidth.gif]]&lt;br /&gt;
&lt;br /&gt;
Auch werden einige von Euch sicherlich die unschönen Treppchen kennen, die vor allem bei solch einfache Konstruktionen wie diesm dickeren Dreieck auftreten können. Die Lösung dagegen heißt bekanntlich &amp;quot;AntiAliasing&amp;quot; … verschlägt es Euch die Sprache, dass ich gleich mit solch derben Geschützen auffahre?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glEnable(GL_LINE_SMOOTH);&lt;br /&gt;
  glDisable(GL_LINE_SMOOTH);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da verschlägt es einem die Sprache oder? Das war es nämlich auch bereits wieder. Wir schalten einfach einen Zustand um und fertig war die Geschichte! Der Zustands-Maschine von OpenGL sei dank, müssen wir nur noch sagen, was gemacht werden soll, ein lästiges &amp;quot;Wie?&amp;quot; entfällt komplett&lt;br /&gt;
&lt;br /&gt;
Und noch ein kleines Beispiel dafür, wie einfach OpenGL nicht nur Informationen schreiben lässt, sondern diese auch wieder preisgibt. Haben wir vergessen wie Dick wir die Linien eingestellt haben? Kein Problem, den OpenGL bietet folgende Funktionen um zu ermitteln, auf welchen Wert ein &amp;quot;Zustand&amp;quot; geschaltet ist:&lt;br /&gt;
&lt;br /&gt;
[[glGetBooleanv]], [[glGetDoublev]], [[glGetFloatv]], [[glGetIntegerv]]&lt;br /&gt;
&lt;br /&gt;
Ich glaube ich brauche nicht wieder damit zu beginnen darauf hinzuweisen, dass man bei jeder Abfrage auch den richtigen Variablen-Typen verwenden sollte *g*&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glGetIntegerv(gl_line_width,@myint);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und schon haben wir in myint die Stärke mit der OpenGL momentan alle Linien zeichnen soll. Das geht mit fast allen Zuständen, die OpenGL haben kann. Ich denke, das Grundprinzip ist recht leicht verständlich. Beachtet auch, dass OpenGL bei solchen Funktionen immer einen Pointer auf eine Variable erwartet (sprich besser: eine Adresse) und nicht die Variable selbst!&lt;br /&gt;
&lt;br /&gt;
Achso… vergaß ich zu erwähnen, dass wir auch weitere Funktionen auf Striche anwenden können? Zum Beispiel glColor, um den Strich einzufärben? Auch der Z-Buffer lasst sich darauf anwenden, eben alles, was man auch bei einem Dreieck tun könnte (wovon man aber zwingend absehen sollte eine Linie zu texturieren, um keine unnötigen Berechnungen durchzuführen)&lt;br /&gt;
&lt;br /&gt;
=== Ein Dreieck, hat drei Ecken… ===&lt;br /&gt;
&lt;br /&gt;
Nun dreht sich erstmal alles um Dreiecke. Den auch hierfür bietet OpenGL mehre Möglichkeiten, wie die Reihenfolge der Punkte verstanden wird:&lt;br /&gt;
&lt;br /&gt;
 GL_TRIANGLES&lt;br /&gt;
 GL_TRIANGLE_STRIP&lt;br /&gt;
 GL_TRIANGLE_FAN&lt;br /&gt;
&lt;br /&gt;
Zunächst widmen wir uns GL_TRIANGLES! Dieser Parameter sollte uns allen noch geläufig sein. Jeweils 3 Punkte werden zusammen zu einem eigenständigen Dreieck verbunden. Nicht neues für uns, bereits im ersten Tutorial könnt ihr den Beweis finden, dass es klappt :-D&lt;br /&gt;
&lt;br /&gt;
Sicherlich werdet ihr Euch denken können, dass dies sehr hilfreich ist, wenn man nur ein Dreieck zeichnen möchte, nicht jedoch, sobald man mehre in einem Rutsch auf dem Bildschirm bringen möchte. Hierzu bietet sich dann eher GL_TRIANGLE_STRIP an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBegin(GL_TRIANGLE_STRIP);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(-1,0,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(1,1,0);&lt;br /&gt;
    glVertex3f(0,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(1,1,1);&lt;br /&gt;
    glVertex3f(1,0,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(0,1,1);&lt;br /&gt;
    glVertex3f(2,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(0,1,0);&lt;br /&gt;
    glVertex3f(3,0,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wem das ganze zu schwer erscheint, sollte er sich die glColor3f weglassen, die habe ich reingesetzt, damit das ganze auch schön aussieht ;) (Programmierer lieben sinnlosen Spielkram der glänzt und bunt ist). Wer sich nur auf die Punkte konzentriert und sich die Position im Kopfe vorstellt, wird ein Gebilde wie folgt vorstellen können:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gl_triangle_strip.gif]]&lt;br /&gt;
&lt;br /&gt;
Streng genommen macht OpenGL nichts anderes, als die ersten drei Eckpunkte zu nehmen und ein Dreieck daraus zu rendern. Anschließend fällt der erste Punkt weg und es wird das Dreieck zwischen 2,3,4 gerendert usw. Im Anschluss dieses Kapitels werden wir nochmals hieraus zurückkommen und das Culling erklären!&lt;br /&gt;
&lt;br /&gt;
=== Ventilatoren und OpenGL ===&lt;br /&gt;
&lt;br /&gt;
Tja, und nur die wenigstens wissen, dass auch ein Ventilator ein Dreieck sein kann *hust* Okay, begnügen wir uns hier lieber mit dem englischen Begriff &amp;quot;Fan&amp;quot;. (Das ist nicht der Kerl, der vorm Fenster steht, einem gierig anstarrt und laut ruft &amp;quot;Ich will ein Kind von Dir…&amp;quot; … nein, sicher nicht ;)&lt;br /&gt;
&lt;br /&gt;
Vielmehr sollten wir uns wirklich mal bildlich einen Fahrrad-Reifen vorstellen. Von den Außenseiten verlaufen die einzelnen Speichen alle zu einem Mittelpunkt. Nach einem ähnlichen Render-Prinzip funktioniert auch GL_TRIANGLE_FAN. Der einfachheitshalber werden wir hier jedoch kein komplexes Objekt anfertigen, sondern nur eine Möglichkeit zeigen, wie man mit dieser Einstellung sinnvoll ein Objekt zeichnen kann! In unserem Fall nehmen wir einfach einen Drachen (nein… nicht das Fabelwesen), denn auch dieser ist ein Ventilator (Chaos… perfekt….) ;)&lt;br /&gt;
&lt;br /&gt;
Bevor nun jeder abdreht, schauen wir uns doch mal das Objekt an, von dem ich sprach:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gl_triangle_fan.gif]]&lt;br /&gt;
&lt;br /&gt;
In diesem Fall benötigen wir nur 6 (!) Punkte, um dieses Gebilde zu erzeugen. Zentraler Ausgangspunkt ist hierbei die Nummer 1. Wie man erkennen kann besteht das erste Dreieck aus 1,2,3. Das zweite aus 1,3,4… für die abstrakt denkenden Menschen lässt sich daraus folgern, dass OpenGL die Punkte wie folgt abarbeitet 1, n+1, n+2. Oder um es neudeutsch zu sagen, der erste Punkte wird immer mit zwei weiteren verbunden. Im Gegensatz zum Strip fällt allerdings nicht der erste Punkt raus, sondern immer der zweite.&lt;br /&gt;
&lt;br /&gt;
Gl_Triangle_Fans und Gl_Triangle_Strips sind wohl die besten Möglichkeiten, um die Anzahl der im Video-Speicher befindlichen Daten zu begrenzen. Allerdings lässt sich schnell erahnen, dass nicht jeder dieser Methoden wirklich komplexe Objekte zulässt. Man sollte sie jedoch nicht total ignorieren, sondern mit Verstand einsetzen. Ach so, bevor ich es vergesse, der Code dafür:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBegin(GL_TRIANGLE_FAN);&lt;br /&gt;
    glColor3f(1,1,0);&lt;br /&gt;
    glVertex3f(0,0,0);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(0,1,0);&lt;br /&gt;
    glVertex3f(1,0,0);&lt;br /&gt;
    glColor3f(0,0,1);&lt;br /&gt;
    glVertex3f(0,-3,0);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(-1,0,0);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(0,1,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass Punkte 2 und Punkte 6 identisch sind, da er sonst das letzte Dreieck weglassen würde ;)&lt;br /&gt;
&lt;br /&gt;
=== Quadratisch, praktisch, gut! ===&lt;br /&gt;
&lt;br /&gt;
Das ist ne wahrer Marathon geworden. Schlimm, wenn man bedenkt, dass wir uns hier nur mit einem Parameter für eine Funktion beschäftigen. Ihr versteht, warum ich so oft geschrieben haben &amp;quot;lassen wir es lieber und klären nicht jeden Parameter&amp;quot; ;) Aber gut, ich halte es für sehr wichtig, solche Dinge zu beherrschen, weil es einfach zu den Grundlagen dazugehört! Immerhin bleiben nicht mehr sonderlich viele übrig, als ran an die letzten drei!&lt;br /&gt;
&lt;br /&gt;
 GL_QUADS&lt;br /&gt;
&lt;br /&gt;
Sollte auch niemanden von uns mehr sonderlich schockieren können. Jeweils vier übergebende Punkte werden zusammengefasst zu einem Quadrat… nichts weiter Aufregendes. Eigentlich sollten inzwischen auch die GL_QUAD_STRIP nichts wirklich erstaunliches mehr liefern. Wir übergeben zunächst vier Punkte und bei jedem Durchgang entfallen die beiden ersten und werden durch die nächsten zwei ersetzt. OpenGL erstellt dann daraus jedes Mal ein Viereck. Bitte nicht verwechseln mit der Punkt-Reihenfolge von GL_QUADS. Folgendes Beispiel sollte die Problematik verdeutlichen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
glBegin(GL_QUAD_STRIP);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(0,0,0);&lt;br /&gt;
    glVertex3f(0,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(0,0,1);&lt;br /&gt;
    glVertex3f(1,0,0);&lt;br /&gt;
    glVertex3f(1,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(0,1,0);&lt;br /&gt;
    glVertex3f(2,0,0);&lt;br /&gt;
    glVertex3f(2,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(0,1,1);&lt;br /&gt;
    glVertex3f(3,0,0);&lt;br /&gt;
    glVertex3f(3,1,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gl_quad_strip.gif]]&lt;br /&gt;
&lt;br /&gt;
Unser erstes Quadrat besteht aus den Punkten 1,2,3,4, dass zweite aus 3,4,5,6 etc. Ich denke, dass das Prinzip dahinter leicht verständlich ist. Anbieten tut sich diese Lösung meist in Schleifen, wenn man längere solcher Quad-Strukturen braucht, die aneinander gereiht sind&lt;br /&gt;
&lt;br /&gt;
=== Vieleckerei ===&lt;br /&gt;
&lt;br /&gt;
Und zu guter letzt GL_POLYGON, was wohl am einfachsten nachzuvollziehen ist ;) Es wird einfach eine Liste von Punkten übergeben und daraus wird schlicht und ergreifend dann ein Vieleck gemacht. Wie variantenreich dies werden kann, könnt ihr Euch vorstellen. Vor allem komplexere und exotischere Formen lassen sich damit darstellen, auch wenn es in diesem Fall eher Leitung kostet als wirklich einbringt. Zeichnet also niemals ein Quadrat mit GL_POLYGON.&lt;br /&gt;
&lt;br /&gt;
Eine mögliche Form wäre zum Beispiel diese:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gl_polygon.png]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBegin(GL_POLYGON);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(0,0,0);&lt;br /&gt;
    glColor3f(0,1,0);&lt;br /&gt;
    glVertex3f(3,0,0);&lt;br /&gt;
    glColor3f(0,0,1);&lt;br /&gt;
    glVertex3f(4,1,0);&lt;br /&gt;
    glColor3f(1,1,0);&lt;br /&gt;
    glVertex3f(2,2,0);&lt;br /&gt;
    glColor3f(0,1,1);&lt;br /&gt;
    glVertex3f(-2,3,0);&lt;br /&gt;
    glColor3f(1,1,1);&lt;br /&gt;
    glVertex3f(-1,1,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ich denke zusammen mit dem Code sollte es keine weiteren Fragen mehr dazu geben. Am besten setzt Ihr Euch nun alle einmal hin und wendet das Wissen Testweise an, denn es muss sitzen und stellt vor allem, wenn es um optimierte Programmierung geht ein absolutes Grundwissen da! Ihr werdet mit etwas praktischer Erfahrung schnell die Grenzen finden, die die einzelnen Parametern mit sich bringen z.B. bei der Texturierung von Objekten!&lt;br /&gt;
&lt;br /&gt;
Ein Quadrat besteht aus einem Face (Fläche), ein Quadrat zusammengesetzt aus zwei Triangle aus zwei. Man könnte zum Beispiel letzteres mit zwei Texturen versehen, ersteres nur mit einer!&lt;br /&gt;
&lt;br /&gt;
== Das Culling-Verfahren ==&lt;br /&gt;
&lt;br /&gt;
=== Wahrheiten und Wirklichkeiten ===&lt;br /&gt;
&lt;br /&gt;
;Dramatik:&lt;br /&gt;
Bevor wir uns mit der eigentlichen Technik des Cullings befassen, möchte ich eine radikale Aufklärung betreiben, die Euer Weltbild für immer verändern wird. Seit gewarnt, dass Ihr nach dem lesen der folgenden Zeilen, Eure Quake3-Gegner mit ganz anderen Augen sehen werdet und eventuell nie wieder zu Eurer alten Denkweise zurückkehren werden könnt!&lt;br /&gt;
&lt;br /&gt;
Habt Ihr Euch bereits gefragt wie die kommerziellen Spiele es schaffen ohne Ruckler (*hust), super Grafiken auf den Bildschirm zu bringen, die auch nach der Optimierung immer noch durch grafische Qualität überzeugen können? Nun, es gibt viele Möglichkeiten seine Szenen zu optimieren, dass wohl einfachste und auch mit am effizientesten ist das so genannte Culling-Verfahren.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_trooper1.gif]]&lt;br /&gt;
&lt;br /&gt;
So sieht unser Protagonist wie gewohnt aus. Wir sehen ihn wie er liebt und lebt. (ja…) Um es philosophisch auszudrücken, sehen wir hier jedoch nur die halbe Wahrheit… ich stelle die wage Behauptung aus, dass wir der Rückseite von ihm gar nicht sehen können und daher auch nicht sagen können, ob sie existiert! Jedes Mal, wenn wir uns um ihn herum bewegen sehen wir nie seine Rückseite. Nun… das wäre auch nicht weiter fatal, wenn ich nicht sofort eine weitere These aufstellen würde: &amp;quot;Der Kerl hat gar keine Rückseite&amp;quot; :-O&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Aber! Wenn wir um ihn herum gehen, dann sehen wir doch seine Rückseite, also ist sie da!&amp;quot;, könnte man mir nun skeptisch zurufen. Tja… beweist mir das Gegenteil und schickt einen Wetteinsatz an mich, ich werde Euch beweisen, dass er immer nur eine Seite hat und seine Rückseite erst erzeugt wird, wenn sie auch benötigt wird. Es würde dann aussage gegen Aussage stehen und ihr könntet mir nicht beweisen, dass Ihr Recht habt, denn wenn wir um ihn herum gehen, würden wir ja wieder seine Rückseite nicht sehen. Ich hingegen würde den Beweis antreten und Euch mitteilen, dass wir ja einfach mal uns die Rückseite ansehen, dann allerdings auf die Forderseite verzichten müssen:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_trooper2.gif]]&lt;br /&gt;
&lt;br /&gt;
Was auf den ersten Blick wie ein verschwommendes Etwas aussieht entpuppt sich auf den zweiten Blick bereits als unser Soldat vom ersten Bild. Skeptisch wird man sich sicherlich fragen, was geschehen ist. Und wenn man genau durch ihn durch sieht, erkennen wir, dass wir hier z.B. seine Hose sehen… und seinen Kopf auch irgendwie von … hinten! Was ist geschehen? Wir betrachten Ihn von vorne und sehen, was hinten ist! (Oh Gott… eine ganze Wirklichkeit stürzt in sich ein *sg* ).&lt;br /&gt;
&lt;br /&gt;
Natürlich ist das verschwommende Ding da oben nicht ein grafisches Ziel, was wir erreichen wollen, sondern nur der Beweis dafür, dass jede Figur zwei Seiten hat (Erkenntnis, aufschreiben!) und wir in OpenGL bestimmen können, welche Seite wir von Vorne oder Hinten betrachten können.&lt;br /&gt;
&lt;br /&gt;
Ich gebe ja auch zu, dass ich ein wenig getrickst habe und im oberen Bild, die Figur zweimal genredert habe, davon einmal die Vorderseite mit Alpha Blending, weil auf einem unbewegten Bild es schwer zu erkennen wäre, welches die Vorder und welches die Rückseite ist, da wir die Texturen eben gespiegelt auf dem Modell sehen würden.&lt;br /&gt;
&lt;br /&gt;
=== Eine abstrakte Wahrheit… ===&lt;br /&gt;
&lt;br /&gt;
An sich klingt bisher doch auch noch alles recht logisch oder? Den wieso sollte OpenGL die Rückseite von Objekten rendern, wenn man sie gar nicht sehen kann. Grob würde dies eben die doppelte Arbeit sein, die sinnlos getätigt wird. Der Laie wird nun vor Freude an die Decke springen, der erfahrene Programmierer skeptisch die Falten runzeln. &amp;quot;Wie erkennt OpenGL, den das es sich um die Rückseite handelt!&amp;quot;. Gute Frage oder? Des Lösung-Rätsel sollte ein Blick auf die Uhr zeigen… (&amp;lt;== nein, er spinnt nicht (Anm. v. Flo2))&lt;br /&gt;
&lt;br /&gt;
Aber was meine ich damit? Was haben unsere Objekte mit einer Uhr gemeinsam? Was zunächst einem komisch vorkommt ist eigentlich logisch: Es ist die Laufrichtung! Unsere Uhr sollte im Normalfall im Uhrzeigersinn laufen (… scheiß Übergang!). Die Entgegengesetzte Richtung nennen wir dann entgegen des Uhrzeigersinnes. Das wird nun vielleicht den einen oder anderen dazu gebracht haben ein leises Aua von sich zu geben und sich mit der Hand an die Rübe zu schlagen. Diese Erkenntnis ist jedoch grundlegend ^__-&lt;br /&gt;
&lt;br /&gt;
Den auch unsere Objekt haben eine gewissen Definitionsreihen folge. Wir werden die Problematik im Weiteren an Hand eines Quadrates verfolgen ;) Folgender Code ist nicht gleich…&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBegin(GL_QUADS);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(0,0,0);&lt;br /&gt;
    glColor3f(0,1,0);&lt;br /&gt;
    glVertex3f(1,0,0);&lt;br /&gt;
    glColor3f(1,1,1);&lt;br /&gt;
    glVertex3f(1,1,0);&lt;br /&gt;
    glColor3f(0,0,1);&lt;br /&gt;
    glVertex3f(0,1,0);&lt;br /&gt;
    glEnd;&lt;br /&gt;
&lt;br /&gt;
  glBegin(GL_QUADS);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(0,0,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(1,1,0);&lt;br /&gt;
    glVertex3f(0,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(1,1,1);&lt;br /&gt;
    glVertex3f(1,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(0,0,1);&lt;br /&gt;
    glVertex3f(1,0,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wer das Culling nicht verstanden hat, wird sich fragen, wo der unterschied liegt, den beide Stücke beschreiben ein und das gleiche Dreieck. Der Unterschied wird erst deutlich, wenn wir uns die Definitionsreihenfolge vor Augen führen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_order.png]]&lt;br /&gt;
&lt;br /&gt;
Und genau hier ist auch für OpenGL die Definition von &amp;quot;Rückseite&amp;quot;. Normalerweise erwartet OpenGL nämlich, dass alle Punkte gegen den Uhrzeigersinn definiert werden, also so wie beim ersten Quadrat. OpenGL würde in seinem Normalzustand dies dann als die Vorderseite ansehen. Mit einer einzigen Zeile können wir dieses Verhalten jedoch auch verändern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glFrontFace(GL_CW);        //Clock-Wise&lt;br /&gt;
  glFrontFace(GL_CCW);       // Counter Clock-Wise&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es ist jedoch zu empfehlen diese Reihenfolge beizubehalten, da andere Programmierer, die Euren Source lesen, ebenfalls davon ausgehen werden, dass ihr die Punkte gegen den Uhrzeigersinn definieren werdet. Nur bei einigen Optimierungsverfahren macht ein umschalten wirklich Sinn.&lt;br /&gt;
&lt;br /&gt;
=== … und eine verlogene Wirklichkeit ===&lt;br /&gt;
&lt;br /&gt;
Vielleicht springt mal wieder jemand auf und wird mich beschuldigen totalen Mist erzählt zu haben… immerhin zeichnet er vielleicht bei Euch mit beiden Code-Schnipseln die Quadrate? Das liegt einfach daran, dass OpenGL das Culling standardgemäß deaktiviert hat und somit auch beide Seiten rendert. Wie man Culling aktivieren kann, ist schon fast ratbar:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glEnable(GL_CULL_FACE);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit glDisable dementsprechend können wir es wieder deaktivieren. Sollten wir beide Quadrate nebeneinander gerendert haben und eben an den Grundeinstellungen nichts verändert haben, sollte mit der Aktivierung dieser Seite nur noch das linke Dreieck angezeigt werden, weil OpenGL es als Vorderseite ansieht. Wollen wir, dass er nur die &amp;quot;Rückseiten&amp;quot; rendert, so lässt sich dies erfolgreich mit einem weiteren Funktionsauruf realisieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glCullface(GL_FRONT);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit glCullface können wir immer die Seite angeben, die OpenGL vernachlässigen soll. In diesem Fall wäre dies dann die Vorderseite, die nicht gezeichnet werden würde (oder eben das rechte Dreieck).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glCullface(GL_BACK);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Würde wieder den ursprünglichen Status herstellen. Zusammengefasst : glCullFace definiert, ob Vorder oder Rückseite weggelassen werden sollen, glFrontFace definiert, wie die Vorderseite definiert ist (im oder gegen Uhrzeigersinn!) und mit glEnable müssen wir das Culling zunächst aktivieren (und das sollten wir auch immer tun, wenn es möglich ist!).&lt;br /&gt;
&lt;br /&gt;
Ich hoffe, ich habe es halbwegsverständlich erklärt. Wenn jemand sich jetzt fragt, wie man nun die Rückseite sehen kann, wenn wir ein Quadrat haben, dass im Uhrzeigersinn definiert ist… Wenn wir von vorne drauf sehen, werden wir es nicht sehen, da die Punkte in der falschen Reihenfolge definiert sind. Bewegen wir uns durch das Dreieck hin durch und drehen uns um, so werden wir feststellen, dass die Punkte wieder in der &amp;quot;richtigen&amp;quot; Reihenfolge definiert sind und OpenGL sie rendern wird. Wir haben dies hier nur bei sehr simplen Gebilden betrachtet, aber auch bei komplexen ist das Prinzip gleich!&lt;br /&gt;
&lt;br /&gt;
Auch bei möchte ich noch einmal ein gutes Beispiel sehen, wo man bei komplexeren Objekten das Culling gut erkennen kann. Nämlich unserer Landschaft aus dem vierten Tutorial:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_culledland.jpg]]&lt;br /&gt;
&lt;br /&gt;
Dies sieht zunächst nach einem schweren Grafikfehler aus… ist es jedoch nicht. Wir betrachten unsere Landschaft hier nur von einer Seite, die der Spieler nicht sehen würde, nämlich von der Unterseite. Wir sehen die Landschaft von unten bei aktivieren Culling (Anm.: Beim vierten Tutorial ist KEIN Culling von mir aktiviert worden!) und da ich die Quadrate alle richtig definiert habe sehen wir auch nur noch die, die dem Spieler zu gewannt sind. Etwas Fantasie benötigt man schon dazu, um dies wieder zu erkennen, allerdings sieht man es oben rechts doch noch recht gut. Ich hoffe, dass spätestens dieses kleine Beispiel es noch verständlicher gemacht hat, wenn nicht… schaut Euch die Samples an und fragt dann im Forum nach ;)&lt;br /&gt;
&lt;br /&gt;
BTW: Für psychische Folgen, die beim Lesen dieses Tutorials entstanden sind, übernimmt der Autor keine Haftung. Auch er hat bisher keinen glaubwürdigen Beweis dafür gefunden, dass seine Mitmenschen eine Rückseite haben. Auch die Betrachtung dieser mit Hilfe eines Spiegels kann eine zusätzlich Render-Routine der Engine sein, in der wir leben. Es ist jedoch höchst wahrscheinlich, dass sie keine haben, weil … wer sollte die ganze Rechenleistung aufbringen, um die Vorder- und Rückseite aller Menschen zu rendern! :-D&lt;br /&gt;
&lt;br /&gt;
== Für das Protokoll ==&lt;br /&gt;
&lt;br /&gt;
=== Grundgedanken zu Display-Listen ===&lt;br /&gt;
&lt;br /&gt;
Bisher sind wir sicherlich noch nicht in die Verlegenheit gekommen, komplexere Programme zu schreiben. Hat es doch jemand bereits versucht, so wird er schnell gemerkt haben, dass man ohne eine solide Organisation keine Chance hat, ein größeres Projekt zu verwirklichen. Die goldene Regel für eine gelungene Organisation ist sehr einfach! Redundanzen vermeiden um jeden Preis. Man sollte nicht wenn man z.B. ein Quad zeichnen will, diesen 20.000 Mal hintereinander erzeugt. Vielleicht tut es ja auch eine Schleife? Genauso, wie man nicht mehrfach ein Modell in den Speicher laden sollte, wenn es bereits einmal geladen wurde.&lt;br /&gt;
&lt;br /&gt;
Das klingt nun sicherlich wie eine große Verarschung meinerseits. Aber wer jetzt hier geschmunzelt hat, sollte aufpassen, dass ihm nicht gleich das Grinsen im Gesichte stecken bleibt. Würdest Ihr Euer Programm richtig aufbauen? Versuchen eben doppelte Daten zu vermeiden, wo man es nur kann? Wer halbwegs geschickt vorgeht, wird mit Prozeduren, die er immer wieder aufrufen kann eine Menge sinnlosen Code vermeiden. Zum Beispiel wenn wir einen Würfel zeichnen wollen! Wir würden diesen in einer Funktion zeichnen lassen und dann einfach diese Prozedur aufrufen, wenn er gerendert werden soll.&lt;br /&gt;
&lt;br /&gt;
Wünschen wir ihn an einer anderen Stelle in der Welt, so rufen wir einfach die entsprechende Matrix vor dem rendern auf und schon haben wir zwei Würfel in einer Welt mit nur einer Prozedur erzeugt… und die Hälfte an Code gespart. Das lässt sich doch sehen! Auch können wir nun zahlreiche andere Dinge mir in diese Funktion setzen, z.B. eine andere Farbe. Das klingt doch sehr nach einem richtige Ansatz!&lt;br /&gt;
&lt;br /&gt;
=== Vorsicht! Aufnahme ===&lt;br /&gt;
&lt;br /&gt;
OpenGL bietet für ein solches vorgehen ein eigenes System und eine Anzahl von Funktionen, die man unter den Namen &amp;quot;Display-Listen&amp;quot; zusammenfassen kann. Dies kann man sich so vorstellen, dass man einen Namen für eine Liste vergibt (nicht irritieren lassen, ein Name ist in diesem Fall eine Nummer!) und sagt OpenGL, dass alles, was jetzt geschieht mitprotokolliert werden soll. Man kann nun anfangen Objekte zu zeichnen, Farben, Texturen, Materialen, einfach alles was einem in den Sinn kommt zu rendern und OpenGL wird dann all dies in einer Liste zusammen fassen. Es steht dann im fortan für uns auf Abruf zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technisch gesehen ließen sich wohl diese Listen auch mit Prozeduren realisieren, allerdings gibt es auch einen weiteren Vorteil (angeblich… ich konnte ihn nicht nachweisen, evtl. nur einen Sinn in seiner Frühzeit, wo es keine 3D-Karten gab oder nur bei extrem vielen Objekten spürbar…). OpenGL kann diese Befehle nämlich schneller durchführen, weil es die Operationen praktisch vormerkt. Wie auch immer man es sehen mag, Display-Listen sind unglaublich praktisch bei der Programmierung und meist weicht die anfängliche Skepsis durch Begeisterung (und wenn nicht, gehört es zum guten Ton es zu wissen, weil man es häufiger sehen wird)&lt;br /&gt;
&lt;br /&gt;
Zunächst holen wir uns von OpenGL eine Nummer ab, unter welcher wir die Display-Liste später erreichen werden. Diese wird im Idealfall von Typ Integer sein. Der Aufruf ist kinderleicht:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  Displaylist := glGenLists(1);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
glGenLists liefert und eben den gewünschten Wert zurück. Wir können auch stattdessen ein Array of GLUint nehmen und gleich mehre anfordern.&lt;br /&gt;
&lt;br /&gt;
Der Rest ist dann praktisch aufgebaut wie ein glBegin und glEnd, nur dass die entsprechenden Funktionen diesmal glNewList und glEndList heißen und wie folgt verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
glNewList(DisplayList,GL_COMPILE);&lt;br /&gt;
  […]&lt;br /&gt;
glEndList;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das GL_COMPILE bewirkt schlicht und ergreifend, dass sich OpenGL die verwendeten Funktionen merkt. Alternativ könnten wie auch noch GL_COMPILE_AND_EXECUTE einsetzen, wenn wir wollen, dass die Liste auch gleich ausgeführt wird. Praktisch alles was zwischen diesen beiden Funktionen steht wird auch mitgeschrieben. Ausnahmen gibt es wir immer und ich werde dies Mal aufführen, ich denke, dass man erahnen kann, warum diese nicht mit unterstützt werden. Es gibt praktisch keinen Sinn diese mit aufzuzeichnen:&lt;br /&gt;
&lt;br /&gt;
[[glColorPointer]], [[glDeleteLists]], [[glDisableClientState]], [[glEdgeFlagPointer]],&lt;br /&gt;
[[glEnableClientState]], [[glFeedbackBuffer]], [[glFinish]], [[glFlush]], [[glGenLists]],&lt;br /&gt;
[[glIndexPointer]],[[glInterleavedArrays]], [[glIsEnabled]], [[glIsList]], [[glNormalPointer]],&lt;br /&gt;
[[glPopClientAttrib]],[[glPixelStore]], [[glPushClientAttrib]], [[glReadPixels]],&lt;br /&gt;
[[glRenderMode]], [[glSelectBuffer]], [[glTexCoordPointer]], [[glVertexPointer]] und&lt;br /&gt;
alle  [[glGet]] Routinen&lt;br /&gt;
&lt;br /&gt;
=== Und Cut! ===&lt;br /&gt;
&lt;br /&gt;
Idealerweise erzeugen wir diese Listen nicht bei jedem Render-Vorgang, sondern nur einmal im Init und führen diese dann nur noch beim Rendern aus. Dies geschieht mit der Funktion glCallList und sollte genauso leicht zu beherrschen sein, wie die bisherigen auch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glCallList(displaylist);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir übergeben einfach den Namen der Liste und schon wird alles was wir drinnen aufgezeichnet haben auch ausgeführt… Perfekt, oder? Das ganze ist doch recht leicht hand zu haben und sollte niemanden vor einem großen Problem stellen.&lt;br /&gt;
&lt;br /&gt;
Im Sample haben wir ein kleines Astroiden-Feld nachgebildet, eben mit Hilfe dieser Display-Listen. Ich denke, dass dort anschaulich gezeigt wird, wie man sie sinnvoll einsetzen kann. Auch die Demo von Jan Horn &amp;quot;Biohazzard&amp;quot; zeigt eindrucksvoll, wie man es am Geschicktesten machen kann ;)&lt;br /&gt;
&lt;br /&gt;
*g* Ach ja… einen kleinen Nachteil haben die Display-Listen doch noch! (ich weiß, dass ist jetzt so, als würde ich euch ins ein tiefes Becken tauchen lassen und am Boden steht: &amp;quot;Du hast die Sauerstoff-Falsche vergessen…&amp;quot;) Sie verbrauchen relativ viel Speicher! Alle Vorgänge werden nämlich direkt im Arbeitsspeicher geschoben. Eine gesunde Mischung zwischen normalen Rendering und Display-Listen ist also anzuraten, den wenn der Speicher voll ist, hilft keine Optimierung mehr, dass Programm vorm ruckeln zu schützen ;)&lt;br /&gt;
&lt;br /&gt;
Auch sollte man bedenken, dass ein Aufbau z.B. eines Quads sich nicht mehr sehr optimieren lässt. Richtig bringen werden Euch die Display-Listen nur dann etwas, wenn ihr mit komplexen Gebilden arbeitet, die sich immer wieder holen. Würdest ihr z.B. bei jedem Render-Durchgang ein Model verändern, also seine Form (nicht seine Farbe), so würde eine Display-Liste eher hinderlich als nützlich sein!&lt;br /&gt;
&lt;br /&gt;
== Nachwort ==&lt;br /&gt;
&lt;br /&gt;
Bitte nicht traurig sein, wenn hier bereits wieder Ende ist ;) Ich weiß, dass ich in diesem Tutorial mehr versprochen habe, als letztendlich drin ist, aber ich habe vermehrt zu hören bekommen, dass ich dazu neige, wahre Monster-Tutorials zu machen und wollte dem hier mal entgegen wirken, da ich noch rund das doppelte an Stoff gehabt hätte. Drum ist hier nun erstmal Schluss! Es ist nicht ganz das geworden, was zunächst geplant war, aber ich denke, dass das Ziel, nämlich das Grundwissen zu vertiefen erfüllt werden konnte!&lt;br /&gt;
&lt;br /&gt;
Das schreiben dieses Tutorials hat mir sehr viel Spaß gemacht, da ich mich teilweise auch selbst momentan damit beschäftige und es auch unglaublich interessant finde. In diesem Sinne möchte ich dann auch beim sechsten Tutorial weiter machen und mal eine kleine Exkursion unternehmen, nämlich Möglichkeiten aufzuzeigen, Daten aus einem Modellierer in unsere Software zu bekommen. Die Direct3D-Welt hat gesagt, dass sie bereit ist für einen Kampf ist - also sollen sie ihn auch bekommen ^__-&lt;br /&gt;
&lt;br /&gt;
Den mit OpenGL lässt sich weitaus mehr machen als man zunächst erahnt und da die meisten Samples immer nur Dreiecke zeigen wollen wir doch mal etwas Komplexeres machen! Ihr dürft also gespannt sein und wie immer gilt… Ideen und Vorschläge sind herzlich willkommen ;) Im Gegensatz zu D3D hat man bei OpenGL erkannt, dass eine Grafik-API keine Model- und Texture-Loader bieten sollte. Hier ist dann eine Hardcodierung notwendig, die wir allerdings nicht als Nachteil ansehen sollten, sondern vielmehr die Chancen nutzen, unsere Programme mit eigenen Routinen zu optimieren. ;)&lt;br /&gt;
&lt;br /&gt;
Have Fun!&lt;br /&gt;
&lt;br /&gt;
Euer [[Benutzer:Phobeus|Phobeus]]&lt;br /&gt;
&lt;br /&gt;
{{TUTORIAL_NAVIGATION | [[Tutorial Lektion 4]] | [[Tutorial Lektion 7]]}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Tutorial|Lektion5]]&lt;/div&gt;</summary>
		<author><name>Gucky</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_Lektion_5&amp;diff=19872</id>
		<title>Tutorial Lektion 5</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_Lektion_5&amp;diff=19872"/>
				<updated>2006-11-16T22:09:05Z</updated>
		
		<summary type="html">&lt;p&gt;Gucky: /* Wahrheiten und Wirklichkeiten */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Artenvielfalten und Ihre Folgen =&lt;br /&gt;
&lt;br /&gt;
== Vorwort ==&lt;br /&gt;
&lt;br /&gt;
Und wieder einmal aufraffen und etwas tippen. Es ist wirklich nicht immer leicht solche Tutorials zu schreiben, vor allem wenn man mal wieder eine Null-Bock-Phase hat. Ich hoffe doch sehr, dass auch Ihr dafür Verständnis habt, den… auch ich bin ein Mensch… egal wie viele Augen und Beine ich habe *sg* Gut… genug &amp;quot;gefaxt&amp;quot;, es geht wieder um den Ernst im Leben: Delphi&lt;br /&gt;
&lt;br /&gt;
Ich habe wirklich eine zeitlang überlegt, was wir machen sollten. Einige haben sich Lichter gewünscht, aber ich konnte mich nicht dazu aufraffen… Stattdessen habe ich irgendwie Lust gehabt, mal was anderes zu machen, etwas wo man etwas kreativ sein kann und was auch mir Spaß macht! :-D Und deswegen, habe ich mir gedacht, dass wir das eine oder andere Wissen, welches zwischendurch behandelt wurde noch etwas vertiefen sollten… einfach in dem wir andere Möglichkeiten aufzeigen, elegant ein Problem zu umschiffen und auch den einen oder anderen Effekt erzielen!&lt;br /&gt;
&lt;br /&gt;
Und genau darum geht es auch diesmal! Ich denke, dass sich dieses Tutorial weitestgehend nur an die richten wird, die bereits ein solides Wissen in OpenGL und Delphi haben, ansonsten wird es wohl schwer sein mir zu folgen, ich werde einiges an Wissen woraus setzen! Wer folgen kann, wird dann mit einem Wissen belohnt werden, dass ihm das eine oder andere Probleme sehr elegant umschiffen lässt in dem man einfach seine Software entsprechend mit OpenGL optimiert! Den nur wer sein Handwerk bis ins Detail beherrscht, darf sich Meister nennen ;)&lt;br /&gt;
&lt;br /&gt;
== Unbekannte Zeichen-Arten ==&lt;br /&gt;
&lt;br /&gt;
=== OpenGL-Maxime ===&lt;br /&gt;
&lt;br /&gt;
Wer nicht gerade erst jetzt hier eingestiegen ist, wird sicherlich bereits bemerkt haben, dass OpenGL streng genommen nach einem sehr einfachen Prinzip arbeitet. Ständig wird an unserer &amp;quot;Zustand-Maschine&amp;quot; etwas manipuliert und mit Matrizen setzen wir die Positionen fest. Jedoch nur an einer einzigen Stelle kommen alle diese Werte zusammen. Nämlich dann, wenn etwas gerendert wird. Genau in diesem Moment werden alle Werte &amp;quot;zusammengerechnet&amp;quot; und erzeugen etwas Sichtbares auf dem Bildschirm. In den meisten Fällen wird dies Eben zwischen glBegin und glEnd geschehen. Und genau diese beiden Funktionen wollen wir nun näher betrachten. Interessant hierbei ist nämlich der Parameter von glBegin…&lt;br /&gt;
&lt;br /&gt;
Streng genommen definieren wir nur eine Anzahl von Punkten zwischen glBegin und glEnd, der Parameter bei glBegin bestimmt aber letztendlich wie diese Verstanden werden sollen. Nur als kleine Übersicht alle verfügbaren Parameter:&lt;br /&gt;
&lt;br /&gt;
 GL_POINTS&lt;br /&gt;
 GL_LINES&lt;br /&gt;
 GL_LINE_STRIP&lt;br /&gt;
 GL_LINE_LOOP&lt;br /&gt;
 GL_TRIANGLES&lt;br /&gt;
 GL_TRIANGLE_STRIP&lt;br /&gt;
 GL_TRIANGLE_FAN&lt;br /&gt;
 GL_QUADS&lt;br /&gt;
 GL_QUAD_STRIP&lt;br /&gt;
 GL_POLYGON&lt;br /&gt;
&lt;br /&gt;
Ich denke aus ethischen Gründen werde ich nun darauf verzichten Euch erneut zu erklären wofür, die bisher für Euch bekannten Parameter (gl_Triangle und gl_Quad) gut sind… jeder wird sich denken können, dass das erste immer per 3er Punkte ein Dreieck bildet, dass zweiter aus 4 Punkten ein Quadrat erzeugt… (mist… *g*) ;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Zunächst war der Strich ===&lt;br /&gt;
&lt;br /&gt;
Interessanter, da neu für uns, sind die restlichen Parameter, auch wenn diese sich weitestgehend selbst erklären. Gl_Points z.B. … könnte es vielleicht bedeuten, dass OpenGL alle mit [[glVertex3f]] definierten Punkte auch nur als Punkte zeichnet?&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gl_point.gif]]&lt;br /&gt;
&lt;br /&gt;
Scheint zu stimmen… ;) Es ist nun auch nicht besonders schwer herzuleiten, was OpenGL mit Vertex-Definitionen macht, die mit gl_Lines beginnen. Versuchen wir doch mal ein Dreieck damit zu zeichnen! Ergo brauchen wir 4 Punkte (der letzte muss, auf den ersten verweisen)…&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gl_lines.gif]]&lt;br /&gt;
&lt;br /&gt;
Doch was ist das? Dies ist kein böswilliger Trick, den ich auf Euch spielen will, sondern folgender Code, ist dafür verantwortlich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBegin(GL_LINES);&lt;br /&gt;
    glVertex3f(-1,0,0);&lt;br /&gt;
    glVertex3f(0,1,0);&lt;br /&gt;
    glVertex3f(1,0,0);&lt;br /&gt;
    glVertex3f(-1,0,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sieht soweit alles in Ordnung aus - ist es auch! Der Fehler liegt nämlich nicht am Code, sondern an uns selbst. Gl_Lines bewirkt nämlich nicht, dass alle Punkte miteinander verbunden werden, sondern nur, dass jeweils Punkte 1 mit Punkte2, Punkte 3 mit Punkt 4 etc. verbunden werden, d.h. immer zweiter Pärchen. Um das Ergebnis zu erreichen, welches wir angestrebt haben (und zwar ohne 6 Punkte zu definieren), wäre in diesem Fall nämlich gl_Line_Strip gewesen, was nämlich bewirkt, dass der &amp;quot;Zeichenstift&amp;quot; immer von seiner letzten Position zum nächst definierten Punkte einen Strich zieht. Dementsprechend sieht dann unser Dreieck mit praktisch dem gleichen Source wie folgt aus:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gl_line_strip.gif]]&lt;br /&gt;
&lt;br /&gt;
hr merkt bereits jetzt wie viele Möglichkeiten einen OpenGL bietet mit nur wenigen Zeilen eine Menge zu verändern. Man bedenkt, dass wir hier nur sehr wenige Punkte haben, allerdings kann man eine Menge Leistung rausschlagen, wenn man statt gl_Lines, gl_Line_Strip verwendet, da einfach weniger Punkte abgearbeitet werden müssen. Logisch oder?&lt;br /&gt;
&lt;br /&gt;
 GL_LINE_LOOP&lt;br /&gt;
&lt;br /&gt;
Der Parameter entspricht praktisch gesehen GL_LINE_STRIP, nur das der letzte Punkt mit dem ersten verbunden wird. Man bräuchte mit diesem Parameter also nur 3 Punkte… das Optimum für unser Dreieck! ;)&lt;br /&gt;
&lt;br /&gt;
== Artenvielfalt ==&lt;br /&gt;
&lt;br /&gt;
Natürlich lassen sich solche Zeichenoperationen auch durch eine Menge anderer Faktoren beeinflussen. Vielleicht hat sich der eine oder andere ja bereits gefragt, wie man es schafft, dass der Rahmen des Dreiecks dicker gezeichnet wird. Sicherlich könnte man nun beginnen und ganz leicht versetzt daneben noch ein Dreieck zu zeichnen. Dies würde dann natürlich einen kleinen Tick größer wirken.&lt;br /&gt;
&lt;br /&gt;
Die Lösung liegt allerdings viel näher - den OpenGL bietet hierfür eine hauseigene Lösung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glLineWidth(3);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir stellen einfach ein, wie OpenGL die Linien Rastern soll. Wir nehmen in diesem Fall die dreifache Dicke von der normalen Einstellung:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gllinewidth.gif]]&lt;br /&gt;
&lt;br /&gt;
Auch werden einige von Euch sicherlich die unschönen Treppchen kennen, die vor allem bei solch einfache Konstruktionen wie diesm dickeren Dreieck auftreten können. Die Lösung dagegen heißt bekanntlich &amp;quot;AntiAliasing&amp;quot; … verschlägt es Euch die Sprache, dass ich gleich mit solch derben Geschützen auffahre?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glEnable(GL_LINE_SMOOTH);&lt;br /&gt;
  glDisable(GL_LINE_SMOOTH);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da verschlägt es einem die Sprache oder? Das war es nämlich auch bereits wieder. Wir schalten einfach einen Zustand um und fertig war die Geschichte! Der Zustands-Maschine von OpenGL sei dank, müssen wir nur noch sagen, was gemacht werden soll, ein lästiges &amp;quot;Wie?&amp;quot; entfällt komplett&lt;br /&gt;
&lt;br /&gt;
Und noch ein kleines Beispiel dafür, wie einfach OpenGL nicht nur Informationen schreiben lässt, sondern diese auch wieder preisgibt. Haben wir vergessen wie Dick wir die Linien eingestellt haben? Kein Problem, den OpenGL bietet folgende Funktionen um zu ermitteln, auf welchen Wert ein &amp;quot;Zustand&amp;quot; geschaltet ist:&lt;br /&gt;
&lt;br /&gt;
[[glGetBooleanv]], [[glGetDoublev]], [[glGetFloatv]], [[glGetIntegerv]]&lt;br /&gt;
&lt;br /&gt;
Ich glaube ich brauche nicht wieder damit zu beginnen darauf hinzuweisen, dass man bei jeder Abfrage auch den richtigen Variablen-Typen verwenden sollte *g*&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glGetIntegerv(gl_line_width,@myint);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und schon haben wir in myint die Stärke mit der OpenGL momentan alle Linien zeichnen soll. Das geht mit fast allen Zuständen, die OpenGL haben kann. Ich denke, das Grundprinzip ist recht leicht verständlich. Beachtet auch, dass OpenGL bei solchen Funktionen immer einen Pointer auf eine Variable erwartet (sprich besser: eine Adresse) und nicht die Variable selbst!&lt;br /&gt;
&lt;br /&gt;
Achso… vergaß ich zu erwähnen, dass wir auch weitere Funktionen auf Striche anwenden können? Zum Beispiel glColor, um den Strich einzufärben? Auch der Z-Buffer lasst sich darauf anwenden, eben alles, was man auch bei einem Dreieck tun könnte (wovon man aber zwingend absehen sollte eine Linie zu texturieren, um keine unnötigen Berechnungen durchzuführen)&lt;br /&gt;
&lt;br /&gt;
=== Ein Dreieck, hat drei Ecken… ===&lt;br /&gt;
&lt;br /&gt;
Nun dreht sich erstmal alles um Dreiecke. Den auch hierfür bietet OpenGL mehre Möglichkeiten, wie die Reihenfolge der Punkte verstanden wird:&lt;br /&gt;
&lt;br /&gt;
 GL_TRIANGLES&lt;br /&gt;
 GL_TRIANGLE_STRIP&lt;br /&gt;
 GL_TRIANGLE_FAN&lt;br /&gt;
&lt;br /&gt;
Zunächst widmen wir uns GL_TRIANGLES! Dieser Parameter sollte uns allen noch geläufig sein. Jeweils 3 Punkte werden zusammen zu einem eigenständigen Dreieck verbunden. Nicht neues für uns, bereits im ersten Tutorial könnt ihr den Beweis finden, dass es klappt :-D&lt;br /&gt;
&lt;br /&gt;
Sicherlich werdet ihr Euch denken können, dass dies sehr hilfreich ist, wenn man nur ein Dreieck zeichnen möchte, nicht jedoch, sobald man mehre in einem Rutsch auf dem Bildschirm bringen möchte. Hierzu bietet sich dann eher GL_TRIANGLE_STRIP an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBegin(GL_TRIANGLE_STRIP);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(-1,0,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(1,1,0);&lt;br /&gt;
    glVertex3f(0,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(1,1,1);&lt;br /&gt;
    glVertex3f(1,0,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(0,1,1);&lt;br /&gt;
    glVertex3f(2,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(0,1,0);&lt;br /&gt;
    glVertex3f(3,0,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wem das ganze zu schwer erscheint, sollte er sich die glColor3f weglassen, die habe ich reingesetzt, damit das ganze auch schön aussieht ;) (Programmierer lieben sinnlosen Spielkram der glänzt und bunt ist). Wer sich nur auf die Punkte konzentriert und sich die Position im Kopfe vorstellt, wird ein Gebilde wie folgt vorstellen können:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gl_triangle_strip.gif]]&lt;br /&gt;
&lt;br /&gt;
Streng genommen macht OpenGL nichts anderes, als die ersten drei Eckpunkte zu nehmen und ein Dreieck daraus zu rendern. Anschließend fällt der erste Punkt weg und es wird das Dreieck zwischen 2,3,4 gerendert usw. Im Anschluss dieses Kapitels werden wir nochmals hieraus zurückkommen und das Culling erklären!&lt;br /&gt;
&lt;br /&gt;
=== Ventilatoren und OpenGL ===&lt;br /&gt;
&lt;br /&gt;
Tja, und nur die wenigstens wissen, dass auch ein Ventilator ein Dreieck sein kann *hust* Okay, begnügen wir uns hier lieber mit dem englischen Begriff &amp;quot;Fan&amp;quot;. (Das ist nicht der Kerl, der vorm Fenster steht, einem gierig anstarrt und laut ruft &amp;quot;Ich will ein Kind von Dir…&amp;quot; … nein, sicher nicht ;)&lt;br /&gt;
&lt;br /&gt;
Vielmehr sollten wir uns wirklich mal bildlich einen Fahrrad-Reifen vorstellen. Von den Außenseiten verlaufen die einzelnen Speichen alle zu einem Mittelpunkt. Nach einem ähnlichen Render-Prinzip funktioniert auch GL_TRIANGLE_FAN. Der einfachheitshalber werden wir hier jedoch kein komplexes Objekt anfertigen, sondern nur eine Möglichkeit zeigen, wie man mit dieser Einstellung sinnvoll ein Objekt zeichnen kann! In unserem Fall nehmen wir einfach einen Drachen (nein… nicht das Fabelwesen), denn auch dieser ist ein Ventilator (Chaos… perfekt….) ;)&lt;br /&gt;
&lt;br /&gt;
Bevor nun jeder abdreht, schauen wir uns doch mal das Objekt an, von dem ich sprach:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gl_triangle_fan.gif]]&lt;br /&gt;
&lt;br /&gt;
In diesem Fall benötigen wir nur 6 (!) Punkte, um dieses Gebilde zu erzeugen. Zentraler Ausgangspunkt ist hierbei die Nummer 1. Wie man erkennen kann besteht das erste Dreieck aus 1,2,3. Das zweite aus 1,3,4… für die abstrakt denkenden Menschen lässt sich daraus folgern, dass OpenGL die Punkte wie folgt abarbeitet 1, n+1, n+2. Oder um es neudeutsch zu sagen, der erste Punkte wird immer mit zwei weiteren verbunden. Im Gegensatz zum Strip fällt allerdings nicht der erste Punkt raus, sondern immer der zweite.&lt;br /&gt;
&lt;br /&gt;
Gl_Triangle_Fans und Gl_Triangle_Strips sind wohl die besten Möglichkeiten, um die Anzahl der im Video-Speicher befindlichen Daten zu begrenzen. Allerdings lässt sich schnell erahnen, dass nicht jeder dieser Methoden wirklich komplexe Objekte zulässt. Man sollte sie jedoch nicht total ignorieren, sondern mit Verstand einsetzen. Ach so, bevor ich es vergesse, der Code dafür:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBegin(GL_TRIANGLE_FAN);&lt;br /&gt;
    glColor3f(1,1,0);&lt;br /&gt;
    glVertex3f(0,0,0);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(0,1,0);&lt;br /&gt;
    glVertex3f(1,0,0);&lt;br /&gt;
    glColor3f(0,0,1);&lt;br /&gt;
    glVertex3f(0,-3,0);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(-1,0,0);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(0,1,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass Punkte 2 und Punkte 6 identisch sind, da er sonst das letzte Dreieck weglassen würde ;)&lt;br /&gt;
&lt;br /&gt;
=== Quadratisch, praktisch, gut! ===&lt;br /&gt;
&lt;br /&gt;
Das ist ne wahrer Marathon geworden. Schlimm, wenn man bedenkt, dass wir uns hier nur mit einem Parameter für eine Funktion beschäftigen. Ihr versteht, warum ich so oft geschrieben haben &amp;quot;lassen wir es lieber und klären nicht jeden Parameter&amp;quot; ;) Aber gut, ich halte es für sehr wichtig, solche Dinge zu beherrschen, weil es einfach zu den Grundlagen dazugehört! Immerhin bleiben nicht mehr sonderlich viele übrig, als ran an die letzten drei!&lt;br /&gt;
&lt;br /&gt;
 GL_QUADS&lt;br /&gt;
&lt;br /&gt;
Sollte auch niemanden von uns mehr sonderlich schockieren können. Jeweils vier übergebende Punkte werden zusammengefasst zu einem Quadrat… nichts weiter Aufregendes. Eigentlich sollten inzwischen auch die GL_QUAD_STRIP nichts wirklich erstaunliches mehr liefern. Wir übergeben zunächst vier Punkte und bei jedem Durchgang entfallen die beiden ersten und werden durch die nächsten zwei ersetzt. OpenGL erstellt dann daraus jedes Mal ein Viereck. Bitte nicht verwechseln mit der Punkt-Reihenfolge von GL_QUADS. Folgendes Beispiel sollte die Problematik verdeutlichen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
glBegin(GL_QUAD_STRIP);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(0,0,0);&lt;br /&gt;
    glVertex3f(0,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(0,0,1);&lt;br /&gt;
    glVertex3f(1,0,0);&lt;br /&gt;
    glVertex3f(1,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(0,1,0);&lt;br /&gt;
    glVertex3f(2,0,0);&lt;br /&gt;
    glVertex3f(2,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(0,1,1);&lt;br /&gt;
    glVertex3f(3,0,0);&lt;br /&gt;
    glVertex3f(3,1,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gl_quad_strip.gif]]&lt;br /&gt;
&lt;br /&gt;
Unser erstes Quadrat besteht aus den Punkten 1,2,3,4, dass zweite aus 3,4,5,6 etc. Ich denke, dass das Prinzip dahinter leicht verständlich ist. Anbieten tut sich diese Lösung meist in Schleifen, wenn man längere solcher Quad-Strukturen braucht, die aneinander gereiht sind&lt;br /&gt;
&lt;br /&gt;
=== Vieleckerei ===&lt;br /&gt;
&lt;br /&gt;
Und zu guter letzt GL_POLYGON, was wohl am einfachsten nachzuvollziehen ist ;) Es wird einfach eine Liste von Punkten übergeben und daraus wird schlicht und ergreifend dann ein Vieleck gemacht. Wie variantenreich dies werden kann, könnt ihr Euch vorstellen. Vor allem komplexere und exotischere Formen lassen sich damit darstellen, auch wenn es in diesem Fall eher Leitung kostet als wirklich einbringt. Zeichnet also niemals ein Quadrat mit GL_POLYGON.&lt;br /&gt;
&lt;br /&gt;
Eine mögliche Form wäre zum Beispiel diese:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gl_polygon.png]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBegin(GL_POLYGON);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(0,0,0);&lt;br /&gt;
    glColor3f(0,1,0);&lt;br /&gt;
    glVertex3f(3,0,0);&lt;br /&gt;
    glColor3f(0,0,1);&lt;br /&gt;
    glVertex3f(4,1,0);&lt;br /&gt;
    glColor3f(1,1,0);&lt;br /&gt;
    glVertex3f(2,2,0);&lt;br /&gt;
    glColor3f(0,1,1);&lt;br /&gt;
    glVertex3f(-2,3,0);&lt;br /&gt;
    glColor3f(1,1,1);&lt;br /&gt;
    glVertex3f(-1,1,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ich denke zusammen mit dem Code sollte es keine weiteren Fragen mehr dazu geben. Am besten setzt Ihr Euch nun alle einmal hin und wendet das Wissen Testweise an, denn es muss sitzen und stellt vor allem, wenn es um optimierte Programmierung geht ein absolutes Grundwissen da! Ihr werdet mit etwas praktischer Erfahrung schnell die Grenzen finden, die die einzelnen Parametern mit sich bringen z.B. bei der Texturierung von Objekten!&lt;br /&gt;
&lt;br /&gt;
Ein Quadrat besteht aus einem Face (Fläche), ein Quadrat zusammengesetzt aus zwei Triangle aus zwei. Man könnte zum Beispiel letzteres mit zwei Texturen versehen, ersteres nur mit einer!&lt;br /&gt;
&lt;br /&gt;
== Das Culling-Verfahren ==&lt;br /&gt;
&lt;br /&gt;
=== Wahrheiten und Wirklichkeiten ===&lt;br /&gt;
&lt;br /&gt;
;Dramatik:&lt;br /&gt;
Bevor wir uns mit der eigentlichen Technik des Cullings befassen, möchte ich eine radikale Aufklärung betreiben, die Euer Weltbild für immer verändern wird. Seit gewarnt, dass Ihr nach dem lesen der folgenden Zeilen, Eure Quake3-Gegner mit ganz anderen Augen sehen werdet und eventuell nie wieder zu Eurer alten Denkweise zurückkehren werden könnt!&lt;br /&gt;
&lt;br /&gt;
Habt Ihr Euch bereits gefragt wie die kommerziellen Spiele es schaffen ohne Ruckler (*hust), super Grafiken auf den Bildschirm zu bringen, die auch nach der Optimierung immer noch durch grafische Qualität überzeugen können? Nun, es gibt viele Möglichkeiten seine Szenen zu optimieren, dass wohl einfachste und auch mit am effizientesten ist das so genannte Culling-Verfahren.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_trooper1.gif]]&lt;br /&gt;
&lt;br /&gt;
So sieht unser Protagonist wie gewohnt aus. Wir sehen ihn wie er liebt und lebt. (ja…) Um es philosophisch auszudrücken, sehen wir hier jedoch nur die halbe Wahrheit… ich stelle die wage Behauptung aus, dass wir der Rückseite von ihm gar nicht sehen können und daher auch nicht sagen können, ob sie existiert! Jedes Mal, wenn wir uns um ihn herum bewegen sehen wir nie seine Rückseite. Nun… das wäre auch nicht weiter fatal, wenn ich nicht sofort eine weitere These aufstellen würde: &amp;quot;Der Kerl hat gar keine Rückseite&amp;quot; :-O&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Aber! Wenn wir um ihn herum gehen, dann sehen wir doch seine Rückseite, also ist sie da!&amp;quot;, könnte man mir nun skeptisch zurufen. Tja… beweist mir das Gegenteil und schickt einen Wetteinsatz an mich, ich werde Euch beweisen, dass er immer nur eine Seite hat und seine Rückseite erst erzeugt wird, wenn sie auch benötigt wird. Es würde dann aussage gegen Aussage stehen und ihr könntet mir nicht beweisen, dass Ihr Recht habt, denn wenn wir um ihn herum gehen, würden wir ja wieder seine Rückseite nicht sehen. Ich hingegen würde den Beweis antreten und Euch mitteilen, dass wir ja einfach mal uns die Rückseite ansehen, dann allerdings auf die Forderseite verzichten müssen:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_trooper2.gif]]&lt;br /&gt;
&lt;br /&gt;
Was auf den ersten Blick wie ein verschwommendes Etwas aussieht entpuppt sich auf den zweiten Blick bereits als unser Soldat vom ersten Bild. Skeptisch wird man sich sicherlich fragen, was gesehen ist. Und wenn man genau durch ihn durch sieht, erkennen wir, dass wir hier z.B. seine Hose sehen… und seinen Kopf auch irgendwie von … hinten! Was ist geschehen? Wir betrachten Ihn von vorne und sehen, was hinten ist! (Oh Gott… eine ganze Wirklichkeit stürzt in sich ein *sg* ).&lt;br /&gt;
&lt;br /&gt;
Natürlich ist das verschwommendes Ding da oben nicht ein grafisches Ziel, was wir erreichen wollen, sondern nur der Beweis dafür, dass jede Figur zwei Seiten hat (Erkenntnis, aufschreiben!) und wir in OpenGL bestimmen können, welche Seite wir von Vorne oder Hinten betrachten können.&lt;br /&gt;
&lt;br /&gt;
Ich gebe ja auch zu, dass ich ein wenig getrickst habe und im oberen Bild, die Figur zweimal genredert habe, davon einmal die Vorderseite mit Alpha Blending, weil auf einem unbewegten Bild es schwer zu erkennen wäre, welches die Vorder und welches die Rückseite ist, da wir die Texturen eben gespiegelt auf dem Modell sehen würden.&lt;br /&gt;
&lt;br /&gt;
=== Eine abstrakte Wahrheit… ===&lt;br /&gt;
&lt;br /&gt;
An sich klingt bisher doch auch noch alles recht logisch oder? Den wieso sollte OpenGL die Rückseite von Objekten rendern, wenn man sie gar nicht sehen kann. Grob würde dies eben die doppelte Arbeit sein, die sinnlos getätigt wird. Der Laie wird nun vor Freude an die Decke springen, der erfahrene Programmierer skeptisch die Falten runzeln. &amp;quot;Wie erkennt OpenGL, den das es sich um die Rückseite handelt!&amp;quot;. Gute Frage oder? Des Lösung-Rätsel sollte ein Blick auf die Uhr zeigen… (&amp;lt;== nein, er spinnt nicht (Anm. v. Flo2))&lt;br /&gt;
&lt;br /&gt;
Aber was meine ich damit? Was haben unsere Objekte mit einer Uhr gemeinsam? Was zunächst einem komisch vorkommt ist eigentlich logisch: Es ist die Laufrichtung! Unsere Uhr sollte im Normalfall im Uhrzeigersinn laufen (… scheiß Übergang!). Die Entgegengesetzte Richtung nennen wir dann entgegen des Uhrzeigersinnes. Das wird nun vielleicht den einen oder anderen dazu gebracht haben ein leises Aua von sich zu geben und sich mit der Hand an die Rübe zu schlagen. Diese Erkenntnis ist jedoch grundlegend ^__-&lt;br /&gt;
&lt;br /&gt;
Den auch unsere Objekt haben eine gewissen Definitionsreihen folge. Wir werden die Problematik im Weiteren an Hand eines Quadrates verfolgen ;) Folgender Code ist nicht gleich…&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBegin(GL_QUADS);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(0,0,0);&lt;br /&gt;
    glColor3f(0,1,0);&lt;br /&gt;
    glVertex3f(1,0,0);&lt;br /&gt;
    glColor3f(1,1,1);&lt;br /&gt;
    glVertex3f(1,1,0);&lt;br /&gt;
    glColor3f(0,0,1);&lt;br /&gt;
    glVertex3f(0,1,0);&lt;br /&gt;
    glEnd;&lt;br /&gt;
&lt;br /&gt;
  glBegin(GL_QUADS);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(0,0,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(1,1,0);&lt;br /&gt;
    glVertex3f(0,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(1,1,1);&lt;br /&gt;
    glVertex3f(1,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(0,0,1);&lt;br /&gt;
    glVertex3f(1,0,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wer das Culling nicht verstanden hat, wird sich fragen, wo der unterschied liegt, den beide Stücke beschreiben ein und das gleiche Dreieck. Der Unterschied wird erst deutlich, wenn wir uns die Definitionsreihenfolge vor Augen führen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_order.png]]&lt;br /&gt;
&lt;br /&gt;
Und genau hier ist auch für OpenGL die Definition von &amp;quot;Rückseite&amp;quot;. Normalerweise erwartet OpenGL nämlich, dass alle Punkte gegen den Uhrzeigersinn definiert werden, also so wie beim ersten Quadrat. OpenGL würde in seinem Normalzustand dies dann als die Vorderseite ansehen. Mit einer einzigen Zeile können wir dieses Verhalten jedoch auch verändern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glFrontFace(GL_CW);        //Clock-Wise&lt;br /&gt;
  glFrontFace(GL_CCW);       // Counter Clock-Wise&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es ist jedoch zu empfehlen diese Reihenfolge beizubehalten, da andere Programmierer, die Euren Source lesen, ebenfalls davon ausgehen werden, dass ihr die Punkte gegen den Uhrzeigersinn definieren werdet. Nur bei einigen Optimierungsverfahren macht ein umschalten wirklich Sinn.&lt;br /&gt;
&lt;br /&gt;
=== … und eine verlogene Wirklichkeit ===&lt;br /&gt;
&lt;br /&gt;
Vielleicht springt mal wieder jemand auf und wird mich beschuldigen totalen Mist erzählt zu haben… immerhin zeichnet er vielleicht bei Euch mit beiden Code-Schnipseln die Quadrate? Das liegt einfach daran, dass OpenGL das Culling standardgemäß deaktiviert hat und somit auch beide Seiten rendert. Wie man Culling aktivieren kann, ist schon fast ratbar:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glEnable(GL_CULL_FACE);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit glDisable dementsprechend können wir es wieder deaktivieren. Sollten wir beide Quadrate nebeneinander gerendert haben und eben an den Grundeinstellungen nichts verändert haben, sollte mit der Aktivierung dieser Seite nur noch das linke Dreieck angezeigt werden, weil OpenGL es als Vorderseite ansieht. Wollen wir, dass er nur die &amp;quot;Rückseiten&amp;quot; rendert, so lässt sich dies erfolgreich mit einem weiteren Funktionsauruf realisieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glCullface(GL_FRONT);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit glCullface können wir immer die Seite angeben, die OpenGL vernachlässigen soll. In diesem Fall wäre dies dann die Vorderseite, die nicht gezeichnet werden würde (oder eben das rechte Dreieck).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glCullface(GL_BACK);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Würde wieder den ursprünglichen Status herstellen. Zusammengefasst : glCullFace definiert, ob Vorder oder Rückseite weggelassen werden sollen, glFrontFace definiert, wie die Vorderseite definiert ist (im oder gegen Uhrzeigersinn!) und mit glEnable müssen wir das Culling zunächst aktivieren (und das sollten wir auch immer tun, wenn es möglich ist!).&lt;br /&gt;
&lt;br /&gt;
Ich hoffe, ich habe es halbwegsverständlich erklärt. Wenn jemand sich jetzt fragt, wie man nun die Rückseite sehen kann, wenn wir ein Quadrat haben, dass im Uhrzeigersinn definiert ist… Wenn wir von vorne drauf sehen, werden wir es nicht sehen, da die Punkte in der falschen Reihenfolge definiert sind. Bewegen wir uns durch das Dreieck hin durch und drehen uns um, so werden wir feststellen, dass die Punkte wieder in der &amp;quot;richtigen&amp;quot; Reihenfolge definiert sind und OpenGL sie rendern wird. Wir haben dies hier nur bei sehr simplen Gebilden betrachtet, aber auch bei komplexen ist das Prinzip gleich!&lt;br /&gt;
&lt;br /&gt;
Auch bei möchte ich noch einmal ein gutes Beispiel sehen, wo man bei komplexeren Objekten das Culling gut erkennen kann. Nämlich unserer Landschaft aus dem vierten Tutorial:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_culledland.jpg]]&lt;br /&gt;
&lt;br /&gt;
Dies sieht zunächst nach einem schweren Grafikfehler aus… ist es jedoch nicht. Wir betrachten unsere Landschaft hier nur von einer Seite, die der Spieler nicht sehen würde, nämlich von der Unterseite. Wir sehen die Landschaft von unten bei aktivieren Culling (Anm.: Beim vierten Tutorial ist KEIN Culling von mir aktiviert worden!) und da ich die Quadrate alle richtig definiert habe sehen wir auch nur noch die, die dem Spieler zu gewannt sind. Etwas Fantasie benötigt man schon dazu, um dies wieder zu erkennen, allerdings sieht man es oben rechts doch noch recht gut. Ich hoffe, dass spätestens dieses kleine Beispiel es noch verständlicher gemacht hat, wenn nicht… schaut Euch die Samples an und fragt dann im Forum nach ;)&lt;br /&gt;
&lt;br /&gt;
BTW: Für psychische Folgen, die beim Lesen dieses Tutorials entstanden sind, übernimmt der Autor keine Haftung. Auch er hat bisher keinen glaubwürdigen Beweis dafür gefunden, dass seine Mitmenschen eine Rückseite haben. Auch die Betrachtung dieser mit Hilfe eines Spiegels kann eine zusätzlich Render-Routine der Engine sein, in der wir leben. Es ist jedoch höchst wahrscheinlich, dass sie keine haben, weil … wer sollte die ganze Rechenleistung aufbringen, um die Vorder- und Rückseite aller Menschen zu rendern! :-D&lt;br /&gt;
&lt;br /&gt;
== Für das Protokoll ==&lt;br /&gt;
&lt;br /&gt;
=== Grundgedanken zu Display-Listen ===&lt;br /&gt;
&lt;br /&gt;
Bisher sind wir sicherlich noch nicht in die Verlegenheit gekommen, komplexere Programme zu schreiben. Hat es doch jemand bereits versucht, so wird er schnell gemerkt haben, dass man ohne eine solide Organisation keine Chance hat, ein größeres Projekt zu verwirklichen. Die goldene Regel für eine gelungene Organisation ist sehr einfach! Redundanzen vermeiden um jeden Preis. Man sollte nicht wenn man z.B. ein Quad zeichnen will, diesen 20.000 Mal hintereinander erzeugt. Vielleicht tut es ja auch eine Schleife? Genauso, wie man nicht mehrfach ein Modell in den Speicher laden sollte, wenn es bereits einmal geladen wurde.&lt;br /&gt;
&lt;br /&gt;
Das klingt nun sicherlich wie eine große Verarschung meinerseits. Aber wer jetzt hier geschmunzelt hat, sollte aufpassen, dass ihm nicht gleich das Grinsen im Gesichte stecken bleibt. Würdest Ihr Euer Programm richtig aufbauen? Versuchen eben doppelte Daten zu vermeiden, wo man es nur kann? Wer halbwegs geschickt vorgeht, wird mit Prozeduren, die er immer wieder aufrufen kann eine Menge sinnlosen Code vermeiden. Zum Beispiel wenn wir einen Würfel zeichnen wollen! Wir würden diesen in einer Funktion zeichnen lassen und dann einfach diese Prozedur aufrufen, wenn er gerendert werden soll.&lt;br /&gt;
&lt;br /&gt;
Wünschen wir ihn an einer anderen Stelle in der Welt, so rufen wir einfach die entsprechende Matrix vor dem rendern auf und schon haben wir zwei Würfel in einer Welt mit nur einer Prozedur erzeugt… und die Hälfte an Code gespart. Das lässt sich doch sehen! Auch können wir nun zahlreiche andere Dinge mir in diese Funktion setzen, z.B. eine andere Farbe. Das klingt doch sehr nach einem richtige Ansatz!&lt;br /&gt;
&lt;br /&gt;
=== Vorsicht! Aufnahme ===&lt;br /&gt;
&lt;br /&gt;
OpenGL bietet für ein solches vorgehen ein eigenes System und eine Anzahl von Funktionen, die man unter den Namen &amp;quot;Display-Listen&amp;quot; zusammenfassen kann. Dies kann man sich so vorstellen, dass man einen Namen für eine Liste vergibt (nicht irritieren lassen, ein Name ist in diesem Fall eine Nummer!) und sagt OpenGL, dass alles, was jetzt geschieht mitprotokolliert werden soll. Man kann nun anfangen Objekte zu zeichnen, Farben, Texturen, Materialen, einfach alles was einem in den Sinn kommt zu rendern und OpenGL wird dann all dies in einer Liste zusammen fassen. Es steht dann im fortan für uns auf Abruf zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technisch gesehen ließen sich wohl diese Listen auch mit Prozeduren realisieren, allerdings gibt es auch einen weiteren Vorteil (angeblich… ich konnte ihn nicht nachweisen, evtl. nur einen Sinn in seiner Frühzeit, wo es keine 3D-Karten gab oder nur bei extrem vielen Objekten spürbar…). OpenGL kann diese Befehle nämlich schneller durchführen, weil es die Operationen praktisch vormerkt. Wie auch immer man es sehen mag, Display-Listen sind unglaublich praktisch bei der Programmierung und meist weicht die anfängliche Skepsis durch Begeisterung (und wenn nicht, gehört es zum guten Ton es zu wissen, weil man es häufiger sehen wird)&lt;br /&gt;
&lt;br /&gt;
Zunächst holen wir uns von OpenGL eine Nummer ab, unter welcher wir die Display-Liste später erreichen werden. Diese wird im Idealfall von Typ Integer sein. Der Aufruf ist kinderleicht:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  Displaylist := glGenLists(1);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
glGenLists liefert und eben den gewünschten Wert zurück. Wir können auch stattdessen ein Array of GLUint nehmen und gleich mehre anfordern.&lt;br /&gt;
&lt;br /&gt;
Der Rest ist dann praktisch aufgebaut wie ein glBegin und glEnd, nur dass die entsprechenden Funktionen diesmal glNewList und glEndList heißen und wie folgt verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
glNewList(DisplayList,GL_COMPILE);&lt;br /&gt;
  […]&lt;br /&gt;
glEndList;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das GL_COMPILE bewirkt schlicht und ergreifend, dass sich OpenGL die verwendeten Funktionen merkt. Alternativ könnten wie auch noch GL_COMPILE_AND_EXECUTE einsetzen, wenn wir wollen, dass die Liste auch gleich ausgeführt wird. Praktisch alles was zwischen diesen beiden Funktionen steht wird auch mitgeschrieben. Ausnahmen gibt es wir immer und ich werde dies Mal aufführen, ich denke, dass man erahnen kann, warum diese nicht mit unterstützt werden. Es gibt praktisch keinen Sinn diese mit aufzuzeichnen:&lt;br /&gt;
&lt;br /&gt;
[[glColorPointer]], [[glDeleteLists]], [[glDisableClientState]], [[glEdgeFlagPointer]],&lt;br /&gt;
[[glEnableClientState]], [[glFeedbackBuffer]], [[glFinish]], [[glFlush]], [[glGenLists]],&lt;br /&gt;
[[glIndexPointer]],[[glInterleavedArrays]], [[glIsEnabled]], [[glIsList]], [[glNormalPointer]],&lt;br /&gt;
[[glPopClientAttrib]],[[glPixelStore]], [[glPushClientAttrib]], [[glReadPixels]],&lt;br /&gt;
[[glRenderMode]], [[glSelectBuffer]], [[glTexCoordPointer]], [[glVertexPointer]] und&lt;br /&gt;
alle  [[glGet]] Routinen&lt;br /&gt;
&lt;br /&gt;
=== Und Cut! ===&lt;br /&gt;
&lt;br /&gt;
Idealerweise erzeugen wir diese Listen nicht bei jedem Render-Vorgang, sondern nur einmal im Init und führen diese dann nur noch beim Rendern aus. Dies geschieht mit der Funktion glCallList und sollte genauso leicht zu beherrschen sein, wie die bisherigen auch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glCallList(displaylist);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir übergeben einfach den Namen der Liste und schon wird alles was wir drinnen aufgezeichnet haben auch ausgeführt… Perfekt, oder? Das ganze ist doch recht leicht hand zu haben und sollte niemanden vor einem großen Problem stellen.&lt;br /&gt;
&lt;br /&gt;
Im Sample haben wir ein kleines Astroiden-Feld nachgebildet, eben mit Hilfe dieser Display-Listen. Ich denke, dass dort anschaulich gezeigt wird, wie man sie sinnvoll einsetzen kann. Auch die Demo von Jan Horn &amp;quot;Biohazzard&amp;quot; zeigt eindrucksvoll, wie man es am Geschicktesten machen kann ;)&lt;br /&gt;
&lt;br /&gt;
*g* Ach ja… einen kleinen Nachteil haben die Display-Listen doch noch! (ich weiß, dass ist jetzt so, als würde ich euch ins ein tiefes Becken tauchen lassen und am Boden steht: &amp;quot;Du hast die Sauerstoff-Falsche vergessen…&amp;quot;) Sie verbrauchen relativ viel Speicher! Alle Vorgänge werden nämlich direkt im Arbeitsspeicher geschoben. Eine gesunde Mischung zwischen normalen Rendering und Display-Listen ist also anzuraten, den wenn der Speicher voll ist, hilft keine Optimierung mehr, dass Programm vorm ruckeln zu schützen ;)&lt;br /&gt;
&lt;br /&gt;
Auch sollte man bedenken, dass ein Aufbau z.B. eines Quads sich nicht mehr sehr optimieren lässt. Richtig bringen werden Euch die Display-Listen nur dann etwas, wenn ihr mit komplexen Gebilden arbeitet, die sich immer wieder holen. Würdest ihr z.B. bei jedem Render-Durchgang ein Model verändern, also seine Form (nicht seine Farbe), so würde eine Display-Liste eher hinderlich als nützlich sein!&lt;br /&gt;
&lt;br /&gt;
== Nachwort ==&lt;br /&gt;
&lt;br /&gt;
Bitte nicht traurig sein, wenn hier bereits wieder Ende ist ;) Ich weiß, dass ich in diesem Tutorial mehr versprochen habe, als letztendlich drin ist, aber ich habe vermehrt zu hören bekommen, dass ich dazu neige, wahre Monster-Tutorials zu machen und wollte dem hier mal entgegen wirken, da ich noch rund das doppelte an Stoff gehabt hätte. Drum ist hier nun erstmal Schluss! Es ist nicht ganz das geworden, was zunächst geplant war, aber ich denke, dass das Ziel, nämlich das Grundwissen zu vertiefen erfüllt werden konnte!&lt;br /&gt;
&lt;br /&gt;
Das schreiben dieses Tutorials hat mir sehr viel Spaß gemacht, da ich mich teilweise auch selbst momentan damit beschäftige und es auch unglaublich interessant finde. In diesem Sinne möchte ich dann auch beim sechsten Tutorial weiter machen und mal eine kleine Exkursion unternehmen, nämlich Möglichkeiten aufzuzeigen, Daten aus einem Modellierer in unsere Software zu bekommen. Die Direct3D-Welt hat gesagt, dass sie bereit ist für einen Kampf ist - also sollen sie ihn auch bekommen ^__-&lt;br /&gt;
&lt;br /&gt;
Den mit OpenGL lässt sich weitaus mehr machen als man zunächst erahnt und da die meisten Samples immer nur Dreiecke zeigen wollen wir doch mal etwas Komplexeres machen! Ihr dürft also gespannt sein und wie immer gilt… Ideen und Vorschläge sind herzlich willkommen ;) Im Gegensatz zu D3D hat man bei OpenGL erkannt, dass eine Grafik-API keine Model- und Texture-Loader bieten sollte. Hier ist dann eine Hardcodierung notwendig, die wir allerdings nicht als Nachteil ansehen sollten, sondern vielmehr die Chancen nutzen, unsere Programme mit eigenen Routinen zu optimieren. ;)&lt;br /&gt;
&lt;br /&gt;
Have Fun!&lt;br /&gt;
&lt;br /&gt;
Euer [[Benutzer:Phobeus|Phobeus]]&lt;br /&gt;
&lt;br /&gt;
{{TUTORIAL_NAVIGATION | [[Tutorial Lektion 4]] | [[Tutorial Lektion 7]]}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Tutorial|Lektion5]]&lt;/div&gt;</summary>
		<author><name>Gucky</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_Lektion_5&amp;diff=19871</id>
		<title>Tutorial Lektion 5</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_Lektion_5&amp;diff=19871"/>
				<updated>2006-11-16T21:54:41Z</updated>
		
		<summary type="html">&lt;p&gt;Gucky: /* Ventilatoren und OpenGL */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Artenvielfalten und Ihre Folgen =&lt;br /&gt;
&lt;br /&gt;
== Vorwort ==&lt;br /&gt;
&lt;br /&gt;
Und wieder einmal aufraffen und etwas tippen. Es ist wirklich nicht immer leicht solche Tutorials zu schreiben, vor allem wenn man mal wieder eine Null-Bock-Phase hat. Ich hoffe doch sehr, dass auch Ihr dafür Verständnis habt, den… auch ich bin ein Mensch… egal wie viele Augen und Beine ich habe *sg* Gut… genug &amp;quot;gefaxt&amp;quot;, es geht wieder um den Ernst im Leben: Delphi&lt;br /&gt;
&lt;br /&gt;
Ich habe wirklich eine zeitlang überlegt, was wir machen sollten. Einige haben sich Lichter gewünscht, aber ich konnte mich nicht dazu aufraffen… Stattdessen habe ich irgendwie Lust gehabt, mal was anderes zu machen, etwas wo man etwas kreativ sein kann und was auch mir Spaß macht! :-D Und deswegen, habe ich mir gedacht, dass wir das eine oder andere Wissen, welches zwischendurch behandelt wurde noch etwas vertiefen sollten… einfach in dem wir andere Möglichkeiten aufzeigen, elegant ein Problem zu umschiffen und auch den einen oder anderen Effekt erzielen!&lt;br /&gt;
&lt;br /&gt;
Und genau darum geht es auch diesmal! Ich denke, dass sich dieses Tutorial weitestgehend nur an die richten wird, die bereits ein solides Wissen in OpenGL und Delphi haben, ansonsten wird es wohl schwer sein mir zu folgen, ich werde einiges an Wissen woraus setzen! Wer folgen kann, wird dann mit einem Wissen belohnt werden, dass ihm das eine oder andere Probleme sehr elegant umschiffen lässt in dem man einfach seine Software entsprechend mit OpenGL optimiert! Den nur wer sein Handwerk bis ins Detail beherrscht, darf sich Meister nennen ;)&lt;br /&gt;
&lt;br /&gt;
== Unbekannte Zeichen-Arten ==&lt;br /&gt;
&lt;br /&gt;
=== OpenGL-Maxime ===&lt;br /&gt;
&lt;br /&gt;
Wer nicht gerade erst jetzt hier eingestiegen ist, wird sicherlich bereits bemerkt haben, dass OpenGL streng genommen nach einem sehr einfachen Prinzip arbeitet. Ständig wird an unserer &amp;quot;Zustand-Maschine&amp;quot; etwas manipuliert und mit Matrizen setzen wir die Positionen fest. Jedoch nur an einer einzigen Stelle kommen alle diese Werte zusammen. Nämlich dann, wenn etwas gerendert wird. Genau in diesem Moment werden alle Werte &amp;quot;zusammengerechnet&amp;quot; und erzeugen etwas Sichtbares auf dem Bildschirm. In den meisten Fällen wird dies Eben zwischen glBegin und glEnd geschehen. Und genau diese beiden Funktionen wollen wir nun näher betrachten. Interessant hierbei ist nämlich der Parameter von glBegin…&lt;br /&gt;
&lt;br /&gt;
Streng genommen definieren wir nur eine Anzahl von Punkten zwischen glBegin und glEnd, der Parameter bei glBegin bestimmt aber letztendlich wie diese Verstanden werden sollen. Nur als kleine Übersicht alle verfügbaren Parameter:&lt;br /&gt;
&lt;br /&gt;
 GL_POINTS&lt;br /&gt;
 GL_LINES&lt;br /&gt;
 GL_LINE_STRIP&lt;br /&gt;
 GL_LINE_LOOP&lt;br /&gt;
 GL_TRIANGLES&lt;br /&gt;
 GL_TRIANGLE_STRIP&lt;br /&gt;
 GL_TRIANGLE_FAN&lt;br /&gt;
 GL_QUADS&lt;br /&gt;
 GL_QUAD_STRIP&lt;br /&gt;
 GL_POLYGON&lt;br /&gt;
&lt;br /&gt;
Ich denke aus ethischen Gründen werde ich nun darauf verzichten Euch erneut zu erklären wofür, die bisher für Euch bekannten Parameter (gl_Triangle und gl_Quad) gut sind… jeder wird sich denken können, dass das erste immer per 3er Punkte ein Dreieck bildet, dass zweiter aus 4 Punkten ein Quadrat erzeugt… (mist… *g*) ;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Zunächst war der Strich ===&lt;br /&gt;
&lt;br /&gt;
Interessanter, da neu für uns, sind die restlichen Parameter, auch wenn diese sich weitestgehend selbst erklären. Gl_Points z.B. … könnte es vielleicht bedeuten, dass OpenGL alle mit [[glVertex3f]] definierten Punkte auch nur als Punkte zeichnet?&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gl_point.gif]]&lt;br /&gt;
&lt;br /&gt;
Scheint zu stimmen… ;) Es ist nun auch nicht besonders schwer herzuleiten, was OpenGL mit Vertex-Definitionen macht, die mit gl_Lines beginnen. Versuchen wir doch mal ein Dreieck damit zu zeichnen! Ergo brauchen wir 4 Punkte (der letzte muss, auf den ersten verweisen)…&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gl_lines.gif]]&lt;br /&gt;
&lt;br /&gt;
Doch was ist das? Dies ist kein böswilliger Trick, den ich auf Euch spielen will, sondern folgender Code, ist dafür verantwortlich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBegin(GL_LINES);&lt;br /&gt;
    glVertex3f(-1,0,0);&lt;br /&gt;
    glVertex3f(0,1,0);&lt;br /&gt;
    glVertex3f(1,0,0);&lt;br /&gt;
    glVertex3f(-1,0,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sieht soweit alles in Ordnung aus - ist es auch! Der Fehler liegt nämlich nicht am Code, sondern an uns selbst. Gl_Lines bewirkt nämlich nicht, dass alle Punkte miteinander verbunden werden, sondern nur, dass jeweils Punkte 1 mit Punkte2, Punkte 3 mit Punkt 4 etc. verbunden werden, d.h. immer zweiter Pärchen. Um das Ergebnis zu erreichen, welches wir angestrebt haben (und zwar ohne 6 Punkte zu definieren), wäre in diesem Fall nämlich gl_Line_Strip gewesen, was nämlich bewirkt, dass der &amp;quot;Zeichenstift&amp;quot; immer von seiner letzten Position zum nächst definierten Punkte einen Strich zieht. Dementsprechend sieht dann unser Dreieck mit praktisch dem gleichen Source wie folgt aus:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gl_line_strip.gif]]&lt;br /&gt;
&lt;br /&gt;
hr merkt bereits jetzt wie viele Möglichkeiten einen OpenGL bietet mit nur wenigen Zeilen eine Menge zu verändern. Man bedenkt, dass wir hier nur sehr wenige Punkte haben, allerdings kann man eine Menge Leistung rausschlagen, wenn man statt gl_Lines, gl_Line_Strip verwendet, da einfach weniger Punkte abgearbeitet werden müssen. Logisch oder?&lt;br /&gt;
&lt;br /&gt;
 GL_LINE_LOOP&lt;br /&gt;
&lt;br /&gt;
Der Parameter entspricht praktisch gesehen GL_LINE_STRIP, nur das der letzte Punkt mit dem ersten verbunden wird. Man bräuchte mit diesem Parameter also nur 3 Punkte… das Optimum für unser Dreieck! ;)&lt;br /&gt;
&lt;br /&gt;
== Artenvielfalt ==&lt;br /&gt;
&lt;br /&gt;
Natürlich lassen sich solche Zeichenoperationen auch durch eine Menge anderer Faktoren beeinflussen. Vielleicht hat sich der eine oder andere ja bereits gefragt, wie man es schafft, dass der Rahmen des Dreiecks dicker gezeichnet wird. Sicherlich könnte man nun beginnen und ganz leicht versetzt daneben noch ein Dreieck zu zeichnen. Dies würde dann natürlich einen kleinen Tick größer wirken.&lt;br /&gt;
&lt;br /&gt;
Die Lösung liegt allerdings viel näher - den OpenGL bietet hierfür eine hauseigene Lösung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glLineWidth(3);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir stellen einfach ein, wie OpenGL die Linien Rastern soll. Wir nehmen in diesem Fall die dreifache Dicke von der normalen Einstellung:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gllinewidth.gif]]&lt;br /&gt;
&lt;br /&gt;
Auch werden einige von Euch sicherlich die unschönen Treppchen kennen, die vor allem bei solch einfache Konstruktionen wie diesm dickeren Dreieck auftreten können. Die Lösung dagegen heißt bekanntlich &amp;quot;AntiAliasing&amp;quot; … verschlägt es Euch die Sprache, dass ich gleich mit solch derben Geschützen auffahre?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glEnable(GL_LINE_SMOOTH);&lt;br /&gt;
  glDisable(GL_LINE_SMOOTH);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da verschlägt es einem die Sprache oder? Das war es nämlich auch bereits wieder. Wir schalten einfach einen Zustand um und fertig war die Geschichte! Der Zustands-Maschine von OpenGL sei dank, müssen wir nur noch sagen, was gemacht werden soll, ein lästiges &amp;quot;Wie?&amp;quot; entfällt komplett&lt;br /&gt;
&lt;br /&gt;
Und noch ein kleines Beispiel dafür, wie einfach OpenGL nicht nur Informationen schreiben lässt, sondern diese auch wieder preisgibt. Haben wir vergessen wie Dick wir die Linien eingestellt haben? Kein Problem, den OpenGL bietet folgende Funktionen um zu ermitteln, auf welchen Wert ein &amp;quot;Zustand&amp;quot; geschaltet ist:&lt;br /&gt;
&lt;br /&gt;
[[glGetBooleanv]], [[glGetDoublev]], [[glGetFloatv]], [[glGetIntegerv]]&lt;br /&gt;
&lt;br /&gt;
Ich glaube ich brauche nicht wieder damit zu beginnen darauf hinzuweisen, dass man bei jeder Abfrage auch den richtigen Variablen-Typen verwenden sollte *g*&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glGetIntegerv(gl_line_width,@myint);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und schon haben wir in myint die Stärke mit der OpenGL momentan alle Linien zeichnen soll. Das geht mit fast allen Zuständen, die OpenGL haben kann. Ich denke, das Grundprinzip ist recht leicht verständlich. Beachtet auch, dass OpenGL bei solchen Funktionen immer einen Pointer auf eine Variable erwartet (sprich besser: eine Adresse) und nicht die Variable selbst!&lt;br /&gt;
&lt;br /&gt;
Achso… vergaß ich zu erwähnen, dass wir auch weitere Funktionen auf Striche anwenden können? Zum Beispiel glColor, um den Strich einzufärben? Auch der Z-Buffer lasst sich darauf anwenden, eben alles, was man auch bei einem Dreieck tun könnte (wovon man aber zwingend absehen sollte eine Linie zu texturieren, um keine unnötigen Berechnungen durchzuführen)&lt;br /&gt;
&lt;br /&gt;
=== Ein Dreieck, hat drei Ecken… ===&lt;br /&gt;
&lt;br /&gt;
Nun dreht sich erstmal alles um Dreiecke. Den auch hierfür bietet OpenGL mehre Möglichkeiten, wie die Reihenfolge der Punkte verstanden wird:&lt;br /&gt;
&lt;br /&gt;
 GL_TRIANGLES&lt;br /&gt;
 GL_TRIANGLE_STRIP&lt;br /&gt;
 GL_TRIANGLE_FAN&lt;br /&gt;
&lt;br /&gt;
Zunächst widmen wir uns GL_TRIANGLES! Dieser Parameter sollte uns allen noch geläufig sein. Jeweils 3 Punkte werden zusammen zu einem eigenständigen Dreieck verbunden. Nicht neues für uns, bereits im ersten Tutorial könnt ihr den Beweis finden, dass es klappt :-D&lt;br /&gt;
&lt;br /&gt;
Sicherlich werdet ihr Euch denken können, dass dies sehr hilfreich ist, wenn man nur ein Dreieck zeichnen möchte, nicht jedoch, sobald man mehre in einem Rutsch auf dem Bildschirm bringen möchte. Hierzu bietet sich dann eher GL_TRIANGLE_STRIP an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBegin(GL_TRIANGLE_STRIP);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(-1,0,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(1,1,0);&lt;br /&gt;
    glVertex3f(0,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(1,1,1);&lt;br /&gt;
    glVertex3f(1,0,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(0,1,1);&lt;br /&gt;
    glVertex3f(2,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(0,1,0);&lt;br /&gt;
    glVertex3f(3,0,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wem das ganze zu schwer erscheint, sollte er sich die glColor3f weglassen, die habe ich reingesetzt, damit das ganze auch schön aussieht ;) (Programmierer lieben sinnlosen Spielkram der glänzt und bunt ist). Wer sich nur auf die Punkte konzentriert und sich die Position im Kopfe vorstellt, wird ein Gebilde wie folgt vorstellen können:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gl_triangle_strip.gif]]&lt;br /&gt;
&lt;br /&gt;
Streng genommen macht OpenGL nichts anderes, als die ersten drei Eckpunkte zu nehmen und ein Dreieck daraus zu rendern. Anschließend fällt der erste Punkt weg und es wird das Dreieck zwischen 2,3,4 gerendert usw. Im Anschluss dieses Kapitels werden wir nochmals hieraus zurückkommen und das Culling erklären!&lt;br /&gt;
&lt;br /&gt;
=== Ventilatoren und OpenGL ===&lt;br /&gt;
&lt;br /&gt;
Tja, und nur die wenigstens wissen, dass auch ein Ventilator ein Dreieck sein kann *hust* Okay, begnügen wir uns hier lieber mit dem englischen Begriff &amp;quot;Fan&amp;quot;. (Das ist nicht der Kerl, der vorm Fenster steht, einem gierig anstarrt und laut ruft &amp;quot;Ich will ein Kind von Dir…&amp;quot; … nein, sicher nicht ;)&lt;br /&gt;
&lt;br /&gt;
Vielmehr sollten wir uns wirklich mal bildlich einen Fahrrad-Reifen vorstellen. Von den Außenseiten verlaufen die einzelnen Speichen alle zu einem Mittelpunkt. Nach einem ähnlichen Render-Prinzip funktioniert auch GL_TRIANGLE_FAN. Der einfachheitshalber werden wir hier jedoch kein komplexes Objekt anfertigen, sondern nur eine Möglichkeit zeigen, wie man mit dieser Einstellung sinnvoll ein Objekt zeichnen kann! In unserem Fall nehmen wir einfach einen Drachen (nein… nicht das Fabelwesen), denn auch dieser ist ein Ventilator (Chaos… perfekt….) ;)&lt;br /&gt;
&lt;br /&gt;
Bevor nun jeder abdreht, schauen wir uns doch mal das Objekt an, von dem ich sprach:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gl_triangle_fan.gif]]&lt;br /&gt;
&lt;br /&gt;
In diesem Fall benötigen wir nur 6 (!) Punkte, um dieses Gebilde zu erzeugen. Zentraler Ausgangspunkt ist hierbei die Nummer 1. Wie man erkennen kann besteht das erste Dreieck aus 1,2,3. Das zweite aus 1,3,4… für die abstrakt denkenden Menschen lässt sich daraus folgern, dass OpenGL die Punkte wie folgt abarbeitet 1, n+1, n+2. Oder um es neudeutsch zu sagen, der erste Punkte wird immer mit zwei weiteren verbunden. Im Gegensatz zum Strip fällt allerdings nicht der erste Punkt raus, sondern immer der zweite.&lt;br /&gt;
&lt;br /&gt;
Gl_Triangle_Fans und Gl_Triangle_Strips sind wohl die besten Möglichkeiten, um die Anzahl der im Video-Speicher befindlichen Daten zu begrenzen. Allerdings lässt sich schnell erahnen, dass nicht jeder dieser Methoden wirklich komplexe Objekte zulässt. Man sollte sie jedoch nicht total ignorieren, sondern mit Verstand einsetzen. Ach so, bevor ich es vergesse, der Code dafür:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBegin(GL_TRIANGLE_FAN);&lt;br /&gt;
    glColor3f(1,1,0);&lt;br /&gt;
    glVertex3f(0,0,0);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(0,1,0);&lt;br /&gt;
    glVertex3f(1,0,0);&lt;br /&gt;
    glColor3f(0,0,1);&lt;br /&gt;
    glVertex3f(0,-3,0);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(-1,0,0);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(0,1,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass Punkte 2 und Punkte 6 identisch sind, da er sonst das letzte Dreieck weglassen würde ;)&lt;br /&gt;
&lt;br /&gt;
=== Quadratisch, praktisch, gut! ===&lt;br /&gt;
&lt;br /&gt;
Das ist ne wahrer Marathon geworden. Schlimm, wenn man bedenkt, dass wir uns hier nur mit einem Parameter für eine Funktion beschäftigen. Ihr versteht, warum ich so oft geschrieben haben &amp;quot;lassen wir es lieber und klären nicht jeden Parameter&amp;quot; ;) Aber gut, ich halte es für sehr wichtig, solche Dinge zu beherrschen, weil es einfach zu den Grundlagen dazugehört! Immerhin bleiben nicht mehr sonderlich viele übrig, als ran an die letzten drei!&lt;br /&gt;
&lt;br /&gt;
 GL_QUADS&lt;br /&gt;
&lt;br /&gt;
Sollte auch niemanden von uns mehr sonderlich schockieren können. Jeweils vier übergebende Punkte werden zusammengefasst zu einem Quadrat… nichts weiter Aufregendes. Eigentlich sollten inzwischen auch die GL_QUAD_STRIP nichts wirklich erstaunliches mehr liefern. Wir übergeben zunächst vier Punkte und bei jedem Durchgang entfallen die beiden ersten und werden durch die nächsten zwei ersetzt. OpenGL erstellt dann daraus jedes Mal ein Viereck. Bitte nicht verwechseln mit der Punkt-Reihenfolge von GL_QUADS. Folgendes Beispiel sollte die Problematik verdeutlichen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
glBegin(GL_QUAD_STRIP);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(0,0,0);&lt;br /&gt;
    glVertex3f(0,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(0,0,1);&lt;br /&gt;
    glVertex3f(1,0,0);&lt;br /&gt;
    glVertex3f(1,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(0,1,0);&lt;br /&gt;
    glVertex3f(2,0,0);&lt;br /&gt;
    glVertex3f(2,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(0,1,1);&lt;br /&gt;
    glVertex3f(3,0,0);&lt;br /&gt;
    glVertex3f(3,1,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gl_quad_strip.gif]]&lt;br /&gt;
&lt;br /&gt;
Unser erstes Quadrat besteht aus den Punkten 1,2,3,4, dass zweite aus 3,4,5,6 etc. Ich denke, dass das Prinzip dahinter leicht verständlich ist. Anbieten tut sich diese Lösung meist in Schleifen, wenn man längere solcher Quad-Strukturen braucht, die aneinander gereiht sind&lt;br /&gt;
&lt;br /&gt;
=== Vieleckerei ===&lt;br /&gt;
&lt;br /&gt;
Und zu guter letzt GL_POLYGON, was wohl am einfachsten nachzuvollziehen ist ;) Es wird einfach eine Liste von Punkten übergeben und daraus wird schlicht und ergreifend dann ein Vieleck gemacht. Wie variantenreich dies werden kann, könnt ihr Euch vorstellen. Vor allem komplexere und exotischere Formen lassen sich damit darstellen, auch wenn es in diesem Fall eher Leitung kostet als wirklich einbringt. Zeichnet also niemals ein Quadrat mit GL_POLYGON.&lt;br /&gt;
&lt;br /&gt;
Eine mögliche Form wäre zum Beispiel diese:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_gl_polygon.png]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBegin(GL_POLYGON);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(0,0,0);&lt;br /&gt;
    glColor3f(0,1,0);&lt;br /&gt;
    glVertex3f(3,0,0);&lt;br /&gt;
    glColor3f(0,0,1);&lt;br /&gt;
    glVertex3f(4,1,0);&lt;br /&gt;
    glColor3f(1,1,0);&lt;br /&gt;
    glVertex3f(2,2,0);&lt;br /&gt;
    glColor3f(0,1,1);&lt;br /&gt;
    glVertex3f(-2,3,0);&lt;br /&gt;
    glColor3f(1,1,1);&lt;br /&gt;
    glVertex3f(-1,1,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ich denke zusammen mit dem Code sollte es keine weiteren Fragen mehr dazu geben. Am besten setzt Ihr Euch nun alle einmal hin und wendet das Wissen Testweise an, denn es muss sitzen und stellt vor allem, wenn es um optimierte Programmierung geht ein absolutes Grundwissen da! Ihr werdet mit etwas praktischer Erfahrung schnell die Grenzen finden, die die einzelnen Parametern mit sich bringen z.B. bei der Texturierung von Objekten!&lt;br /&gt;
&lt;br /&gt;
Ein Quadrat besteht aus einem Face (Fläche), ein Quadrat zusammengesetzt aus zwei Triangle aus zwei. Man könnte zum Beispiel letzteres mit zwei Texturen versehen, ersteres nur mit einer!&lt;br /&gt;
&lt;br /&gt;
== Das Culling-Verfahren ==&lt;br /&gt;
&lt;br /&gt;
=== Wahrheiten und Wirklichkeiten ===&lt;br /&gt;
&lt;br /&gt;
;Dramatik:&lt;br /&gt;
Bevor wir uns mit der eigentlichen Technik des Cullings befassen, möchte ich eine radikale Aufklärung betreiben, die Euer Weltbild für immer verändern wird. Seit gewarnt, dass Ihr nach dem lesen der folgenden Zeilen, Eure Quake3-Gegner mit ganz anderen Augen sehen werdet und eventuell nie wieder zu Eurer alten Denkweise zurückkehren werden könnt!&lt;br /&gt;
&lt;br /&gt;
Habt Ihr Euch bereits gefragt wie die kommerziellen Spiele es schaffen ohne Ruckler (*hust), super Grafiken auf den Bildschirm zu bringen, die auch nach der Optimierung immer noch durch grafische Qualität überzeugen können? Nun, es gibt viele Möglichkeiten seine Szenen zu optimieren, dass wohl einfachste und auch mit am effizientesten ist das so genannte Culling-Verfahren.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_trooper1.gif]]&lt;br /&gt;
&lt;br /&gt;
So sieht unser Protagonist wie gewohnt aus. Wir sehen ihn wie er liebt und lebt. (ja…) Um es philosophisch auszudrücken, sehen wir hier jedoch nur die halbe Wahrheit… ich stelle die wage Behauptung aus, dass wir der Rückseite von ihm gar nicht sehen können und daher auch nicht sagen können, ob sie existiert! Jedes Mal, wenn wir uns um ihn herum bewegen sehen wir nie seine Rückseite. Nun… das wäre auch nicht weiter fatal, wenn ich nicht sofort eine weitere These aufstellen würde: &amp;quot;Der Kerl hat gar keine Rückseite&amp;quot; :-O&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Aber! Wenn wir um ihn herum gehen, dann sehen wir doch seine Rückseite, also ist sie da!&amp;quot;, könnte man mir nun skeptisch zurufen. Tja… beweist mir das Gegenteil und schickt einen Wetteinsatz an mich, ich werde Euch beweisen, dass er immer nur eine Seite hat und seine Rückseite erst erzeugt wird, wenn sie auch benötigt wird. Es würde dann aussage gegen Aussage stehen und ihr könntet mir nicht beweisen, dass Ihr Recht habt, den wenn wir um ihn herum gehen, würden wir ja wieder seine Rückseite nicht sehen. Ich hingegen würde den Beweis antreten und Euch mitteilen, dass wir ja einfach mal uns die Rückseite ansehen, dann allerdings auf die Forderseite verzichten müssen:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_trooper2.gif]]&lt;br /&gt;
&lt;br /&gt;
Was auf den ersten Blick wie ein verschwommendes Etwas aussieht entpuppt sich auf den zweiten Blick bereits als unser Soldat vom ersten Bild. Skeptisch wird man sich sicherlich fragen, was gesehen ist. Und wenn man genau durch ihn durch sieht, erkennen wir, dass wir hier z.B. seine Hose sehen… und seinen Kopf auch irgendwie von … hinten! Was ist geschehen? Wir betrachten Ihn von vorne und sehen, was hinten ist! (Oh Gott… eine ganze Wirklichkeit stürzt in sich ein *sg* ).&lt;br /&gt;
&lt;br /&gt;
Natürlich ist das verschwommendes Ding da oben nicht ein grafisches Ziel, was wir erreichen wollen, sondern nur der Beweis dafür, dass jede Figur zwei Seiten hat (Erkenntnis, aufschreiben!) und wir in OpenGL bestimmen können, welche Seite wir von Vorne oder Hinten betrachten können.&lt;br /&gt;
&lt;br /&gt;
Ich gebe ja auch zu, dass ich ein wenig getrickst habe und im oberen Bild, die Figur zweimal genredert habe, davon einmal die Vorderseite mit Alpha Blending, weil auf einem unbewegten Bild es schwer zu erkennen wäre, welches die Vorder und welches die Rückseite ist, da wir die Texturen eben gespiegelt auf dem Modell sehen würden.&lt;br /&gt;
&lt;br /&gt;
=== Eine abstrakte Wahrheit… ===&lt;br /&gt;
&lt;br /&gt;
An sich klingt bisher doch auch noch alles recht logisch oder? Den wieso sollte OpenGL die Rückseite von Objekten rendern, wenn man sie gar nicht sehen kann. Grob würde dies eben die doppelte Arbeit sein, die sinnlos getätigt wird. Der Laie wird nun vor Freude an die Decke springen, der erfahrene Programmierer skeptisch die Falten runzeln. &amp;quot;Wie erkennt OpenGL, den das es sich um die Rückseite handelt!&amp;quot;. Gute Frage oder? Des Lösung-Rätsel sollte ein Blick auf die Uhr zeigen… (&amp;lt;== nein, er spinnt nicht (Anm. v. Flo2))&lt;br /&gt;
&lt;br /&gt;
Aber was meine ich damit? Was haben unsere Objekte mit einer Uhr gemeinsam? Was zunächst einem komisch vorkommt ist eigentlich logisch: Es ist die Laufrichtung! Unsere Uhr sollte im Normalfall im Uhrzeigersinn laufen (… scheiß Übergang!). Die Entgegengesetzte Richtung nennen wir dann entgegen des Uhrzeigersinnes. Das wird nun vielleicht den einen oder anderen dazu gebracht haben ein leises Aua von sich zu geben und sich mit der Hand an die Rübe zu schlagen. Diese Erkenntnis ist jedoch grundlegend ^__-&lt;br /&gt;
&lt;br /&gt;
Den auch unsere Objekt haben eine gewissen Definitionsreihen folge. Wir werden die Problematik im Weiteren an Hand eines Quadrates verfolgen ;) Folgender Code ist nicht gleich…&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glBegin(GL_QUADS);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(0,0,0);&lt;br /&gt;
    glColor3f(0,1,0);&lt;br /&gt;
    glVertex3f(1,0,0);&lt;br /&gt;
    glColor3f(1,1,1);&lt;br /&gt;
    glVertex3f(1,1,0);&lt;br /&gt;
    glColor3f(0,0,1);&lt;br /&gt;
    glVertex3f(0,1,0);&lt;br /&gt;
    glEnd;&lt;br /&gt;
&lt;br /&gt;
  glBegin(GL_QUADS);&lt;br /&gt;
    glColor3f(1,0,0);&lt;br /&gt;
    glVertex3f(0,0,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(1,1,0);&lt;br /&gt;
    glVertex3f(0,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(1,1,1);&lt;br /&gt;
    glVertex3f(1,1,0);&lt;br /&gt;
&lt;br /&gt;
    glColor3f(0,0,1);&lt;br /&gt;
    glVertex3f(1,0,0);&lt;br /&gt;
  glEnd;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wer das Culling nicht verstanden hat, wird sich fragen, wo der unterschied liegt, den beide Stücke beschreiben ein und das gleiche Dreieck. Der Unterschied wird erst deutlich, wenn wir uns die Definitionsreihenfolge vor Augen führen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_order.png]]&lt;br /&gt;
&lt;br /&gt;
Und genau hier ist auch für OpenGL die Definition von &amp;quot;Rückseite&amp;quot;. Normalerweise erwartet OpenGL nämlich, dass alle Punkte gegen den Uhrzeigersinn definiert werden, also so wie beim ersten Quadrat. OpenGL würde in seinem Normalzustand dies dann als die Vorderseite ansehen. Mit einer einzigen Zeile können wir dieses Verhalten jedoch auch verändern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glFrontFace(GL_CW);        //Clock-Wise&lt;br /&gt;
  glFrontFace(GL_CCW);       // Counter Clock-Wise&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es ist jedoch zu empfehlen diese Reihenfolge beizubehalten, da andere Programmierer, die Euren Source lesen, ebenfalls davon ausgehen werden, dass ihr die Punkte gegen den Uhrzeigersinn definieren werdet. Nur bei einigen Optimierungsverfahren macht ein umschalten wirklich Sinn.&lt;br /&gt;
&lt;br /&gt;
=== … und eine verlogene Wirklichkeit ===&lt;br /&gt;
&lt;br /&gt;
Vielleicht springt mal wieder jemand auf und wird mich beschuldigen totalen Mist erzählt zu haben… immerhin zeichnet er vielleicht bei Euch mit beiden Code-Schnipseln die Quadrate? Das liegt einfach daran, dass OpenGL das Culling standardgemäß deaktiviert hat und somit auch beide Seiten rendert. Wie man Culling aktivieren kann, ist schon fast ratbar:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glEnable(GL_CULL_FACE);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit glDisable dementsprechend können wir es wieder deaktivieren. Sollten wir beide Quadrate nebeneinander gerendert haben und eben an den Grundeinstellungen nichts verändert haben, sollte mit der Aktivierung dieser Seite nur noch das linke Dreieck angezeigt werden, weil OpenGL es als Vorderseite ansieht. Wollen wir, dass er nur die &amp;quot;Rückseiten&amp;quot; rendert, so lässt sich dies erfolgreich mit einem weiteren Funktionsauruf realisieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glCullface(GL_FRONT);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit glCullface können wir immer die Seite angeben, die OpenGL vernachlässigen soll. In diesem Fall wäre dies dann die Vorderseite, die nicht gezeichnet werden würde (oder eben das rechte Dreieck).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glCullface(GL_BACK);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Würde wieder den ursprünglichen Status herstellen. Zusammengefasst : glCullFace definiert, ob Vorder oder Rückseite weggelassen werden sollen, glFrontFace definiert, wie die Vorderseite definiert ist (im oder gegen Uhrzeigersinn!) und mit glEnable müssen wir das Culling zunächst aktivieren (und das sollten wir auch immer tun, wenn es möglich ist!).&lt;br /&gt;
&lt;br /&gt;
Ich hoffe, ich habe es halbwegsverständlich erklärt. Wenn jemand sich jetzt fragt, wie man nun die Rückseite sehen kann, wenn wir ein Quadrat haben, dass im Uhrzeigersinn definiert ist… Wenn wir von vorne drauf sehen, werden wir es nicht sehen, da die Punkte in der falschen Reihenfolge definiert sind. Bewegen wir uns durch das Dreieck hin durch und drehen uns um, so werden wir feststellen, dass die Punkte wieder in der &amp;quot;richtigen&amp;quot; Reihenfolge definiert sind und OpenGL sie rendern wird. Wir haben dies hier nur bei sehr simplen Gebilden betrachtet, aber auch bei komplexen ist das Prinzip gleich!&lt;br /&gt;
&lt;br /&gt;
Auch bei möchte ich noch einmal ein gutes Beispiel sehen, wo man bei komplexeren Objekten das Culling gut erkennen kann. Nämlich unserer Landschaft aus dem vierten Tutorial:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tutorial_lektion5_culledland.jpg]]&lt;br /&gt;
&lt;br /&gt;
Dies sieht zunächst nach einem schweren Grafikfehler aus… ist es jedoch nicht. Wir betrachten unsere Landschaft hier nur von einer Seite, die der Spieler nicht sehen würde, nämlich von der Unterseite. Wir sehen die Landschaft von unten bei aktivieren Culling (Anm.: Beim vierten Tutorial ist KEIN Culling von mir aktiviert worden!) und da ich die Quadrate alle richtig definiert habe sehen wir auch nur noch die, die dem Spieler zu gewannt sind. Etwas Fantasie benötigt man schon dazu, um dies wieder zu erkennen, allerdings sieht man es oben rechts doch noch recht gut. Ich hoffe, dass spätestens dieses kleine Beispiel es noch verständlicher gemacht hat, wenn nicht… schaut Euch die Samples an und fragt dann im Forum nach ;)&lt;br /&gt;
&lt;br /&gt;
BTW: Für psychische Folgen, die beim Lesen dieses Tutorials entstanden sind, übernimmt der Autor keine Haftung. Auch er hat bisher keinen glaubwürdigen Beweis dafür gefunden, dass seine Mitmenschen eine Rückseite haben. Auch die Betrachtung dieser mit Hilfe eines Spiegels kann eine zusätzlich Render-Routine der Engine sein, in der wir leben. Es ist jedoch höchst wahrscheinlich, dass sie keine haben, weil … wer sollte die ganze Rechenleistung aufbringen, um die Vorder- und Rückseite aller Menschen zu rendern! :-D&lt;br /&gt;
&lt;br /&gt;
== Für das Protokoll ==&lt;br /&gt;
&lt;br /&gt;
=== Grundgedanken zu Display-Listen ===&lt;br /&gt;
&lt;br /&gt;
Bisher sind wir sicherlich noch nicht in die Verlegenheit gekommen, komplexere Programme zu schreiben. Hat es doch jemand bereits versucht, so wird er schnell gemerkt haben, dass man ohne eine solide Organisation keine Chance hat, ein größeres Projekt zu verwirklichen. Die goldene Regel für eine gelungene Organisation ist sehr einfach! Redundanzen vermeiden um jeden Preis. Man sollte nicht wenn man z.B. ein Quad zeichnen will, diesen 20.000 Mal hintereinander erzeugt. Vielleicht tut es ja auch eine Schleife? Genauso, wie man nicht mehrfach ein Modell in den Speicher laden sollte, wenn es bereits einmal geladen wurde.&lt;br /&gt;
&lt;br /&gt;
Das klingt nun sicherlich wie eine große Verarschung meinerseits. Aber wer jetzt hier geschmunzelt hat, sollte aufpassen, dass ihm nicht gleich das Grinsen im Gesichte stecken bleibt. Würdest Ihr Euer Programm richtig aufbauen? Versuchen eben doppelte Daten zu vermeiden, wo man es nur kann? Wer halbwegs geschickt vorgeht, wird mit Prozeduren, die er immer wieder aufrufen kann eine Menge sinnlosen Code vermeiden. Zum Beispiel wenn wir einen Würfel zeichnen wollen! Wir würden diesen in einer Funktion zeichnen lassen und dann einfach diese Prozedur aufrufen, wenn er gerendert werden soll.&lt;br /&gt;
&lt;br /&gt;
Wünschen wir ihn an einer anderen Stelle in der Welt, so rufen wir einfach die entsprechende Matrix vor dem rendern auf und schon haben wir zwei Würfel in einer Welt mit nur einer Prozedur erzeugt… und die Hälfte an Code gespart. Das lässt sich doch sehen! Auch können wir nun zahlreiche andere Dinge mir in diese Funktion setzen, z.B. eine andere Farbe. Das klingt doch sehr nach einem richtige Ansatz!&lt;br /&gt;
&lt;br /&gt;
=== Vorsicht! Aufnahme ===&lt;br /&gt;
&lt;br /&gt;
OpenGL bietet für ein solches vorgehen ein eigenes System und eine Anzahl von Funktionen, die man unter den Namen &amp;quot;Display-Listen&amp;quot; zusammenfassen kann. Dies kann man sich so vorstellen, dass man einen Namen für eine Liste vergibt (nicht irritieren lassen, ein Name ist in diesem Fall eine Nummer!) und sagt OpenGL, dass alles, was jetzt geschieht mitprotokolliert werden soll. Man kann nun anfangen Objekte zu zeichnen, Farben, Texturen, Materialen, einfach alles was einem in den Sinn kommt zu rendern und OpenGL wird dann all dies in einer Liste zusammen fassen. Es steht dann im fortan für uns auf Abruf zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technisch gesehen ließen sich wohl diese Listen auch mit Prozeduren realisieren, allerdings gibt es auch einen weiteren Vorteil (angeblich… ich konnte ihn nicht nachweisen, evtl. nur einen Sinn in seiner Frühzeit, wo es keine 3D-Karten gab oder nur bei extrem vielen Objekten spürbar…). OpenGL kann diese Befehle nämlich schneller durchführen, weil es die Operationen praktisch vormerkt. Wie auch immer man es sehen mag, Display-Listen sind unglaublich praktisch bei der Programmierung und meist weicht die anfängliche Skepsis durch Begeisterung (und wenn nicht, gehört es zum guten Ton es zu wissen, weil man es häufiger sehen wird)&lt;br /&gt;
&lt;br /&gt;
Zunächst holen wir uns von OpenGL eine Nummer ab, unter welcher wir die Display-Liste später erreichen werden. Diese wird im Idealfall von Typ Integer sein. Der Aufruf ist kinderleicht:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  Displaylist := glGenLists(1);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
glGenLists liefert und eben den gewünschten Wert zurück. Wir können auch stattdessen ein Array of GLUint nehmen und gleich mehre anfordern.&lt;br /&gt;
&lt;br /&gt;
Der Rest ist dann praktisch aufgebaut wie ein glBegin und glEnd, nur dass die entsprechenden Funktionen diesmal glNewList und glEndList heißen und wie folgt verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
glNewList(DisplayList,GL_COMPILE);&lt;br /&gt;
  […]&lt;br /&gt;
glEndList;&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das GL_COMPILE bewirkt schlicht und ergreifend, dass sich OpenGL die verwendeten Funktionen merkt. Alternativ könnten wie auch noch GL_COMPILE_AND_EXECUTE einsetzen, wenn wir wollen, dass die Liste auch gleich ausgeführt wird. Praktisch alles was zwischen diesen beiden Funktionen steht wird auch mitgeschrieben. Ausnahmen gibt es wir immer und ich werde dies Mal aufführen, ich denke, dass man erahnen kann, warum diese nicht mit unterstützt werden. Es gibt praktisch keinen Sinn diese mit aufzuzeichnen:&lt;br /&gt;
&lt;br /&gt;
[[glColorPointer]], [[glDeleteLists]], [[glDisableClientState]], [[glEdgeFlagPointer]],&lt;br /&gt;
[[glEnableClientState]], [[glFeedbackBuffer]], [[glFinish]], [[glFlush]], [[glGenLists]],&lt;br /&gt;
[[glIndexPointer]],[[glInterleavedArrays]], [[glIsEnabled]], [[glIsList]], [[glNormalPointer]],&lt;br /&gt;
[[glPopClientAttrib]],[[glPixelStore]], [[glPushClientAttrib]], [[glReadPixels]],&lt;br /&gt;
[[glRenderMode]], [[glSelectBuffer]], [[glTexCoordPointer]], [[glVertexPointer]] und&lt;br /&gt;
alle  [[glGet]] Routinen&lt;br /&gt;
&lt;br /&gt;
=== Und Cut! ===&lt;br /&gt;
&lt;br /&gt;
Idealerweise erzeugen wir diese Listen nicht bei jedem Render-Vorgang, sondern nur einmal im Init und führen diese dann nur noch beim Rendern aus. Dies geschieht mit der Funktion glCallList und sollte genauso leicht zu beherrschen sein, wie die bisherigen auch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pascal&amp;gt;&lt;br /&gt;
  glCallList(displaylist);&lt;br /&gt;
&amp;lt;/pascal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir übergeben einfach den Namen der Liste und schon wird alles was wir drinnen aufgezeichnet haben auch ausgeführt… Perfekt, oder? Das ganze ist doch recht leicht hand zu haben und sollte niemanden vor einem großen Problem stellen.&lt;br /&gt;
&lt;br /&gt;
Im Sample haben wir ein kleines Astroiden-Feld nachgebildet, eben mit Hilfe dieser Display-Listen. Ich denke, dass dort anschaulich gezeigt wird, wie man sie sinnvoll einsetzen kann. Auch die Demo von Jan Horn &amp;quot;Biohazzard&amp;quot; zeigt eindrucksvoll, wie man es am Geschicktesten machen kann ;)&lt;br /&gt;
&lt;br /&gt;
*g* Ach ja… einen kleinen Nachteil haben die Display-Listen doch noch! (ich weiß, dass ist jetzt so, als würde ich euch ins ein tiefes Becken tauchen lassen und am Boden steht: &amp;quot;Du hast die Sauerstoff-Falsche vergessen…&amp;quot;) Sie verbrauchen relativ viel Speicher! Alle Vorgänge werden nämlich direkt im Arbeitsspeicher geschoben. Eine gesunde Mischung zwischen normalen Rendering und Display-Listen ist also anzuraten, den wenn der Speicher voll ist, hilft keine Optimierung mehr, dass Programm vorm ruckeln zu schützen ;)&lt;br /&gt;
&lt;br /&gt;
Auch sollte man bedenken, dass ein Aufbau z.B. eines Quads sich nicht mehr sehr optimieren lässt. Richtig bringen werden Euch die Display-Listen nur dann etwas, wenn ihr mit komplexen Gebilden arbeitet, die sich immer wieder holen. Würdest ihr z.B. bei jedem Render-Durchgang ein Model verändern, also seine Form (nicht seine Farbe), so würde eine Display-Liste eher hinderlich als nützlich sein!&lt;br /&gt;
&lt;br /&gt;
== Nachwort ==&lt;br /&gt;
&lt;br /&gt;
Bitte nicht traurig sein, wenn hier bereits wieder Ende ist ;) Ich weiß, dass ich in diesem Tutorial mehr versprochen habe, als letztendlich drin ist, aber ich habe vermehrt zu hören bekommen, dass ich dazu neige, wahre Monster-Tutorials zu machen und wollte dem hier mal entgegen wirken, da ich noch rund das doppelte an Stoff gehabt hätte. Drum ist hier nun erstmal Schluss! Es ist nicht ganz das geworden, was zunächst geplant war, aber ich denke, dass das Ziel, nämlich das Grundwissen zu vertiefen erfüllt werden konnte!&lt;br /&gt;
&lt;br /&gt;
Das schreiben dieses Tutorials hat mir sehr viel Spaß gemacht, da ich mich teilweise auch selbst momentan damit beschäftige und es auch unglaublich interessant finde. In diesem Sinne möchte ich dann auch beim sechsten Tutorial weiter machen und mal eine kleine Exkursion unternehmen, nämlich Möglichkeiten aufzuzeigen, Daten aus einem Modellierer in unsere Software zu bekommen. Die Direct3D-Welt hat gesagt, dass sie bereit ist für einen Kampf ist - also sollen sie ihn auch bekommen ^__-&lt;br /&gt;
&lt;br /&gt;
Den mit OpenGL lässt sich weitaus mehr machen als man zunächst erahnt und da die meisten Samples immer nur Dreiecke zeigen wollen wir doch mal etwas Komplexeres machen! Ihr dürft also gespannt sein und wie immer gilt… Ideen und Vorschläge sind herzlich willkommen ;) Im Gegensatz zu D3D hat man bei OpenGL erkannt, dass eine Grafik-API keine Model- und Texture-Loader bieten sollte. Hier ist dann eine Hardcodierung notwendig, die wir allerdings nicht als Nachteil ansehen sollten, sondern vielmehr die Chancen nutzen, unsere Programme mit eigenen Routinen zu optimieren. ;)&lt;br /&gt;
&lt;br /&gt;
Have Fun!&lt;br /&gt;
&lt;br /&gt;
Euer [[Benutzer:Phobeus|Phobeus]]&lt;br /&gt;
&lt;br /&gt;
{{TUTORIAL_NAVIGATION | [[Tutorial Lektion 4]] | [[Tutorial Lektion 7]]}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Tutorial|Lektion5]]&lt;/div&gt;</summary>
		<author><name>Gucky</name></author>	</entry>

	<entry>
		<id>https://wiki.delphigl.com/index.php?title=Tutorial_Lektion_3&amp;diff=19700</id>
		<title>Tutorial Lektion 3</title>
		<link rel="alternate" type="text/html" href="https://wiki.delphigl.com/index.php?title=Tutorial_Lektion_3&amp;diff=19700"/>
				<updated>2006-09-28T10:04:27Z</updated>
		
		<summary type="html">&lt;p&gt;Gucky: /* Vom Pushen und Poppen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Eine Welt des Grauens =&lt;br /&gt;
== Vorwort ==&lt;br /&gt;
Liebe Leser,&lt;br /&gt;
nachdem Ihr hoffentlich den kleinen Schock des letzten Tutorials alle gut überstanden habt, freue ich mich, Euch hier wieder begrüßen zu können. Wer dachte, dass die letzte Lektion bereits schwer war, der sollte dringend Urlaub nehmen. Seid gewarnt! Bevor ich jetzt richtig loslege, solltet Ihr einigermaßen entspannt sein. Wenn dies einer der Tage ist, an denen Ihr nur noch durch eine Kippe oder die Flasche Coke am Leben gehalten werdet, ordne ich erstmal eine kleine Zwangspause an ;).&lt;br /&gt;
&lt;br /&gt;
Der Stoff der jetzt kommt ist sicherlich nicht gerade die leichteste Lektüre. Wer Mathematik studiert, hat Vorteile, wer einigermaßen logisch denken kann auch. Ein Fachidiot, wie ich, hat nur eine Chance... probieren, testen und lernen :). Und weil ich weiß, wie schwer es eventuell sein kann, werde ich nun einfach mal versuchen, ganz simpel anzufangen und das Ganze mit vielen Bildern so gut wie nur irgend möglich zu illustrieren. &lt;br /&gt;
&lt;br /&gt;
Und wenn Ihr nun Angst habt, dass Ihr das nicht packt, weil ich hier so eine Panik verbreite, dann solltet Ihr mich sehen, wie ich hier verzweifelt vor meinen Tasten hänge und mich frage, wie ich das alles bloß in Worte fassen soll :). Aber was soll es? Ran an den Kram!!! Heulen könnt Ihr anschließend im Forum ;).&lt;br /&gt;
&lt;br /&gt;
== Das Grauen hat einen Namen ==&lt;br /&gt;
=== &amp;quot;Kinofilm&amp;quot; vs. &amp;quot;Bahnhof&amp;quot; ===&lt;br /&gt;
Wer kennt nicht &amp;quot;Matrix&amp;quot; und hätte gedacht, dass es davon nicht nur eine, sondern unendlich viele gibt die so genannten &amp;quot;Matrizen&amp;quot;. Vor allem am Anfang werden diese Dinger Euch das Leben erschweren und Ihr werdet leichte Neigungen tief in Euch verspüren, dass am besten Euer ganzes Projekt sich nur im Zentrum Eurer Welt abspielt (und dies ist nicht im wahrsten Sinne des Wortes gemeint).&lt;br /&gt;
&lt;br /&gt;
Wer mit Matrizen umgehen kann, beherrscht das Wichtigste an der 3D-Programmierung. Wer nicht zu den Genies zählt, sollte nicht sofort aufgeben, sondern sich viele Beispiele ansehen und viel mit diesen herumspielen.&lt;br /&gt;
&lt;br /&gt;
Wie auch immer... Ihr solltest wissen, dass es bei jedem Rendervorgang mehrere Matrizen gibt, die ganz drastisch das Aussehen der Szene bestimmen. Es gibt in OpenGL drei Bereiche, in denen Matrizen eingesetzt werden. Die so genannte World- oder Modelviewmatrix, die Texturenmatrix und die Perspektivenmatrix. Die zweifellos wichtigste dieser Matrizen ist die Worldmatrix, da sie die Position Eures &amp;quot;Zeichenstiftes&amp;quot; beschreibt und somit Objekte positioniert. Die Texturenmatrix funktioniert fast genauso wie die Worldmatrix nur definiert sie die Ausrichtung der Texturen. Wir werden später auf sie zurückkommen. Die Perspektivenmatrix definiert wesentliche Eigenschaften des Blickfeldes des Betrachters.&lt;br /&gt;
&lt;br /&gt;
Der Befehl [[glMatrixMode]] erlaubt eine Änderung der aktuellen Matrix. Mit Hilfe der Konstanten GL_MODELVIEW, GL_TEXTURE oder GL_PROJECTION kann man die Matrix bestimmen. Die Namen der Konstanten sind soweit hoffentlich selbst erklärend. Von nun an bewirken alle Matrixoperationen wie beispielsweise [[glLoadIdentity]] oder [[glTranslate|glTranslate*]] eine Veränderung der aktuell gesetzten Matrix.&lt;br /&gt;
&lt;br /&gt;
=== Die Perspektivenmatrix ===&lt;br /&gt;
Wie bereits erwähnt beschreibt die Perspektivenmatrix das aktuelle Sichtfeld. Zum Setzen der Perspektivenmatrix verwendet man in der Regel Befehle wie [[glFrustum]], [[gluPerspective]], [[glOrtho]] oder [[gluOrtho2D]].&lt;br /&gt;
&lt;br /&gt;
gluPerspective lässt den Raum beispielsweise dreidimensional erscheinen, indem sich die Größe von Objekten mit zunehmender Entfernung vom Betrachter verringert. Diese Verringerung ist abhängig von dem im ersten Parameter übergebenen Winkel. &lt;br /&gt;
&lt;br /&gt;
glOrtho hingegen ist phantastisch für ein 2D-Blickfeld geeignet, denn diese Art der Projektion enthält keine Tiefe mehr.&lt;br /&gt;
&lt;br /&gt;
Genauere Informationen zu den einzelnen Funktionen könnt ihr unserem OpenGL-Wiki entnehmen.&lt;br /&gt;
&lt;br /&gt;
Auf eine Sache möchte ich jedoch noch eingehen: Einige dieser Funktionen verlangen die Parameter &amp;quot;znear&amp;quot; und &amp;quot;zfar&amp;quot;. Diese Parameter beschreiben die zwei Schnittflächen, die das Blickfeld vor dem Betrachter begrenzen. &amp;quot;znear&amp;quot; definiert die Entfernung der Nearclippingplane vom Betrachter. Alle Objekte, die sich vor dieser Ebene befinden sind nicht sichtbar. &amp;quot;zfar&amp;quot; beschreibt die Entfernung der Farclippingplane vom Betrachter. Alle Objekte, die sich hinter dieser befinden werden weggeschnitten. Wenn also wieder einmal nur die Hälfte Eurer Szene auf dem Bildschirm sichtbar ist, versucht die Schnittflächen entsprechend anzupassen!&lt;br /&gt;
&lt;br /&gt;
== Die Worldmatrix ==&lt;br /&gt;
=== D3D-Verrat ===&lt;br /&gt;
Wenn Ihr ebenfalls zu den D3D-Verrätern gehört (wie z.&amp;amp;nbsp;B. ich *g*), dann solltet Ihr Euch möglichst von eurer Denkweise trennen: Jede Positionierung der Kamera erfolgt über ein Drehen und Bewegen der Szene. Das ist vor allem am Anfang ein wenig gewöhnungsbedürftig (&amp;quot;Die Welt dreht sich! Nicht Du!&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
Vielmehr wird jede Manipulation direkt an die Worldmatrix übergeben. Das heißt, wenn Ihr glTranslate* aufruft, so ist diese Manipulation bereits bei Euch in die Worldmatrix eingetragen, alle weiteren Objekte werden mit dieser Matrix transformiert. Wer zweimal glTranslate*(2, 0, 0) aufruft wird feststellen, dass glTranslate* nicht zwei Matrizen zurückliefert, sondern nur eine, die dann auf (4, 0, 0) verweist. Am Anfang solltet Ihr also sehr darauf achten, es wird eine Weile dauern bis Ihr dies fest in Eurem Herzen tragt. Nicht sofort aufgeben :)!&lt;br /&gt;
&lt;br /&gt;
== Einfach, Kompakt und Funktional ==&lt;br /&gt;
Neben dem Befehl glTranslate*, den wir im letzten Tutorial lieben gelernt haben, gibt es noch weitere Befehle, die sich auf die einzelnen Matrizen auswirken und auf die ich in dem folgenden Abschnitt eingehen möchte.&lt;br /&gt;
&lt;br /&gt;
=== Dreh- und Angelpunkt des Geschehens ===&lt;br /&gt;
Zunächst beschäftigen wir uns mit der Rotation. Wir versuchen ein Objekt zu erzeugen, welches um 90° um seine eigene Achse gedreht wurde. Das ist nicht sonderlich schwer: &amp;lt;pascal&amp;gt;[[glRotate|glRotatef]](90,0,1,0);&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Ein Objekt auf das wir diese Matrix anwenden, wird um 90° um seine Y-Achse gedreht sein. Daraus schließen wir, dass der erste Parameter den Winkel im Gradmaß um den wir rotieren möchten definiert und die 3 folgenden Parameter den Vektor beschreiben um den die Rotation durchgeführt werden soll. Rotationen bergen tatsächlich eine Schwierigkeit, denn es ist nicht immer einfach ein Objekt um die Eigene Achse zu rotieren. Verschiebt Ihr euer Objekt zuerst und beginnt dann die Rotation, so rotiert das Objekt um die eigene Achse.&lt;br /&gt;
&lt;br /&gt;
Ruft Ihr jedoch zuerst glRotate* und anschließend glTranslate* auf, so unterscheidet sich erzielte Ergebnis von dem vorherigen.&lt;br /&gt;
&lt;br /&gt;
Um diesen Problemen aus dem Weg zu gehen so müsst Ihr Euch vorstellen, dass Ihr den Rotationspunkt im Moment des Aufrufs von glRotate* setzt. Verschiebt Ihr Euer Objekt anschließend so bleibt der Rotationspunkt derselbe und das Objekt beginnt den Rotationspunkt auf einer Bahn zu umkreisen. So gesehen gibt es keinen wirklichen Unterschied und die Rotation um die eigene Achse ist ein Ausnahmefall, denn der Rotationspunkt liegt dann in dem von uns erwarteten Mittelpunkt des Objektes.&lt;br /&gt;
&lt;br /&gt;
=== Eine Frage der Größe ===&lt;br /&gt;
Nun ja... was kann man denn noch alles in einer 3D-Welt machen? Ihr habt gelernt, wie man Objekte bewegt und diese durch Rotationen ausrichten kann. Das ist doch schon eine Menge zum Spielen oder? Nun, bei einer Transformation kann für uns noch eine Sache sehr wichtig werden, nämlich die Möglichkeit, zu bestimmen in welcher Größe ein Objekt dargestellt werden kann.&lt;br /&gt;
&lt;br /&gt;
Sicher würde es Sinn machen ein benötigtes Objekt gleich in der richtigen Größe in die Anwendung zu laden. Manchmal ist es aber von Vorteil, wenn man die Größe eines Objektes ohne großen Aufwand verändern kann oder aber mehrere Objekte desselben Typs mit unterschiedlicher Größe darzustellen.&lt;br /&gt;
&lt;br /&gt;
Eine Größenveränderung ist mit Hilfe des Befehls [[glScale|glScale*]] möglich.&lt;br /&gt;
&amp;lt;pascal&amp;gt;glScalef(1,1,1);&amp;lt;/pascal&amp;gt;&lt;br /&gt;
In diesem Fall würden wir das Objekt so zeichnen, wie wir es auch angegeben haben. Das heißt in seiner Originalgröße. Sicherlich ahnst Du bereits, was die Parameter aussagen... sie stehen für das Verhältnis der einzelnen Seiten nach dem Muster X, Y und Z. Würden wir als ersten Parameter statt einer 1 eine 2 einsetzen, würde unser Objekt in seiner räumlichen Ausdehnung bezogen auf der X-Achse doppelt so groß sein. Würden wir stattdessen 0.5 angeben, wäre es nur noch halb so groß.&lt;br /&gt;
&lt;br /&gt;
In Wirklichkeit bewirkt glScale* jedoch keine Veränderung der Größe sondern eine Verzerrung unseres Koordinatensystems. Daher wirkt jeder Aufruf von glScale* auch auf Befehle wie glTranslate*. Aus Sicht der Matrizen lässt sich dieses Phänomen auch recht leicht erklären. In Wirklichkeit erzeugt jeder Aufruf von glRotate*, glTranslate* oder glScale* eine eigene Matrix, die dann mit der aktuellen Matrix (z.B. der Modelviewmatrix) multipliziert wird. Wurde in der Modelviewmatrix bereits ein glScale* verewigt, so wirkt sich dieses bei der Multiplikation der Matrizen auf die Transformationsmatrix aus.&lt;br /&gt;
&lt;br /&gt;
Ich denke ich muss nicht näher darauf eingehen, dass es keine gute Idee ist das Koordinatensystem mit einem Aufruf von glScalef(0, 0, 0) zu zerstören. Was würde es auch für einen Sinn ergeben, das Koordinatensystem in einem Punkt unterzubringen? &amp;lt;br&amp;gt;&lt;br /&gt;
'''''Hinweis''': Es ist bereits gefährlich nur eine Achse auf 0 zu skalieren, denn dadurch wird die aktuelle Matrix beschädigt ([http://www.math.unizh.ch/fachverein/forum/detail.jsp?FORUM=120 singulär]) und Befehle wie [[gluProject]] und [[gluUnProject]] funktionieren nicht mehr.''&lt;br /&gt;
&lt;br /&gt;
Ich hoffe ich habe Euch nun nicht den letzten Funken Hoffnung OpenGL zu verstehen geraubt. Ich kann Euch versichern, dass dieses Verständnis mit der Zeit heranreifen wird.&lt;br /&gt;
[[bild:Tutimg_lektion3_skalierung.gif|256px|right]]&lt;br /&gt;
glScale* ist aber aufgrund seiner phantastischen Eigenschaften nicht nur in der Lage die Größe von Objekten zu verändern sondern es ist auch möglich Objekte zu spiegeln indem man negative Werte an glScale* übergibt. Somit kann sich z.&amp;amp;nbsp;B. ein D3D-Umsteiger das leben vereinfachen indem er mit einem Aufruf von glScalef(1, 1, -1) sicherstellt, dass die Z-Achse in den Bildschirm hinein positiv verläuft.&lt;br /&gt;
&lt;br /&gt;
Jeder, der die Funktion der Modelviewmatrix begreifen möchte, dem möchte ich das Programm Matrixcontrol von Lithander (siehe Anhang) ans Herz legen. Es erlaubt Euch bestimmte Änderungen an der Matrix direkt nachzuvollziehen und wenn man mit dem Programm ein wenig umherspielt wird selbst dem letzten ziemlich schnell ein Licht aufgehen. Wenn nicht, dann hilft nur fleißiges Programmieren und Anwenden und irgendwann klatscht es dann laut, wenn die eigene Handfläche auf der Stirn zum stehen kommt ;).&lt;br /&gt;
&lt;br /&gt;
== Virtuelle Gedächtnisse ==&lt;br /&gt;
=== Vom Pushen und Poppen ===&lt;br /&gt;
Wer diesen Titel ließt und sich nun zwangsläufig daran erinnert, was er bereits alles in seinem Leben konsumiert hat oder neben wem er am nächsten Morgen aufgewacht ist, sei entwarnt! Wir reden hier von etwas ganz anderem...&lt;br /&gt;
&lt;br /&gt;
Wie wir bereits gelernt haben, verändert jeder Aufruf von glTranslate*, glRotate* oder glScale* die Worldmatrix und wirkt sich unmittelbar auf andere Objekte aus. Dies kann durchaus sehr erwünscht sein. Wenn wir allerdings in einer großen Welt verschiedene Objekte positionieren, kann dies schnell zum Fluch werden. Natürlich können wir dem entgegenwirken und zum Beispiel jedes Mal glLoadIdentity aufrufen. Dies hat jedoch den Nachteil, dass die World- Matrix dann eben wieder leer ist und wir z.B. die Camera erst wieder setzen müssen, damit die Objekte an die richtige Stelle transformiert werden.&lt;br /&gt;
&lt;br /&gt;
Eine weitaus elegantere Lösung ist die Verwendung des &amp;quot;Matrixstacks&amp;quot;! Jeder Aufruf von [[glPushMatrix]] bewirkt, dass die momentane Matrix auf den [[Stack]] geschrieben wird. Alle Manipulationen, die wir dann durchführen, werden wie gewohnt vollzogen. Ist alles gezeichnet, rufen wir mit [[glPopMatrix]], die letzte Matrix vom Stack wieder herunter und haben im Prinzip den letzten Zustand vor unseren Veränderungen rekonstruiert und können dann wieder anfangen, auf dieser Matrix aufzubauen. Natürlich lassen sich auch mehre Matrizen auf den Stack pushen! Dies kann reichlich Zeit sparen, wenn man bedenkt, dass man ansonsten andauernd die Matrix der Szene neu setzen muss.&lt;br /&gt;
&lt;br /&gt;
Um das ganze etwas anschaulicher zu machen, verwenden wir ein einfaches Beispiel. Wir werden zwei Dreiecke jeweils rechts und links vom Ursprung zeichnen. Nachdem wir das linke Objekt gezeichnet haben, werden wir nicht glLoadIdentity einsetzen oder das zweite Objekt entsprechend nach rechts transformieren, sondern eben den Matrixstack zu Hilfe nehmen.&lt;br /&gt;
&amp;lt;pascal&amp;gt;glLoadIdentity();&lt;br /&gt;
glTranslatef(0,0,-10);&lt;br /&gt;
glPushMatrix();&lt;br /&gt;
  glTranslatef(-2,0,0);&lt;br /&gt;
  glBegin(GL_TRIANGLES);&lt;br /&gt;
    glColor3f(1,0,0); glVertex3f(-1,-1, 0);&lt;br /&gt;
    glColor3f(0,0,1); glVertex3f( 1,-1, 0);&lt;br /&gt;
    glColor3f(0,1,0); glVertex3f( 0, 1, 0);&lt;br /&gt;
  glEnd();&lt;br /&gt;
glPopMatrix();&lt;br /&gt;
&lt;br /&gt;
glTranslatef(2,0,0);&lt;br /&gt;
glBegin(GL_TRIANGLES);&lt;br /&gt;
  glColor3f(1,0,0); glVertex3f(-1,-1, 0);&lt;br /&gt;
  glColor3f(0,0,1); glVertex3f( 1,-1, 0);&lt;br /&gt;
  glColor3f(0,1,0); glVertex3f( 0, 1, 0);&lt;br /&gt;
glEnd();&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Wie Ihr seht, setzen wir am Anfang praktisch die Distanz zum Objekt, speichern die Matrix und zeichnen das erste Objekt. Wir setzen dann die Matrix wieder zurück, so dass sie nur noch den Aufruf von glTranslatef(0, 0, -10) beschreibt und transformieren das zweite Objekt... fertig. Man kann auch ohne diese Matrixstacks leben sie können einem aber das Leben auch sehr erleichtern.&lt;br /&gt;
&lt;br /&gt;
== I wanna all! ==&lt;br /&gt;
Wer glaubt, dass es das bereits war der irrt. Es gibt noch weitere Möglichkeiten auf die Matrix Einfluss zu nehmen. Der Befehl [[glLoadMatrix|glLoadMatrix*]] erlaubt es eine beliebige 4x4 Matrix zu laden und die aktuelle Matrix durch die geladene komplett zu ersetzen.&lt;br /&gt;
&lt;br /&gt;
Der Befehl [[glMultMatrix|glMultMatrix*]] multipliziert die übergebene 4x4-Matrix mit der aktuellen Matrix. Mit diesem Befehl könnte man beispielsweise eigene glScale*- und glRotate*-Befehle schreiben, obgleich sich auch hier über den Sinn streiten lässt. Eine sinnvollere Anwendungsmöglichkeit wird in einem späteren Tutorial vorgestellt werden.&lt;br /&gt;
&lt;br /&gt;
Der Befehl [[glGet|glGet*]] mit den Tokens GL_MODELVIEW_MATRIX, GL_TEXTURE_MATRIX und GL_PROJECTION_MATRIX ermöglicht euch die einzelnen Matrizen anzufordern.&lt;br /&gt;
&lt;br /&gt;
== Weltenformeln ==&lt;br /&gt;
=== Hier kommt die Sonne... ===&lt;br /&gt;
Genug der Theorie! Es wird langsam Zeit für etwas Praxis...&lt;br /&gt;
&lt;br /&gt;
Nun, um ehrlich zu sein, habe ich an der folgenden Idee ein wenig Zeit benötigt, um ein einigermaßen gut anschauliches Beispiel zu finden... ich will hier nicht weiter darauf eingehen, was ich gerade gehört habe, als mir die Sonne aufging :-D.&lt;br /&gt;
&lt;br /&gt;
Wie auch immer, man kann komplexere Matrixtransformationen sehr leicht mit unseren Sonnensystem beschreiben (Hey, wenn jetzt jemand denkt, dass ich spinne...). Um das Ganze etwas übersichtlicher zu gestalten, werden wir unser Prinzip auf Sonne, Mond und Erde beschränken.&lt;br /&gt;
&lt;br /&gt;
Wie funktioniert unser Sonnensystem? (Warnung, der Autor betritt mal wieder seine ausgeprägte Fantasywelt! [Anm. d. Lektors: Die richtige Sprache dafür hat er ja schon]).&lt;br /&gt;
&lt;br /&gt;
Die Sonne steht im Mittelpunkt und sollte sich in unserem Beispiel nicht selbst bewegen. Sie ist einfach nur im Mittelpunkt und vegetiert dort vor sich hin! Nun gibt es einen kleinen blauen Planeten, namens Erde (wieso er im Sample ein Dreieck ist, sei Eurer Kreativität überlassen...), der zum einen um seine eigene Achse rotiert, viel wichtiger jedoch, sich auch noch um die Sonne dreht. Nun werden sich vor allem die Neulinge unter Euch sadistisch freuen, das Fenster schließen, ihr Delphi starten und drauf los proggen! Das ist doch alles kein Ding, der gerade mal frisch aufgeschnappte Sinus-Satz lässt sich prima mit einbringen! Und schon berechnet man mit Hilfe von Sinus, die Position auf der Kreisbahn und positioniert den Planeten mit glTranslate* an seine Position. Und oh Wunder es klappt. HALT! Wer es nicht gemerkt hat, da war ne Portion Ironie dabei. Bitte Weiterlesen ^__^!&lt;br /&gt;
&lt;br /&gt;
Denn spätestens wenn wir folgende Ergänzung zum Besten geben, werden die ersten Augen wässrig werden, weil die meisten Hirne einem Informationskoller unterliegen. Meines stieg ja schon beim einfachen Sinus aus :-D. Ab und an kann man nämlich nachts weißgräuliche Flecken am Himmel beobachten! Wer annimmt, dass es sich hierbei um eine geschickt platzierte, in Echtzeit berechnete, Textur handelt, ist schief gewickelt! Es handelt sich dabei nämlich um ein Decal, namens Mond. Oha... so schlimm war es schon lange nicht mehr. Dieser bewegt sich in unserem Beispiel auch nicht entlang der Erdachse, sondern auf einer schiefen Bahn, damit man auf der unteren Hemissphäre den Mond auch am Tage noch sehen kann ^___^ (*selbstgefälliges, göttliches Grinsen*). Dies lässt sich nur sehr schwer mit Sinus beschreiben. Bevor wir uns nun jedoch mit einem Block bewaffnen oder den nächsten Mathematik-Professor entführen, damit er die Mathematik für uns übernimmt, greifen wir lieber zu den Sternen und nehmen ein einfacheres Verfahren! Les Matrices!&lt;br /&gt;
&lt;br /&gt;
=== Und es dreht sich doch! ===&lt;br /&gt;
Die Vorgehensweise ist eigentlich relativ simpel. Wir Zeichnen unsere Sonne im Mittelpunkt unseres Universums. Mit dem Aufruf von glRotate* setzen wir den Rotationspunkt an die Position unseres &amp;quot;Zeichenstiftes&amp;quot;. In unserem Fall ist das noch immer die Position der Sonne. &lt;br /&gt;
&lt;br /&gt;
Nun verschieben wir die Erde nach links und die erste Hürde ist genommen. Jetzt müssen wir nur noch den Mond setzen und das funktioniert genauso einfach wie zuvor mit der Erde.&lt;br /&gt;
&lt;br /&gt;
Unser Zeichenstift befindet sich nun an der Position der Erde und daher rufen wir glRotate* auf. Nun müssen wir den Zeichenstift nur noch ein kleines Stück neben der Erde positionieren und den Mond zeichnen. Voila! Es ist vollbracht!&lt;br /&gt;
&lt;br /&gt;
Wenn man genauer darüber nachdenkt erkennt man sehr schnell wie elegant man dieses verhältnismäßig komplizierte Problem gelöst hat.&lt;br /&gt;
&lt;br /&gt;
Wer nun auf Wolke Sieben schwebt und glaubt er könnte sich nun an den nächsten DOOMTitel werfen, der sollte das Programm zuvor noch so erweitern, dass die Sonne, die Erde und auch der Mond sich zusätzlich um die eigene Achse drehen.&lt;br /&gt;
&lt;br /&gt;
== [[Timebased Movement]] ==&lt;br /&gt;
Wer bereits versucht hat eines der ersten selbst geschriebenen OpenGL-Programme voller Stolz seinem Kumpel vorzuführen wird festgestellt haben, dass die Bewegungen auf dem anderen Rechner schneller oder langsamer abgelaufen sind als es auf dem eigenen Rechner der Fall war.&lt;br /&gt;
&lt;br /&gt;
Dieses Phänomen lässt sich sehr einfach erklären. Nehmen wir an wir bewegen ein Dreieck von links nach rechts um den Wert 1 über den Bildschirm. Eine alte Krücke wie mein PC schafft vielleicht 50 FPS. Das bedeutet das Objekt wird in einer Sekunde genau 50mal um eine Einheit nach rechts verschoben. Das bedeutet insgesamt also um 50 Einheiten in einer Sekunde. Wenn das Programm nun aber auf einem High-End-System läuft werden wir die Szene... sagen wir... 500mal aktualisieren können. Folglich bewegt sich auf diesem Rechner das Dreieck in einer Sekunde um 500 Einheiten.&lt;br /&gt;
&lt;br /&gt;
Das einzige was wir von unserem Dreieck erhaschen können ist ein blitzartiges Zucken auf dem Bildschirm, welches wir schnell als optische Täuschung abtun würden. Wie kann man diesem Problem nun aber entgegen wirken?&lt;br /&gt;
&lt;br /&gt;
Nehmen wir an wir kennen die Zeit, die wir zum Zeichnen eines Frames benötigen und ermitteln anhand dieses Wertes einen Zeitfaktor, den wir in die Bewegung mit einfließen lassen.&lt;br /&gt;
&amp;lt;pascal&amp;gt;NewPosition := OldPosition + Movement * TimeFactor;&amp;lt;/pascal&amp;gt;&lt;br /&gt;
Handelt es sich um einen schnellen Rechner so ist der Zeitfaktor entsprechend niedrig und die Bewegung pro Frame verringert sich. Ist der Rechner hingegen langsam so besitzen wir einen hohen Zeitfaktor. Folglich legt das Dreieck auf dem schnelleren Rechner pro Frame weniger Weg zurück als auf dem langsamen. Insgesamt jedoch legen die beiden Dreiecke im gleichen Zeitabstand (z.&amp;amp;nbsp;B. in einer Sekunde) denselben Weg zurück.&lt;br /&gt;
&lt;br /&gt;
Um den Zeitfaktor zu ermitteln muss man lediglich die Zeit für einen Schleifendurchlauf ermitteln. Um eine möglichst hohe Genauigkeit zu erhalten sollte man die Zeit für den letzten Schleifendurchlauf ermitteln und im aktuellen Schleifendurchlauf verwenden.&lt;br /&gt;
&lt;br /&gt;
== Nachwort ==&lt;br /&gt;
Wow... und wieder ein wenig Zeit zum Ausspannen und ein paar persönlichen Worten. Ich kann getrost nur eines sagen: Wenn Ihr nun denkt &amp;quot;das war alles ja sehr leicht&amp;quot; habt Ihr irgendetwas falsch gemacht und solltet das Tutorial noch einmal von Vorn durcharbeiten ;). Wenn Ihr jedoch leichte Kopfschmerzen verspürt (ob es nun die Matrizen sind oder der fiese Elementarbereich *sg*...) so habt Ihr alles richtig gemacht ;).&lt;br /&gt;
&lt;br /&gt;
Matrizen sind vor allem vielen Einsteigern sehr fremd. &amp;quot;Übung macht den Meister&amp;quot;. Versucht Euch doch einfach mal die eine oder andere Aufgabe selbst zu stellen oder unser &amp;quot;Sonnensystem&amp;quot; selbst einmal nachzuschreiben. Wenn Ihr glaubt, dass das Ganze ganz nett geworden ist, dann schickt es mir doch einfach mal zu. Ich freue mich immer darüber zu sehen, was für Früchte meine Tutorials tragen. *lach* Und wenn sich plötzlich alles um die Erde dreht, dann habe ich a) das Gefühl, dass die Kenntnisse über das Sonnensystem nicht mehr ganz up to date sind oder b) ich einen ziemlich fatalen Fehler beim Erklären gemacht habe :-&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Schreckt auch bitte nicht davor zurück, eine Frage diesbezüglich im Forum zu stellen. Es ist keine Schande, mit einer Matrix Probleme zu haben und vor allem die Erfahrenen unter uns, sollten nur zu gut wissen, was alles die Ursache dafür sein kann, wenn man plötzlich gar nichts mehr auf dem Screen sieht :). &lt;br /&gt;
&lt;br /&gt;
Das [[Tutorial Matrix2]] behandelt das Thema Matrizen, und vor allem wie man Sachen in OpenGL positioniert, noch genauer.&lt;br /&gt;
&lt;br /&gt;
Wir selbst werden uns in den folgenden Kapiteln noch etwas intensiver mit den Matrizen beschäftigen und unter anderem die eine oder andere interessante Spielerei zeigen!&lt;br /&gt;
&lt;br /&gt;
'''Euer'''&amp;lt;br&amp;gt;&lt;br /&gt;
'''Phobeus'''&lt;br /&gt;
&lt;br /&gt;
== Anhang ==&lt;br /&gt;
&lt;br /&gt;
[http://www.pixelpracht.net Pixelpracht] -  Hier könnt Ihr das Programm &amp;quot;Matrix Control&amp;quot; finden&lt;br /&gt;
&lt;br /&gt;
{{TUTORIAL_NAVIGATION | [[Tutorial Lektion 2]] | [[Tutorial Lektion 4]]}}&lt;br /&gt;
[[Kategorie:Tutorial|Lektion3]]&lt;/div&gt;</summary>
		<author><name>Gucky</name></author>	</entry>

	</feed>