Texture Mapping
Alle unsere Objekte hatten bisher immer nur eine einzige Farbe und die Oberfläche der Objekte war glatt und durchgehend.
Jedoch hat eine solche Oberfläche so gut wie kein Objekt der realen Welt. Wenn man sich umschaut sind solche regelmäßigen
Anordnungen sehr selten und Oberflächen sind auch nicht sehr oft so glatt und "perfekt" wie wir sie bisher hatten.
Deshalb greifen wir auf das Texture Mapping zurück: Wir weisen unserem Objekt nicht einfach eine Farbe zu, sondern eine
Textur. Was ist jetzt eine Textur? Eine Textur ist ganze einfach ein Bild das wir über das Objekt legen. Dadurch können
wir jetzt zB einen Ziegelstein nicht einfach nur rot färben, sondern ihn auch ein realistisches Aussehen verpassen.
Sehen wir uns nun einmal an wie wir unsere Box texturieren können. Zunächst brauchen wir eine neue Variable vom Typ
Texture. In ihr speichern wir unsere Textur:
Texture textur;
Nun müssen wir unsere Textur laden. Das geht ziemlich einfach, da uns Direct3D die Klasse TextureLoader zur Verfügung
stellt, in der bereits vorgefertigte Funktionen zum Laden von Texturen enthalten sind:
public void OnCreateDevice(object sender, EventArgs e)
{
Device dev = (Device)sender;
textur = TextureLoader.FromFile(dev,"iron04.jpg");
Wir verwenden die Funktion FromFile, die als ersten Parameter das verwende Device verlangt und als zweiten das Bild,
das wir in der Textur speichern wollen. Die FromFile Funktion ist ein wahres Multitalent, denn laut SDK unterstützt
es die folgenden Bildformate: .bmp, .dds, .dib, .hdr, .jpg, .pfm, .png, .ppm, und .tga
In echten Spielen sollte man wenn möglich nicht jpg verwenden, so wie ich das hier tue, jedoch ist eine jpg schön klein
und als Beispiel sollte es reichen. Der Nachteil ist halt das ein jpg als Textur nicht wirklich toll aussieht.
Der Rückgabewert der Funktion ist ein Texture Objekt das die geladene Textur enthält.
Als nächstes müssen wir unsere Box anpassen. Bisher haben wir ja die Vertices der Box mit einer Farbkomponente erweitert,
jetzt müssen wir sie mit Texturkoordinaten erweitern.
Texturkoordinaten? Was sind Texturkoordinaten?
Wenn wir eine Textur auf ein Dreieck legen, woher soll Direct3D jetzt wissen wie es die Textur auf das Dreieck legen soll?
Deshalb gibt es die Texturkoordinaten, mit ihnen können wir bestimmen wie eine Textur auf ein Dreieck gelegt werden soll.
Als Anlehnung an Vertex werden diese Koordinaten auch Texel genannt. Ein Texel ist immer zwei dimensional(Bilder sind ja
auch nur zwei dimensional) und hat also 2 Achsen: Die u-Achse und die v-Achse. Die u-Achse ist eigentlich die x-Achse,
während die v-Achse die y-Achse ist. Der Koordinatenursprung einer Textur liegt jedoch in der linken oberen Ecke.
Eine weitere Besonderheit von Texel sind das sie in einem Intervall von [0,1] liegen. Dh das es bei allen Texturen nur
Texel zwischen 0 und 1 gibt unabhängig von der eigentlichen Größe der Textur. Der Texel 0,5 liegt daher immer in der Mitte
einer Achse.
Wir müssen nun also jedem Vertex einen Texel zuweisen, der anzeigt welcher Teil einer Textur auf diesen Vertex gelegt
werden soll.
Ein Beispiel: Nehmen wir an wir haben ein Rechteck mit 4 Vertices und eine Textur. Die Textur ist ebenfalls rechteckig
und wir wollen nun die Textur auf dieses Rechteck legen. Dazu müssen wir jetzt jedem Vertex des Rechteck einen Texel
der Textur zuweisen.
Sehen wir uns es jetzt den 1. Vertex an. Nehmen wir mal an das dieser in der linken oberen Ecke liegt. Wir können diesem
Vertex jetzt den Texel (0.0/0.0) zuweisen. Oder (1.0/1.0). Uns sind hierbei keine Grenzen gesetzt. Wir wollen aber die
Textur normal auf das Rechteck legen, also so wie ein Bildprogramm uns die Textur anzeigt, so wollen wir diese auf dem
Rechteck haben. Also weisem wir dem 1. Vertex den Texel (0/0) zu. Der 2. Vertex liegt nun in der rechten oberen Ecke.
Also weisen wir diesem Vertex den Texel (1/0) zu(wir müssen auf der u-Achse ganz nach rechts). Der 3. Vertex ist nun
die linke untere Ecke. Also brauchen wir hier den Texel (0/1). Und für den letzten Vertex brauchen wir schließlich den
Texel (1/1).
Ich hoffe mit diesem Beispiel versteht man die Texturkoordinaten recht gut. Am Besten ist es hier eine Zeichnung zu machen.
Kommen wir nun zum praktischen:
box = Mesh.Box(dev,2.0f,1.0f,2.0f);
VertexFormats format = VertexFormats.PositionNormal | VertexFormats.Texture1;
Mesh tempBox = box.Clone( box.Options.Value, format, dev );
box.Dispose();
box = tempBox;
CustomVertex.PositionNormalTextured[] verts = (CustomVertex.PositionNormalTextured[])box.VertexBuffer.Lock( 0,
typeof( CustomVertex.PositionNormalTextured ),
LockFlags.None,
box.NumberVertices );
Wir wir sehen ist der Code nicht allzu schwer. Das VertexFormat der Box müssen wir auf PositionNormal und Texture1 setzen.
Texture1 beschreibt, das wir pro Vertex 1 Texel haben.
Danach müssen wir noch die neuen Vertices die wir schreiben wollen erstellen. Hierbei müssen wir PositionNormalTextured
verwenden, anstatt PositionNormalColored.
Nun kommen wir zum zuweisen der Texel, was nicht sonderlich schwer ist:
for(int i=0;i < verts.Length;++i)
{
verts[i].Tu = verts[i].X * 0.8f;
verts[i].Tv = verts[i].Y * 0.8f;
}
box.VertexBuffer.Unlock();
}
Sehen wir uns jetzt das Rendern an. Bevor unsere Box normal rendern können, müssen wir noch die Textur setzen. Dh wir
müssen Direct3D sagen das es jetzt für die nächsten Operationen wo es Texturen braucht die Textur nehmen soll, die wir
gesetz haben.
device.SetTexture(0,textur);
Was hat die 0 hier zu bedeuten? Wie wir später sehen werden gibt es verschiedene Texture Stages. Die untereste dieser
Schichten ist die Stufe 0. Mit diesen Texturstages werden wir später zwei oder mehr Texturen miteinander verbinden können
und auf unser Objekt legen. Dieses Verfahren nennt man dann Multi-Texturing.
Nun können wir unser Objekt mit der Textur rendern:
box.DrawSubset(0);
Und das wars schon. Mit Texturen kann man natürlich noch viel mehr anfangen, doch dazu mehr in späteren Teilen
Anbei ist natürlich wieder das Visual Studio .net 2003 Projekt sowie die Datei iron04.jpg