Spielwiese/Tutorial 4/grey: Unterschied zwischen den Versionen

Aus DGL Wiki
Wechseln zu: Navigation, Suche
K
K (Der Ausdruck ''<pascal>(.*?)</pascal>'' wurde ersetzt mit ''<source lang="pascal">$1</source>''.)
 
(3 dazwischenliegende Versionen von 3 Benutzern werden nicht angezeigt)
Zeile 3: Zeile 3:
 
==Vorwort==
 
==Vorwort==
 
Willkommen in der Welt der Bilder!
 
Willkommen in der Welt der Bilder!
In diesem Tutorial wirst du erfahren, wie man auf ein Polygon (Dreieck, Viereck usw) eine Textur zeichnen kann. Angefangen mit dem  
+
In diesem Tutorial wirst du erfahren, wie man auf ein Polygon (Dreieck, Viereck usw) eine Textur zeichnen kann. Angefangen mit dem Laden über das einfache Platzieren kommen wir schlussendlich dann dazu, wie man nur einzelne Bereiche einer Textur darstellen kann (dies ist das sog. UV-Mapping).
 
 
Laden über das einfache Platzieren kommen wir schlussendlich dann dazu, wie man nur einzelne Bereiche einer Textur darstellen kann  
 
 
 
(dies ist das sog. UV-Mapping).
 
  
 
Wofür denn überhaupt Texturieren? Nach den letzen Tutorials werden sicher einige den Wunsch verspürt haben mehr als nur abstrakte Kunst mit Farben und Farbverläufen darzustellen - dem Wunsch soll hiermit nachgekommen werden :D
 
Wofür denn überhaupt Texturieren? Nach den letzen Tutorials werden sicher einige den Wunsch verspürt haben mehr als nur abstrakte Kunst mit Farben und Farbverläufen darzustellen - dem Wunsch soll hiermit nachgekommen werden :D
Zeile 16: Zeile 12:
  
 
===Vorbereitung===
 
===Vorbereitung===
In dem Ursprünglichen Tutorial hatte Phobeus hatte Tapeten zur Verbildlichung der Materie genutzt und da mir das soweit ganz gut  
+
<!--In dem Ursprünglichen Tutorial hatte Phobeus hatte Tapeten zur Verbildlichung der Materie genutzt und da mir das soweit ganz gut gefällt will ich das auch beibehalten.-->
 
+
Nun, am Besten fangen wir mit einem möglichst praktischen Beispiel an: Stell dir vor dein Polygon sei deine Wand und die Textur ist deine Tapete. Wer sich nun denkt "Yeah, ich hab ne 12 Megapixel Cam - das gibt geile Texturen", dem kann ich nur zustimmen und dabei den Kopf schütteln. Klar ist so nen Bild ne geile Textur - wenn du nur eine Textur verwenden willst, dann geht das vielleicht noch, aber sobald du einen eigenen Level erstellen möchtest, wird dir auffallen wie gigantisch auch der Speicherverbrauch und wie schlecht die Performance davon ist.
gefällt will ich das auch beibehalten.
+
Also heißt es erstmal zurecht schneiden. Was Texturen angeht ist OpenGL leider wählerisch - es nimmt erstmal nur Texturen im Format 2^n x 2^n also z.B. 16x16, 64x64 aber auch 16x1024 oder 32x64 usw. <!-- (Ausnahmen sind möglich, werden in diesem Tutorial aber nicht behandelt) -->
Nun, am Besten fangen wir mit einem möglichst praktischen Beispiel an. Stell dir vor dein Polygon sei deine Wand und die Textur ist  
 
 
 
deine Tapete. Wer sich nun denk - Yeah ich hab ne 12 Megapixel Cam - das gibt geile Texturen, dem kann ich nur zustimmen und dabei den  
 
 
 
Kopfschütteln. Klar ist so nen Bild ne geile Textur - wenn du nur eine Textur Verwenden willst, dann geht das vielleicht noch, aber  
 
 
 
sobald du einen eigenen Level erstellen möchtest wird dir auffallen wie gigantisch auch der Speicherverbrauch und wie schlecht die  
 
 
 
Performance davon ist.
 
Also heißt es erstmal zurecht schneiden. Was Texturen angeht ist OpenGl leider wählerisch - es nimmt nur Texturen im Format 2^n x 2^n
 
also z.B. 16x16, 64x64 aber auch 16x1024 oder 32x64 usw. (Ausnahmen sind möglich, werden in diesem Tutorial aber nicht behandelt)
 
 
 
Mit unserem überdimensionierten Tapetenmesser haben wir nun unsere Tapete (Textur) in die Richtige Form gebracht - Jetzt gilt es sie
 
 
 
mit Kleister zu versehen(Laden) und auf den Tapeziertisch(Grafikspeicher) zu legen, damit wir sie dann auch schnell an die Wand
 
  
bekommen.
+
Mit unserem überdimensionierten Tapetenmesser haben wir nun unsere Tapete (Textur) in die richtige Form gebracht - Jetzt gilt es sie mit Kleister zu versehen (Laden) und auf den Tapeziertisch (Grafikspeicher) zu legen, damit wir sie dann auch schnell an die Wand bekommen.
  
  
 
===Laden der Textur===
 
===Laden der Textur===
Okay, wir haben unsere Textur jetzt zurecht geschnitten und auf unserer Festplatte abgelegt - doch wie kommt sie jetzt von da in den  
+
Okay, wir haben unsere Textur jetzt zurecht geschnitten und auf unserer Festplatte abgelegt - doch wie kommt sie jetzt von da in den Grafikspeicher? Wer mal mit den Standard I/O-Methoden von Delphi rumgespielt hat weiß, dass es nicht immer einfach ist an die gewünschten Daten in einer Datei zu kommen - daher will ich jetzt auch niemanden damit quälen, denn wir haben ja die SDL, welche bereits fertige Methoden zum Einlesen von Bilddaten besitzt.  
 
 
Grafikspeicher? Wer mal mit den standart I/O-Methoden von Delphi rumgespielt hat weiß, dass es nicht immer einfach ist an die  
 
 
 
gewünschten Daten in einer Datei zu kommen - daher will ich jetzt auch niemanden damit quälen, denn wir haben ja die SDL, welche  
 
 
 
bereits fertige Methoden zum einlesen von Bilddaten besitzt.  
 
 
Nehmen wir uns jetzt einfach ein frisches ungebrauchtes easySDL Template und spielen damit etwas rum.
 
Nehmen wir uns jetzt einfach ein frisches ungebrauchtes easySDL Template und spielen damit etwas rum.
Als erstes machen wir bekannt, das wir eine neue Prozedur haben (und erstellen in dem Zug gleich auch die Variable in der die Textur  
+
Als erstes machen wir bekannt, das wir eine neue Prozedur haben (und Erstellen in dem Zug gleich auch die Variable in der die Textur gespeichert werden soll):
  
gespeichert werden soll).
+
<source lang="pascal">
 
 
<pascal>
 
 
type
 
type
 
   TMyProgram=class(TEasySDL)
 
   TMyProgram=class(TEasySDL)
Zeile 58: Zeile 31:
 
     procedure LoadTexture;        //Hier wird die Texur geladen
 
     procedure LoadTexture;        //Hier wird die Texur geladen
 
   end;
 
   end;
</pascal>
+
</source>
 
<br>
 
<br>
 
<br>
 
<br>
 
Und jetzt laden wir die Textur:
 
Und jetzt laden wir die Textur:
 
<br>
 
<br>
<pascal>
+
<source lang="pascal">
 
procedure TMyProgram.LoadTexture;
 
procedure TMyProgram.LoadTexture;
 
var
 
var
Zeile 84: Zeile 57:
 
   end;
 
   end;
 
end;
 
end;
</pascal>
+
</source>
 +
 
 +
Obwohl ich dich jetzt nicht für den DAU halte erkläre ich grade mal den Programmablauf in Worten:
 +
# Zwischenspeicher für die Textur erstellen
 +
# Bilddatei in den Zwischenspeicher laden
 +
# OpenGL sagen, das wir eine Textur erstellen möchten (1), wobei der erste Parameter für die Anzahl der zu erstellenden Texturen steht und der Zweite für die Adresse der Variable.
 +
# Dann teilen wir OpenGL mit (2), dass sich von nun an alle Änderungen und Anweisungen, die sich auf Texturen beziehen auf die Textur ''texure'' beziehen, sowie das diese eine 2D-Textur ist.
 +
# Dann setzen wir noch ein paar Texturfilter (3) (näheres darüber gibts im Tutorial Blending).
 +
# Jetzt endlich wird die Textur in den Grafikspeicher eingeladen. Der erste Parameter steht für den Typ der Textur. Die Dimension des Typs muss hier mit der des Befehls übereinstimmen (glTexImage2D erlaubt also nur GL_TEXTURE_2D). Der zweite Parameter gibt die Nummer des Level of Detail (LoD) an. Für den Anfang reicht hier der Level 0. Der dritte Parameter gibt an, wie viele Farbkomponenten in dem Bild enthalten sind (1-4). Die zwei folgenden Parameter übermitteln OpenGL die Breite und die Höhe des Bildes. Der sechste Parameter gibt die Breite des Rahmens an. Im siebenten Parameter wird das Format verlangt, in welcher Reihenfolge die einzelnen Farbkomponenten gespeichert sind. Der Typ, der einzelnen Farbwerte muss im 8. Parameter angegeben werden. Letztendlich müssen im 9. Parameter nur noch die Bildpunkte selbst übergeben werden.
 +
 
  
Obwohl ich dich jetzt nicht für den DAU halte erkläre ich grade mal den Programablauf in Worten: <br><br>
 
1.Zwischenspeicher für die Textur erstellen
 
<br><br>
 
2. Bilddatei in den Zwischenspeicher laden
 
<br><br>
 
3. OpenGL sagen, das wir eine Textur erstellen möchten (1) wobei der erste Parameter für die Anzahl der zu erstellenden Texturen steht und der Zweite für die Adresse der Variable.
 
<br><br>
 
4. Dann teilen wir OpenGL mit (2), dass sich von nun an alle Änderungen und Anweisungen, die sich auf Texturen beziehen auf die Textur "texure" beziehen, sowie das diese eine 2D-Textur ist.
 
<br><br>
 
5. Dann setzen wir noch ein paar Texturfilter (3) (näheres darüber gibts im Tutorial Blending).
 
<br><br>
 
6. Jetzt endlich wird die Textur in den Grafikspeicher eingeladen. Der erste Parameter steht für den Typ der Textur. Die Dimension des Typs muss hier mit der des Befehls übereinstimmen (glTexImage2D erlaubt also nur GL_TEXTURE_2D). Der zweite Parameter gibt die Nummer des Level of Detail (LoD) an. Für den Anfang reicht hier der Level 0. Der dritte Parameter gibt an, wie viele Farbkomponenten in dem Bild enthalten sind (1-4). Die zwei folgenden Parameter übermitteln OpenGL die Breite und die Höhe des Bildes. Der sechste Parameter gibt die Breite des Rahmens an. Im siebenten Parameter wird das Format verlangt, in welcher Reihenfolge die einzelnen Farbkomponenten gespeichert sind. Der Typ, der einzelnen Farbwerte muss im 8. Parameter angegeben werden. Letztendlich müssen im 9. Parameter nur noch die Bildpunkte selbst übergeben werden.
 
<br>
 
 
Welches Farbformat dein Bildformat verwendet kannst du meist auf Wikipedia nachschlagen, dennoch will ich hier mal die am häufigsten genutzten Auflisten:
 
Welches Farbformat dein Bildformat verwendet kannst du meist auf Wikipedia nachschlagen, dennoch will ich hier mal die am häufigsten genutzten Auflisten:
<br><br>
+
 
 +
 
 
<table>
 
<table>
 
<tr>
 
<tr>
Zeile 106: Zeile 76:
 
</tr>
 
</tr>
 
<tr>
 
<tr>
  <td>BMP</td><td>GL_RGB</td>
+
  <td>BMP</td><td>GL_BGR / GL_BGRA</td>
 
</tr>
 
</tr>
 
<tr>
 
<tr>
  <td>JPG/JPEG</td><td>GL_RGB / GL_RGBA</td>
+
  <td>JPG/JPEG</td><td>GL_RGB</td>
 
</tr>
 
</tr>
 
<tr>
 
<tr>
Zeile 116: Zeile 86:
 
</table>
 
</table>
 
<br>
 
<br>
Sollte jetzt irgendjemand den Drang verspüren eine Ausführlichere Übersicht zu erstellen, so sei er dazu herzlich eingeladen und möge dies dann doch bitte im Forum (oder gleich im Wiki) posten.
+
Sollte jetzt irgendjemand den Drang verspüren eine ausführlichere Übersicht zu erstellen, so sei er dazu herzlich eingeladen. <!--und möge dies dann doch bitte im Forum (oder gleich im Wiki) posten.-->
<br>
 
  
 
===Tapeten an die Wand!===
 
===Tapeten an die Wand!===
  
So, jetzt ist die Textur endlich da wo wir sie haben wollen, doch was nun? - Ganz einfach wir nehmen die Textur und kleben sie auf ein Viereck. Doch bevor wir anfangen sollen wir uns ersteinmal überlegen, wie das mit dem Auflegen der Textur genau funktioniert.
+
So, jetzt ist die Textur endlich da wo wir sie haben wollen, doch was nun? - Ganz einfach wir nehmen die Textur und kleben sie auf ein Viereck. Doch bevor wir anfangen sollen wir uns erst einmal überlegen, wie das mit dem Auflegen der Textur genau funktioniert.
<br>
+
 
Wofür steht eigentlich UV? In OpenGl (bzw in allem was mit 3D und Texturen zu tun hat) nutzt man UV-Mapping um einen bestimmten Abschnitt einer Textur auf ein Polygon abzubilden. "U" steht für die Höhe der Textur und "V" für die Breite (jeweils in Prozent). Es ist total egal wie groß eine Textur ist, ihre Kantenlänge  da sie nur einen Wertebereich von 0 bis 1 haben kann. Das heißt, wenn eine Textur 256x256 groß ist und eine andere 512x512, so haben beide eine maximale Größe von 1 bzw 1 ist immer die volle Kantenlänge(man kann es quasi als Multiplikator mit der eigentlichen Breite sehen). Wir brauchen also das UV-Mapping nicht verändern, selbst wenn ein Objekt eine neue Textur mit anderer Größe erhält (sofern die Seitenverhältnisse gleich bleiben).
+
Wofür steht eigentlich UV? In OpenGL (bzw. in allem was mit 3D und Texturen zu tun hat) nutzt man UV-Mapping um einen bestimmten Abschnitt einer Textur auf ein Polygon abzubilden. "U" steht für die Höhe der Textur und "V" für die Breite (jeweils in Prozent). Es ist total egal wie groß eine Textur ist, ihre Kantenlänge, da sie nur einen Wertebereich von 0 bis 1 haben kann. Das heißt, wenn eine Textur 256x256 groß ist und eine andere 512x512, so haben beide eine maximale Größe von 1 bzw 1 ist immer die volle Kantenlänge (man kann es quasi als Multiplikator mit der eigentlichen Breite sehen). Wir brauchen also das UV-Mapping nicht verändern, selbst wenn ein Objekt eine neue Textur mit anderer Größe erhält (sofern die Seitenverhältnisse gleich bleiben).
<br>
+
 
Die obere linke Ecke der Textur trägt die Texturkoordinaten von (0 / 0) (d.h. u = 0 und v = 0), die untere linke Ecke (0 / 1), die untere rechte Ecke (1 / 1) und schließlich die obere rechte Ecke die Koordinaten (1 / 0). Das heißt alles was wir machen müssen um auf unser Objekt eine Textur zu kleben ist dem jeweiligen Eckpunkt unseres Quadrates die entsprechende Texturkoordinate zuzuweisen. Wobei in diesem Sinne korrekt in Anführungszeichnen stehen sollte. Es gibt kein falsches UV-Mapping. Man kann tolle Sachen mit diesen Textur-Koordinaten machen und so z. B. auch eine Textur auf einem Objekt spiegeln. Dafür müssten wir in unserem Beispiel nur die linken und rechten UV-Mapping vertauschen und z. B. für den zweiten Punkt die Koordinaten von (0 / 1) setzen und dafür beim dritten (1 / 1). Genauso würden wir auch die unteren vertauschen. Die UV-Koordinaten, wie sie oben angegeben sind bewirken nur, dass die Textur, so wie sie in der Datei vorkommt auch auf das Objekt geklebt wird. Selbstverständlich ist es auch möglich eine Textur gekachelt aufzukleben, nämlich indem Ihr Texturkoordinaten > 1 vergebt. Ebenso ist es möglich nur Teile einer Textur zu verwenden. Spielt ruhig ein wenig damit herum und schaut Euch an was passiert! Auf einige tolle Spielereien kommen wir zum Schluss noch mal zurück.
+
Die obere linke Ecke der Textur trägt die Texturkoordinaten von (0 / 0) (d.h. u = 0 und v = 0), die untere linke Ecke (0 / 1), die untere rechte Ecke (1 / 1) und schließlich die obere rechte Ecke die Koordinaten (1 / 0). Das heißt alles was wir machen müssen um auf unser Objekt eine Textur zu kleben ist dem jeweiligen Eckpunkt unseres Quadrates die entsprechende Texturkoordinate zuzuweisen. Wobei in diesem Sinne korrekt in Anführungszeichen stehen sollte. Es gibt kein falsches UV-Mapping. Man kann tolle Sachen mit diesen Textur-Koordinaten machen und so z.B. auch eine Textur auf einem Objekt spiegeln. Dafür müssten wir in unserem Beispiel nur die linken und rechten UV-Mapping vertauschen und z.B. für den zweiten Punkt die Koordinaten von (0 / 1) setzen und dafür beim dritten (1 / 1). Genauso würden wir auch die unteren vertauschen. Die UV-Koordinaten, wie sie oben angegeben sind bewirken nur, dass die Textur, so wie sie in der Datei vorkommt auch auf das Objekt geklebt wird. Selbstverständlich ist es auch möglich eine Textur gekachelt aufzukleben, nämlich indem Ihr Texturkoordinaten > 1 vergebt. Ebenso ist es möglich nur Teile einer Textur zu verwenden. Spielt ruhig ein wenig damit herum und schaut Euch an was passiert! Auf einige tolle Spielereien kommen wir zum Schluss noch mal zurück.
 
<br>
 
<br>
 
Nach so viel Vorwissen und Theorie kommen wir nun endlich zur Praxis!
 
Nach so viel Vorwissen und Theorie kommen wir nun endlich zur Praxis!
 
<br>
 
<br>
 
Wie fast überall muss erstmal wieder etwas aktiviert werden - und, wer hätte es erraten - es ist die Texturierung, die wir mit glEnable() einschalten müssen, doch am besten erkläre ich das mal an folgendem Beispiel:
 
Wie fast überall muss erstmal wieder etwas aktiviert werden - und, wer hätte es erraten - es ist die Texturierung, die wir mit glEnable() einschalten müssen, doch am besten erkläre ich das mal an folgendem Beispiel:
<pascal>
+
<source lang="pascal">
 
procedure TMyProgram.DrawScene;
 
procedure TMyProgram.DrawScene;
 
begin
 
begin
Zeile 147: Zeile 116:
 
   glDisable(GL_TEXTURE_2D); //Texturierung deaktivieren (1)
 
   glDisable(GL_TEXTURE_2D); //Texturierung deaktivieren (1)
 
end;
 
end;
</pascal>
+
</source>
 
<br>
 
<br>
 
<br>
 
<br>
Zeile 156: Zeile 125:
 
  <li>(3) Die Textur wird so auf dem Viereck abgebildet, wie sie in der Datei gespeichert ist.(Erklärung im voranstehenden Abschnitt)</li>
 
  <li>(3) Die Textur wird so auf dem Viereck abgebildet, wie sie in der Datei gespeichert ist.(Erklärung im voranstehenden Abschnitt)</li>
 
</ul>
 
</ul>
 +
<br>
 +
 +
==Die Texturmatrix==
 +
===Nen Wasserfall gefällig?===
 +
<br>
 +
Was haben wir den bisher über Matrizen gelernt? - Na das man toll damit spielen kann :D Alle Bewegungen die wir in OpenGL erzeugen sind ja eigentlich Verschiebungen der Modelview-Matrix!<br>
 +
Also wie ist das jetzt mit der Texturmatrix? Wer ahnts schon ? Es ist genau so! Wir können einfach über den Translate Befehl die Texturmatrix verschieben (und somit die Textur auf dem Polygon!). Folgender Code macht das ganze etwas klarer:
 +
 +
<source lang="pascal">
 +
procedure TMyProgram.UpdateWorld(Seconds: TGLFloat);
 +
begin
 +
  x:=x+1*Seconds;
 +
end;
 +
</source>
 +
 +
<source lang="pascal">
 +
glMatrixMode(GL_TEXTURE);  //Die Texturmatrix aktivieren
 +
  glLoadIdentity;          //Zurücksetzen
 +
  glTranslatef(x,0,0);    //Verschieben auf der X-Achse
 +
glMatrixMode(GL_MODELVIEW);//Wieder zur Modelview-Matrix springen
 +
</source> 
 +
<br>
 +
 +
Würden wir X jedes Mal um 1 Einheit erhöhen, so würde diese Operation ohne Effekt bleiben, da wir die Textur immer um ihre ganze Größe nach links projizieren würden. Würden wir X z. B. bei jedem Vorgang um 0.01 erhöhen, so würde die Textur sich langsam von rechts nach links bewegen.
 +
<br>
 +
Wer das jetzt nicht verstanden hat, der sollte sich vielleicht nochmal Tutorial 3 vornehmen! Verstanden? - Wie wär es denn dann z.B. mit einem kleinen Wasserfall? <!--Kostenlose Texturen findest du zu Hauf im Internet (da ich aber keine finden konnte die unter der GPL steht ist hier auch keine dabei) - such einfach mal mit google ... oder mach dir halt selbst welche :D-->
 +
<br>
 +
 +
  
  

Aktuelle Version vom 10. März 2009, 19:10 Uhr

Texturen - Wie kommt die Tapete an die Wand?

Vorwort

Willkommen in der Welt der Bilder! In diesem Tutorial wirst du erfahren, wie man auf ein Polygon (Dreieck, Viereck usw) eine Textur zeichnen kann. Angefangen mit dem Laden über das einfache Platzieren kommen wir schlussendlich dann dazu, wie man nur einzelne Bereiche einer Textur darstellen kann (dies ist das sog. UV-Mapping).

Wofür denn überhaupt Texturieren? Nach den letzen Tutorials werden sicher einige den Wunsch verspürt haben mehr als nur abstrakte Kunst mit Farben und Farbverläufen darzustellen - dem Wunsch soll hiermit nachgekommen werden :D

Genug geschwafelt, fangen wir mal endlich an.

Crash-Kurs im Tapezieren

Vorbereitung

Nun, am Besten fangen wir mit einem möglichst praktischen Beispiel an: Stell dir vor dein Polygon sei deine Wand und die Textur ist deine Tapete. Wer sich nun denkt "Yeah, ich hab ne 12 Megapixel Cam - das gibt geile Texturen", dem kann ich nur zustimmen und dabei den Kopf schütteln. Klar ist so nen Bild ne geile Textur - wenn du nur eine Textur verwenden willst, dann geht das vielleicht noch, aber sobald du einen eigenen Level erstellen möchtest, wird dir auffallen wie gigantisch auch der Speicherverbrauch und wie schlecht die Performance davon ist. Also heißt es erstmal zurecht schneiden. Was Texturen angeht ist OpenGL leider wählerisch - es nimmt erstmal nur Texturen im Format 2^n x 2^n also z.B. 16x16, 64x64 aber auch 16x1024 oder 32x64 usw.

Mit unserem überdimensionierten Tapetenmesser haben wir nun unsere Tapete (Textur) in die richtige Form gebracht - Jetzt gilt es sie mit Kleister zu versehen (Laden) und auf den Tapeziertisch (Grafikspeicher) zu legen, damit wir sie dann auch schnell an die Wand bekommen.


Laden der Textur

Okay, wir haben unsere Textur jetzt zurecht geschnitten und auf unserer Festplatte abgelegt - doch wie kommt sie jetzt von da in den Grafikspeicher? Wer mal mit den Standard I/O-Methoden von Delphi rumgespielt hat weiß, dass es nicht immer einfach ist an die gewünschten Daten in einer Datei zu kommen - daher will ich jetzt auch niemanden damit quälen, denn wir haben ja die SDL, welche bereits fertige Methoden zum Einlesen von Bilddaten besitzt. Nehmen wir uns jetzt einfach ein frisches ungebrauchtes easySDL Template und spielen damit etwas rum. Als erstes machen wir bekannt, das wir eine neue Prozedur haben (und Erstellen in dem Zug gleich auch die Variable in der die Textur gespeichert werden soll):

type
  TMyProgram=class(TEasySDL)
    texture : gluInt;              //Hier wird die Textur gespeichert  
    procedure DrawScene;override;  //Hier wird die Scene gezeichnet
    procedure LoadTexture;         //Hier wird die Texur geladen
  end;



Und jetzt laden wir die Textur:

procedure TMyProgram.LoadTexture;
var
  tex : PSDL_Surface;              //Zwischenspeicher für die Textur
begin
  tex := IMG_Load('./wall.jpg'); //Laden der Textur
  if assigned(tex) then
  begin
    glGenTextures(1, @texture);   {1}      
    glBindTexture(GL_TEXTURE_2D, texture); {2}

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);{3}
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    
    glTexImage2D(GL_TEXTURE_2D, 0, 3, tex^.w, tex^.h,0, GL_RGB, GL_UNSIGNED_BYTE, tex^.pixels); {4}
    // Achtung! Einige Bildformate erwarten statt GL_RGB, GL_BGR. Diese Konstante fehlt in den Standard-Headern

    SDL_FreeSurface(tex);
  end;
end;

Obwohl ich dich jetzt nicht für den DAU halte erkläre ich grade mal den Programmablauf in Worten:

  1. Zwischenspeicher für die Textur erstellen
  2. Bilddatei in den Zwischenspeicher laden
  3. OpenGL sagen, das wir eine Textur erstellen möchten (1), wobei der erste Parameter für die Anzahl der zu erstellenden Texturen steht und der Zweite für die Adresse der Variable.
  4. Dann teilen wir OpenGL mit (2), dass sich von nun an alle Änderungen und Anweisungen, die sich auf Texturen beziehen auf die Textur texure beziehen, sowie das diese eine 2D-Textur ist.
  5. Dann setzen wir noch ein paar Texturfilter (3) (näheres darüber gibts im Tutorial Blending).
  6. Jetzt endlich wird die Textur in den Grafikspeicher eingeladen. Der erste Parameter steht für den Typ der Textur. Die Dimension des Typs muss hier mit der des Befehls übereinstimmen (glTexImage2D erlaubt also nur GL_TEXTURE_2D). Der zweite Parameter gibt die Nummer des Level of Detail (LoD) an. Für den Anfang reicht hier der Level 0. Der dritte Parameter gibt an, wie viele Farbkomponenten in dem Bild enthalten sind (1-4). Die zwei folgenden Parameter übermitteln OpenGL die Breite und die Höhe des Bildes. Der sechste Parameter gibt die Breite des Rahmens an. Im siebenten Parameter wird das Format verlangt, in welcher Reihenfolge die einzelnen Farbkomponenten gespeichert sind. Der Typ, der einzelnen Farbwerte muss im 8. Parameter angegeben werden. Letztendlich müssen im 9. Parameter nur noch die Bildpunkte selbst übergeben werden.


Welches Farbformat dein Bildformat verwendet kannst du meist auf Wikipedia nachschlagen, dennoch will ich hier mal die am häufigsten genutzten Auflisten:


Dateiformat  Farbformat
BMPGL_BGR / GL_BGRA
JPG/JPEGGL_RGB
PNGGL_RGB / GL_RGBA


Sollte jetzt irgendjemand den Drang verspüren eine ausführlichere Übersicht zu erstellen, so sei er dazu herzlich eingeladen.

Tapeten an die Wand!

So, jetzt ist die Textur endlich da wo wir sie haben wollen, doch was nun? - Ganz einfach wir nehmen die Textur und kleben sie auf ein Viereck. Doch bevor wir anfangen sollen wir uns erst einmal überlegen, wie das mit dem Auflegen der Textur genau funktioniert.

Wofür steht eigentlich UV? In OpenGL (bzw. in allem was mit 3D und Texturen zu tun hat) nutzt man UV-Mapping um einen bestimmten Abschnitt einer Textur auf ein Polygon abzubilden. "U" steht für die Höhe der Textur und "V" für die Breite (jeweils in Prozent). Es ist total egal wie groß eine Textur ist, ihre Kantenlänge, da sie nur einen Wertebereich von 0 bis 1 haben kann. Das heißt, wenn eine Textur 256x256 groß ist und eine andere 512x512, so haben beide eine maximale Größe von 1 bzw 1 ist immer die volle Kantenlänge (man kann es quasi als Multiplikator mit der eigentlichen Breite sehen). Wir brauchen also das UV-Mapping nicht verändern, selbst wenn ein Objekt eine neue Textur mit anderer Größe erhält (sofern die Seitenverhältnisse gleich bleiben).

Die obere linke Ecke der Textur trägt die Texturkoordinaten von (0 / 0) (d.h. u = 0 und v = 0), die untere linke Ecke (0 / 1), die untere rechte Ecke (1 / 1) und schließlich die obere rechte Ecke die Koordinaten (1 / 0). Das heißt alles was wir machen müssen um auf unser Objekt eine Textur zu kleben ist dem jeweiligen Eckpunkt unseres Quadrates die entsprechende Texturkoordinate zuzuweisen. Wobei in diesem Sinne korrekt in Anführungszeichen stehen sollte. Es gibt kein falsches UV-Mapping. Man kann tolle Sachen mit diesen Textur-Koordinaten machen und so z.B. auch eine Textur auf einem Objekt spiegeln. Dafür müssten wir in unserem Beispiel nur die linken und rechten UV-Mapping vertauschen und z.B. für den zweiten Punkt die Koordinaten von (0 / 1) setzen und dafür beim dritten (1 / 1). Genauso würden wir auch die unteren vertauschen. Die UV-Koordinaten, wie sie oben angegeben sind bewirken nur, dass die Textur, so wie sie in der Datei vorkommt auch auf das Objekt geklebt wird. Selbstverständlich ist es auch möglich eine Textur gekachelt aufzukleben, nämlich indem Ihr Texturkoordinaten > 1 vergebt. Ebenso ist es möglich nur Teile einer Textur zu verwenden. Spielt ruhig ein wenig damit herum und schaut Euch an was passiert! Auf einige tolle Spielereien kommen wir zum Schluss noch mal zurück.
Nach so viel Vorwissen und Theorie kommen wir nun endlich zur Praxis!
Wie fast überall muss erstmal wieder etwas aktiviert werden - und, wer hätte es erraten - es ist die Texturierung, die wir mit glEnable() einschalten müssen, doch am besten erkläre ich das mal an folgendem Beispiel:

procedure TMyProgram.DrawScene;
begin
  glClear(GL_COLOR_BUFFER_BIT OR GL_DEPTH_BUFFER_BIT);  //Buffer löschen
  glLoadIdentity;
  gltranslatef(0,0,-5);  //Scene in den Bildschirm hinein verschieben, damit wir überhaupt etwas zu sehen bekommen.

  glEnable(GL_TEXTURE_2D);                  //Texturierung aktivieren (1)
  glBindTexture(GL_TEXTURE_2D,texture);     //Hier sagen wir wieder bescheid auf welche Textur sich folgene Anweisungen beziehen.(2)
  glBegin(GL_QUADS);
    glTexCoord2f(0,0); glVertex3f(-1,-1,0); //Links oben(3)
    glTexCoord2f(0,1); glVertex3f(-1,1,0);  //Links unten
    glTexCoord2f(1,1); glVertex3f(1,1,0);   //Rechts unten
    glTexCoord2f(1,0); glVertex3f(1,-1,0);  //Rechts oben
  glEnd;
  glDisable(GL_TEXTURE_2D); //Texturierung deaktivieren (1)
end;



  • (1)Als erstes müssen wir die Texturierung aktivieren, damit OpenGL weis, was wir vorhaben zu tun. Auf alle folgenden Polygone wird jetzt die ausgewählte Textur gelegt - mit glDisable(GL_TEXTURE_2D) beenden wir den Texturmodus, würden wir das nicht machen würde die Textur mit den zuvor definierten Koordinaten auf jedes neue Polygon gezeichnet.
  • (2) Das kennen wir schon vom Laden der Textur!
  • (3) Die Textur wird so auf dem Viereck abgebildet, wie sie in der Datei gespeichert ist.(Erklärung im voranstehenden Abschnitt)


Die Texturmatrix

Nen Wasserfall gefällig?


Was haben wir den bisher über Matrizen gelernt? - Na das man toll damit spielen kann :D Alle Bewegungen die wir in OpenGL erzeugen sind ja eigentlich Verschiebungen der Modelview-Matrix!
Also wie ist das jetzt mit der Texturmatrix? Wer ahnts schon ? Es ist genau so! Wir können einfach über den Translate Befehl die Texturmatrix verschieben (und somit die Textur auf dem Polygon!). Folgender Code macht das ganze etwas klarer:

procedure TMyProgram.UpdateWorld(Seconds: TGLFloat);
begin
  x:=x+1*Seconds;
end;
glMatrixMode(GL_TEXTURE);  //Die Texturmatrix aktivieren
  glLoadIdentity;          //Zurücksetzen
  glTranslatef(x,0,0);     //Verschieben auf der X-Achse
glMatrixMode(GL_MODELVIEW);//Wieder zur Modelview-Matrix springen


Würden wir X jedes Mal um 1 Einheit erhöhen, so würde diese Operation ohne Effekt bleiben, da wir die Textur immer um ihre ganze Größe nach links projizieren würden. Würden wir X z. B. bei jedem Vorgang um 0.01 erhöhen, so würde die Textur sich langsam von rechts nach links bewegen.
Wer das jetzt nicht verstanden hat, der sollte sich vielleicht nochmal Tutorial 3 vornehmen! Verstanden? - Wie wär es denn dann z.B. mit einem kleinen Wasserfall?



// bisherige kritik:

öhmm.... was heißt 'if assigned(tex)'? gucken obs jetz auch belegt is? also so wie if (string<>)

'4. Dann teilen wir OpenGL mit (2)' reihnfolge?! 1-2-3-4-wieder 2? ich glaub ich raff die materie nich so ganz, ohne die tuts davor^^ lass die nummerierung 1-6 weg und mach nur die die du in { } klammern hast, das verwirrt nur und du kannst ja auch absätze machen ohne sie durchzunummerieren.

'6. Jetzt endlich wird die Textur in den Grafikspeicher eingeladen.' da würd ich {4} davor schreiben, damit ma weiß wo du grad bist

//Kritik ende




Vorhergehendes Tutorial:
Tutorial Lektion 3
Nächstes Tutorial:
Tutorial Lektion 5

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