Lektion
5 
Texturen verwenden
Texturkoordinaten
Texturen in der Beispielszene
Zum Download des Quellcodes dieser Lektion: Auf das
Bild klicken oder hier.
Texturen verwenden
Durch die Verwendung von Texturen lässt sich eine Szene interessanter
gestalten. Texturen sind Bilder, die auf Oberflächen angebracht
werden, um diesen ein natürlicheres Aussehen zu geben.
In JOGL gibt es die Möglichkeit 1D-, 2D- und 3D-Texturen zu
verwenden. Allerdings wird hier nur auf 2D-Texturen eingegangen
werden, da diese am meisten genutzt werden.
Leider stellt JOGL keine Methode zur Verfügung,
um Bilddateien einlesen zu können. Deshalb müssen solche
Methoden entweder selbst geschrieben werden, oder man greift auf
bereits vorhandene Implementierungen zurück. Eine gute Quelle
hierfür ist die Seite von Nehe Productions (http://nehe.gamedev.net),
auf der die Codebeispiele vielfach auch in JOGL implementiert zur
Verfügung stehen.
Wenn Texturen verwendet werden sollen, müssen
diese erst mit glEnable(..) aktiviert werden. Im Fall von 2D-Texturen
wird glEnable(..) mit der Konstanten GL.GL_TEXTURE_2D aufgerufen.
Um Texturen zu deaktivieren wird glDisable(..) verwendet.
Um eine 2D-Textur zu erzeugen, Wird der
Befehl glTexImage2D(..) benötigt. Diesem werden neun Parameter übergeben.
Der erste Parameter ist eine Integerkonstante. Diese wird bei der
Erzeugung von 2D-Texturen auf GL.GL_TEXTURE_2D gesetzt.
Der nächste Parameter ist nur dann von Bedeutung, wenn für
unterschiedliche Abstände zwischen Betrachter und betrachtetem
Objekt verschiedene Detaillierungsgrade zur Verfügung gestellt
werden sollen (Stichwort Mipmaps). Standardmässig sollte für
diesen Parameter 0 angegeben werden. Danach wird die
Anzahl der Farbkomponenten in der Textur spezifiziert. Die wichtigsten
Konstanten sind GL.GL_RGB, GL.GL_RGBA, GL.GL_ALPHA, GL.GL_LUMINANCE,
GL.GL_LUMINANCE_ALPHA, GL.GL_INTENSITY.
Die nächsten zwei Parameter definieren die Breite und die Höhe
der Textur. Breite bzw. Höhe müssen eine Potenz zur
Basis 2 sein. Danach wird angegeben, ob ein Rahmen für die Textur
verwendet werden soll (1) oder nicht (0).
Die nächsten beiden Parameter geben Format und Datentyp des
Arrays an, in dem die Bilddaten gespeichert sind. Als Werte für
Format kommen z.B. GL.GL_RGB oder GL.GL_RGBA in Frage, während
der Typ beispielsweise durch GL.GL_BYTE oder GL.GL_INT übergeben
werden kann. Das Array, das die Bilddaten für die Textur enthält,
wird durch den letzten Parameter übergeben.
Wenn mehrere Texturen angelegt werden sollen, kommen Texturobjekte
zum Einsatz, an die die Texturen gebunden werden. Diese Objekte
werden dann im Arbeitsspeicher abgelegt von wo aus sie beliebig
oft geladen werden können. Dies beschleunigt den Vorgang erheblich,
da ein erneutes Einlesen der Texturen entfällt.
Um Texturobjekte zu erzeugen, müssen zuerst Objektnamen in
einem Integerarray bereitgestellt werden. Dies geschieht in der
Methode glGenTextures(..). Dieser wird zuerst die Anzahl der bereitgestellten
Namen übergeben, dann das Integerarray. Nach dem Methodenaufruf
existieren jedoch noch keine Texturobjekte. Im zweiten Schritt wird
deshalb glBindTexture(..) aufgerufen. Dieser Methode wird die Art der Textur,
bei 2D GL.GL_TEXTURE_2D, und der Name des Texturobjekts übergeben.
nach
oben
Texturkoordinaten
Bei der Erstellung von Texturen
muss zudem auf die Berechnung der Texturkoordinaten geachtet werden.
Da zwischen den Koordinaten der Textur und den Koordinaten der Szene
(Vertexkoordinaten) unterschieden wird, müssen die Texturkoordinaten
an die Vertexkoordinaten angepasst werden.
Texturkoordinaten haben vier Komponenten, die als
s-, t-, r- und q-Koordinate bezeichnet werden. Diese entsprechen
den bekannten x-, y-, z- und w-Koordinaten. Für 2D-Texturen
sind daher nur s und t von Bedeutung; q wird im Allgemeinen der Wert 1
zugeordnet und bei der Erzeugung von homogenen Koordinaten benötigt.
In JOGL lassen sich Texturkoordinaten automatisch
berechnen. Dies geschieht mit den Methoden glTexGeni(..) und glTexGenfv(..).
In glTexGeni(..) wird der Modus festgelegt, nach dem die Texturkoordinaten
berechnet werden. Als erster Parameter wird die Texturkoordinate
angegeben, die berechnet werden soll, also entweder GL.GL_S,GL.GL_T,
GL.GL_R oder GL.GL_Q.
Der zweite Parameter ist die Konstante GL.GL_TEXTURE_GEN_MODE.
Dies ist der Generierungsmodus. Als letzter Parameter wird mittels
einer Konstante übergeben, mit welcher Funktion die Texturkoordinaten
berechnet werden sollen.
Wenn GL.GL_OBJECT_LINEAR angegeben wird, werden
die Texturkoordinaten als Linearkombination einer Ebene mit den
geometrischen Koordinaten erzeugt. Wenn (p1, p2, p3, p4) die übergebene
Ebene und (x, y, z, w) die Objektkoordinaten des Vertex darstellen,
dann berechnet sich die Texturkoordinate wie folgt [Ker02]:
generierte Texturkoordinate = p1x + p2y + p3z + p4w
Wird GL.GL_EYE_LINEAR gewählt, dann bewirkt dies ebenfalls
eine lineare Gewichtung, allerdings in eye-Koordinaten [Ker02]:
generierte Texturkoordinate = p'1x + p'2y + p'3z + p'4w
wobei (p'1, p'2, p'3, p'4) = (p1, p2, p3, p4) M-1
Eine dritte Möglichkeit ist GL.GL_SPHERE_MAP.
Diese bewirkt sphärisches Mapping bezüglich der Parameter
s und t. Hierzu werden keine weiteren Angaben benötigt, da
sich die Texturkoordinaten allein auf der Basis der geometrischen
Koordinaten berechnen. Außerdem werden Normalenvektoren und
der Blickpunkt berücksichtigt, um einen "Rundumblick"
bezüglich des Objekts zu erzeugen. Dies erscheint dann so,
als ob die Umgebung auf der Oberfläche gespiegelt werden würde.
Danach werden mit der Methode glTexGenfv(..) die
Werte für (p1, p2, p3, p4) übergeben. Als erster Parameter
wird wieder die Konstante übergeben, die für die zu berechnende
Texturkoordinate steht, also entweder GL.GL_S, GL.GL_T, GL.GL_R
oder GL.GL_Q. Danach wird wiederum der Name der Generierungsmodus
für die Texturkoordinaten angegeben. Als symbolische Namen
können hierbei GL.GL_TEXTURE_GEN_MODE, GL.GL_OBJECT_PLANE oder
GL.GL_EYE_PLANE übergeben werden.
Der letzte Parameter gibt das Array an, in dem sich
die Koeffizienten für die Generierung der Texturkoordinaten
befinden. Wurde als Generierungsmodus GL.GL_TEXTURE_GEN_MODE gewählt,
dann muß dieses Array eine einzige symbolische Konstante enthalten
und zwar entweder GL.GL_OBJECT_LINEAR, GL.GL_EYE_LINEAR oder GL.GL_SPHERE_MAP.
Ansonsten wird ein Array mit den vier Werten (p1, p2, p3, p4) übergeben.
Schließlich muss noch die Berechnung der Koordinaten
eingeschaltet werden. Dies geschieht mit glEnable(..), wobei je nach Bedarf
GL.GL_TEXTURE_GEN_S, GL.GL_TEXTURE_GEN_T, GL.GL_TEXTURE_GEN_R
oder GL.GL_TEXTURE_GEN_Q als symbolische Konstante übergeben
wird.
Oft kann es vorkommen, dass eine Textur auf einen
Bereich gezeichnet wird, dessen Ausmaße größer
oder kleiner sind als die der Textur. Deshalb ist es sinnvoll, festzulegen,
auf welche Art und Weise die Textur vergrößert oder verkleinert
werden soll, so dass sie in den Bereich der angegebenen Pixel passt.
Diese Verfahrensweise gehört in den Bereich des Filterings und
die Einstellungen bezüglich der Vergrößerung (magnification)
bzw. der Verkleinerung (minification) geschehen über den Befehl
gl.glTexParameteri(..). Diesem Befehl wird zuerst die Dimension
der Textur übergeben, dann eine der beiden symbolischen Konstanten
GL.GL_TEXTURE_MIN_FILTER (minification) bzw. GL.GL_TEXTURE_MAX_FILTER
(magnification).
Als letzter Parameter wird die Einstellung für
das Filtering angegeben, d.h. die Methode, mit der bestimmt wird,
in welcher Farbe die Pixel gezeichnet werden sollen. Mit GL.GL_NEAREST
wird dem Pixel die Farbe des Texels gegeben, dessen Mittelpunkt
dem Mittelpunkt des Pixels am nächsten liegt. Mit der Einstellung
GL.GL_LINEAR wird eine Mittelung der vier nächstgelegenen Farbwerte
bewirkt.
Abschließend sei erwähnt, dass bei der Verwendung von
glu-Primitiven mit der Methode glu.gluQuadricTexture(..) die Texturkoordinaten
erstellt werden. Dieser Methode wird das Quadrics-Objekt und true
übergeben, wenn die Koordinaten erstellt werden sollen.
nach
oben
Texturen in der Beispielszene
Nun wollen wir Texturen verwenden um das Spielfeld
realistischer zu gestalten. Nachdem wir eine Textur mit den Maßen
512x512 Pixeln mit Photoshop erzeugt haben, soll diese geladen und
auf das Spielfeld angebracht werden. Die Methoden makeRGBTexture(..)
und readPNGImage(..) stammen aus der Lesson06 von der Nehe Productions
Seite [Dul04].
Wir haben dabei die Erzeugung der Textur in die
Methode defineTexture(..) ausgelagert, die einmal in der init-Methode
aufgerufen wird:
private int texture;
public void defineTexture(GL gl,GLU glu)
{
gl.glShadeModel(GL.GL_SMOOTH);
gl.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST);
//Texturobjekt erstellen
texture = TextureGenerator.genTexture(gl);
gl.glBindTexture(GL.GL_TEXTURE_2D, texture);
//Erzeugen der Textur
BufferedImage img = TextureGenerator.readPNGImag ("spielfeld2.png");
TextureGenerator.makeRGBTexture(gl, glu, img,
GL.GL_TEXTURE_2D, false);
//Einstellen der Filtering-Komponenten
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
//wir wollen, dass die Textur das aktuell verwendete
Material
//ersetzt
gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_REPLACE);
}
Die Methode drawField(..) wird ebenfalls abgeändert:
public void drawField(GL gl, GLU
glu)
{
gl.glEnable(GL.GL_TEXTURE_2D);
gl.glBindTexture(GL.GL_TEXTURE_2D, texture);
gl.glBegin(GL.GL_QUADS);
gl.glTexCoord2f(0f, 0f);
gl.glVertex3f(-6.5f, -1.5f, -6.5f);
gl.glTexCoord2f(1f, 0f);
gl.glVertex3f(-6.5f, -1.5f, 6.5f);
gl.glTexCoord2f(1f, 1f);
gl.glVertex3f(6.5f, -1.5f, 6.5f);
gl.glTexCoord2f(0f, 1f);
gl.glVertex3f(6.5f, -1.5f, -6.5f);
gl.glEnd();
gl.glDisable(GL.GL_TEXTURE_2D);
}
nach
oben
|