Tutorial Quickstart: Unterschied zwischen den Versionen

Aus DGL Wiki
Wechseln zu: Navigation, Suche
(Der OpenGL-Header)
K (Methode 1: Timer)
 
(43 dazwischenliegende Versionen von 9 Benutzern werden nicht angezeigt)
Zeile 3: Zeile 3:
 
==Einleitung==
 
==Einleitung==
  
Willkommen beim Delphi & OpenGL Quickstart. Diese kurze Einleitung soll Euch auf die Tutorials bei DelphiGL.com und allgemein auf die Grafikprogrammierung mit OpenGL und Delphi vorbereiten. Dieser Quickstart ist kein Tutorial für sich, sondern soll ein Grundgefühl vermitteln wie OpenGL und Delphi miteinander arbeiten.
+
Willkommen beim Delphi & OpenGL Quickstart. Diese kurze Einleitung soll euch auf die Tutorials bei DelphiGL.com und allgemein auf die Grafikprogrammierung mit OpenGL und Delphi vorbereiten. Dieser Quickstart ist kein Tutorial für sich, sondern soll ein Grundgefühl vermitteln, wie OpenGL und Delphi miteinander arbeiten.
  
Wozu OpenGL? Mit OpenGL kann man eine Vielzahl von Aufgaben bewältigen. Ob man Forschungsergebnisse aller Art visualisieren, 2D oder 3D Spiele schreiben oder einfach seiner Anwendung eine Oberfläche geben möchte, die nicht dem windowsgrauen Standardlook entspricht. All das ist möglich mit OpenGL.
+
{{Hinweis|Dieses Tutorial beschreibt den Einstieg in OpenGL auf Basis eines Delphi-VCL* Ansatzes. Dieses Tutorial basiert damit nicht auf SDL oder easySDL wie die folgenden Einsteigertutorials. Aus folgenden Gründen sollte man trotzdem dieses Tutorial lesen: <br/>
 +
#Weil der hier vorgestellte Ansatz für eure (Windows-)Projekte genutzt werden kann (easySDL ist nicht dafür gedacht).<br/>
 +
#Weil ihr so seht wie der Code aus den easySDL Beispielen in ein VCL Beispiel übertragen werden kann.<br/>
 +
#Weil der zweite Abschnitt des Tuts ein paar allgemeine Worte zur Spieleprogrammierung enthält, '''die jeder Einsteiger lesen sollte'''.}}
 +
<small>''*Zur Erklärung für nicht Delphiprogrammierer: die VCL ist die '''Visuelle Component Library''' von Delphi. Diese enthält die GUI Komponenten (z.B. Formulare) aus denen man unter Delphi Anwendungen zusammensetzen kann.''</small>
 +
 
 +
''Wozu OpenGL?'' Mit OpenGL kann man eine Vielzahl von Aufgaben bewältigen. Ob man Forschungsergebnisse aller Art visualisieren, 2D oder 3D Spiele schreiben oder einfach seiner Anwendung eine Oberfläche geben möchte, die nicht dem grauen Standardlook entspricht. All das ist möglich mit OpenGL.
  
 
==Wie fange ich an?==
 
==Wie fange ich an?==
Genau zwei Dinge braucht der OpenGL-Programmierer um effektiv  arbeiten zu können:
+
Genau zwei Dinge braucht der OpenGL-Programmierer, um effektiv  arbeiten zu können:
 
#Einen OpenGL Header
 
#Einen OpenGL Header
#Eine Codebasis von der aus man neue Projekte starten kann (ein sog. Template)
+
#Eine Codebasis, von der aus man neue Projekte starten kann (ein sogenanntes Template)
  
 +
===Der OpenGL-Header===
 +
Das is ja easy! Denn Delphi bringt ja schon einen OpenGL-Header mit...
  
===Der OpenGL-Header===
 
Das is ja easy! Denn Delphi bringt ja schon einen OpenGL-Header mit... <br>
 
 
'''STOP!'''
 
'''STOP!'''
  
Denn wir reden von einem guten Header. Leider ist der original von Delphi mitgelieferte Header alles andere als zu empfehlen. Er ist fehlerhaft, hält sich nicht an OpenGL-Normen und außerdem ist er absolut veraltet.
+
Denn wir reden hier von einem guten Header. Leider ist der standardmäßig von Delphi mitgelieferte Header alles andere als zu empfehlen. Er ist fehlerhaft, hält sich nicht an OpenGL-Normen und ist außerdem absolut veraltet.
  
 
Was nun? Ganz einfach: Bei [http://www.DelphiGL.com DelphiGL.com] (kurz DGL) gibt es '''DEN''' OpenGL-Header für alle Pascalsprachen:  
 
Was nun? Ganz einfach: Bei [http://www.DelphiGL.com DelphiGL.com] (kurz DGL) gibt es '''DEN''' OpenGL-Header für alle Pascalsprachen:  
 
'''Die [[DGLOpenGL.pas]].'''
 
'''Die [[DGLOpenGL.pas]].'''
  
Diesen solltet Ihr Euch jetzt besorgen, wenn Ihr ihn nicht schon habt. Der Header wird bei neuen OpenGL-Versionen vom DGL-Team aktualisiert.
+
Diesen solltet Ihr euch jetzt besorgen, wenn Ihr ihn nicht schon habt. Der Header wird bei neuen OpenGL-Versionen vom DGL-Team aktualisiert, und ist so immer auf der Höhe der Zeit.
  
 
===Codebasis/Templates===
 
===Codebasis/Templates===
So... das war schon alles was Ihr aus dem Netz benötigt. Den Rest machen wir jetzt per Hand.
+
So... das war schon alles, was Ihr aus dem Netz benötigt. Den Rest machen wir jetzt per Hand.
  
Im nächsten Kapitel zeige ich Euch wie man sich ein einfaches Template schreibt. Natürlich hat DelphiGL.com auch bereits fertige Lösungen, die durchaus zu empfehlen sind und auch extra Features wie Vollbildrendering besitzen. ABER aus Erfahrung kann ich sagen: Man findet sich im eigenen Code viel einfacher zurecht. (Und die Extras kann man nachher immer noch einbauen.)
+
Im nächsten Kapitel zeige ich euch wie man sich ein einfaches [[Template]] schreibt. Natürlich hat DelphiGL.com auch bereits [[:Kategorie:TemplateFile|fertige Lösungen]], die durchaus zu empfehlen sind und auch extra Features wie Vollbildrendering besitzen. ABER aus Erfahrung kann ich sagen: Man findet sich im eigenen Code viel einfacher zurecht. (Und die Extras kann man nachher immer noch einbauen.)
  
 +
==Das Template - Delphi fit für OpenGL machen==
 +
Bevor man wirklich loslegen kann, muss noch die runtergeladene DGLOpenGL.pas an den richtigen Ort gebracht werden. Gut wäre z.B., sie in das Verzeichnis "\lib" in Eurem Delphiverzeichnis zu legen. (Wenn ihr das [[DGLSDK]] verwendet, wurden die Suchpfade schon eingerichtet.)
  
 +
Dann startet mal Delphi. Vor euch sollte jetzt ein leeres Projekt erscheinen. Das leere Formular kann gleich minimiert werden, denn jetzt wird erstmal hübsch gecodet.
  
==Das Template – Oder Delphi fit für OpenGL machen==
+
{{Hinweis|Lest das Tutorial erst einmal durch bevor ihr versucht die Teile zusammen zu fügen. Vieles wird erst im Zusammenhang klar. Wer Zweifel an der Richtigkeit seines Templates hat, kann sich ein DGL-Template runterladen, um den Code als Vorlage zu verwenden.}}
Bevor man wirklich loslegen kann, muss noch die runtergeladene DGLOpenGL.pas an den richtigen Ort gebracht werden. Gut wäre z.B. sie in das Verzeichnis "\lib" in Eurem Delphiverzeichnis zu legen. (Wenn ihr den [[DGLSDK]] verwendet wurden die Suchpfade schon eingerichtet.)
+
{{Hinweis|Am Ende von Kapitel 1.3 findet ihr den Kopf der Template-Klasse. Dort seht ihr auch, welche Variablen von welchem Typ deklariert werden müssen.}}
 
 
Dann startet mal Delphi. Vor Euch sollte jetzt ein leeres Projekt erscheinen. Das leere Formular kann gleich minimiert werden, denn jetzt wird erstmal hübsch gecodet.<br>
 
''(Am Ende von Kapitel 1.3 findet ihr den Kopf der Template-Klasse. Dort seht ihr auch welche Variablen, von welchem Typ deklariert werden müssen. )''
 
  
 
===Initialisieren von OpenGL===
 
===Initialisieren von OpenGL===
Zeile 43: Zeile 49:
  
 
Die eigentliche Initialisierung soll direkt beim Erstellen des Formulars gemacht werden. Deshalb kommt der folgende Quelltext ins OnCreate-Ereignis des Formulars.
 
Die eigentliche Initialisierung soll direkt beim Erstellen des Formulars gemacht werden. Deshalb kommt der folgende Quelltext ins OnCreate-Ereignis des Formulars.
<pascal>procedure TForm1.FormCreate(Sender: TObject);
+
<source lang="pascal">procedure TForm1.FormCreate(Sender: TObject);
 
begin
 
begin
 
   DC:= GetDC(Handle);
 
   DC:= GetDC(Handle);
Zeile 54: Zeile 60:
 
                               0);
 
                               0);
 
   ActivateRenderingContext(DC, RC);
 
   ActivateRenderingContext(DC, RC);
end;</pascal>
+
end;</source>
 
'''Zeile 3:''' Hier wird der Gerätekontext (Device Context) von Formular Form1 abgefragt.
 
'''Zeile 3:''' Hier wird der Gerätekontext (Device Context) von Formular Form1 abgefragt.
  
'''Zeile 4:''' Mit InitOpenGL wird OpenGL initialisiert. Wenn das nicht funktioniert wird die gesamte Anwendung sofort beendet.  
+
'''Zeile 4:''' Mit InitOpenGL wird OpenGL initialisiert. Wenn das nicht funktioniert, wird die gesamte Anwendung sofort beendet.  
  
'''Zeile 5:''' Hier wird der [[Renderkontext]] erzeugt. Den braucht OpenGL zum Zeichnen auf das Formular. Was die Parameter genau bewirken lernt ihr im [[Tutorial_lektion1]].
+
'''Zeile 5:''' Hier wird der [[Renderkontext]] erzeugt. Den braucht OpenGL zum Zeichnen auf das Formular. Mehr dazu lernt ihr im [[Tutorial_lektion1]].
  
 
'''Zeile 11:''' Abschließend wird der Renderkontext aktiviert. OpenGL ist jetzt prinzipiell startbereit.
 
'''Zeile 11:''' Abschließend wird der Renderkontext aktiviert. OpenGL ist jetzt prinzipiell startbereit.
Zeile 65: Zeile 71:
 
{{Hinweis|DC und RC sind Eigenschaften des Formulars. Siehe [[Tutorial_quickstart#Das_fertige_Templateformular|Definition des Templateformulars]].}}
 
{{Hinweis|DC und RC sind Eigenschaften des Formulars. Siehe [[Tutorial_quickstart#Das_fertige_Templateformular|Definition des Templateformulars]].}}
  
Nach dieser durchaus simplen Initialisierung (man kann auch alles per Hand machen was InitOpenGL macht!) steht OpenGL ziemlich nackt da. Soll heißen, alle OpenGL Eigenschaften/Zustände stehen auf den definierten Anfangswerten. Es kommt aber durchaus oft - eigentlich ständig - vor, dass bestimmte Einstellungen von OpenGL benutzt werden sollen. Deshalb schreiben wir uns noch eine kleine Zusatzprozedur: SetupOpenGL
+
Nach dieser durchaus simplen Initialisierung (man kann auch alles per Hand machen, was InitOpenGL macht!) steht OpenGL ziemlich nackt da. Soll heißen, alle OpenGL Eigenschaften/Zustände stehen auf den definierten Anfangswerten. Es kommt aber durchaus oft - eigentlich ständig - vor, dass bestimmte Einstellungen von OpenGL benutzt werden sollen. Deshalb schreiben wir uns noch eine kleine Zusatzprozedur: SetupGL
  
<pascal>procedure TForm1.SetupGL;
+
<source lang="pascal">procedure TForm1.SetupGL;
 
begin
 
begin
   glClearColor(0.3, 0.4, 0.7, 0.0); //Hintergrundfarbe
+
   glClearColor(0.3, 0.4, 0.7, 0.0); //Hintergrundfarbe: Hier ein leichtes Blau
 
   glEnable(GL_DEPTH_TEST);          //Tiefentest aktivieren
 
   glEnable(GL_DEPTH_TEST);          //Tiefentest aktivieren
 
   glEnable(GL_CULL_FACE);          //Backface Culling aktivieren
 
   glEnable(GL_CULL_FACE);          //Backface Culling aktivieren
end;</pascal>
+
end;</source>
  
Was hier passiert wird durch die Kommentare bereits erklärt (Für mehr Infos siehe [[Tiefentest]] bzw. [[Backface Culling]]). Die Hintergrundfarbe könnt ihr nach Belieben einstellen. (Wenn ihr später einmal geschlossene Szenen rendern wollt, dann ist es günstig eine sehr schräge Farbe als Hintergrundfarbe einzustellen, so findet man leichter Fehler in der Szene.)
+
Was hier passiert, wird durch die Kommentare bereits erklärt (Für mehr Infos siehe [[Tiefentest]] bzw. [[Backface Culling]]).  
 +
{{Hinweis|Backface Culling ist eine simple, aber effektive Methode eure Anwendung zu beschleunigen. Allerdings muss man bei der Ausgabe auf die Reihenfolge der Vertices achten. Lest euch deshalb kurz den Artikel zu Backface Culling durch oder kommentiert die obige Zeile einfach ganz aus. Sonst könnte es passieren, dass Teile eurer Szene plötzlich "verschwinden".}}
 +
Die Hintergrundfarbe könnt ihr nach Belieben (mit [[glClearColor]]) einstellen. (Wenn ihr später einmal geschlossene Szenen rendern wollt, dann ist es für die Entwicklung günstig eine sehr schräge Farbe als Hintergrundfarbe einzustellen, so findet man leichter Fehler in der Szene. In OpenGL werden Farben immer als Mischung aus Rot, Grün und Blau angegeben. Dies wird in späteren Tutorials noch erklärt. Aber herumspielen an den Werten hilft hier auch schnell weiter. ;-) )
  
Außerdem hat man ja hin und wieder auch noch globale Variablen, die man initialisieren möchte. Da wir mit solchen Sachen unser schön aufgeräumtes '''FormCreate''' nicht zumüllen wollen bietet sich ein Unterprogramm namens '''InitGlobals''' oder kurz '''Init''' an.
+
Außerdem hat man ja hin und wieder auch noch globale Variablen, die man initialisieren möchte. Da wir mit solchen Sachen unser schön aufgeräumtes '''FormCreate''' nicht zumüllen wollen, bietet sich ein Unterprogramm namens '''InitGlobals''' oder kurz '''Init''' an. (Wobei das jetzt nicht direkt etwas mit OpenGL zu tun hat.)
 
Beide Unterprogramme (SetupGL und Init) sollten am Ende von '''FormCreate''' gerufen werden:
 
Beide Unterprogramme (SetupGL und Init) sollten am Ende von '''FormCreate''' gerufen werden:
  
<pascal>  [...]
+
<source lang="pascal">  [...]
 
   ActivateRenderingContext(DC, RC);
 
   ActivateRenderingContext(DC, RC);
 
   SetupGL;
 
   SetupGL;
   Init;
+
   Init; //Was man sonst so initialisieren will.
end;</pascal>
+
end;</source>
  
 
===Die Ereignisbehandlung===
 
===Die Ereignisbehandlung===
Für OpenGL sind vor allem die Ereignisse von Bedeutung, die an der Zeichenfläche von OpenGL herumwerkeln. Da OpenGL direkt auf das Formular (oder auch auf ein Panel) zeichnet, müssen Ereignisse, die diese Zeichenfläche ändern, behandelt werden. Dies wären das '''OnCreate-''' und das '''OnDestroy'''-Ereignis.
+
Für OpenGL sind vor allem die Ereignisse von Bedeutung, die an der Zeichenfläche von OpenGL herumwerkeln. Da OpenGL direkt auf das Formular (oder auch auf ein Panel) zeichnet, müssen Ereignisse, die diese Zeichenfläche ändern, behandelt werden. Dies wären das '''OnResize-''' und das '''OnDestroy'''-Ereignis.
  
 
Zuerst '''FormResize''':
 
Zuerst '''FormResize''':
<pascal>procedure TForm1.FormResize(Sender: TObject);
+
<source lang="pascal">procedure TForm1.FormResize(Sender: TObject);
 
var tmpBool : Boolean;
 
var tmpBool : Boolean;
 
begin
 
begin
Zeile 100: Zeile 108:
 
   glLoadIdentity;
 
   glLoadIdentity;
 
   IdleHandler(Sender, tmpBool);
 
   IdleHandler(Sender, tmpBool);
end;</pascal>
+
end;</source>
  
Außerdem müssen im '''const''' – Teil die beiden Konstanten Near- bzw. FarClipping definiert werden. Diese geben die Entfernung für die [[Clipping Plane|Clippingebenen]] (Szenenbegrenzung) an.
+
Außerdem müssen im '''const''' Teil die beiden Konstanten Near- bzw. FarClipping definiert werden. Diese geben die Entfernung für die [[Clipping Plane|Clippingebenen]] (Szenenbegrenzung) an und spielen beim [[Tiefentest]] eine gewisse Rolle.
  
<pascal>  NearClipping = 1;
+
<source lang="pascal">  NearClipping = 1;
   FarClipping  = 1000;</pascal>
+
   FarClipping  = 1000;</source>
  
'''Zu FormResize''':<br>
+
'''Zu FormResize''':
'''Zeile 2:''' Diese Boolean-Variable wird in Zeile 11 verwendet und ist nur ein Dummy.
 
  
'''Zeile 4:''' Mittels [[glViewport]] sagt Ihr OpenGL wie groß die OpenGL-Ausgabe werden soll. Genau diese Größe hatte sich ja durch das Resize verändert.
+
'''Zeile 2:''' Diese Boolean-Variable wird in Zeile 11 verwendet und ist nur ein [[Dummy]].
  
'''Zeile 5/9:''' Hier seht Ihr 2 der 3 möglichen Matrixmodi. '''GL_PROJECTION''' wird benutzt um nachfolgend die OpenGL-Ausgabe zu manipulieren, '''GL_MODELVIEW''' benutzt man um OpenGL mit Daten zu füttern.
+
'''Zeile 4:''' Mittels [[glViewport]] sagt Ihr OpenGL, wie groß die OpenGL-Ausgabe werden soll. Genau diese Größe hatte sich ja durch das Resize verändert.
 +
 
 +
'''Zeile 5/9:''' Hier seht Ihr zwei der drei möglichen Matrixmodi. '''GL_PROJECTION''' wird benutzt, um nachfolgend die OpenGL-Ausgabe zu manipulieren, '''GL_MODELVIEW''', um OpenGL mit Daten zu füttern.
  
 
'''Zeile 6:''' [[glLoadIdentity]] füllt die aktuelle Matrix mit der Identitätsmatrix.  
 
'''Zeile 6:''' [[glLoadIdentity]] füllt die aktuelle Matrix mit der Identitätsmatrix.  
  
'''Zeile 7:''' Hier wird eingestellt wie der Betrachter die Welt sehen soll.
+
'''Zeile 7:''' Hier wird eingestellt, wie der Betrachter die Welt sehen soll.  
 
 
'''Zeile 11:''' Was der IdleHandler macht kommt später im Abschnitt 1.3.3 (Zeichenroutine).
 
  
 +
'''Zeile 11:''' Was der IdleHandler macht, kommt später im Abschnitt 1.3.3 (Zeichenroutine).
  
 
Nun noch schnell das '''FormDestroy''':
 
Nun noch schnell das '''FormDestroy''':
  
<pascal>procedure TForm1.FormDestroy(Sender: TObject);
+
<source lang="pascal">procedure TForm1.FormDestroy(Sender: TObject);
 
begin
 
begin
 
   DeactivateRenderingContext;
 
   DeactivateRenderingContext;
 
   DestroyRenderingContext(RC);
 
   DestroyRenderingContext(RC);
 
   ReleaseDC(Handle, DC);
 
   ReleaseDC(Handle, DC);
end;</pascal>
+
end;</source>
  
 
Was es macht? Steht doch da: Den RenderingContext deaktivieren und freigeben.
 
Was es macht? Steht doch da: Den RenderingContext deaktivieren und freigeben.
 
  
 
===Die Zeichenroutine===
 
===Die Zeichenroutine===
 
Das Herzstück unseres Templates fehlte bisher. Irgendwann muss der Grafikkarte ja auch gesagt werden, was sie denn überhaupt ausgeben soll. Das kommt jetzt: '''TForm1.Render'''
 
Das Herzstück unseres Templates fehlte bisher. Irgendwann muss der Grafikkarte ja auch gesagt werden, was sie denn überhaupt ausgeben soll. Das kommt jetzt: '''TForm1.Render'''
  
<pascal>procedure TForm1.Render;
+
<source lang="pascal">procedure TForm1.Render;
 
begin
 
begin
 
   glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
 
   glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
Zeile 143: Zeile 150:
 
   glLoadIdentity;
 
   glLoadIdentity;
 
   gluPerspective(45.0, ClientWidth/ClientHeight, NearClipping, FarClipping);
 
   gluPerspective(45.0, ClientWidth/ClientHeight, NearClipping, FarClipping);
 
  glTranslatef(0, 0, -5);
 
  
 
   glMatrixMode(GL_MODELVIEW);
 
   glMatrixMode(GL_MODELVIEW);
 
   glLoadIdentity;
 
   glLoadIdentity;
 +
 +
  glTranslatef(0, 0, -5);
  
 
   glBegin(GL_QUADS);
 
   glBegin(GL_QUADS);
Zeile 157: Zeile 164:
  
 
   SwapBuffers(DC);
 
   SwapBuffers(DC);
end;</pascal>
+
end;</source>
  
'''Zeile 3:''' Dieser Aufruf sorgt dafür, dass der [[Farbpuffer]] und [[Tiefenpuffer]] gelöscht werden. Wenn man das nicht macht, sieht man alles mögliche, nur nicht das was Ihr rendern wollt. Probiert es ruhig mal ohne aus! Man wird dadurch nicht dümmer.
+
'''Zeile 3:''' Dieser Aufruf sorgt dafür, dass [[Farbpuffer]] und [[Tiefenpuffer]] gelöscht werden. Wenn man das nicht macht, sieht man alles mögliche, nur nicht das, was Ihr rendern wollt. Probiert es ruhig mal ohne aus! Man wird dadurch nicht dümmer. Der Farbpuffer wird nicht einfach gelöscht sondern mit der ClearColor überschrieben, die ihr per glClearColor definiert habt. (siehe FormCreate-Methode)
  
'''Zeile 7:''' Hier wird wieder die Perspektive gesetzt. Dieser Aufruf und der bei '''FormResize'''  
+
'''Zeile 7:''' Hier wird wieder die Perspektive gesetzt. Dieser Aufruf und der bei '''FormResize''' müssen von den Parametern identisch sein. Sonst sieht die Ausgabe nach einem Resize ganz einfach anders aus.  
müssen von den Parametern identisch sein. Sonst sieht die Ausgabe nach einem Resize kurz anders aus.  
+
Wenn sich die Perspektive zwischen den Renderdurchgängen nicht ändert, kann dieser Befehl auch weggelassen werden.
Wenn sich die Perspektive zwischen den Renderdurchgängen nicht ändert kann das auch weg gelassen werden.
 
  
'''Zeile 9:''' Dieser Aufruf verschiebt die "Kamera" (so etwas gibt es eigentlich nicht, aber da wir
+
'''Zeile 12:''' Dieser Aufruf verschiebt die "Kamera" (so etwas gibt eigentlich nicht, aber das nur am Rande. Mehr zu diesem Thema gibt es im [[Tutorial Matrix2]]) etwas nach hinten. Schließlich wollen wir das, was wir zeichnen, auch sehen. Alles, was zu nah ist, wird durch die '''Near-[[Clipping Plane]]''' abgeschnitten.
uns gerade in der GL_PROJECTION Matrix befinden passt diese Beschreibung am besten) etwas nach hinten. Schließlich wollen wir das, was wir zeichnen auch sehen. Alles was zu nah ist wird durch die '''Near-[[Clipping Plane]]''' abgeschnitten.
 
  
[[Bild:glShadeModel_SMOOTH.jpg|right]]
+
[[Bild:GlShadeModel_SMOOTH.jpg|right]]
  
'''Zeile 14:''' [[glBegin]]/[[glEnd]] kapseln die eigentlichen Zeichenbefehle. Diese sorgen hier für ein '''hübsches buntes Viereck'''. Das soll für den ersten Test ausreichen. Wichtig ist in dem Zusammenhang noch folgendes: OpenGL ist es egal woher ein Befehl kommt. Alles wird ausgewertet und landet unter Umständen im Framebuffer. Ihr könnt also ein Unterprogramm, welches ein Unterprogramm, welches ... ..., welches die OpenGL Befehle enthält schreiben. Das interessiert OpenGL bzw. die Grafikkarte überhaupt nicht.
+
'''Zeile 14:''' [[glBegin]]/[[glEnd]] kapseln die eigentlichen Zeichenbefehle. Diese sorgen hier für ein '''hübsches buntes Viereck''' (Die ClearColor hatte ich aus ästhetischen Gründen für dieses Bild geändert. ;) ). Das soll für den ersten Test ausreichen. Wichtig ist in dem Zusammenhang noch Folgendes: OpenGL ist es egal, woher ein Befehl kommt. Alles wird ausgewertet und landet unter Umständen im Framebuffer. Ihr könnt also ein Unterprogramm, welches ein Unterprogramm, welches ein Unterprogramm ... , welches die OpenGL Befehle enthält, schreiben. Das interessiert OpenGL bzw. die Grafikkarte überhaupt nicht.
  
'''Zeile 21:''' [[SwapBuffers]] sorgt Ihr dafür, dass der Inhalt des [[Framebuffer]]s auf dem  
+
'''Zeile 21:''' [[SwapBuffers]] sorgt dafür, dass der Inhalt des [[Framebuffer]]s auf dem Bildschirm erscheint. Ohne diesen Befehl seht Ihr gar nichts von OpenGL. (Interessanter Artikel dazu: [[Doppelpufferung]])
Bildschirm erscheint. Ohne diesen Befehl seht Ihr gar nichts von OpenGL. (Interessanter Artikel dazu: [[Doppelpufferung]])
 
  
 
+
So... ganz toll. Jetzt habt Ihr Eure Zeichenfunktion... und nun? Irgendwie müsst Ihr diese auch aufrufen. Das wäre aber zu einfach. Die Ausgabe ändert sich ja normalerweise (z.B. in Spielen). Deshalb muss die Zeichenfunktion immer wieder ausgegeben werden. Dazu gibt es zwei Möglichkeiten, die beide Ihre Vor- und Nachteile haben.
So...ganz toll. Jetzt habt Ihr Eure Zeichenfunktion ... und nun? Irgendwie müsst Ihr diese auch aufrufen. Das wäre aber zu einfach. Die Ausgabe ändert sich ja normalerweise (z.B. in Spielen). Deshalb muss die Zeichenfunktion immer wieder ausgegeben werden. Dazu gibt es zwei Möglichkeiten, die beide Ihre Vor- und Nachteile haben.
 
  
 
<div align="center">
 
<div align="center">
{|{{Prettytable}}
+
{|{{Prettytable_B1}}
 
!Argument
 
!Argument
 
!Timer
 
!Timer
Zeile 210: Zeile 213:
 
</div>
 
</div>
  
Wer einfache Anwendungen schreiben möchte, die auch mit 25 FpS (Bilder Pro Sekunde) auskommen und den Akku von Laptopusern schonen will, sollte die Timervariante nutzen. Wer die Potenziale der Grafikkarten voll ausnutzen möchte sollte OnIdle verwenden.
+
Wer einfache Anwendungen schreiben möchte, die auch mit 25 [[FPS]] (Bilder pro Sekunde) auskommen, und den Akku von Laptopusern schonen will, sollte die Timervariante nutzen. Wer die Potenziale der Grafikkarten voll ausnutzen möchte, sollte OnIdle verwenden.
 
 
  
 
====Methode 1: Timer====
 
====Methode 1: Timer====
Bei dieser Methode muss ein Timer (zu finden bei den Systemkomponenten) auf das Formular gezogen werden. Der Timer besitzt eine Eigenschaft names "Interval". Mit dieser Eigenschaft kann man einstellen nach wie vielen Millisekunden das Ereignis OnTimer ausgelöst wird. Man kann "Interval" nicht beliebig verkleinern. Werte unter 25 können vom Standardtimer den Windows verwendet nicht mehr korrekt erzeugt werden.
+
Bei dieser Methode muss ein Timer (zu finden bei den Systemkomponenten) auf das Formular gezogen werden. Der Timer besitzt eine Eigenschaft names "Interval". Mit dieser Eigenschaft kann man einstellen, nach wie vielen Millisekunden das Ereignis OnTimer ausgelöst wird. Man kann "Interval" nicht beliebig verkleinern. Werte unter 25 können von dem Standardtimer, den Windows verwendet, nicht mehr korrekt erzeugt werden.
  
 
Der Inhalt von OnTimer könnte dieser sein:
 
Der Inhalt von OnTimer könnte dieser sein:
  
<pascal>procedure TForm1.Timer1Timer(Sender: TObject);
+
<source lang="pascal">procedure TForm1.Timer1Timer(Sender: TObject);
 
begin
 
begin
  inc(FrameCount);
+
  inc(FrameCount);
  Render;
+
  Render;
  If FrameCount = 20 then
+
  If FrameCount = 20 then begin
      begin
+
    ErrorHandler;
          ErrorHandler;
+
    FrameCount := 0;
          FrameCount := 0;
+
  end;
      end;
+
end;</source>
end;</pascal>
 
  
Tiefgreifende Erklärungen sind hier nicht notwendig. Was der ErrorHandler ist wird nach der Methode 2 erklärt.
+
Tiefgreifende Erklärungen sind hier nicht notwendig. Was der ErrorHandler ist, wird nach der Methode 2 erklärt.
  
 +
====Methode 2: OnIdle====
  
====Methode 2 : OnIdle====
+
OnIdle ist ein besonderes Ereignis, welches das gesamte Programm betrifft. Wenn die Anwendung nichts zu tun hat, also faul ist (englisch idle), tritt das Ereignis ein.
  
OnIdle ist ein besonderes Ereignis, welches das gesamte Programm betrifft. Wenn die Anwendung nichts zu tun hat, also faul ist (engl. idle), tritt das Ereignis ein.<br>
 
 
Die Methode mit OnIdle kann gleich mit zum Auswerten der Framezahlen (Anzahl Bildwiederholungen pro Sekunde) benutzt werden (Framecounter). Der nachfolgende Code enthält selbigen bereits.
 
Die Methode mit OnIdle kann gleich mit zum Auswerten der Framezahlen (Anzahl Bildwiederholungen pro Sekunde) benutzt werden (Framecounter). Der nachfolgende Code enthält selbigen bereits.
  
<pascal>procedure TForm1.IdleHandler(Sender: TObject; var Done: Boolean);
+
<source lang="pascal">procedure TForm1.IdleHandler(Sender: TObject; var Done: Boolean);
 
begin
 
begin
 
   StartTime:= GetTickCount;
 
   StartTime:= GetTickCount;
Zeile 254: Zeile 255:
  
 
   Done:= false;
 
   Done:= false;
end;</pascal>
+
end;</source>
  
'''Zeile 3:''' Mittels GetTickCount wird die Systemzeit gemessen. Dies ist nicht nötig um  
+
'''Zeile 3:''' Mittels GetTickCount wird die Systemzeit gemessen. Dies ist nicht nötig, um erfolgreich zu zeichnen, sondern dient ausschließlich der Berechnung der Framerate. Diese wiederum ist ein guter Performancemesser.
erfolgreich zu zeichnen, sondern dient ausschließlich der Berechnung der Framerate. Diese wiederum ist ein guter Performancemesser.
 
  
 
'''Zeile 4:''' Hier erfolgt der Aufruf unserer Zeichenroutine.
 
'''Zeile 4:''' Hier erfolgt der Aufruf unserer Zeichenroutine.
  
'''Zeile 9:''' Der hier angeordnete Block wird nur pro Sekunde einmal ausgeführt und sorgt  
+
'''Zeile 9:''' Der hier angeordnete Block wird nur pro Sekunde einmal ausgeführt und sorgt dafür, dass die Framerate angezeigt wird. Außerdem wird der Errorhandler aufgerufen.
dafür, dass die Framerate angezeigt wird. Außerdem wird der Errorhandler aufgerufen.
 
  
'''Zeile 14:''' Der Errorhandler wird im Anschluß beschrieben.
+
'''Zeile 14:''' Der Errorhandler wird im Anschluss beschrieben.
  
'''Zeile 17:''' Wenn ''Done'' nach der Ausführung ''false'' ist und das Programm wieder nichts zu tun  
+
'''Zeile 17:''' Wenn ''Done'' nach der Ausführung ''false'' ist und das Programm wieder nichts zu tun hat, wird OnIdle erneut ausgeführt. Wenn ''Done = true'' ist wird OnIdle nur einmal ausgeführt.
hat, wird OnIdle erneut ausgeführt. Wenn ''Done = true'' ist wird OnIdle nur einmal ausgeführt.
 
  
 +
Um auf das "Idle-Event" reagieren zu können, müsst ihr jetzt nur noch diese Funktion an das Event koppeln. Das macht ihr, indem ihr den nachfolgenden Code in die letzte Zeile eurer FormCreate-Methode schreibt.
  
Um auf das "Idle-Event" reagieren zu können, müsst ihr jetzt nur noch diese Funktion an das Event koppeln. Das macht ihr, indem ihr den nachfolgenden Code in die letzte Zeile eurer FormCreate-Methode schreibt.
+
<source lang="pascal">Application.OnIdle := IdleHandler;</source>
  
<pascal>Application.OnIdle := IdleHandler;</pascal>
+
Wie ihr bald selbst feststellen werdet, sorgt diese Methode für eine hundertprozentige Prozessorauslastung (Und das ist schlecht für die Akkulaufzeiten von Notebooks). Umgehen könnt ihr dies mit einem kleinen Trick:
 +
:Fügt vor dem "Done := false" (Zeile 17 des IdleHandlers) noch ein sleep(1) oder sleep(5) ein. Dadurch sinkt die Prozessorlast auf ca. 80%, was schon ein Fortschritt ist.
  
Wie ihr bald selbst feststellen werdet, sorgt diese Methode für eine hundertprozentige Prozessorauslastung. Umgehen könnt ihr dies mit einem kleinen Trick:<br>
+
''Noch ein Hinweis, weil im Forum öfters Probleme damit auftraten: OnIdle ist eine Funktion des Formulars und muss entsprechend auch in der Klassendeklaration eingetragen werden. Am Ende des Tutorials findet ihr die Klassendeklartion aus meinem Beispiel. Wenn ihr das vergesst meldet der Compiler "Undefinierter Bezeichner: 'IdleHandler'".''
Fügt vor dem "Done := false" (Zeile 17 des IdleHandlers) noch ein sleep(1) oder sleep(5) ein. Dadurch sinkt die Prozessorlast auf ca. 80%, was schon ein Fortschritt ist.<br>
 
  
===Der ErrorHandler – Fehler erkannt, Fehler gebannt===
+
===Der ErrorHandler - Fehler erkannt, Fehler gebannt===
Der Errorhandler ist wieder eine total einfache Funktion, denn OpenGL bietet von Haus aus eine Möglichkeit OpenGL-Fehler zu erkennen. Deshalb ist der ErrorHandler auch so klein:
+
Der Errorhandler ist wieder eine sehr einfache Funktion, denn OpenGL bietet von Haus aus eine Möglichkeit, OpenGL-Fehler zu erkennen. Deshalb ist der ErrorHandler auch so klein:
  
<pascal>procedure TForm1.ErrorHandler;
+
<source lang="pascal">procedure TForm1.ErrorHandler;
 
begin
 
begin
 
   Form1.Caption := gluErrorString(glGetError);
 
   Form1.Caption := gluErrorString(glGetError);
end;</pascal>
+
end;</source>
  
Enttäuscht? Ihr könnt den ErrorHandler nach belieben auch komplexer machen. Zum Beispiel könnt Ihr anstatt die Fehler im Fenstertitel anzuzeigen lieber den Fehler in ein Logfile schreiben. Ganz nebenbei: Falls kein Fehler auftritt liefert [[glGetError]] '''GL_NO_ERROR'''.
+
Enttäuscht? Ihr könnt den ErrorHandler nach belieben auch komplexer machen. Zum Beispiel könnt ihr, anstatt die Fehler im Fenstertitel anzuzeigen, lieber den Fehler in ein Logfile schreiben. Ganz nebenbei: Falls kein Fehler auftritt, liefert [[glGetError]] '''GL_NO_ERROR'''.
  
 
===Das fertige Templateformular===
 
===Das fertige Templateformular===
Eure Klasse TForm1 sollte also jetzt so, oder so ähnlich aussehen:
+
Eure Klasse TForm1 sollte also jetzt so oder so ähnlich aussehen:
<pascal>TForm1 = class(TForm)
+
<source lang="pascal">TForm1 = class(TForm)
 
     procedure FormCreate(Sender: TObject);
 
     procedure FormCreate(Sender: TObject);
 
     procedure IdleHandler(Sender: TObject; var Done: Boolean);
 
     procedure IdleHandler(Sender: TObject; var Done: Boolean);
Zeile 298: Zeile 297:
 
     Frames, DrawTime                  : Cardinal; //& Timebased Movement
 
     Frames, DrawTime                  : Cardinal; //& Timebased Movement
 
     procedure SetupGL;
 
     procedure SetupGL;
    procedure Init;
 
 
     procedure Render;
 
     procedure Render;
 
     procedure ErrorHandler;
 
     procedure ErrorHandler;
Zeile 304: Zeile 302:
 
     DC                                : HDC;  //Handle auf Zeichenfläche
 
     DC                                : HDC;  //Handle auf Zeichenfläche
 
     RC                                : HGLRC;//Rendering Context
 
     RC                                : HGLRC;//Rendering Context
   end;</pascal>
+
   end;</source>
  
'''Zeile 3:''' Wie man sieht benutzt mein Template die zweite Methode (OnIdle).
+
'''Zeile 3:''' Wie man sieht, benutzt mein Template die zweite Methode (OnIdle).
  
'''Zeile 8:''' Was [[Timebased Movement]] ist könnt ihr ja mal nachlesen.
+
'''Zeile 8:''' Was [[Timebased Movement]] ist, könnt ihr ja mal nachlesen.
  
'''Zeile 14/15:''' HDC und HGLRC sind Typen die von Windows zur verfügung gestellt werden. Ihr findet sie in der Unit "Windows". (Diese Unit sollte bei einem neuen Projekt bereits durch Delphi eingebunden worden sein.)
+
'''Zeile 14/15:''' HDC und HGLRC sind Typen die von Windows zur Verfügung gestellt werden. Ihr findet sie in der Unit "Windows". (Diese Unit sollte bei einem neuen Projekt bereits durch Delphi eingebunden worden sein.)
  
 
+
So... fertig. Eigentlich seid Ihr jetzt soweit, von den Tutorialschreibern so richtig mit OpenGL-Wissen vollgepumpt zu werden. Das nachfolgende Kapitel könnt Ihr euch trotzdem ruhigen Gewissens durchlesen. Wer es liest, tappt vielleicht nicht gleich mit der ersten Frage im DGL-Forum ins berüchtigte Fettnäpfchen.
So....fertig. Eigentlich seid Ihr jetzt soweit von den Tutorialschreibern so richtig mit OpenGL–Wissen voll gepumpt zu werden. Das nachfolgende Kapitel könnt Ihr Euch trotzdem ruhigen Gewissens durchlesen. Wer es liest tappt vielleicht nicht gleich bei der ersten Frage im DGL-Forum ins berüchtigte Fettnäpfchen.
 
 
 
<br><br>
 
  
 
==Tipps für den OpenGL Anfänger==
 
==Tipps für den OpenGL Anfänger==
Wenn man mit OpenGL anfängt ist man meist total perplex und ein "Das war ja einfach!" huscht einem nicht nur einmal über die Lippen. Vor allem zu Beginn Eurer OpenGL-Karriere werdet Ihr viel lernen und dabei nur auf verhältnismäßig geringen Widerstand stoßen. Aber glaubt mir es gibt ihn...
+
Wenn man mit OpenGL anfängt, ist man meist total perplex und ein "Das war ja einfach!" huscht einem nicht nur einmal über die Lippen. Vor allem zu Beginn Eurer OpenGL-Karriere werdet Ihr viel lernen und dabei nur auf verhältnismäßig geringen Widerstand stoßen. Aber glaubt mir, es gibt ihn...
  
Häufig tauchen hoch motivierte OpenGL Anfänger im Forum auf und verkünden stolz, sie würden gerade an einer Engine arbeiten die "nur auf Doom 1 Niveau arbeiten soll".  
+
Häufig tauchen hochmotivierte OpenGL Anfänger im Forum auf und verkünden stolz, sie würden gerade an einer Engine arbeiten die ''"nur auf Doom 1 Niveau arbeiten soll"''.  
  
Dazu gibt es häufig eine durchaus beachtliche Anzahl von Forenmitgliedern die dann ungefähr folgendes sagen: „Fang gar nicht erst damit an. Vergiss es, und komm in einem Jahr noch mal darauf zurück!“ (Die Forensuche sollte Euch einige dieser Threads zeigen)
+
Dazu gibt es häufig eine durchaus beachtliche Anzahl von Forenmitgliedern die dann ungefähr folgendes sagen: ''"Fang gar nicht erst damit an. Vergiss es, und komm in einem Jahr noch mal darauf zurück!"'' (Die Forensuche sollte euch einige dieser Threads zeigen)
  
Sind das böse Pessimisten, die Probleme nutzen um aufzugeben? '''Nein!''' Sie haben zumeist die Erfahrungen gemacht, die Ihr noch machen werdet. Deshalb will ich Euch an dieser Stelle einweihen:
+
Sind das böse Pessimisten, die Probleme nutzen um aufzugeben? '''Nein!''' Sie haben zumeist die Erfahrungen gemacht, die Ihr noch machen werdet. Deshalb will ich euch an dieser Stelle einweihen:
  
 +
<u>1. Je älter der Code ist, desto besser wird er.</u>
  
<u>1. Je älter der Code ist desto besser wird er.</u>
+
Leider nein. Code und Wein unterscheiden sich hier leider grundlegend. Viele... die meisten... eigentlich alle von uns (die weniger als 3Jahre programmieren) bekommen Ausschlag, wenn sie sich ihren Code vom Vorjahr angucken. Erst wenn man so schätzungsweise 3 bis 5 Jahre mit OpenGL gearbeitet hat, hat man ein echtes Gefühl für den Code. Am Anfang hat man nämlich, ob man will oder nicht, die Tendenz, sich schon nach den ersten 3 Wochen Programmierarbeit den Code so zu zerschießen, dass die weitere Arbeit keinen Spaß mehr macht.
 
 
Leider nein. Code und Wein unterscheiden sich hier leider grundlegend. Viele ... die meisten ... eigentlich alle von uns bekommen Ausschlag, wenn sie sich ihren Code von vor 1 Jahr angucken. Erst wenn man so schätzungsweise 3 bis 5 Jahre mit OpenGL gearbeitet hat, hat man ein echtes Gefühl für den Code. Am Anfang hat man nämlich, ob man will oder nicht, die Tendenz schon nach den ersten 3 Wochen Programmierarbeit sich den Code so zu zerschießen, dass die weitere Arbeit keinen Spaß mehr macht.
 
  
 
''Ich selbst wollte jetzt ein Projekt weiterbearbeiten, welches ungefähr ein 3/4 Jahr alt ist... Ich hab den Code weggeworfen und das fast fertige Spiel neu angefangen. (Glaubt mir: Aus solchen Fehlern lernt man!)''
 
''Ich selbst wollte jetzt ein Projekt weiterbearbeiten, welches ungefähr ein 3/4 Jahr alt ist... Ich hab den Code weggeworfen und das fast fertige Spiel neu angefangen. (Glaubt mir: Aus solchen Fehlern lernt man!)''
  
 +
<u>2. Große Projekte = Großer Ruhm</u>
  
<u>2. Große Projekte = Großer Ruhm</u>
+
Stimmt! ABER Ruhm gibt es in der Szene nur für beendete Projekte. Und dreimal dürft Ihr raten, was durch die im Punkt 1 angesprochenen Probleme meist nicht mit euren Projekten passiert...
  
Stimmt! ABER Ruhm gibt es in der Szene nur für beendete Projekte. Und dreimal dürft Ihr raten was durch die im Punkt 1 angesprochenen Probleme meist nicht mit euren Projekten passiert ... .
+
Wenn Ihr OpenGL nur zur Visualisierung von z.B. wissenschaftlichen Ergebnissen benutzt, ist die Arbeit im Bezug auf OpenGL sehr übersichtlich. Wenn Ihr allerdings Spiele programmieren wollt, werdet Ihr schnell merken, dass Probleme auf euch zukommen werden, die euch beim Projektstart völlig unbekannt waren. Dies ist ein sicheres Zeichen dafür, dass doch noch etwas mehr Erfahrung nötig sein wird.
  
Wenn Ihr OpenGL nur zur Visualisierung von z.B. wissenschaftlichen Ergebnissen benutzt, ist die Arbeit im Bezug auf OpenGL sehr übersichtlich. Wenn Ihr allerdings Spiele programmieren wollt, werdet Ihr schnell merken, dass Probleme auf Euch zukommen werden, die Euch beim Projektstart völlig unbekannt waren. Dies ist ein sicheres Zeichen dafür, dass doch noch etwas mehr Erfahrung nötig sein wird.
+
'''Und woher soll ich bitte Erfahrung nehmen?'''
  
'''Und woher soll ich bitte Erfahrung nehmen?'''<br>
+
Darin liegt der Trick:
Darin liegt der Trick: <br>
 
Alle OpenGL’er haben einmal "klein" angefangen. Berühmt berüchtigt sind die 3DPong-Clone, die zahlreich im Internet anzufinden sind. Auch Tetris-, Memory- oder "Vier Gewinnt"-Clone sind solche stillen Zeugen eines großen Lernvorgangs. Das Besondere an solchen Spielen ist, dass die Spiellogik relativ einfach ist und Euch damit nur wenig von der Visualisierung ablenkt.
 
  
 +
Alle OpenGLer haben einmal "klein" angefangen. Berühmt-berüchtigt sind die 3DPong-Clone, die zahlreich im Internet anzufinden sind. Auch Tetris-, Memory- oder "Vier Gewinnt"-Clone sind solche stillen Zeugen eines großen Lernvorgangs. Das Besondere an solchen Spielen ist, dass die Spiellogik relativ einfach ist und euch damit nur wenig von der Visualisierung ablenkt.
  
 
Ihr denkt jetzt bestimmt "Was kann man denn schon in so nem Clon unterbringen?" '''VERDAMMT VIEL!''' Hier mal ein kleiner Auszug:
 
Ihr denkt jetzt bestimmt "Was kann man denn schon in so nem Clon unterbringen?" '''VERDAMMT VIEL!''' Hier mal ein kleiner Auszug:
Zeile 355: Zeile 349:
 
*Kamerasteuerung /Bewegung durch die Szene (Da gibt es ganze Tutorials dazu)
 
*Kamerasteuerung /Bewegung durch die Szene (Da gibt es ganze Tutorials dazu)
  
Als Ansporn solltet Ihr Euch am Anfang ein kleines Ziel setzen und sagen "Ich möchte den besten und schönsten XYZ-Clon im ganzen Internet schaffen!". So etwas trifft in der Szene auf wesentlich mehr Anerkennung als wenn mal wieder ein Anfänger etwas von Engine und Doom X faselt.<br>
+
Als Ansporn solltet Ihr euch am Anfang ein kleines Ziel setzen und sagen "Ich möchte den besten und schönsten XYZ-Clon im ganzen Internet schaffen!". So etwas trifft in der Szene auf wesentlich mehr Anerkennung, als wenn mal wieder ein Anfänger etwas von Engine und Doom X faselt.
Wenn Ihr ein oder zwei solcher Projekte abgeschlossen habt, und regelmäßig im Forum bzw. Wiki gelesen habt werdet Ihr schon merken, wenn Euer Traumprojekt endlich in Angriff genommen werden kann. (Ganz vergessen müsst ihr es nämlich doch nicht ;) )
 
  
'''Als kurze Zusammenfassung solltet Ihr Euch merken:'''
+
Wenn Ihr ein oder zwei solcher Projekte abgeschlossen habt, und regelmäßig im Forum bzw. Wiki gelesen habt, werdet Ihr es schon merken, wenn Euer Traumprojekt endlich in Angriff genommen werden kann. (Ganz vergessen müsst ihr es nämlich doch nicht ;) )
  
*Ein fertiges Projekt bringt Euch Ruhm (Seelenbalsam).
+
'''Als kurze Zusammenfassung solltet Ihr euch merken:'''
*Ein hübsches, fertiges Projekt bringt Euch mehr Ruhm.
 
*Ein großes, fertiges, hübsches Projekt bringt Euch noch mehr Ruhm.<br>
 
  
*Ein abgebrochenes oder eingefrorenes Projekt bringt Euch Frust.
+
*Ein fertiges Projekt bringt euch Ruhm (Seelenbalsam).
*Ein großes, abgebrochenes Projekt bringt Euch noch mehr Frust, denn der verschwendeten Zeit werdet Ihr nachtrauern.
+
*Ein hübsches, fertiges Projekt bringt euch mehr Ruhm.
 +
*Ein großes, fertiges, hübsches Projekt bringt euch noch mehr Ruhm.
  
 +
*Ein abgebrochenes oder eingefrorenes Projekt bringt euch Frust.
 +
*Ein großes, abgebrochenes Projekt bringt euch noch mehr Frust, denn der verschwendeten Zeit werdet Ihr nachtrauern.
  
 
<u>3.Das es lang dauert ist egal. Ich interessiere mich halt dafür.</u>
 
<u>3.Das es lang dauert ist egal. Ich interessiere mich halt dafür.</u>
  
Diese Aussage gilt nur dann, wenn Ihr schon über ein Jahr an ein und demselben Projekt gearbeitet habt. Anfänglich ist das kein Problem. Aber wenn man lange an etwas arbeitet und die gemachten Änderungen sind nicht sichtbar (weil sie z.B. den eigentlichen Motor der Anwendung betreffen und nicht die Ausgabe) dann verliert man schon mal die Lust. Folge sind die berüchtigten "Erfolgsmeldungen" wie: "Das Projekt wurde von mir bis auf weiteres aufs Eis gelegt. Ich werde sicherlich später daran weiterarbeiten." In Verbindung mit Punkt 1 und den letzen Satz aus Punkt 2 sollte Euch das Endergebnis klar sein.
+
Diese Aussage gilt nur dann, wenn Ihr schon über ein Jahr an ein und demselben Projekt gearbeitet habt. Anfänglich ist das kein Problem. Aber wenn man lange an etwas arbeitet und die gemachten Änderungen sind nicht sichtbar (weil sie z.B. den eigentlichen Motor der Anwendung betreffen und nicht die Ausgabe), dann verliert man schon mal die Lust. Folge sind die berüchtigten "Erfolgsmeldungen" wie: "Das Projekt wurde von mir bis auf weiteres aufs Eis gelegt. Ich werde sicherlich später daran weiterarbeiten." Was stand gleich nochmal unter Punkt 1? - Das Endergebnis dürfte klar sein.
 +
 
 +
 
 +
'''Tipps für den Erfolg'''
 +
# Lernt eure Programmiersprache kennen.
 +
# Lernt ObjektOrientierung. Nur weil ihr Klassen benutzt, heißt das nicht, dass ihr Objekt orientiert programmiert.
 +
# Lernt eure Entwicklungsumgebung kennen. (Debugging! Dazu gibts hier ein Tutorial für Delphi.)
 +
# Kleine Brötchen backen (Testprojekte) und Projekte zu Ende bringen!
  
 
==Nachwort==
 
==Nachwort==
  
Soviel zur Euphoriebremse. OpenGL ist toll. OpenGL ist die Lösung für Eure Traumanwendung. Aber die wird auch mit OpenGL nicht von heute auf morgen programmiert. Deshalb heißt es Tutorials lesen kleine Testanwendungen schreiben um Effekte zu testen und Projekte bearbeiten. Dann wird es auch etwas mit den Traumprojekten. Zudem kann man anfangs auch versuchen in sehr kleinen Anwendungen einen Effekt auszuprobieren und diesen in Form eines kleinen Beispielprogramms oder eines Tutorials zu veröffenlichen. Somit lernt man nicht nur selbst etwas dazu...
+
Soviel zur Euphoriebremse. OpenGL ist toll. OpenGL ist die Lösung für Eure Traumanwendung. Aber die wird auch mit OpenGL nicht von heute auf morgen programmiert. Deshalb heißt es, Tutorials lesen, kleine Testanwendungen schreiben um Effekte zu testen und Projekte bearbeiten. Dann wird es auch etwas mit den Traumprojekten. Zudem kann man anfangs auch versuchen, in sehr kleinen Anwendungen einen Effekt auszuprobieren und diesen in Form eines kleinen Beispielprogramms oder eines Tutorials zu veröffenlichen. Somit lernt man nicht nur selbst etwas dazu...
  
 +
Nach diesen unglaublich Weise klingenden Worten, die ich mit einem hoch ernsten Gesicht geschrieben habe, könnt ihr euch jetzt hochmotiviert an die restlichen Tutorials machen.
  
Nach diesen unglaublich Weise klingenden Worten die ich mit einem hoch ernsten Gesicht geschrieben habe, könnt ihr euch jetzt hochmotiviert an die restlichen Tutorials machen.
+
Bis bald im [http://delphiGL.com/forum Forum]
  
 +
'''Euer'''
  
Bis bald im [http://delphiGL.com/forum Forum]<br>
 
'''Euer''' <br>
 
 
'''Flash (Kevin Fleischer)'''
 
'''Flash (Kevin Fleischer)'''
  
 +
PS: Feedback wird nicht nur gewünscht, sondern ausdrücklich gefordert. Deshalb: Ab mit deinen Meinungen und Ratschlägen ins [http://www.delphigl.com/forum/viewforum.php?f=8 Feedback-Forum]!
  
PS: Feedback wird nicht nur gewünscht, sondern ausdrücklich gefordert. Deshalb: Ab mit deinen Meinungen und Ratschläge ins [http://www.delphigl.com/forum/viewforum.php?f=8 Feedback-Forum]!
+
{{TUTORIAL_NAVIGATION|-|[[Tutorial Lektion 1]]}}
 
 
{{TUTORIAL_NAVIGATION|-|[[Tutorial_lektion1]]}}
 
  
 
[[Kategorie:Tutorial|Quickstart]]
 
[[Kategorie:Tutorial|Quickstart]]

Aktuelle Version vom 15. Juli 2018, 22:06 Uhr

Quickstart: Delphi & OpenGL

Einleitung

Willkommen beim Delphi & OpenGL Quickstart. Diese kurze Einleitung soll euch auf die Tutorials bei DelphiGL.com und allgemein auf die Grafikprogrammierung mit OpenGL und Delphi vorbereiten. Dieser Quickstart ist kein Tutorial für sich, sondern soll ein Grundgefühl vermitteln, wie OpenGL und Delphi miteinander arbeiten.

Info DGL.png Dieses Tutorial beschreibt den Einstieg in OpenGL auf Basis eines Delphi-VCL* Ansatzes. Dieses Tutorial basiert damit nicht auf SDL oder easySDL wie die folgenden Einsteigertutorials. Aus folgenden Gründen sollte man trotzdem dieses Tutorial lesen:
  1. Weil der hier vorgestellte Ansatz für eure (Windows-)Projekte genutzt werden kann (easySDL ist nicht dafür gedacht).
  2. Weil ihr so seht wie der Code aus den easySDL Beispielen in ein VCL Beispiel übertragen werden kann.
  3. Weil der zweite Abschnitt des Tuts ein paar allgemeine Worte zur Spieleprogrammierung enthält, die jeder Einsteiger lesen sollte.

*Zur Erklärung für nicht Delphiprogrammierer: die VCL ist die Visuelle Component Library von Delphi. Diese enthält die GUI Komponenten (z.B. Formulare) aus denen man unter Delphi Anwendungen zusammensetzen kann.

Wozu OpenGL? Mit OpenGL kann man eine Vielzahl von Aufgaben bewältigen. Ob man Forschungsergebnisse aller Art visualisieren, 2D oder 3D Spiele schreiben oder einfach seiner Anwendung eine Oberfläche geben möchte, die nicht dem grauen Standardlook entspricht. All das ist möglich mit OpenGL.

Wie fange ich an?

Genau zwei Dinge braucht der OpenGL-Programmierer, um effektiv arbeiten zu können:

  1. Einen OpenGL Header
  2. Eine Codebasis, von der aus man neue Projekte starten kann (ein sogenanntes Template)

Der OpenGL-Header

Das is ja easy! Denn Delphi bringt ja schon einen OpenGL-Header mit...

STOP!

Denn wir reden hier von einem guten Header. Leider ist der standardmäßig von Delphi mitgelieferte Header alles andere als zu empfehlen. Er ist fehlerhaft, hält sich nicht an OpenGL-Normen und ist außerdem absolut veraltet.

Was nun? Ganz einfach: Bei DelphiGL.com (kurz DGL) gibt es DEN OpenGL-Header für alle Pascalsprachen: Die DGLOpenGL.pas.

Diesen solltet Ihr euch jetzt besorgen, wenn Ihr ihn nicht schon habt. Der Header wird bei neuen OpenGL-Versionen vom DGL-Team aktualisiert, und ist so immer auf der Höhe der Zeit.

Codebasis/Templates

So... das war schon alles, was Ihr aus dem Netz benötigt. Den Rest machen wir jetzt per Hand.

Im nächsten Kapitel zeige ich euch wie man sich ein einfaches Template schreibt. Natürlich hat DelphiGL.com auch bereits fertige Lösungen, die durchaus zu empfehlen sind und auch extra Features wie Vollbildrendering besitzen. ABER aus Erfahrung kann ich sagen: Man findet sich im eigenen Code viel einfacher zurecht. (Und die Extras kann man nachher immer noch einbauen.)

Das Template - Delphi fit für OpenGL machen

Bevor man wirklich loslegen kann, muss noch die runtergeladene DGLOpenGL.pas an den richtigen Ort gebracht werden. Gut wäre z.B., sie in das Verzeichnis "\lib" in Eurem Delphiverzeichnis zu legen. (Wenn ihr das DGLSDK verwendet, wurden die Suchpfade schon eingerichtet.)

Dann startet mal Delphi. Vor euch sollte jetzt ein leeres Projekt erscheinen. Das leere Formular kann gleich minimiert werden, denn jetzt wird erstmal hübsch gecodet.

Info DGL.png Lest das Tutorial erst einmal durch bevor ihr versucht die Teile zusammen zu fügen. Vieles wird erst im Zusammenhang klar. Wer Zweifel an der Richtigkeit seines Templates hat, kann sich ein DGL-Template runterladen, um den Code als Vorlage zu verwenden.
Info DGL.png Am Ende von Kapitel 1.3 findet ihr den Kopf der Template-Klasse. Dort seht ihr auch, welche Variablen von welchem Typ deklariert werden müssen.

Initialisieren von OpenGL

Dieser Teil ließ früher dem OpenGL Anfänger die Haare nicht nur zu Berge stehen, sondern gleich ausfallen. Dank der DGLOpenGL.pas wurde das aber um Längen einfacher.

Zuerst einmal solltet Ihr die DGLOpenGL.pas in die uses-Klausel des interface-Teils der Unit1 schreiben.

Die eigentliche Initialisierung soll direkt beim Erstellen des Formulars gemacht werden. Deshalb kommt der folgende Quelltext ins OnCreate-Ereignis des Formulars.

procedure TForm1.FormCreate(Sender: TObject);
begin
  DC:= GetDC(Handle);
  if not InitOpenGL then Application.Terminate;
  RC:= CreateRenderingContext( DC,
                               [opDoubleBuffered],
                               32,
                               24,
                               0,0,0,
                               0);
  ActivateRenderingContext(DC, RC);
end;

Zeile 3: Hier wird der Gerätekontext (Device Context) von Formular Form1 abgefragt.

Zeile 4: Mit InitOpenGL wird OpenGL initialisiert. Wenn das nicht funktioniert, wird die gesamte Anwendung sofort beendet.

Zeile 5: Hier wird der Renderkontext erzeugt. Den braucht OpenGL zum Zeichnen auf das Formular. Mehr dazu lernt ihr im Tutorial_lektion1.

Zeile 11: Abschließend wird der Renderkontext aktiviert. OpenGL ist jetzt prinzipiell startbereit.

Info DGL.png DC und RC sind Eigenschaften des Formulars. Siehe Definition des Templateformulars.

Nach dieser durchaus simplen Initialisierung (man kann auch alles per Hand machen, was InitOpenGL macht!) steht OpenGL ziemlich nackt da. Soll heißen, alle OpenGL Eigenschaften/Zustände stehen auf den definierten Anfangswerten. Es kommt aber durchaus oft - eigentlich ständig - vor, dass bestimmte Einstellungen von OpenGL benutzt werden sollen. Deshalb schreiben wir uns noch eine kleine Zusatzprozedur: SetupGL

procedure TForm1.SetupGL;
begin
  glClearColor(0.3, 0.4, 0.7, 0.0); //Hintergrundfarbe: Hier ein leichtes Blau
  glEnable(GL_DEPTH_TEST);          //Tiefentest aktivieren
  glEnable(GL_CULL_FACE);           //Backface Culling aktivieren
end;

Was hier passiert, wird durch die Kommentare bereits erklärt (Für mehr Infos siehe Tiefentest bzw. Backface Culling).

Info DGL.png Backface Culling ist eine simple, aber effektive Methode eure Anwendung zu beschleunigen. Allerdings muss man bei der Ausgabe auf die Reihenfolge der Vertices achten. Lest euch deshalb kurz den Artikel zu Backface Culling durch oder kommentiert die obige Zeile einfach ganz aus. Sonst könnte es passieren, dass Teile eurer Szene plötzlich "verschwinden".

Die Hintergrundfarbe könnt ihr nach Belieben (mit glClearColor) einstellen. (Wenn ihr später einmal geschlossene Szenen rendern wollt, dann ist es für die Entwicklung günstig eine sehr schräge Farbe als Hintergrundfarbe einzustellen, so findet man leichter Fehler in der Szene. In OpenGL werden Farben immer als Mischung aus Rot, Grün und Blau angegeben. Dies wird in späteren Tutorials noch erklärt. Aber herumspielen an den Werten hilft hier auch schnell weiter. ;-) )

Außerdem hat man ja hin und wieder auch noch globale Variablen, die man initialisieren möchte. Da wir mit solchen Sachen unser schön aufgeräumtes FormCreate nicht zumüllen wollen, bietet sich ein Unterprogramm namens InitGlobals oder kurz Init an. (Wobei das jetzt nicht direkt etwas mit OpenGL zu tun hat.) Beide Unterprogramme (SetupGL und Init) sollten am Ende von FormCreate gerufen werden:

  [...]
  ActivateRenderingContext(DC, RC);
  SetupGL;
  Init; //Was man sonst so initialisieren will.
end;

Die Ereignisbehandlung

Für OpenGL sind vor allem die Ereignisse von Bedeutung, die an der Zeichenfläche von OpenGL herumwerkeln. Da OpenGL direkt auf das Formular (oder auch auf ein Panel) zeichnet, müssen Ereignisse, die diese Zeichenfläche ändern, behandelt werden. Dies wären das OnResize- und das OnDestroy-Ereignis.

Zuerst FormResize:

procedure TForm1.FormResize(Sender: TObject);
var tmpBool : Boolean;
begin
  glViewport(0, 0, ClientWidth, ClientHeight);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity;
  gluPerspective(45.0, ClientWidth/ClientHeight, NearClipping, FarClipping);    

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity;
  IdleHandler(Sender, tmpBool);
end;

Außerdem müssen im const Teil die beiden Konstanten Near- bzw. FarClipping definiert werden. Diese geben die Entfernung für die Clippingebenen (Szenenbegrenzung) an und spielen beim Tiefentest eine gewisse Rolle.

  NearClipping = 1;
  FarClipping  = 1000;

Zu FormResize:

Zeile 2: Diese Boolean-Variable wird in Zeile 11 verwendet und ist nur ein Dummy.

Zeile 4: Mittels glViewport sagt Ihr OpenGL, wie groß die OpenGL-Ausgabe werden soll. Genau diese Größe hatte sich ja durch das Resize verändert.

Zeile 5/9: Hier seht Ihr zwei der drei möglichen Matrixmodi. GL_PROJECTION wird benutzt, um nachfolgend die OpenGL-Ausgabe zu manipulieren, GL_MODELVIEW, um OpenGL mit Daten zu füttern.

Zeile 6: glLoadIdentity füllt die aktuelle Matrix mit der Identitätsmatrix.

Zeile 7: Hier wird eingestellt, wie der Betrachter die Welt sehen soll.

Zeile 11: Was der IdleHandler macht, kommt später im Abschnitt 1.3.3 (Zeichenroutine).

Nun noch schnell das FormDestroy:

procedure TForm1.FormDestroy(Sender: TObject);
begin
  DeactivateRenderingContext;
  DestroyRenderingContext(RC);
  ReleaseDC(Handle, DC);
end;

Was es macht? Steht doch da: Den RenderingContext deaktivieren und freigeben.

Die Zeichenroutine

Das Herzstück unseres Templates fehlte bisher. Irgendwann muss der Grafikkarte ja auch gesagt werden, was sie denn überhaupt ausgeben soll. Das kommt jetzt: TForm1.Render

procedure TForm1.Render;
begin
  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity;
  gluPerspective(45.0, ClientWidth/ClientHeight, NearClipping, FarClipping);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity;

  glTranslatef(0, 0, -5);

  glBegin(GL_QUADS);
    glColor3f(1, 0, 0); glVertex3f(0, 0, 0);
    glColor3f(0, 1, 0); glVertex3f(1, 0, 0);
    glColor3f(0, 0, 1); glVertex3f(1, 1, 0);
    glColor3f(1, 1, 0); glVertex3f(0, 1, 0);
  glEnd;

  SwapBuffers(DC);
end;

Zeile 3: Dieser Aufruf sorgt dafür, dass Farbpuffer und Tiefenpuffer gelöscht werden. Wenn man das nicht macht, sieht man alles mögliche, nur nicht das, was Ihr rendern wollt. Probiert es ruhig mal ohne aus! Man wird dadurch nicht dümmer. Der Farbpuffer wird nicht einfach gelöscht sondern mit der ClearColor überschrieben, die ihr per glClearColor definiert habt. (siehe FormCreate-Methode)

Zeile 7: Hier wird wieder die Perspektive gesetzt. Dieser Aufruf und der bei FormResize müssen von den Parametern identisch sein. Sonst sieht die Ausgabe nach einem Resize ganz einfach anders aus. Wenn sich die Perspektive zwischen den Renderdurchgängen nicht ändert, kann dieser Befehl auch weggelassen werden.

Zeile 12: Dieser Aufruf verschiebt die "Kamera" (so etwas gibt eigentlich nicht, aber das nur am Rande. Mehr zu diesem Thema gibt es im Tutorial Matrix2) etwas nach hinten. Schließlich wollen wir das, was wir zeichnen, auch sehen. Alles, was zu nah ist, wird durch die Near-Clipping Plane abgeschnitten.

GlShadeModel SMOOTH.jpg

Zeile 14: glBegin/glEnd kapseln die eigentlichen Zeichenbefehle. Diese sorgen hier für ein hübsches buntes Viereck (Die ClearColor hatte ich aus ästhetischen Gründen für dieses Bild geändert. ;) ). Das soll für den ersten Test ausreichen. Wichtig ist in dem Zusammenhang noch Folgendes: OpenGL ist es egal, woher ein Befehl kommt. Alles wird ausgewertet und landet unter Umständen im Framebuffer. Ihr könnt also ein Unterprogramm, welches ein Unterprogramm, welches ein Unterprogramm ... , welches die OpenGL Befehle enthält, schreiben. Das interessiert OpenGL bzw. die Grafikkarte überhaupt nicht.

Zeile 21: SwapBuffers sorgt dafür, dass der Inhalt des Framebuffers auf dem Bildschirm erscheint. Ohne diesen Befehl seht Ihr gar nichts von OpenGL. (Interessanter Artikel dazu: Doppelpufferung)

So... ganz toll. Jetzt habt Ihr Eure Zeichenfunktion... und nun? Irgendwie müsst Ihr diese auch aufrufen. Das wäre aber zu einfach. Die Ausgabe ändert sich ja normalerweise (z.B. in Spielen). Deshalb muss die Zeichenfunktion immer wieder ausgegeben werden. Dazu gibt es zwei Möglichkeiten, die beide Ihre Vor- und Nachteile haben.

Argument Timer OnIdle
Maximale Framezahl erreichbar
"Benchmark"
nein ja
Framezahl steuerbar ja bedingt (/umständlich)
Für flüssige Animationen nutzbar
(Egoshooter)
bedingt/schlecht ja
Für Menüs nutzbar ja ja
Für einfache Animationen nutzbar
(Strategiespiele)
ja ja
Laptopfreundlich (Anti-Akku-Killer) ja NEIN!

Wer einfache Anwendungen schreiben möchte, die auch mit 25 FPS (Bilder pro Sekunde) auskommen, und den Akku von Laptopusern schonen will, sollte die Timervariante nutzen. Wer die Potenziale der Grafikkarten voll ausnutzen möchte, sollte OnIdle verwenden.

Methode 1: Timer

Bei dieser Methode muss ein Timer (zu finden bei den Systemkomponenten) auf das Formular gezogen werden. Der Timer besitzt eine Eigenschaft names "Interval". Mit dieser Eigenschaft kann man einstellen, nach wie vielen Millisekunden das Ereignis OnTimer ausgelöst wird. Man kann "Interval" nicht beliebig verkleinern. Werte unter 25 können von dem Standardtimer, den Windows verwendet, nicht mehr korrekt erzeugt werden.

Der Inhalt von OnTimer könnte dieser sein:

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  inc(FrameCount);
  Render;
  If FrameCount = 20 then begin
    ErrorHandler;
    FrameCount := 0;
  end;
end;

Tiefgreifende Erklärungen sind hier nicht notwendig. Was der ErrorHandler ist, wird nach der Methode 2 erklärt.

Methode 2: OnIdle

OnIdle ist ein besonderes Ereignis, welches das gesamte Programm betrifft. Wenn die Anwendung nichts zu tun hat, also faul ist (englisch idle), tritt das Ereignis ein.

Die Methode mit OnIdle kann gleich mit zum Auswerten der Framezahlen (Anzahl Bildwiederholungen pro Sekunde) benutzt werden (Framecounter). Der nachfolgende Code enthält selbigen bereits.

procedure TForm1.IdleHandler(Sender: TObject; var Done: Boolean);
begin
  StartTime:= GetTickCount;
  Render;
  DrawTime:= GetTickCount - StartTime;
  Inc(TimeCount, DrawTime);
  Inc(FrameCount);

  if TimeCount >= 1000 then begin
    Frames:= FrameCount;
    TimeCount:= TimeCount - 1000;
    FrameCount:= 0;
    Caption:= InttoStr(Frames) + 'FPS';
    ErrorHandler;
  end;

  Done:= false;
end;

Zeile 3: Mittels GetTickCount wird die Systemzeit gemessen. Dies ist nicht nötig, um erfolgreich zu zeichnen, sondern dient ausschließlich der Berechnung der Framerate. Diese wiederum ist ein guter Performancemesser.

Zeile 4: Hier erfolgt der Aufruf unserer Zeichenroutine.

Zeile 9: Der hier angeordnete Block wird nur pro Sekunde einmal ausgeführt und sorgt dafür, dass die Framerate angezeigt wird. Außerdem wird der Errorhandler aufgerufen.

Zeile 14: Der Errorhandler wird im Anschluss beschrieben.

Zeile 17: Wenn Done nach der Ausführung false ist und das Programm wieder nichts zu tun hat, wird OnIdle erneut ausgeführt. Wenn Done = true ist wird OnIdle nur einmal ausgeführt.

Um auf das "Idle-Event" reagieren zu können, müsst ihr jetzt nur noch diese Funktion an das Event koppeln. Das macht ihr, indem ihr den nachfolgenden Code in die letzte Zeile eurer FormCreate-Methode schreibt.

Application.OnIdle := IdleHandler;

Wie ihr bald selbst feststellen werdet, sorgt diese Methode für eine hundertprozentige Prozessorauslastung (Und das ist schlecht für die Akkulaufzeiten von Notebooks). Umgehen könnt ihr dies mit einem kleinen Trick:

Fügt vor dem "Done := false" (Zeile 17 des IdleHandlers) noch ein sleep(1) oder sleep(5) ein. Dadurch sinkt die Prozessorlast auf ca. 80%, was schon ein Fortschritt ist.

Noch ein Hinweis, weil im Forum öfters Probleme damit auftraten: OnIdle ist eine Funktion des Formulars und muss entsprechend auch in der Klassendeklaration eingetragen werden. Am Ende des Tutorials findet ihr die Klassendeklartion aus meinem Beispiel. Wenn ihr das vergesst meldet der Compiler "Undefinierter Bezeichner: 'IdleHandler'".

Der ErrorHandler - Fehler erkannt, Fehler gebannt

Der Errorhandler ist wieder eine sehr einfache Funktion, denn OpenGL bietet von Haus aus eine Möglichkeit, OpenGL-Fehler zu erkennen. Deshalb ist der ErrorHandler auch so klein:

procedure TForm1.ErrorHandler;
begin
  Form1.Caption := gluErrorString(glGetError);
end;

Enttäuscht? Ihr könnt den ErrorHandler nach belieben auch komplexer machen. Zum Beispiel könnt ihr, anstatt die Fehler im Fenstertitel anzuzeigen, lieber den Fehler in ein Logfile schreiben. Ganz nebenbei: Falls kein Fehler auftritt, liefert glGetError GL_NO_ERROR.

Das fertige Templateformular

Eure Klasse TForm1 sollte also jetzt so oder so ähnlich aussehen:

TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure IdleHandler(Sender: TObject; var Done: Boolean);
    procedure FormResize(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private    { Private-Deklarationen }
    StartTime, TimeCount, FrameCount  : Cardinal; //FrameCounter
    Frames, DrawTime                  : Cardinal; //& Timebased Movement
    procedure SetupGL;
    procedure Render;
    procedure ErrorHandler;
  public    { Public-Deklarationen }
    DC                                : HDC;  //Handle auf Zeichenfläche
    RC                                : HGLRC;//Rendering Context
  end;

Zeile 3: Wie man sieht, benutzt mein Template die zweite Methode (OnIdle).

Zeile 8: Was Timebased Movement ist, könnt ihr ja mal nachlesen.

Zeile 14/15: HDC und HGLRC sind Typen die von Windows zur Verfügung gestellt werden. Ihr findet sie in der Unit "Windows". (Diese Unit sollte bei einem neuen Projekt bereits durch Delphi eingebunden worden sein.)

So... fertig. Eigentlich seid Ihr jetzt soweit, von den Tutorialschreibern so richtig mit OpenGL-Wissen vollgepumpt zu werden. Das nachfolgende Kapitel könnt Ihr euch trotzdem ruhigen Gewissens durchlesen. Wer es liest, tappt vielleicht nicht gleich mit der ersten Frage im DGL-Forum ins berüchtigte Fettnäpfchen.

Tipps für den OpenGL Anfänger

Wenn man mit OpenGL anfängt, ist man meist total perplex und ein "Das war ja einfach!" huscht einem nicht nur einmal über die Lippen. Vor allem zu Beginn Eurer OpenGL-Karriere werdet Ihr viel lernen und dabei nur auf verhältnismäßig geringen Widerstand stoßen. Aber glaubt mir, es gibt ihn...

Häufig tauchen hochmotivierte OpenGL Anfänger im Forum auf und verkünden stolz, sie würden gerade an einer Engine arbeiten die "nur auf Doom 1 Niveau arbeiten soll".

Dazu gibt es häufig eine durchaus beachtliche Anzahl von Forenmitgliedern die dann ungefähr folgendes sagen: "Fang gar nicht erst damit an. Vergiss es, und komm in einem Jahr noch mal darauf zurück!" (Die Forensuche sollte euch einige dieser Threads zeigen)

Sind das böse Pessimisten, die Probleme nutzen um aufzugeben? Nein! Sie haben zumeist die Erfahrungen gemacht, die Ihr noch machen werdet. Deshalb will ich euch an dieser Stelle einweihen:

1. Je älter der Code ist, desto besser wird er.

Leider nein. Code und Wein unterscheiden sich hier leider grundlegend. Viele... die meisten... eigentlich alle von uns (die weniger als 3Jahre programmieren) bekommen Ausschlag, wenn sie sich ihren Code vom Vorjahr angucken. Erst wenn man so schätzungsweise 3 bis 5 Jahre mit OpenGL gearbeitet hat, hat man ein echtes Gefühl für den Code. Am Anfang hat man nämlich, ob man will oder nicht, die Tendenz, sich schon nach den ersten 3 Wochen Programmierarbeit den Code so zu zerschießen, dass die weitere Arbeit keinen Spaß mehr macht.

Ich selbst wollte jetzt ein Projekt weiterbearbeiten, welches ungefähr ein 3/4 Jahr alt ist... Ich hab den Code weggeworfen und das fast fertige Spiel neu angefangen. (Glaubt mir: Aus solchen Fehlern lernt man!)

2. Große Projekte = Großer Ruhm

Stimmt! ABER Ruhm gibt es in der Szene nur für beendete Projekte. Und dreimal dürft Ihr raten, was durch die im Punkt 1 angesprochenen Probleme meist nicht mit euren Projekten passiert...

Wenn Ihr OpenGL nur zur Visualisierung von z.B. wissenschaftlichen Ergebnissen benutzt, ist die Arbeit im Bezug auf OpenGL sehr übersichtlich. Wenn Ihr allerdings Spiele programmieren wollt, werdet Ihr schnell merken, dass Probleme auf euch zukommen werden, die euch beim Projektstart völlig unbekannt waren. Dies ist ein sicheres Zeichen dafür, dass doch noch etwas mehr Erfahrung nötig sein wird.

Und woher soll ich bitte Erfahrung nehmen?

Darin liegt der Trick:

Alle OpenGLer haben einmal "klein" angefangen. Berühmt-berüchtigt sind die 3DPong-Clone, die zahlreich im Internet anzufinden sind. Auch Tetris-, Memory- oder "Vier Gewinnt"-Clone sind solche stillen Zeugen eines großen Lernvorgangs. Das Besondere an solchen Spielen ist, dass die Spiellogik relativ einfach ist und euch damit nur wenig von der Visualisierung ablenkt.

Ihr denkt jetzt bestimmt "Was kann man denn schon in so nem Clon unterbringen?" VERDAMMT VIEL! Hier mal ein kleiner Auszug:

  • 3D-Spieldarstellung
  • 2D Menüführung (glOrtho)
  • Blending
  • Texturen
  • Licht und Materialien (Ihr werdet Augen machen!)
  • Selektion (Hin und wieder zum Haare raufen.)
  • Kamerasteuerung /Bewegung durch die Szene (Da gibt es ganze Tutorials dazu)

Als Ansporn solltet Ihr euch am Anfang ein kleines Ziel setzen und sagen "Ich möchte den besten und schönsten XYZ-Clon im ganzen Internet schaffen!". So etwas trifft in der Szene auf wesentlich mehr Anerkennung, als wenn mal wieder ein Anfänger etwas von Engine und Doom X faselt.

Wenn Ihr ein oder zwei solcher Projekte abgeschlossen habt, und regelmäßig im Forum bzw. Wiki gelesen habt, werdet Ihr es schon merken, wenn Euer Traumprojekt endlich in Angriff genommen werden kann. (Ganz vergessen müsst ihr es nämlich doch nicht ;) )

Als kurze Zusammenfassung solltet Ihr euch merken:

  • Ein fertiges Projekt bringt euch Ruhm (Seelenbalsam).
  • Ein hübsches, fertiges Projekt bringt euch mehr Ruhm.
  • Ein großes, fertiges, hübsches Projekt bringt euch noch mehr Ruhm.
  • Ein abgebrochenes oder eingefrorenes Projekt bringt euch Frust.
  • Ein großes, abgebrochenes Projekt bringt euch noch mehr Frust, denn der verschwendeten Zeit werdet Ihr nachtrauern.

3.Das es lang dauert ist egal. Ich interessiere mich halt dafür.

Diese Aussage gilt nur dann, wenn Ihr schon über ein Jahr an ein und demselben Projekt gearbeitet habt. Anfänglich ist das kein Problem. Aber wenn man lange an etwas arbeitet und die gemachten Änderungen sind nicht sichtbar (weil sie z.B. den eigentlichen Motor der Anwendung betreffen und nicht die Ausgabe), dann verliert man schon mal die Lust. Folge sind die berüchtigten "Erfolgsmeldungen" wie: "Das Projekt wurde von mir bis auf weiteres aufs Eis gelegt. Ich werde sicherlich später daran weiterarbeiten." Was stand gleich nochmal unter Punkt 1? - Das Endergebnis dürfte klar sein.


Tipps für den Erfolg

  1. Lernt eure Programmiersprache kennen.
  2. Lernt ObjektOrientierung. Nur weil ihr Klassen benutzt, heißt das nicht, dass ihr Objekt orientiert programmiert.
  3. Lernt eure Entwicklungsumgebung kennen. (Debugging! Dazu gibts hier ein Tutorial für Delphi.)
  4. Kleine Brötchen backen (Testprojekte) und Projekte zu Ende bringen!

Nachwort

Soviel zur Euphoriebremse. OpenGL ist toll. OpenGL ist die Lösung für Eure Traumanwendung. Aber die wird auch mit OpenGL nicht von heute auf morgen programmiert. Deshalb heißt es, Tutorials lesen, kleine Testanwendungen schreiben um Effekte zu testen und Projekte bearbeiten. Dann wird es auch etwas mit den Traumprojekten. Zudem kann man anfangs auch versuchen, in sehr kleinen Anwendungen einen Effekt auszuprobieren und diesen in Form eines kleinen Beispielprogramms oder eines Tutorials zu veröffenlichen. Somit lernt man nicht nur selbst etwas dazu...

Nach diesen unglaublich Weise klingenden Worten, die ich mit einem hoch ernsten Gesicht geschrieben habe, könnt ihr euch jetzt hochmotiviert an die restlichen Tutorials machen.

Bis bald im Forum

Euer

Flash (Kevin Fleischer)

PS: Feedback wird nicht nur gewünscht, sondern ausdrücklich gefordert. Deshalb: Ab mit deinen Meinungen und Ratschlägen ins Feedback-Forum!


Vorhergehendes Tutorial:
-
Nächstes Tutorial:
Tutorial Lektion 1

Schreibt was ihr zu diesem Tutorial denkt ins Feedbackforum von DelphiGL.com.
Lob, Verbesserungsvorschläge, Hinweise und Tutorialwünsche sind stets willkommen.