Irrlicht - from Noob to Pro: Texturen


Über Texturen Bearbeiten

Gute Texturen sind in der Spielewelt neben dem Gameplay das Salz in der Suppe. Mittlerweile besteht eine texturierte Oberfläche nicht mehr nur aus einer Grafikdatei, sondern es können sogar mehrere sein, um den Detailreichtum einer Oberfläche noch zusätzlich zu erhöhen. Bei neueren Grafikkarten müssen Texturen nicht mehr zwingend das n² - Format (256 x 256, 512 x 512, 1024 x 1024 Pixel usw.) einhalten, sondern sind frei definierbar. Sehen wir uns doch mal die verschiedenen Arten von Texturen an ...

Die Diffuse-Map Bearbeiten

 
Eine Textur (Quelle: http://www.texturenwelt.de/)

Die Diffuse Map (auch engl. Color Map) ist die eigentliche Textur eines Objekts, welche mittels Farbwerten die Bildinformationen der Oberfläche speichert. Es ist pro Objekt auch möglich, mehrere Diffuse Maps zu verwenden, um unterschiedliche Bereiche des Objekts darzustellen. Eine Diffuse-Map speichert die Daten in Rot-, Grün-, Blau- und Alpha-Werten.

Die Alpha-Map Bearbeiten

Eine Alpha-Map bestimmt, welcher Teil der Textur durchsichtig ist und welcher nicht. Wenn Sie z.B. einen Maschendrahtzaun erstellen wollen, dann benötigen Sie eine Alpha-Map, welche vorgibt, wo die Öffnungen der Maschen sind. Es gibt 2 Möglichkeiten, wie Alpha-Maps erstellt werden :

Die color-keyed Alpha-Map Bearbeiten

Die Color-Keyed Alpha-Map wird während der Laufzeit des Programms generiert. In diesem Fall wird Irrlicht mitgeteilt, welcher Farbwert als "transparent" zu setzen ist. Meist ist das der Pixel in der linken oberen Ecke einer Bilddatei. Dies kann jedoch definiert werden. Dies geschieht durch den Aufruf

driver->makeColorKeyTexture(textur1, core::position2d<s32>(0,0));

Alternativ kann auch der Farbwert direkt angegeben werden :

driver->makeColorKeyTexture(textur1, video::SColor(255,255,0,255));

Die Image-based Alpha-Map Bearbeiten

Diese Alpha-Map wird entweder in einer Datei als Anhang gespeichert (ist z.B. bei Targa-Dateien (*.tga) oder Portable Networks-Grafiken (*.png) der Fall), oder man speichert die Alpha-Map als zweite Bilddatei ab und kombinert diese dann mit dem Bild, welches die Textur enthält.

Bild und Alpha-Map
 
Originalbild
 
Alpha-Map

Die Displacement-Map Bearbeiten

 
Die Displacement Map

Die Displacement Map ist eine Grafikdatei, welche eine 256 Farben Graustufenpalette verwendet, um Höheninformationen einer Oberfläche zu speichern. Im Englischen wird meist der Begriff "Height Map" verwendet, was aber wiederum auch die Technik zur Erstellung von Landschaften aus Height Maps (ebenfalls Graustufenbilder) benennt. Diese beiden werden gerne verwechselt. Generell gilt, dass die hellen Stellen einer Displacement-Map höher aus der Oberfläche herausragen als dunkle Stellen. Da der Alphawert die Helligkeit vorgibt (mehr Alpha = mehr Helligkeit), wird meist dieser als Multiplikator für die Berechnung der Höhe verwendet. Beim Displacement-Mapping wird im Gegensatz zu anderen Verfahren die Geometrie einer Oberfläche verändert, wodurch auch bei näherer Betrachtung aus einem flachen Winkel die Oberfläche ihre Struktur behält. Deshalb nennt man Displacement auch das "wirkliche" Bump-Mapping, da man reale Oberflächen damit erzeugt. Der Nachteil liegt darin, dass diese Methode sehr rechenintensiv ist, wenn viel Flächen damit dargestellt werden.

Die Normal-Map Bearbeiten

 
Die Normal-Map

Die Normal-Map dient der Darstellung der Flächennormalen, welches Richtungsvektoren sind, die den rechten Winkel einer Fläche beschreiben. Dies ist besonders bei der Beleuchtung der Oberfläche interessant, da dadurch die Lichtberechnung, der Schatten sowie das Abstrahlverhalten beeinflusst wird. Dadurch wird eine unebene Oberfläche simuliert, obwohl diese eigentlich flach ist. Die drei Werte der Flächennormale sind in den Farbwerten der Normal-Map versteckt. In fast allen Fällen beschreibt der rote Farbwert die X-Achse, der grüne Farbwert die Y-Achse und der blaue Farbwert die Z-Achse. Wegen der Verwendung der 3 Achsen wird diese Technik auch Dot3 Bump-Mapping bezeichnet.

Parallax-Mapping Bearbeiten

Parallax Mapping verwendet wie Displacement-Mapping ein Graustufenbild zur Errechnung von Höheninformationen. Im Gegensatz zum Displacement-Mapping wird allerdings die Geometrie der Fläche nicht verändert, um Rechenzeit einzusparen. Das Parallax-Mapping verwendet den Winkel, aus dem der Benutzer eine Fläche betrachtet und errechnet daraus den Schatten, den diese Fläche wirft, wenn Sie uneben ist. Somit wird also die Beleuchtungsberechnung direkt beeinflusst.

Texturkoordinaten Bearbeiten

 
Ein Rechteck mit Texturkoordinaten (1 mal Wiederholt)

Um festzulegen, wie Texturen über 3-dimensionale Objekte gelegt werden, verwendet man Texturkoordinaten. Diese bestehen aus 3 Koordinaten :

  • die U-Koordinate, welche die horizontale Ausbreitung einer Textur bestimmt,
  • die V-Koordinate, welche die vertikale Ausbreitung einer Textur bestimmt und
  • die W-Koordinate, welche die Stärke einer oberflächenverändernden Prozedur (Displacement-Mapping, Parallax-Mapping usw.) bestimmt. Dies wird in diesem Buch später unter dem Thema Bump-Mapping noch weiter behandelt.

Man spricht deshalb auch von UVW-Mapping. Der Koordinatenursprung liegt bei Irrlicht in der oberen linken Ecke der zu texturierenden Fläche. Die UV-Koordinaten geben an, wie oft eine Textur in welcher Richtung auf einer Fläche wiederholt wird. Da es sich in bei den Koordinaten um Start- und End-Koordinaten handelt, ist es auch möglich, nur einen bestimmten Ausschnitt einer Bilddatei als Textur anzugeben :

Koordinaten und Ergebnisse
 
Definierter Ausschnitt
 
Ergebnis
 
Definiertes Dreieck
 
Ergebnis

Die Angabe der Texturkoordinaten Bearbeiten

Die Texturkoordinaten sind in der Struktur S3DVertex als die letzten 2 Parameter deklariert :

core::vector2d<f32>  irr::video::S3DVertex::TCoords

Im Beispielcode (siehe unten) sind die Texturkoordinaten also ebenfalls die letzten 2 Parameter in der Definition des Arrays für das Viereck (erster Parameter U, zweiter Parameter V):
vaVertices[0] = S3DVertex(-0.3f,-0.3f, z_vorne,0,0,0,SColor(255,255,255,255),0,1); //Punkt A (l.u.)
vaVertices[1] = S3DVertex(-0.3f, 0.3f, z_vorne,0,0,0,SColor(255,255,255,255),0,0); //Punkt B (l.o.)
vaVertices[2] = S3DVertex( 0.3f,-0.3f, z_vorne,0,0,0,SColor(255,255,255,255),1,1); //Punkt C (r.u.)
vaVertices[3] = S3DVertex( 0.3f, 0.3f, z_vorne,0,0,0,SColor(255,255,255,255),1,0); //Punkt D (r.o.)

Ein Beispielcode Bearbeiten

 
Textur mit Alpha-Map

Ich habe Ihnen hier ein kleines Beispiel programmiert, welches zeigen soll, wie man einen Color-Key erstellt und wie man eine Alpha-Map aus einer Bilddatei verwendet.
Wenn Sie die Variable bVerwendeColorKey auf true setzen, so wird der linke obere Pixel der Bilddatei verwendet, um einen Color-Key zu erstellen. Ist bVerwendeColorKey auf false, so wird die Alpha-Map aus der Bilddatei verwendet (falls vorhanden).

//Einbinden der Header-Datei von Irrlicht
#include <irrlicht.h>

//Einbinden der Namespaces
using namespace irr;
using namespace core;
using namespace video;
//Die Hauptprozedur main()
int main()
{
	//Unser Irrlicht-Device erstellen und initialisieren
	IrrlichtDevice *device =
		createDevice( video::EDT_OPENGL, dimension2d<u32>(640, 480), 32,
			false, false, false, 0);

	//Konnte das Device erstellt werden ?
	if (!device)
		return 1; //Falls nicht, Fehlercode zurückgeben und Programm abbrechen
	
	//Den Text des Hauptfensters festlegen
	device->setWindowCaption(L"Texturen darstellen mit Irrlicht !");
	
	//Den Videotreiber erstellen und Zeiger aus dem Device abholen
	IVideoDriver* driver = device->getVideoDriver();	
	
	//Ein Material deklarieren
	video::SMaterial material;

	//Das Material nimmt kein Licht an
	material.Lighting = false;

	//Das Material soll voll gezeichnet werden
	material.Wireframe = false;
	
	//Die Textur aus der Bilddatei laden
	material.setTexture(0,driver->getTexture("Datei.png"));

	bool bVerwendeColorKey = false; //Soll ein Color-Key verwendet werden ?
	
	if (bVerwendeColorKey) //Ja
	{
		//Verwende Color-Key
		material.MaterialType = EMT_TRANSPARENT_ALPHA_CHANNEL; //Materialeigenschaft umstellen
		//Linker oberer Pixel legt den Color-Key fest
		driver->makeColorKeyTexture(material.getTexture(0),position2d<s32>(0,0));
	} else //Nein
		//Verwende Alpha-Map in der Bilddatei
		material.MaterialType = EMT_TRANSPARENT_ALPHA_CHANNEL_REF; 

	//Z-Position 
	const irr::f32 z_vorne  = -0.3f; //Vorderseite	
		
	//Definieren der Vertices
	video::S3DVertex vaVertices[4]; //Das Array vom Typ S3DVertex	    
	
	vaVertices[0] = S3DVertex(-0.3f,-0.3f, z_vorne,0,0,0,SColor(255,255,255,255),0,1); //Punkt A (l.u.)
	vaVertices[1] = S3DVertex(-0.3f, 0.3f, z_vorne,0,0,0,SColor(255,255,255,255),0,0); //Punkt B (l.o.)
	vaVertices[2] = S3DVertex( 0.3f,-0.3f, z_vorne,0,0,0,SColor(255,255,255,255),1,1); //Punkt C (r.u.)
	vaVertices[3] = S3DVertex( 0.3f, 0.3f, z_vorne,0,0,0,SColor(255,255,255,255),1,0); //Punkt D (r.o.)
	
	//Definieren der Indicies
	u16 uiIndicies[6] = {0, 1, 2,  //A->B->C=Dreieck1 (Vorderseite)
			     2, 1, 3}; //C->B->D=Dreieck2 (Vorderseite)

	//Während das Device aktiv ist ...
	while(device->run())
	{		
		//Szene beginnen
		driver->beginScene(true, true, SColor(3,150,203,255));		
		
		//Material festlegen
		driver->setMaterial(material);

		//Zeichnen des Vierecks
		driver->drawIndexedTriangleList(
			&vaVertices[0],		//Zeiger auf das erste Element im Vertices-Array
			4,			//Anzahl der Elemente im Vertices-Array
			&uiIndicies[0],		//Zeiger auf das erste Element in Indicies-Array
			2);			//Anzahl der zu zeichnenden Dreiecke		

		//Szene beenden
		driver->endScene();
	}
	//Das Device freigeben
	device->drop();
	
	//Keinen Fehler zurückgeben
	return 0;
}