Die Community zu .NET und Classic VB.
Menü

OpenGL in Visual Basic - Texture Mapping II

 von 

Grundlagen 

Nachdem wir im letzten, reichlich umfangreichen, Kapitel gelernt haben, wie man Bilder auf Flächen klebt, gehen wir jetzt noch einen Schritt weiter. Wir werden Bilder auf Flächen bewegen.

Wozu soll das nur gut sein? Ganz einfach: Stellen Sie sich vor, sie haben für ein 3D-Spiel ein schönes Haus entworfen. Innerhalb des Spiels kann sich der Spieler frei im Haus bewegen. Was aber, wenn der Spieler vor einem Fenster stehen bleibt? In der Mehrzahl der Fälle wird er irgend ein Straßenszenario sehen, das in Wirklichkeit nur eine Textur auf einer Fläche ist. Damit das Ganze aber realistischer erscheint, wäre es doch schön, wenn am blauen Himmel Wolken vorbei ziehen würden. Das könnten wir jetzt natürlich lösen, in dem wir für das Fensterglas zwei Flächen definieren. Beide Flächen könnten zusammen durch bewegen und verformen den Eindruck vermitteln, es ziehen Wolken vorbei. Das läuft in etwa so wie eine endlose Filmschleife mit 2 gleichen Bildern, von denen immer nur Teile zu sehen sind. Der Aufwand dafür wäre aber unverhältnismässig gross und zum Glück auch unnötig.

In OpenGL gibt es dafür eine wesentlich einfachere Methode - wir bewegen nur die Textur auf der Fläche des Fensterglases. Dazu müssen wir die aktuelle Matrix wechseln. Bisher haben wir alles in der Modell-Matrix getan. OpenGL kennt aber noch weitere Matrizen. Eine davon ist die Textur-Matrix. Sobald wir in diese Matrix gewechselt haben, beziehen sich alle Anweisungen auf die aktuelle(n) Textur(en). Das einzige, was wir also tun müssen, ist in die Texur-Matrix zu wechseln, die Textur zu bewegen und wieder zurück in die Modell-Matrix zu gehen. Sobald keine Textur mehr bewegt werden soll, müssen wir wieder in die Textur-Matrix wechseln, die Bewegung aufheben und wieder in die Modell-Matrix zurückkehren.


Abbildung 1: Weitere Spielereien mit Texturen.

Beispiel animiertes Fenster  

Im folgenden Beispiel erschaffen wir einen Fensterrahmen und eine Fensterscheibe. Dem Rahmen geben wir eine schöne Holz-Textur und auf der Scheibe sollen Wolken vorbeiziehen. Dazu müssen natürlich die Texturen in der Prozedur CreateGLWindow geladen werden.

Call LoadTextureS(App.Path & "\fenster.bmp", 1) 'laden und erzeugen einer Textur
Call LoadTextureS(App.Path & "\wolkene.bmp", 2) 'laden und erzeugen einer weiteren Textur

Listing 1: Laden der Fenstertexturen

Dann ändern wir die Sub Main und kreieren die beiden beschriebenen Flächen. Beide Fläche sind auf der X und Y-Achse zentriert und liegen auf der Z-Achse in der gleichen Ebene. Sie unterscheiden sich nur durch ihre Abmessungen. Das sollte Ihnen nach den vergangenen Lektionen keine Schwierigkeiten bereiten. Wichtig sind die Wechsel zwischen den Matrizen mit der Funktion glMatrixMode. Die Variable tGlid dient nur dem Hochzählen der Bewegung in sehr kleinen Schritten.

Public Sub Main()
   Static tGlid As GLfloat 'Schritte, um die eine Textur verschoben wird
 
   Dim frm As Form
   PrgRun = True 'Flag = das Programm läuft
   Set frm = New Form1 'Fenster für OpenGL-Ausgabe
   frm.ScaleMode = vbPixels 'das Fenster muss zwingend in Pixel bemessen werden
 
   If CreateGLWindow(frm, 640, 480, 16) Then 'Fenster initialisieren
      Do 'Endlosschleife, in der das Fenster laufend gelöscht und neu aufgebaut wird.
         'Die Laufzeit dieser Schleife ist ausschlaggebend, wieviele Objekt gezeichnet werden können
         glClear clrColorBufferBit Or clrDepthBufferBit ' löscht das Fenster und den Tiefenpuffer
         glLoadIdentity 'setzt die aktuelle Modell-Matrix zurück
 
         glTranslatef -1#, 0#, -8# 'Zeichenpunkt zurücksetzen
 
         glBindTexture GL_TEXTURE_2D, Texture(1) 'Auswahl der gewünschten Textur
         glBegin bmQuads 'Fensterrahmen
            glTexCoord2f 1#, 1#: glVertex3f 2#, 2#, 1# 'Oben Rechts
            glTexCoord2f 0#, 1#: glVertex3f -2#, 2#, 1# 'Oben Links
            glTexCoord2f 0#, 0#: glVertex3f -2#, -2#, 1# 'Unten Links
            glTexCoord2f 1#, 0#: glVertex3f 2#, -2#, 1# 'Unten Rechts
         glEnd 'Ende des Fensterrahmens
 
         glMatrixMode GL_TEXTURE 'Umschalten in die Textur-Matrix
            glLoadIdentity 'setzt die aktuelle Textur-Matrix zurück
            glTranslatef tGlid, 0, 0 'verschiebt die aktuelle Textur entlang der X-Achse
            'OpenGL setzt automatisch den 'überstehenden' Teil wieder an
         glMatrixMode GL_MODELVIEW 'und zurück in den Modell-Matrix
 
         glBindTexture GL_TEXTURE_2D, Texture(2) 'Auswahl der gewünschten Textur
         glBegin bmQuads 'Fensterscheibe
            glTexCoord2f 1#, 1#: glVertex3f 1.335, 1.49, 1# 'Oben Rechts
            glTexCoord2f 0#, 1#: glVertex3f -1.3, 1.49, 1#   'Oben Links
            glTexCoord2f 0#, 0#: glVertex3f -1.3, -1.4, 1#   'Unten Links
            glTexCoord2f 1#, 0#: glVertex3f 1.335, -1.4, 1#   'Unten Rechts
         glEnd 'Ende der Fensterscheibe
 
         glMatrixMode GL_TEXTURE 'Umschalten in die Textur-Matrix
            glLoadIdentity 'setzt die aktuelle Textur-Matrix zurück
         glMatrixMode GL_MODELVIEW 'und zurück in die Modell-Matrix
 
         SwapBuffers (frm.hDC) 'Puffer tauschen (Double Buffering)
         DoEvents
 
         tGlid = tGlid + 0.00005 'der Wert zum Verschieben ist Rechnerabhängig!
         'Eleganter wäre es, die Verschiebung anhand der verstrichenen Zeit zu berechnen.
      Loop While PrgRun 'Programm nur beenden, wenn PrgRun = False
      'PrgRun ist Global definiert und wird im KeyDown-Ereignis von Form1 bei Drücken von Escape gesetzt.
 
      'alles freigeben und Programm beenden
      If hrc <> 0 Then 'hatten wir einen Gerätekontext für OpenGL?
         wglMakeCurrent 0, 0 'Freigeben des Gerätekontexts
         wglDeleteContext (hrc) 'Freigeben des Renderingkontexts
      End If
      Unload frm
      Set frm = Nothing
      End
   End If
End Sub

Listing 2: Sub Main für ein animiertes Fenster

Wie Sie sehen, ist der Aufwand minimal, der Effekt im Verhältnis aber gewaltig. Natürlich lassen sich Texturen ebenso wie Flächen auch in alle anderen Richtungen bewegen. Und weil es so schön ist, setzen wir noch einen drauf.

UV-Mapping  

Gesetzt den Fall, wir möchten auf einer Fläche eine animierte Textur darstellen, also so etwas wie ein animiertes GIF. Jetzt könnten wir dazu natürlich eine größere Zahl an einzelnen Bildern in ebenso viele Texturen laden und diese dann nacheinander anzeigen. Sind wir mal ehrlich: Das ist wieder jede Menge Arbeit für die Katz - gerade wenn es um 3D-Grafik geht, ist jede überflüssige Anweisung in der Hauptschleife eine zu viel.

Auch hier hilft uns OpenGL ganz entscheidend weiter. Wir benötigen nur eine Grafik, in der alle Einzelbilder enthalten sind. Um, wie im folgenden Beispiel vorgestellt, einen Zähler auf eine Fläche zu bekommen, sagen wir OpenGL einfach, an welcher Position unser Einzelbild steht. Das Ganze nennt sich dann UV-Mapping. Dazu verwenden wir eine Grafik mit den Zahlen von 1 - 8. Für diese Grafik gelten wieder die üblichen Einschränkungen: Länge und Breite müssen aus 2er Potenzen bestehen. Aber diese muss an sich nicht quadratisch sein, nur die verwendeten Einzelbilder müssen diese Bedingung erfüllen. So haben wir eine Grafik, die 256 * 32 Pixel groß ist, jedes Einzelbild aber nur 32 * 32 Pixel belegt.


Abbildung 2: Einzelbilder für einen Zähler

Jetzt müssen wir OpenGL nur noch sagen, wo die Einzelbilder stehen. Dazu ist ein minimaler Rechnaufwand nötig:

PicLength = 1 / 8
Das Bild besteht aus 8 Einzelbildern, eines hat somit 1/8 der Breite des Gesamtbildes
PicPos = Round(Pic) * PicLength
Errechnen, an welcher Position das Einzelbild beginnt.
Durch einsetzen der Round()-Funktion wird PicPos immer um genau 1 hochgezählt. Dadurch schaltet der Zähler genau von einem zum anderen Einzelbild. Entfernen Sie die Round()-Funktion und Sie haben eine Laufschrift!

Beispiel Zähler  

Erweitern Sie die Sub Main einfach um folgenden Code:

Dim PicPos As GLfloat 'Position des Einzelbildes in der Gesamtgrafik
Dim Piclength As GLfloat 'Breite einer Einzelgrafik
Dim Pic As GLfloat 'Zähler für den Counter

glLoadIdentity 'setzt die aktuelle Modell-Matrix zurück
glTranslatef 2#, 0#, -8# 'Zeichenpunkt setzen

Piclength = 1 / 8 'das Bild besteht aus 8 Einzelbildern
PicPos = Round(Pic) * Piclength 'errechnen, an welcher Position das Einzelbild steht

glBindTexture GL_TEXTURE_2D, Texture(3) 'Auswahl der gewünschten Textur
glBegin bmQuads 'Beginn Zähler
  glTexCoord2f PicPos + Piclength, 1#: glVertex3f 0.5, 0.5, 1#   'Oben Rechts
  glTexCoord2f PicPos, 1#: glVertex3f -0.5, 0.5, 1#   'Oben Links
  glTexCoord2f PicPos, 0#: glVertex3f -0.5, -0.5, 1#   'Unten Links
  glTexCoord2f PicPos + Piclength, 0#: glVertex3f 0.5, -0.5, 1#   'Unten Rechts
glEnd 'Ende des Zählers

Listing 3: Sub Main für einen Zähler

In der Hauptschleife sollte ausserdem noch die Variable Pic erhöht werden:
Pic = Pic + 0.001
Auch hier wäre die bessere Lösung eine Erhöhung anhand der verstrichenen Zeit, damit die Geschwindigkeit nicht hardwareabhängig ist.

Es geht weiter  

Damit hätten wir das umfangreiche Thema Texture Mapping erst einmal erledigt. Es bleibt ganz Ihrer Kreativität überlassen, was Sie aus diesen Möglichkeiten machen. Im Kapitel 5 werden wir dann lernen, wie Licht in unsere Welt kommt.

Beispielprojekt  

Beispielprojekt zum Tutorial [410087 Bytes]

Ihre Meinung  

Falls Sie Fragen zu diesem Tutorial haben oder Ihre Erfahrung mit anderen Nutzern austauschen möchten, dann teilen Sie uns diese bitte in einem der unten vorhandenen Themen oder über einen neuen Beitrag mit. Hierzu können sie einfach einen Beitrag in einem zum Thema passenden Forum anlegen, welcher automatisch mit dieser Seite verknüpft wird.