Spielwiese/Tutorial 4/grey
Inhaltsverzeichnis
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
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 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.
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 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. 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 Programablauf 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 |
---|---|
BMP | GL_RGB |
JPG/JPEG | GL_RGB / GL_RGBA |
PNG | GL_RGB / GL_RGBA |
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.
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.
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ü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.
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 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? 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
// 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. |