Chris-Tec
Kontakt Sitemap International Impressum
Home
Leistungen
Referenzen
Downloads
Tutorials
Ihr Auftrag
 
OpenGL Tutorial: Flutlicht


Im folgenden Tutorial möchte ich euch zeigen, wie man in OpenGL einen netten grafischen Effekt erreicht: Einen 3D-Text, der von einem Flutlicht in beliebiger Farbe von links nach rechts durchschienen wird:

Um das Tutorial verstehen zu können, sollte man fortgeschrittener OpenGL Programmierer sein, da ich keine Details erklären werde. Folgende Codeauszüge sind in Delphi geschrieben, am Ende des Tutorials kann man das gesamte Programm samt Source Code als Delphi Projekt herunterladen.

Um ein Flutlicht zu erzeugen, müssen wir einen Filtereffekt anwenden, den man in Grafikprogrammen meist als radialen Weichzeichner beschreibt. Doch darauf gehe ich später genauer ein. Jetzt brauchen wir erst einmal 2 Prozeduren, mit denen wir schnell und einfach zwischen dem orthogonalen und dem perspektivischem Modus umschalten können, da wir später beide Modi benötigen werden:

procedure ViewOrtho;            { in orthogonalen Modus wechseln (2D) }
begin
  glMatrixMode(GL_PROJECTION);  
{ Projektionsmatrix }
  glPushMatrix;                
{ Matrix in den Stack schreiben }
  glLoadIdentity;              
{ Matrix mit Identitätsmatrix ersetzen }
  glOrtho(0,1024,768,0,-1,1);  
{ Matrix für Parallel-Projektion erstellen }
  glMatrixMode(GL_MODELVIEW);  
{ Modelviewmatrix }
  glPushMatrix;                
{ Matrix in den Stack schreiben }
  glLoadIdentity;              
{ Matrix mit Identitätsmatrix ersetzen }
end;

procedure ViewPerspective;      
{ in perspektivischen Modus wechseln (3D) }
begin
  glMatrixMode(GL_PROJECTION);  
{ Projektionsmatrix }
  glPopMatrix;                  
{ Matrix vom Stack holen und ersetzen }
  glMatrixMode(GL_MODELVIEW);  
{ Modelviewmatrix }
  glPopMatrix;                  
{ Matrix vom Stack holen und ersetzen }
end;



Mit dem orthogonalen Modus ist es nun möglich, mit pixelgenauen Koordinaten zu arbeiten, wohingegen mit einem Aufruf von ViewPerspective() alles wieder in gewohnter 3D Projektionsmatrix abläuft. Diese 2 Prozeduren sind sehr hilfreich, wenn man Projekte hat, in denen sowohl 3D als auch 2D gezeichnet und gerendert wird. Einsatzmöglichkeiten gibt es z.B. in 3D-Spielen, in denen ein Head-Up-Display (HUD) benötigt wird.

Nun, da wir unsere Prozeduren zum wechseln zwischen 2D und 3D-Modus haben, können wir uns dem Eigentlichem widmen. Als erstes brauchen wir einen Text, den wir mit dem Flutlicht durchscheinen können. OpenGL bietet leider von Haus aus keine Funktionen an, mit denen man einfachen oder 3D Text erzeugen kann. Deshalb müssen wir selber Hand anlegen, und uns eine solche Funktion programmieren:

procedure BuildFont3D;                            { 3D-Font erstellen }
  var
  Font: HFONT;                                    
{ Windows Font ID }
begin
  base3d := glGenLists(256);                      
{ Platz für 256 Zeichen }

  
{ Font erstellen }
  font := CreateFont(24,                        
{ Höhe der Buchstaben }
                      0,                          
{ Breite der Buchstaben }
                      0,                          
{ Rotationswinkel }
                      0,                          
{ Ausrichtungswinkel }
                      FW_BOLD,                    
{ Font-Weight }
                      0,                          
{ Kursiv }
                      0,                          
{ Unterstrichen }
                      0,                          
{ Durchgestrichen }
                      ANSI_CHARSET,              
{ Zeichensatz }
                      OUT_TT_PRECIS,              
{ Ausgabe Genauigkeit (Truetype) }
                      CLIP_DEFAULT_PRECIS,        
{ Clipping Genauigkeit }
                      ANTIALIASED_QUALITY,        
{ Ausgabe Qualität }
                      FF_DONTCARE or DEFAULT_PITCH,  
{ Family und Pitch }
                      'Arial');                  
{ Name der Schriftart }

  SelectObject(HC, font);                        
{ Erstellte Font auswählen }

  
{ Outline-Font erstellen }
  wglUseFontOutlines(HC,                          
{ aktuellen Device Context wählen }
                      0,                          
{ Anfangszeichen }
                      255,                        
{ Anzahl der zu erstellenden Displaylisten }
                      base3d,
                      0,                          
{ Abweichung zu den tatsächlichen Außenlinien }
                      0.2,                        
{ Dicke/Tiefe der Schrift in Z-Richtung }
                      WGL_FONT_POLYGONS,          
{ OpenGL soll Polygone zum erstellen verwenden }
                      @gmf);                      
{ Pointer zur Displayliste }

end;



Die Erstellung der Font geschieht durch die Benutzung des Befehls CreateFont. Dieser benötigt einige Parameter: Als erstes die Höhe der Buchstaben. Als nächstes geben wir die Breite der Buchstaben an, wobei man hier am besten 0 einsetzt, da dann Windows automatisch den richtigen Wert proportional zur Höhe einsetzt. Mit dem nächsten Parameter kann man die einzelnen Buchstaben um einen bestimmen Grad drehen, falls man dies möchte. Den Ausrichtungswinkel lassen wir immer bei 0. Nun kann man die Schriftdicke angeben, dafür gibt es einige vorgefertigt Parameter: FW_BOLD, FW_EXTRABOLD, FW_LIGHT, FW_THIN und einige mehr. Man kann alternativ auch einfach einen Wert zwischen 100 und 900 angeben, um die Dicke der Schrift zu bestimmen. Nun können wir festlegen, on die Schrift Kursiv, unterstrichen und/oder durchgestrichen sein soll. 0 bedeutet hierbei, dass dies jeweils nicht der Fall ist, bei 1 wird die Schrift entsprechend Kursiv etc.

Weitere Parameter sind der zu benutzende Zeichensatz, die Ausgabegenauigkeit, die Clippinggenauigkeit, die Ausgabequalität und die Familyart und Pitch. Diese Werte sollte man alle so setzen, wie im obigen Code, da so das beste Ergebnis erzielt wird. Als letzten Parameter geben wir nun noch den Namen der zu verwendenden Schriftart an.

In den Parametern des Befehls wglUseFontOutlines können wir nun noch 2 wichtige Parameter angeben: Die Abweichung der erstellten Buchstaben zu den tatsächlichen Außenlinien der Buchstaben. Diesen Wert sollten wird auf 0 setzen, da somit das beste Ergebnis erzielt wird. Je größer dieser Wert, desto eckiger die Schrift. Der Parameter zur Angabe der Schriftarttiefe lässt und bestimmen, die tief die Schrift wird. Setzt man diesen Parameter auf 0, so erhalten wir z.B. eine 2D-ähnliche flache Schrift.

Unsere Schrift sollten wir nun beim Initialisieren von OpenGL erstellen:

function Initialize(window: PGL_Window; key: PKeys): boolean;    { OpenGL initialisieren }
begin

  g_window := window;
  g_keys := key;
  glViewport(0,0,window.init.width,window.init.height);
  glMatrixMode(GL_PROJECTION);  
{ Projektionsmatrix }
  glLoadIdentity;      
{ Matrix mit Identitätsmatrix ersetzen }
  gluPerspective(50,window.init.width / window.init.height,5,2000);
  glMatrixMode(GL_MODELVIEW);  
{ Modelviewmatrix }
glLoadIdentity;      
{ Matrix mit Identitätsmatrix ersetzen }
  glEnable(GL_DEPTH_TEST);    
{ Tiefentest aktivieren }
  BuildFont3d();              
{ 3D-Font erstellen }

  Result:=true;
end;



Nun, da wir unsere Schrift erstellt haben, brauchen wir noch eine Prozedur, um nun auch Text auf den Bildschirm ausgeben zu können:

procedure glPrint(text: string);
var
  fontwidth: glfloat;        
{ Breite des Texts }
  loop: integer;            
{ Zählvariable }
begin
  if text = '' then exit;    
{ Ist überhaupt etwas zum Ausgeben da? }
  fontwidth := 0;            
{ Länge initialisieren }
  for loop:=1 to length(text)-1 do fontwidth := fontwidth + gmf[loop].gmfCellIncX;  
{ Textbreite inkrementieren für jedes Zeichen }
  glTranslatef(-fontwidth/2,0.0,0.0);  
{ Text positionieren (Mitte des Bildschirms) }
  glPushAttrib(GL_LIST_BIT);
{ Stellt sicher, dass keine Auswirkungen auf evtl. andere Displaylisten in unserem Programm passieren }
  glListBase(base3d);        
{ Sagt OpenGL, wo die richtige Displayliste für die Zeichen zu finden ist }
  glCallLists(length(text),GL_UNSIGNED_BYTE,Pchar(text));
{ Zeichnet den Text }
  glPopAttrib;              
{ Displaylistenbits wiederherstellen }
end;



In dieser Prozedur beginnen wir damit, den auszugebenden Text Zeichen für Zeichen zu durchlaufen und für jedes Zeichen die Breite von diesem zu ermitteln und zu addieren. Damit erhalten wir die Gesamtbreite des Strings, die wir dazu benötigen werden, um den Text an die richtige Stelle zu positionieren. gmf[loop].gmfCellIncX ist der Code, der uns die Breite eines Zeichens gibt. (Falls benötigt, gmf[loop].gmfCellIncY gibt die Höhe eines Zeichens zurück).

Nachdem wir die Breite des Textes ermittelt haben, positionieren wir den Text nun in die Mitte unseres Bildschirmes. Dazu müssen wir die X Koordinate berechnen, wir müssen den Text nach links verschieben. Und zwar genau um die Hälfte der Breite des Textes, damit der Text zentriert wird. Daher -fontwidth/2.

Nun teilen wir OpenGL mit, wo die Displayliste für die Zeichen zu finden ist. Vorher schreiben wir die aktuellen Displaylistenbits aber noch in den Stack, um zu verhindern, dass es Probleme mit andern Displaylisten gibt, die man evtl. im Programm verwendet.

Dann ist alles bereit, um den Text auf den Bildschirm zu bringen. Hier reicht ein einziger Befehl, der das Ganze übernimmt. Er ruft alle nötigen Displaylisten nacheinander auf: glCallLists. Für diesen Befehl müssen wir als erstes die Anzahl der zu zeichnenden Displaylisten angeben, in unserem Fall ist das die Anzahl der Zeichen des auszugebenden Textes. Der nächste Parameter gibt einen Datentyp an, den OpenGL verwenden soll. Dieser muss ausreichend Platz besitzen, um die Anzahl der Displaylisten speichern zu können. In unserem Fall sind es 256 Displaylisten, das entspricht 2^8. Der Datentyp Unsigned Byte bietet genau so viel Platz wie wir brauchen. Als letzen Parameter müssen wir noch angeben, was wir ausgeben möchten. In unserem Fall also der Text.

Als letztes holen wir wieder die vorherigen Displaylistenbits aus dem Stack, um die Verwendung von weiteren Displaylisten im Programm zu gewährleisten.

Nun brauchen wir als nächstes eine Prozedur, die uns einen Text ausgibt. Dies ist nötig, da wir für den Flutlichteffekt das Darzustellende zweimal rendern lassen müssen, und da wäre es unangebracht, alles zu kopieren und so unschöne Redundanzen entstehen zu lassen. Daher packen wir das Ganze einfach in eine Prozedur, die wir beliebig oft aufrufen können:

procedure DrawText(); { Text ausgeben }
begin

glLoadIdentity;                  
{ Matrix mit Identitätsmatrix ersetzen }

glPushMatrix;                    
{ Matrix in den Stack schreiben }
gltranslatef(0,0,-10);          
{ Den Text etwas nach hinten setzen }
glcolor3f(1,1,1);                
{ Die Farbe des Textes bestimmen }

glPrint('Flutlicht');            
{ Text ausgeben }

glPopMatrix;                    
{ Matrix vom Stack holen und ersetzen }
end;



Hier gibt es nicht viel zu erklären, wir schieben den Text etwas nach hinten, da er sonst direkt vor der Kamera pappen würde. Dann geben wir eine Farbe an, in der der Text erscheinen soll, in obigem Falle ist das Weiß (R,G,B jeweils von 0..1).

Wenn wir nun in unserer Main Prozedur den Text ausgeben lassen:

procedure Draw;         { Szene zeichnen }
begin
  glClearColor(0.0,0.0,0.0,0.0);  
{ Löschfarbe setzen }
  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);  
{ Löschen }
  glLoadIdentity;            
{ Matrix mit Identitätsmatrix ersetzen }
  DrawText();        
{ Text zeichnen }
  glFlush;            
{ Sicherstellen, das alle Befehle ausgeführt wurden }
end;



Dann sollte sich etwa folgendes Bild ergeben, wenn wir das Programm laufen lassen:

Flutlicht Tutorial - Screenshot


Nun müssen wir uns dem Flutlicht widmen. Ich werde erst kurz die Funktionsweise erklären: Da wir keine Möglichkeit haben, eine Szene einfach weichzuzeichnen bzw. unscharf zu machen, wie es für unseren Effekt nötig ist, müssen wir eine Alternative benutzen: Mit OpenGL lassen sich Texturen ganz einfach unscharf machen, indem man sie vergrößert. Das ist zwar kein wirklicher radialer Weichzeichner, aber das Ergebnis kommt sehr nahe an diesen heran, und daher werden wir diese Methode auch benutzen. Wir müssen also unsere Szene in eine kleine Textur packen, und diese dann auf den gesamten Bildschirm vergrößern. Das allein reicht aber noch nicht aus. Um den Effekt wirklich schön zu machen, müssen wir nicht nur eine, sondern mehrere dieser Texturen auf den Bildschirm bringen, und diese immer etwas größer machen. So sollten wir einen schönen Flutlicht Effekt bekommen.

Als erstes brauchen wir also eine Textur, in die wir unsere Szene Frame für Frame speichern können. Dazu müssen wir erst einmal Speicher reservieren und dann eine leere Textur generieren:

function EmptyTexture: GLuint;           { Leere Texture erstellen }
var
  txtnumber: GLuint;                    
{ Textur-ID }
  data: PGLuint;                        
{ Gespeicherte Daten }
begin
  data := AllocMem(128*128*4*sizeof(GLuint));  
{ Speicher für den Texturinhalt reservieren }
  glGenTextures(1,txtnumber);      
{ Eine Textur generieren }
  glBindTexture(GL_TEXTURE_2D,txtnumber);  
{ Texture binden }
  glTexImage2D(GL_TEXTURE_2D,0,4,128,128,0,GL_RGBA,GL_UNSIGNED_BYTE,data);  
{ Texturdaten erstellen }
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);  
{ Größenänderungsmethode setzen }
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);  
{ Größenänderungsmethode setzen }
  FreeMem(data);        
{ Speicher freigeben }
  Result := txtnumber;  
{ ID der erstellen Textur zurückgeben }
end;



Das setzen der Methode zur Änderung der Größe ist wichtig, da standardmäßig GL_NEAREST benutzt wird. Und das sieht beim Vergrößern einer Textur ziemlich schlecht aus.

Bei der Initialisierung müssen wir nun noch einer Textur eine leere Textur zuweisen, in die wir dann später unsere Szene kopieren:

function Initialize(window: PGL_Window; key: PKeys): boolean;    { OpenGL initialisieren }
begin
...
  BlurTexture := EmptyTexture;  
{ BlurTexture erstellen und leeren }
...
end;



Jetzt brauchen wir eine Prozedur, die das aktuelle Szenenbild immer in diese Textur kopiert und in der Größe verringert, damit sich später beim vergrößern auf den gesamten Bildschirm auch der gewünschte Weichzeichnereffekt einstellt:

procedure RenderToTexture();   { Szene in Textur zeichnen }
begin

  glViewport(0,0,128,128);  
{ Viewport verkleinern }

  DrawText();
{ Text zeichnen }
  glBindTexture(GL_TEXTURE_2D,BlurTexture);  
{ Blurtextur binden }
  glCopyTexImage2D(GL_TEXTURE_2D,0,GL_LUMINANCE,0,0,128,128,0);  
{ Szene in Textur kopieren }

  glClearColor(0.0,0.0,0.0,0.0);    
{ Löschfarbe setzen }
  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);  
{Löschen }
  glViewport(0,0,1024, 768);      
{ Viewport wieder vergrößern }
  ViewPerspective();              
{ perspektivischen Modus wiederherstellen }
end;



Wir kopieren also die aktuelle Szene verkleinert in die BlurTextur. Wir benutzen hierbei den Befehl glCopyTexImage2D unter anderem mit dem Parameter GL_LUMINANCE, so dass die hellen Teile des Bildes kopiert werden. Das gibt später einen schöneren Effekt.

Jetzt haben wir also das aktuelle Szenenbild weichgezeichnet in einer Textur vorliegen. Damit wollen wir jetzt den Flutlichteffekt realisieren:

procedure DrawBlur(times: integer; inc: GLfloat); { Flutlicht zeichnen }
var
  spost: GLfloat;    
{ Textur Offset }
  alphadec: GLfloat;  
{ Zunahme des Alphawertes }
  alpha: GLfloat;    
{ Alphawert }
  num: integer;      
{ Zählvariable }
begin
  spost := 0.0;      
{ Offset initialisieren }
  alpha := 0.2;      
{ Alpha initialisieren }

  ViewOrtho;          
{ in den 2D Modus wechseln }
  alphadec := alpha / times;  
{ Abnahme des Alphawertes errechnen }

  glLoadIdentity();  
{ Matrix mit Identitätsmatrix ersetzen }
  glEnable(GL_TEXTURE_2D);    
{ Texturen aktivieren }
  glDisable(GL_DEPTH_TEST);  
{ Tiefentest deaktivieren }
  glBlendFunc(GL_SRC_ALPHA,GL_ONE);
{ Blendmodus setzen }
  glEnable(GL_BLEND);
{ Blending aktivieren }
  glBindTexture(GL_TEXTURE_2D,BlurTexture);
{ BlurTextur binden }

  glloadIdentity();  
{ Matrix mit Identitätsmatrix ersetzen }
  
  glBegin(GL_QUADS);    
{ Quads zeichnen }

  for num := 0 to times - 1 do
  begin
    glColor4f(0,0.5,1.0,alpha);  
{ Farbe des Blurs setzen, hier: Blau }

    glTexCoord2f(0+spost,1-spost);            
{ Texturkoodinaten 0, 1 }
    glVertex2f(0, 0);  
{ Vertex 0, 768 }
    glTexCoord2f(0+spost,0+spost);            
{ Texturkoodinaten 0, 0 }
    glVertex2f(0,768);
{ Vertex 0, 0 }
    glTexCoord2f(1-spost,0+spost);            
{ Texturkoodinaten 1, 0 }
    glVertex2f(1024,768);
{ Vertex 1024, 0 }
    glTexCoord2f(1-spost,1-spost);            
{ Texturkoodinaten 1, 1 }
    glVertex2f(1024,0);  
{ Vertex 1024, 768 }

    spost := spost + inc;        
{ neues Offset der Textur berechnen }
    alpha := alpha - alphadec;  
{ neuen Alpha Wert berechnen }
  end;

  glEnd;    
{ Quads beenden }


  ViewPerspective;          
{ in perspektivischen Modus wechseln }
  glEnable(GL_DEPTH_TEST);  
{ Tiefentest aktivieren }
  glDisable(GL_TEXTURE_2D);  
{ Texturen deaktivieren }
  glDisable(GL_BLEND);      
{ Blending deaktivieren }
  glBindTexture(GL_TEXTURE_2D,0);  
{ Texturen entbinden }
end;



Diese Prozedur macht nun genau das, was ich vorhin schon angedeutet habe: Sie legt die weichgezeichnete Textur mehrere Male über den gesamten Bildschirm. Dazu wird einfach ein Quad über die gesamte Fläche gezeichnet, auf dem sich die Textur befindet. Das Quad muss öfters gezeichnet werden, um den Effekt zu vergrößern. Würde man das Quad nur einmal zeichnen, so würde der Text nur ganz leicht "glühen". Da wir aber das Quad mehrmals zeichnen, und bei jedem Durchgang etwas vergrößern (dies geschieht durch die Berechnung der Texturkoordianten und dem Offset, das wir bei jedem Durchgang erhöhe) erhöht sich dieser Effekt. Um das Ganze zu perfektionieren wählen wir als Blendmodus GL_SRC_ALPHA, GL_ONE. Damit werden Farben heller, sobald die öfters aufeinander liegen.

Jetzt müssen wir das Ganze noch in unsere Main Prozedur einbinden: Zuerst zeichnen wir die BlurTexturen, und dann das eigentliche Objekt. Vorher kopieren wir noch den aktuellen Szeneninhalt in die BlurTextur. Daher meinte ich vorhin, dass wir das was eigentlich gezeichnet werden soll, lieber in eine eigene Prozedur legen, damit wir das Ganze nicht zweimal hinschreiben müssen:

procedure Draw;         { Szene zeichnen }
begin
  glClearColor(0.0,0.0,0.0,0.0);  
{ Löschfarbe setzen }
  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);  
{ Löschen }
  glLoadIdentity;            
{ Matrix mit Identitätsmatrix ersetzen }
  RenderToTexture();  
{ Text in Textur zeichnen }
  DrawText();        
{ Text zeichnen }
  DrawBlur(25,0.02);  
{ Weichzeichnereffekt bzw. Flutlicht zeichnen }
glFlush;            
{ Sicherstellen, das alle Befehle ausgeführt wurden }
end;



Wenn wir das nun laufen lassen, sollte das Ganze etwa wie folgt aussehen:

Flutlicht Tutorial - Screenshot


Das sieht doch schon ganz nett aus, nicht wahr ;)? An dieser Stelle kann man nun übrigens statt einem statischen, unbeweglichen Text auch andere Dinge einbauen, die sich bewegen. Man kann den Text z.B. auch rotieren lassen. Da die Szene bei jeder Berechnung des Bildes neu in die BlurTextur kopiert wird, passt sich das Flutlicht sofort an das aktuelle Bild an. So lassen sich auch bewegte Objekte mit einem Flutlicht durchscheinen.

Nun wollen wir aber den Effekt so verändern, dass wir einen ähnlichen Effekt erhalten, wie man ihn oft im Fernsehen sieht: Das Licht bewegt sich hinter dem Text von Links nach Rechts. Um dies zu realisieren, müssen wir einen Trick anwenden und ein paar Sachen verändern: Die erste Veränderung findet in der Prozedur statt, in der wir die Szene in eine Textur speichern. Dort kopieren wir nicht mehr das gesamte Bild, sondern nur noch einen Ausschnitt in die Textur:

procedure RenderToTexture(pPos: real);   { Szene in Textur zeichnen }
begin
  glViewport(128-round(pPos*128),0,128,128);  
{ Viewport verkleinern }
...
end;



Wir übergeben dieser Prozedur nun also einen Parameter, der einen Wert von 0..1 annehmen kann. Nun ziehen wir von der Gesamtbreite des Bildes die maximale Breite des Bildes multipliziert mit dem Parameter ab, und dies stellt dann unseren Anfangswert dar. Zusammengefasst bedeutet das also, dass nun ein Ausschnitt kopiert wird, der immer ganz Rechts anfängt und mit steigendem Parameter nach Links hin immer größer wird.

Wenn wir das Programm nun laufen lassen, sehen wir schon eine große Veränderung. Das Flutlicht sieht schon mal genau so aus, wie wir uns das vorstellen. Leider verläuft jetzt nun aber das Flutlicht seltsam von Rechts nach Links, und bleibt nicht an seiner Stelle:

Flutlicht Tutorial - Screenshot


Jetzt müssen wir deshalb ein paar Veränderungen in der Prozedur vornehmen, in der wir das Flutlicht zeichnen. Wir positionieren die Quads, auf die wir die Blurtextur binden, vorher so, dass der entstandene Fehler wieder korrigiert wird:

procedure DrawBlur(times: integer; inc: GLfloat); { Flutlicht zeichnen }
begin
...
  glloadIdentity();  
{ Matrix mit Identitätsmatrix ersetzen }
  gltranslatef(-1024+(1024/128)*round(FlutFade*128),0,0)
...
end;



Die Variable Flutfade müssen wir einführen, dass ist unsere Zeitvariable, die das Flutlicht steuert. In der Main Prozedur müssen wir diesen Wert entsprechend erhöhen.

Wir positionieren die Quads also so, dass das Flutlicht immer genau über dem Text erscheint. Dazu schieben wir die Quads von -1024, unserer Fensterbreite, bis hin auf 0. Und zwar immer im selben Verhältnis, wie unser Viewport verändert wurde. Daher die Positionenumrechnung, verhältnismäßig Fensterbreite:Viewportbreite.

Nun können wir das Programm wieder laufen lassen. Leider ist das Ergebnis immer noch nicht zufrieden stellend, da nun an den Seiten etwas abgeschnitten wird:

Flutlicht Tutorial - Screenshot


Um dieses Problem in den Griff zu kriegen, müssen wir nun das Zeichnen der Quads überarbeiten:

  for num := 0 to times - 1 do
  begin
    glColor4f(0,0.5,1.0,alpha);  
{ Farbe des Blurs setzen, hier: Blau }

    glTexCoord2f(0,1);            
{ Texturkoodinaten 0, 1 }
    glVertex2f(-1024*spost,-768*spost);  
{ Vertex 0, 768 }
    glTexCoord2f(0,0);            
{ Texturkoodinaten 0, 0 }
    glVertex2f(-1024*spost,768+768*spost);
{ Vertex 0, 0 }
    glTexCoord2f(1,0);            
{ Texturkoodinaten 1, 0 }
    glVertex2f(1024+1024*spost,768+768*spost);
{ Vertex 1024, 0 }
    glTexCoord2f(1,1);            
{ Texturkoodinaten 1, 1 }
    glVertex2f(1024+1024*spost,-768*spost);  
{ Vertex 1024, 768 }

    spost := spost + inc;        
{ neues Offset der Textur berechnen }
    alpha := alpha - alphadec;  
{ neuen Alpha Wert berechnen }
  end;



Wir verändern nun nicht mehr die Texturkoordinaten, denn dies hat dazu geführt, dass die Textur abgeschnitten wird (siehe oben). Dafür vergrößern wir nun einfach die Textur, bis zu 2-fach. Wir benutzen natürlich das Offset weiter, multiplizieren dies aber diesmal mit der Breite bzw. Höhe des Fensters und addieren das Ergebnis dann zur negativen oder positiven Breite bzw. Höhe unseres Fensters. Dadurch erreichen wir eine Vergrößerung und automatische Zentrierung unserer Quads. Zwar ragen diese Quads nun etwas über den Bildschirm hinaus, dafür haben wir aber unseren Effekt endlich genau so, wie wir ihn wollen:

Flutlicht Tutorial - Screenshot


Ich hoffe, das Tutorial hat Spaß gemacht und war nicht zu schwer zu verstehen!


Hier den Source Code + Exe herunterladen (199kb)



Info: Der Sourcecode wurde auf Basis des OpenGL Templates für Delphi von NeHe erstellt. Es wird benötigt um den Source Code kompilieren zu können.
Impressum Haftung Kontakt FAQ © 2005-2007

Valid HTML 4.01 Transitional