59
Eine Einf ¨ uhrung in JOGL

Eine Einf¨uhrung in JOGL - informatik.uni-augsburg.de · In der main-Methode passiert nichts besonders aufregendes; die erzeugt einen Frame, fugt ihm einen von¨ GLCanvasabgeleiteten

Embed Size (px)

Citation preview

Eine Einfuhrung in JOGL

Inhaltsverzeichnis

1 Erste Schritte mit JOGL 4

1.1 Grundstrukturen . . . . . . . . . . . . . . . . . . . . . . . . . . . 41.2 Beispiel: Ein einfaches Fenster . . . . . . . . . . . . . . . . . . . . 5

2 Kamera und Projektionen 7

2.1 Modellmatrix und Projektionsmatrix . . . . . . . . . . . . . . . . 72.2 Die Kamera . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72.3 Projektionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82.4 Der Bildausschnitt (Viewport) . . . . . . . . . . . . . . . . . . . 8

3 Grundlegende Graphikelemente 9

3.1 Beispiel: Ein Fenster mit einfacher Graphik . . . . . . . . . . . . 93.2 Weitere Graphikelemente . . . . . . . . . . . . . . . . . . . . . . 13

3.2.1 Punkte . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133.2.2 Gruppierung . . . . . . . . . . . . . . . . . . . . . . . . . 133.2.3 Grafikfunktionen in GLUT . . . . . . . . . . . . . . . . . 15

4 Weiteres Zubehor 16

4.1 Farben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164.2 Elimination verdeckter Flachen . . . . . . . . . . . . . . . . . . . 17

5 Animation 18

6 Bezier-Kurven 19

7 Quadriken 20

7.1 Grundbegriffe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207.2 Die Zeichenfunktionen . . . . . . . . . . . . . . . . . . . . . . . . 207.3 Attributverwaltung . . . . . . . . . . . . . . . . . . . . . . . . . . 21

8 Beleuchtung 22

9 Texturen 25

9.1 Erste Beispiele . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259.2 Texturobjekte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

9.2.1 Benennung von Texturobjekten . . . . . . . . . . . . . . . 299.2.2 Erzeugung und Verwendung von Texturobjekten . . . . . 299.2.3 Entsorgen von Texturobjekten . . . . . . . . . . . . . . . 30

9.3 Texturspezifikation . . . . . . . . . . . . . . . . . . . . . . . . . . 309.4 Die Klassen TextureData und TextureIO . . . . . . . . . . . . . . 33

9.4.1 Die Klasse TextureIO . . . . . . . . . . . . . . . . . . . . 339.4.2 Die Klasse TextureData . . . . . . . . . . . . . . . . . . . 33

9.5 Ersetzen von (Teil-)Texturen . . . . . . . . . . . . . . . . . . . . 349.6 1D-Texturen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359.7 Rander . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 369.8 Detailniveaus (Mipmaps) . . . . . . . . . . . . . . . . . . . . . . 369.9 Filterung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379.10 Texturfunktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . 389.11 Zuweisen von Texturkoordinaten . . . . . . . . . . . . . . . . . . 40

2

9.11.1 Wiederholen und Strecken von Texturen . . . . . . . . . . 409.11.2 Automatische Erzeugung von Texturkoordinaten . . . . . 429.11.3 Erzeugung von Hohenlinien . . . . . . . . . . . . . . . . . 429.11.4 Texturierung von Quadriken . . . . . . . . . . . . . . . . 44

9.12 Environment Mapping . . . . . . . . . . . . . . . . . . . . . . . . 449.13 Weiteres Zubehor . . . . . . . . . . . . . . . . . . . . . . . . . . . 44

9.13.1 Funktionen fur residente Texturen . . . . . . . . . . . . . 449.13.2 Texturproxies . . . . . . . . . . . . . . . . . . . . . . . . . 459.13.3 Der Texturmatrix-Keller . . . . . . . . . . . . . . . . . . . 469.13.4 Die q-Koordinate . . . . . . . . . . . . . . . . . . . . . . . 47

10 Transparenz und Farbmischung 47

10.1 Grundbegriffe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4710.2 Quell- und Zielfaktoren . . . . . . . . . . . . . . . . . . . . . . . 4810.3 Einige wichtige Mischarten . . . . . . . . . . . . . . . . . . . . . 4810.4 Beispiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5010.5 Dreidimensionales Mischen mit dem Tiefenpuffer . . . . . . . . . 53

3

1 Erste Schritte mit JOGL

1.1 Grundstrukturen

Um JOGL-Funktionen in einem Javaprogramm nutzen zu konnen, muß mandas Interface GLEventListener implementieren. Dieses Interface hat folgendeMethoden:

• public void display(GLAutoDrawable gLDrawable):In dieser Methode wird die Szenerie erstellt und die Kamera positioniert.Die Details werden spater an den Beispielen erlautert.

• public void displayChanged(GLAutoDrawable gLDrawable, boolean

modeChanged, boolean deviceChanged):Diese Methode soll aufgerufen werden, wenn der Bildschirmmodusgeandert wird, wird aber gegenwartig nicht unterstutzt. Der Rumpf dieserMethode sollte deshalb am besten leer bleiben.

• public void init(GLAutoDrawable gLDrawable):Diese Methode wird einmal beim Erzeugen aufgerufen und eignet sich, umVariablen oder Eigenschaften zu initialisieren.

• public void reshape(GLAutoDrawable gLDrawable, int x, int y,

int width, int height):Diese Methode behandelt das Verandern der Fenstergroße und dasVerschieben des Fensters. Auch hier werden die Details spater erlautert.

Um das erzeugte Bild nicht nur abstrakt im Rechner zu haben, sollte man esauf einem GLCanvas ausgeben lassen. Diese Klasse erbt von Canvas und lasstsich einem Frame hinzufugen.

4

1.2 Beispiel: Ein einfaches Fenster

Der folgende Code erzeugt ein schwarzes Fenster ohne jegliche Graphik, das sichnicht in gewohnter Weise schließen lasst.

import java.awt.*;

import java.awt.event.*;

import javax.media.opengl.*;

import javax.media.opengl.glu.*;

import com.sun.opengl.util.*;

public class Beispiel_1 {

static class WinRenderer extends GLCanvas

implements GLEventListener

{

//GL und GLU zum Aufrufen der Graphikfunktionen

private GL gl;

private GLU glu;

//Konstruktor

public WinRenderer(){

super();

}

//Erstellen des Fensters

public void display(GLAutoDrawable gLDrawable){

//GL-Objekt holen

gl = gLDrawable.getGL();

//Buffer leeren

gl.glClear(gl.GL_COLOR_BUFFER_BIT);

//und ab geht die Post

gl.glFlush();

}

//nicht implementiert

public void displayChanged(GLAutoDrawable gLDrawable,

boolean modeChanged, boolean deviceChanged){}

//hier gibt es nichts zu initialisieren

public void init(GLAutoDrawable gLDrawable){}

//Auch das Verandern des Fensters interessiert uns nicht

public void reshape(GLAutoDrawable gLDrawable, int x, int y,

int width, int height){}

5

}

public static void main(String[] args) {

//Frame mit Titel erzeugen

Frame frame = new Frame("Ein leeres Fenster");

//neuen WinRenderer erzeugen

WinRenderer canvas = new WinRenderer();

//zum Frame hinzufugen

frame.add(canvas);

//Große setzen

frame.setSize(640, 480);

//und anzeigen

frame.show();

}

}

6

2 Kamera und Projektionen

2.1 Modellmatrix und Projektionsmatrix

JOGL halt intern zwei 4D-Matrizen:

• Die Modellmatrix M enthalt die Gesamttransformation, der die ganze Mo-dellwelt unterworfen wird, bevor die Projektion auf die 2D-Bildflache statt-findet. Insbesondere ist dabei der Wechsel ins Kamera-Koordinatensystementhalten.

• Die Projektionsmatrix P gibt an, ob Parallel- oder Zentralprojektion statt-findet.

Zum Anzeigen des Bildes wird dann P ·M auf alle Objektkoordinaten angewen-det.Beide Matrizen konnen schrittweise aus Grundmatrizen durch Matrixmultipli-kation zusammengesetzt werden; auch explizite Angabe ist moglich. Die jeweilsaktuelle Modell- oder Projektionsmatrix wird mit C (

”current“) bezeichnet. Vie-

le JOGL-Funktionen verandern C, und wir werden oft die Veranderung in alge-braischer Form angeben.Mit

void GL.glMatrixMode (int mode);

wird ausgewahlt, ob mit C die aktuelle Modell- oder Projektionsmatrixgemeint ist; die entsprechenden Parameter sind GL.GL_MODELVIEW bzw.GL.GL_PROJECTION; die Voreinstellung ist C = M .Zur Initialisierung der jeweiligen Matrix dient

void GL.glLoadIdentity (void);

mit der Wirkung C ← E (E ist die Einheitsmatrix).Einfache Transformationen werden bewirkt durch folgende Funktionen:

void GL.glTranslatef (float x, y, z); C ← C · T (x, y, z)void GL.glScalef (float x, y, z); C ← C · S(x, y, z)void GL.glRotatef (float angle, x, y, z); C ← C · R(angle, x, y, z)

2.2 Die Kamera

Sie wird definiert durch

void GLU.gluLookAt (double eyex, eyey, eyez, // Augpunkt

centerx, centery, centerz, // Hauptpunkt

upx, upy, upz); // Obenvektor

Diese Funktion wird ublicherweise in der Methode display aufgerufen.

7

2.3 Projektionen

Eine Orthogonalprojektion wird spezifiziert durch

void GL.glOrtho (double left, right,

bottom, top,

near, far);

Eine Zentralprojektion ist gegeben durch

void GLU.gluPerspective (double alpha, aspect, near, far);

Warum die Orthogonalprojektion und die Zentralprojektion in verschiedenenKlassen implementiert sind, ist unklar.

2.4 Der Bildausschnitt (Viewport)

Manchmal will man das Frustum nicht auf dem Gesamtschirm anzeigen, sondernnur auf einem Ausschnitt daraus. Dazu dient

void GL.glViewport (int x, y, width, height);

x und y geben die linke untere Ecke des Ausschnitts an. Im Gegensatz zu Welt-und Bildkoordinaten werden hier Pixelkoordinaten verwendet.Achtung: Stimmt das Verhaltnis von width und height nicht mit dem des Fru-stums uberein, kommt es zu Verzerrungen.Bildausschnitt und Projektion werden meist in der Methode reshape gesetzt.

8

3 Grundlegende Graphikelemente

3.1 Beispiel: Ein Fenster mit einfacher Graphik

Das folgende Programm erzeugt ein schwarzes Fenster, auf dem ein weißes Drei-eck zu sehen ist. Der Code wird im Anschluß an den Quelltext besprochen.

import java.awt.*;

import java.awt.event.*;

import javax.media.opengl.*;

import javax.media.opengl.glu.*;

import com.sun.opengl.util.*;

public class Beispiel_3 {

static class WinRenderer extends GLCanvas

implements GLEventListener

{

private GL gl;

public WinRenderer(){

super();

}

//Erzeugen der Szenerie

public void display(GLAutoDrawable gLDrawable){

//GL-Objekt holen

gl = gLDrawable.getGL();

//Buffer leeren

gl.glClear(gl.GL_COLOR_BUFFER_BIT);

//Modellmatrix initialisieren

gl.glLoadIdentity();

//Kamera positionieren

glu.gluLookAt(0,0,0.5,0,0,0,0,1,0);

//geschlossenen Linienzug beginnen

gl.glBegin(gl.GL_LINE_LOOP);

//Punkte setzen

gl.glVertex3f(-0.3f,-0.3f,0f);

gl.glVertex3f(0f,0.3f,0f);

gl.glVertex3f(0.3f,-0.3f,0f);

//Linienzug beenden

gl.glEnd();

//Pufferinhalt weitergeben

9

gl.glFlush();

}

public void displayChanged(GLAutoDrawable gLDrawable,

boolean modeChanged, boolean deviceChanged){}

public void init(GLAutoDrawable gLDrawable){}

//Reaktion auf Fensteranderungen

public void reshape(GLAutoDrawable gLDrawable, int x, int y,

int width, int height){

//GL-Objekt holen

gl = gLDrawable.getGL();

//Bildausschnitt im Fenster positionieren

gl.glViewport(0,0,width,height);

//Auf Projektionsmatrix umschalten

gl.glMatrixMode(gl.GL_PROJECTION);

//Projektionsmwtrix initialisieren

gl.glLoadIdentity();

//Orthogonalprojektion wahlen

gl.glOrtho(-1,1,-1,1,-1,1);

//und wieder auf Modellmatrix zurucksetzen

gl.glMatrixMode(gl.GL_MODELVIEW);

}

}

public static void main(String[] args) {

//Frame mit Titel erzeugen

Frame frame = new Frame("weißes Dreieck auf schwarzem Grund"

//neuen WinRenderer erzeugen

WinRenderer canvas = new WinRenderer();

//GLEventlistener hinzufugen

canvas.addGLEventListener(canvas);

//GLCanvas auf dem Frame kleben

frame.add(canvas);

//zum besseren Schließen :-)

frame.addWindowListener(new WindowAdapter()

{

public void windowClosing(WindowEvent e)

{

System.exit(0);

}

});

//Große setzen

frame.setSize(640, 480);

//anzeigen

frame.show();

//GLCanvas in den Vordergrund holen

canvas.requestFocus();

10

}

}

Zunachst wollen wir uns die display-Methode dieses Beispiels ansehen.In der Zeile

gl = gLDrawable.getGL();

wird vom ubergebenen GLAutoDrawable-Objekt das GL-Objekt ausgelesen. DieKlasse GL stellt viele Methoden zum Erstellen und Zeichnen einer Szenerie zurVerfugung.Mit

glu = new GLU();

wird eine Instanz von GLU erzeugt. Auch hier sind viele Methoden zum Zeichenund Poitionieren von Szenerien zu finden.Die Anweisung

gl.glClear(gl.GL_COLOR_BUFFER_BIT);

leert den Graphikpuffer.Mittels

gl.glLoadIdentity();

wird die Modellmatrix initialisiert. Da der Defaultwert fur die Matrix die Mo-dellmatrix ist (siehe das Kapitel uber Kamera und Projektionen), muß mannicht explizit auf den Modellmatrixmodus umschalten. Das Initialisieren ent-spricht einem push der Einheitsmatrix auf den (leeren) Matrixkeller.Die Positionierung der Kamera geschieht mit

glu.gluLookAt(0,0,0.5,0,0,0,0,1,0);

Warum diese Funktion in GLU und nicht in GL enthalten ist, mag jeder ebensowie den Grund fur die Unterteilung in GL, GLU und GLUT selbst herausfinden. Inunserem Fall wird die Kamera im Punkt (0,0,0.5) positioniert, blickt in Richtungdes Ursprungs (0,0,0) und ist derart gedreht, daß der Vektor (0,1,0) im Bild nachoben zeigt.Die Zeile

gl.glBegin(gl.GL_LINE_LOOP);

kundigt den Beginn eines geschlossenen Linienzuges an, dessen Punkte durch

gl.glVertex3f(-0.3f,-0.3f,0f);

gl.glVertex3f(0f,0.3f,0f);

gl.glVertex3f(0.3f,-0.3f,0f);

festgelegt sind. Das Ende dieses Linienzuges wird durch

gl.glEnd();

11

markiert.GL.glBegin(int figure) kann noch andere Argumente außerGL.GL_LINE_LOOP haben; dazu spater mehr.Die letzte Zeile

gl.glFlush();

gibt den aktuellen Pufferinhalt an das Fenster weiter.Die Methoden displayChanged und init bleiben unimplementiert: Fur

displayChanged wurde der Grund bereits erlautert, und init bleibt leer, weilwir nichts zu initialisieren haben.Die reshape-Methode ist fur das Neuzeichnen des Fensters bei Format- oder

Ortsanderungen desselben zustandig. Die aktuelle Position des Fensters auf demBildschirm wird in Gestalt der int-Werte x und y ubergeben. Die Fensterbreite-und hohe erhalt die Methode durch die Integer width bzw. height.Nach dem Auslesen eines GL-Objekts durch

gl = gLDrawable.getGL();

wird mittels

gl.glViewport(0,0,width,height);

der Viewport gesetzt. Hier wird die linke untere Ecke des Bildes in die linkeuntere Ecke des Fensters (dafur sind die beiden ersten Nullen verantwortlich)gelegt und die Breite und die Hohe des Bildes genau auf die Fensterbreite- undhohe gesetzt. Die ubergebenen int-werte rechnen alle in Pixeln.

gl.glMatrixMode(gl.GL_PROJECTION);

schaltet auf die Modellmatrix um, die dann durch

gl.glLoadIdentity();

auch gleich initialsiert wird (vgl. das Vorgehen in display).Nun gilt es noch, die Projektion zu spezifizieren. Der Aufruf

gl.glOrtho(-1,1,-1,1,-1,1);

spezifiziert eine Parallelprojektion mit einem quaderformigen Frustum, dessenlinke vordere Ecke (−1,−1,−1) (also hinter der Kamera) und dessen rech-te vordere Ecke (1, 1, 1) ist. Allgemein beschreibt GL.glOrtho(double umin,

umax, vmin, vmax, near, far) einen Quader mit (umin, vmin, near) alslinker vorderer und (umax, vmax, far) als rechter hinterer Ecke. Hierbei istdarauf zu achten, daß umin < umax, vmin < vmax und near < far gelten, vgl.auch das Skript von Toby Howard, Seite 61. Der Koordinatenursprung, bezug-lich dessen der Quader ermittelt wird, ist die Kameraposition, die Richtungenlinks/rechts, oben/unten und vorn/hinten werden durch die Blickrichtung undden view-up-Vektor festgelegt.Da wir uns noch im Projektionsmatrixmode befinden, setzen wir mit

gl.glMatrixMode(gl.GL_MODELVIEW);

12

den Matrixmode wieder auf die Modellmatrix zuruck.In der main-Methode passiert nichts besonders aufregendes; die erzeugt einen

Frame, fugt ihm einen von GLCanvas abgeleiteten WinRenderer hinzu und zeigtdas Ganze an.

3.2 Weitere Graphikelemente

3.2.1 Punkte

Wie bereits angedeutet kann man zwischen GL.glBegin(int figure) undGL.glEnd noch mehr anstellen als nur einen Linienzug zu zeichnen. Beginnenwir mit dem einfachsten, einem kleinen einsamen Punkt.Ein Punkt im R

3 wird definiert durch:

void GL.glVertex3f (float x, y, z);

Analog dazu gibt es eine Fassung, die drei double-Werte erwartet:

void GL.glVertex3d (double x, y, z);

Punkte im R2 werden durch zwei float- oder double-Werte charakterisiert

und mittels der Funtionen void GL.glVertex2f (float x, y) bzw. void

GL.glVertex2d (double x, y) erzeugt.

3.2.2 Gruppierung

Mehrere Punkte konnen zu einer Figur zusammengesetzt werden mit

void GL.glBegin (int mode);

void GL.glEnd (void);

Dabei gibt mode die Art der Gruppe an:

• GL.GL_POINTS: unverbundene Punkte

• GL.GL_LINES: bildet Punktepaare, von denen jedes verbunden wird, abernicht mit den anderen zusammenhangt.

• GL.GL_LINE_STRIP: Die Punkte werden zu einem Kantenzug zusammen-gefugt.

• GL.GL_LINE_LOOP: Der Kantenzug wird zu einem Kreis geschlossen.

• GL.GL_TRIANGLES: Unverbundene Dreiecke aus Dreiergruppen von Punk-ten.

• GL.GL_TRIANGLE_STRIP: Bildet Dreiecke aus

v0, v1, v2, v3, v4, . . .

wie folgt:∆v0v1v2,∆v2v1v3,∆v2v3v4,∆v4v3v5, . . .

13

Damit wird ein Dreiecksnetz als Ausschnitt einer angenaherten 3D-Flache

gebildet.

b b

b

b

b

v0 v1

v2

v3

v4

Abbildung 1. GL_TRIANGLE_STRIP

• GL.GL_TRIANGLE_FAN: Zeichnet Dreiecke ∆v0v1v2,∆v0v2v3,∆v0v3v4, d.h.einen Facher mit v0 im Zentrum.

b

b

b

b

b

v0

v1

v2

v3

v4

Abbildung 2. GL.GL_TRIANGLE_FAN

• GL.GL_QUADS: Unverbundene Vierecke aus Vierergruppen von Punkten.

• GL.GL_QUAD_STRIP: analog zu GL.GL_TRIANGLE_STRIP:

b

b

b

b

b

b

b

b

v0

v1

v2

v3

v4

v5

v6

v7

Abbildung 3. GL.GL_QUAD_STRIP

• GL.GL_POLYGON: verbindet Punkte zu einem Polygonzug. Das Polygonmuss konvex sein und darf sich nicht selbst uberschneiden. Sonstige Poly-gone mussen in konvexe zerlegt werden (Tesselierung).

14

Zu Linien und Polygonen konnen zusatzliche Attribute spezifiziert werden;

• void GL.glLineWidth (float width); stellt die Linienbreite ein.

• void GL.glLineStipple (int factor, short pattern); definiert dieStrichelung (muss zuerst eingeschaltet werden, s. u.); die Moglichkeitenwerden binar codiert.

• void GL.glPolygonMode (int face, mode) mit face ∈ {GL.GL_FRONT, GL.GL_BACK, GL.GL_FRONT_AND_BACK }mode ∈ { GL.GL_FILL, GL.GL_LINE } Der Parameter face erlangt erstspater Bedeutung fur uns, uber mode kann eingestellt werden, ob ein Po-lygon o.a. ausgefullt oder nur als Liniengerippe erscheint.

3.2.3 Grafikfunktionen in GLUT

GLUT stellt einige komplexe Korper vordefiniert zur Verfugung.

• GLUT.glutWireCube (double size)

zeichnet einen Wurfel der Seitenlange size mit Mittelpunkt im Ursprung(in Weltkoordinaten) als Drahtmodell.

• GLUT.glutSolidCube (double size)

analog, aber als festen Korper.

• GLUT.glutWireSphere (double radius, int slices, stacks)

GLUT.glutSolidSphere (double radius, int slices, stacks)

Kugel mit Radius radius und Mittelpunkt im Ursprung.

slices gibt die Anzahl der”Langengrade“,

stacks die der”Breitengrade“ an.

• GLUT.glutWireCone (double base, height, int slices, stacks)

GLUT.glutSolidCone (double base, height, int slices, stacks)

Kegel mit Grundradius base und Hohe height; die Grundflache liegt aufder xy-Ebene und die z-Achse ist die Kegelachse.

slices, stacks wirken wie bei den Kugeln.

• GLUT.glutWireTorus (double innerRadius, outerRadius,

int nsides, rings)

GLUT.glutSolidTorus (double innerRadius, outerRadius,

int nsides, rings)

Torus mit Mittelpunkt im Ursprung und der z-Achse als Achse.

nsides ist die Zahl der Seiten in jedem Radialschnitt,

rings die Zahl der Radialschnitte.

15

• Die platonischen Polyeder (jeweils mit Mittelpunkt im Ursprung) lassensich mit folgenden Funktionen zeichnen:

void GLUT.glutWireTetrahedron (void) Radius√

3

void GLUT.glutSolidTetrahedron (void) Radius√

3

void GLUT.glutWireOctahedron (void) Radius 1

void GLUT.glutSolidOctahedron (void) Radius 1

void GLUT.glutWireDodecahedron (void) Radius√

3

void GLUT.glutSolidDodecahedron (void)Radius√

3

void GLUT.glutWireIcosahedron (void) Radius 1

void GLUT.glutSolidIcosahedron (void) Radius 1

• void GLUT.glutWireTeapot (double scale)

void GLUT.glutSolidTeapot (double scale)

Die klassische Teekanne, um den Faktor scale gestreckt.

4 Weiteres Zubehor

4.1 Farben

Im RGB-Farbmodell wird jede Farbe durch ihren Rot-, Grun- und Blauanteildargestellt; wie erwahnt gibt OpenGL dazu Bruchzahlen in [0, 1] an.Der Zusammenhang zwischen den Farben wird durch folgenden Farbwurfel be-schrieben:

G

R

B

S

W

Y

M

C

Schwarz S = (0.0, 0.0, 0.0)Weiß W = (1.0, 1.0, 1.0)Cyan C = (0.0, 1.0, 1.0)

Magenta M = (1.0, 0.0, 1.0)Gelb Y = (1.0, 1.0, 0.0)

Abbildung 4. Farbwurfel

Auf der Raumdiagonalen zwischen S und W liegen die Graustufen. Dieses Farb-modell ist die Voreinstellung in JOGL.Die aktuelle Zeichenfarbe wird mit

16

void GL.glColor3f (float red, green, blue);

gesetzt.

4.2 Elimination verdeckter Flachen

JOGL stellt hierfur eigene Befehle zur Verfugung; auf den algorithmischen Hin-tergrund werden wir spater eingehen.Per Voreinstellung zeichnet JOGL die Objekte einer Szenerie in der Reihenfolge,wie sie im Programm erscheinen. Das fuhrt insbesondere bei Animationen oftzu grotesken Verdeckungen.JOGL unterstutzt hier die Tiefenpufferung (z-buffering). Dabei wird fur jedesPixel in einem speziellen Feld Tiefeninformation gespeichert. Bei der Aufbe-reitung fur ein Objekt wird beim Schreiben des entsprechenden Pixels jeweilsgepruft, ob es zu einem Punkt geringerer Tiefe gehort, als bisher vermerkt;dann wird der alte, durch das neue Objekt verdeckte, Pixelwert uberschrieben,ansonsten andert er sich nicht.

17

Die Tiefenpufferung schaltet man so ein:

• Der Aufruf von GLUT.glutInitDisplayMode wird zu

void GLUT.glutInitDisplayMode (GLUT_DOUBLE | GLUT_DEPTH);

• In der display-Funktion muss zu Beginn auch der Tiefenpuffer geloschtwerden:

void glClear (GL.GL_COLOR_BUFFER_BIT |

GL.GL_DEPTH_BUFFER_BIT);

• und die Tiefenpufferung ermoglicht werden:

void GL.glEnable (GL.GL_DEPTH_TEST);

5 Animation

Bis jetzt haben wir uns nur mit der Erzeugung von statischen Blidern beschaf-tigt; nun wollen wir uns noch damit vertraut werden, wie man unter JOGLdie Bilder zum Laufen bringen kann. Dazu dient die Animator-Klasse. IhrKonstruktor hat entweder kein Argument oder bekommt ein GLAutoDrawable-Objekt ubergeben. Im Regelfall wird das ein GLCanvas sein, der mittels desAnimators zum Leben erweckt wird. Durch den Aufruf der start-Methodedes Animators wird zuerst die init-Methode des ubergebenen Objekts auf-gerufen, anschließend wird in einer Endlosschleife immer wieder die display-Methode aufgerufen, unterbrochen von eventuellen Einschuben der reshape-Methode, falls das Fenster verschoben oder in der Große geadert wurde. DerAnimator verfugt unter anderem auch uber eine wait-Methode, mit der derLauf der Ereignisse fur eine gewisse Zeit angehalten werden kann, und ubereine stop-Methode, mit der man ihn unbegrenzte Zeit aufhalten kann. Hierzuvergleiche man auch Beispiel 5 auf der Webseite (http://www.informatik.uni-augsburg.de/de/lehrstuehle/dbis/pmi/lectures/ws0708/graphikprogrammierung/Beispiele/)

Dieses Vorgehen fuhrt zwar zum Ziel, hat aber einige Nachteile:

• Die CPU-Auslastung steigt je nach Rechner auf bis zu 95%

• Die Geschwindigkeit, mit der die Anzeige erfolgt, hangt von der Leistungs-fahigkeit des Rechners ab

Der Grund hierfur ist, daß der Animator so ost wie moglich die Methoden ausdem ubergebenen GLAutoDrawable-Objekt abzuarbeiten versucht. Die dadurchentstehenden Probleme kann man zu einem großen Teil mit der Verwendungeines FPSAnimators umgehen. Er besitzt die gleichen Methoden wie ein nor-maler Animator, bekommt aber im Konstruktor neben einem GLAutoDrawable-Objekt noch einen int-Wert ubergeben, der angibt, wie oft pro Sekunde dasGLAutoDrawable-Objekt abgearbeitet werden soll. Ein solcher FPSAnimators

wird im Beispiel 5b auf der Webseite eingesetzt.

18

6 Bezier-Kurven

JOGL bietet die Moglichkeit, Bezier-Kurven nach der Angabe von Stutz-punkten berechnen zu lassen. Dazu betrachten wir das kleine DemoprogrammBezier.java, zu finden auf der Webseite unter Beispiele.Im Vergleich zu den bisherigen Programmen fuheren wir hier ein float-Arraycontrol_points ein:

float [] control_points = {0.0f, 0.0f, 0.0f,

0.0f, 1.0f, 0.0f,

1.0f, 1.0f, 0.0f,

1.0f, 0.0f, 0.0f};

Dieses Array spezifiziert die Stutzpunkte unserer Bezier-Kurve. Wie dieEintrage vom Programm interpretiert werden, sehen wir in Balde.Zur weiteren Verwendung muß dieses float-Array noch mittels

buf = FloatBuffer.wrap(control_points);

in einen FloatBuffer eingepackt werden.Am wichtigsten fur das Ergebnis ist die Zeile

gl.glMap1f(gl.GL_MAP1_VERTEX_3, 0.0f, 1.0f, 3, 4, buf);

Zuerst wird mit gl.GL_MAP1_VERTEX_3 die Art der Auswertung festgelegt:Es sollen Punkte im R

3 berechnet werden. Die beiden float-Werte 0.0f

und 1.0f geben den Bereich an, in den der Parameter t der Bezier-Kurven-Funktion sich bewegen soll. Der int-Wert 3 ist wichtig fur die Interpretationdes control_point-Array als Stutzpunkte: Ein neuer Stutzpunkt beginnt nachjedem dritten Eintrag im control_point-Array. In unserem Beispiel sind dieKontrollpunkte (da wir im R

3 auswerten) die Punkte (0, 0, 0), (0, 1, 0), (1, 1, 0)und (1, 0, 0), insbesondere ist also (0, 0, 0) der Anfangspunkt und (1, 0, 0) derEndpunkt; die beiden anderen Punkte sind Stutzpunkte. Die 4 gibt die Anzahlder Stutzpunkte, also die Ordung der Bezier-Kurve an, und buf die zu verwen-denden Kontrollpunkte.Die Berechnung der Kurvenpunkte findet in der Zeile

gl.glEvalCoord1f(i/num);

statt. Das Argument dieser Funktion ist der Wert, der in der vorher spezifizier-ten Bezier-Kurve fur t eingesetzt wird. In unserem Fall wird der Bereich [0, 1]in num Teilbereiche aufgespalten, d.h., es werden num + 1 Punkte berechnetund durch die GL_LINE_STRIP-Umgebung durch einen Linienzug verbunden.Die GL.glEvalCoord1f-Methode kann verschiedene Ruckgabewerte haben; indiesem Kontext wird voon ihr ein FloatBuffer erwartet, der dann als Punktim R

3 interpretiert wird.Das Ergebnis dieses Programmes ist sehr symmetrisch, was auf die Wahl derStutzpunkte zuruckzufuhren ist. Es ist interessant, die Anderungen zu beobach-ten, die sich ergeben, wenn man den num-Wert und die Stutzpunkte verandert.

19

7 Quadriken

7.1 Grundbegriffe

Unter Quadriken versteht man geometrische Objekte, die durch Gleichungen derForm P (x, y, z) = 0 mit einem Polynom P zweiten Grades in den Koordinatenx, y, z beschrieben werden konnen. Darunter fallen Kugeln (allgemeiner Ellip-soide), Paraboloide, Hyperboloide und Zylinder, aber auch (Ausschnitte von)Kreisscheiben.Ausgeschrieben hat die allgemeine Quadrikgleichung die Form

a11x2 + a22y

2 + a33y2 + 2a12xy + 2a23yz + 2a13xz + b1x + b2y + b3z + c = 0 .

In Matrix-Vektor-Schreibweise kann man das auch formulieren als

(x y z)T ·

a11 a12 a13

a12 a22 a23

a13 a23 a33

·

x

y

z

+ (b1 b2 b3)T ·

x

y

z

+ a10 = 0 .

Die obige Form des Polynoms wurde gewahlt, damit sich eine symmetrische Ma-trix ergibt; sie laßt sich durch Halbieren der

”gemischten“ Koeffizienten immer

erreichen.In JOGL werden Quadriken uber GLUquadric-Objekte verwaltet. Zur Erzeu-gung eines neuen GLUquadric-Objekts dient

GLUquadric glu.gluNewQuadric();

Dieses Objekt muß dann in einer Variablen gespeichert werden, um es weiterverwenden zu konnen.

7.2 Die Zeichenfunktionen

Die Funktion

void glu.gluSphere (GLUquadric quad, double radius,

int slices, int stacks);

zeichnet eine Kugel mit dem gegebenen Radius um den Ursprung (0 0 0). Dabeigibt slices die Anzahl der

”Langengrade“ und stacks die Anzahl der

”Brei-

tengrade“ ein (Achse in z-Richtung).Werden Texturkoordinaten miterzeugt (siehe unten), variiert die t-Koordinatevon 0.0 bei z = −radius bis 1.0 bei z = radius und zwar linear entlangden Langengraden. Dagegen variiert die s-Koordinate von 0.0 auf der positiveny-Achse zu 0.25 auf der positiven x-Achse, dann zu 0.5 auf der negativen y-Achse, dann zu 0.75 auf der negativen x-Achse und schließlich zu 1.0, um denUmfangskreis auf der positiven y-Achse zu schließen.Die Funktion

void glu.gluCylinder (GLUquadric quad, double baseRadius,

double topRadius, double height,

int slices, int stacks);

20

zeichnet einen Kegelstumpf mit Radius baseRadius bzw. topRadius ein Boden-bzw. Deckflache. Fur topRadius = 0 ergibt sich ein echter Kegel. Die Boden-flache liegt in der xy-Ebene, die Hohe ist height und die Achse ist wieder diez-Achse. slices und stacks haben analoge Bedeutung wie bei der Kugel.Achtung: Der Kegelstumpf ist oben und unten nicht geschlossen (außer furtopRadius = 0); die Boden- und Deckflache werden nicht mitgezeichnet.Werden Texturkoordinaten miterzeugt, variiert die t-Koordinate linear von 0.0bei z = 0 bis 1.0 bei z = height. Die s-Texturkoordinaten werden genau wie beider Kugel erzeugt.Die Funktion

void glu.gluDisk (GLUquadric quad, double innerRadius,

double outerRadius, int slices, int rings);

zeichnet eine Kreisscheibe auf der xy-Ebene mit Radius outerRadius und einemkonzentrischen kreisrunden Loch mit Radius innerRadius. Ist innerRadius =0, wird kein Loch gezeichnet. Die Scheibe wird in slices Sektoren und rings

konzentrische Ringe um die z-Achse unterteilt. Die positive z-Richtung zahlt alsAußenseite der Scheibe.Die Texturkoordinaten sehen folgendermaßen aus: Sei R = outerRadius. Dannhat man die Entsprechungen

(x, y, z) (s, t)

(R, 0, 0) (1, 0.5)(0, R, 0) (0.5, 1)

(−R, 0, 0) (0, 0.5)(0,−R, 0) (0.5, 0)

Die Funktion

void glu.gluPartialDisk

(GLUquadric quad, double innerRadius,

double outerRadius, int slices, int rings,

double startAngle, double sweepAngle);

zeichnet einen Ausschnitt aus einer Kreisscheibe auf der xy-Ebene. Die Be-deutung von outerRadius, innerRadius, slices und rings ist wie fur einevollstandige Scheibe. Es wird aber nur der Ausschnitt von startAngle bisstartAngle+sweepAngle gezeichnet (beide Winkel in Grad gemessen). Dabeibedeuten 0◦ die positive y-Achse, 90◦ die positive x-Achse usw.Orientierung und Texturkoordinaten sind wie bei der vollstandigen Scheibe.

Bei allen Quadriken ist es gunstiger, ihre Kenngroßen direkt uber die Parametereinzustellen, als sie mit gl.glScale() zu verandern, denn dann brauchen dieNormalenvektoren nicht renormalisiert zu werden. Außerdem sollten die Un-terteilungsparameter deutlich großer als Eins gesetzt werden, um eine feinereBeleuchtungsrechnung zu erzwingen, besonders bei stark spiegelnden Materiali-en.

7.3 Attributverwaltung

Schließlich konnen noch die Zeichenattribute von Quadriken eingestellt werden.Dazu gibt es die folgenden Funktionen (die naturlich jeweils vor dem eigentlichenZeichnen aufzurufen sind). Sie werden im nachsten Beispiel verwendet.

21

Die Funktion

void glu.gluQuadricDrawStyle

(GLUquadric quad, int drawStyle);

spezifiziert uber drawStyle die Darstellungsart. Folgende Werte sind zulassig.

• GLU.GLU_POINT und GLU.GLU_LINE: Darstellung durch einen Punkt einjeder Ecke bzw. durch eine Strecke zwischen je zwei Ecken.

• GLU.GLU_SILHOUETTE: Darstellung durch Strecken unter Weglassen vonKanten zwischen koplanaren Flachen. Dies wird am haufigsten fur (parti-elle) Scheiben verwendet.

• GLU.GLU_FILL: Darstellung durch gefullte Polygone, die bezuglich ihrerNormalen im Gegenuhrzeigersinn orientiert sind. Die Orientierung kannmit der nachfolgenden Funktion geandert werden.

Die Funktion

void glu.gluQuadricOrientation

(GLUquadric quad, int orientation);

gibt die Orientierung der Normalen von quad ein. Per Voreinstellung ist sieGLU.GLU_OUTSIDE; sie kann zu GLU.GLU_INSIDE umgestellt werden.Die Funktion

void glu.gluQuadricNormals (GLUquadric quad, int normals);

stellt ein, wann Normalen erzeugt werden sollen.

• Bei GLU.GLU_NONE (der Voreinstellung) werden keine Normalen erzeugt;dieser Modus ist nur interessant, wenn ohne Beleuchtung gearbeitet wird.

• Bei GLU.GLU_FLAT wird fur jede Flache nur eine Normale erzeugt; dieserModus ist fur Flachenschattierung geeignet.

• Bei GLU.GLU_SMOOTH wird fur jede Ecke einer Flache die Eckennormaleerzeugt; dieser Modus ist fur Gouraudschattierung geeignet.

Die Funktion

void glu.gluQuadricTexture (GLUquadric quad,

boolean texturCoords);

stellt ein, ob fur quad Texturkoordinaten erzeugt werden sollen. Die Voreinstel-lung ist GL.GL_FALSE, die Alternative GL.GL_TRUE.

8 Beleuchtung

Im folgenden werden wir mit gl immer eine Instanz der Klasse GL bezeichnen.

JOGL erlaubt die Verwaltung von mindestens acht Lichtquellen (unter den Be-zeichnungen GL_LIGHT0 bis GL_LIGHTi mit i = GL_MAX_LIGHTS). Fur jede mussfolgendes angegeben werden:

22

• die Position,

• die Farbe,

• die Abhangigkeit der Intensitat von der Entfernung,

• der Typ (ambient, diffus, spiegelnd (specular)).

Damit Lichter definiert werden konnen, ist mitgl.glEnable(GL_LIGHTING)

die Beleuchtungsrechnung zu aktivieren. Nach Definition eines LichtsGL.GL_LIGHTi muss es mit

gl.glEnable(GL.GL_LIGHTi)

eingeschaltet werden.Per Voreinstellung hat jedes Licht die Farbe Weiß und die Position (0, 0, 1).Andere Einstellungen wahlt man mit

void gl.glLightfv (int light, int pname,

float[] params, int params_offset)

Dabei sind folgende Werte fur paramName moglich:

Parameter setzt Voreinstellung

GL.GL_AMBIENT ambiente Lichtfarbe (0.0, 0.0, 0.0, 1.0)GL.GL_DIFFUSE diffuse Lichtfarbe (1.0, 1.0, 1.0, 1.0)GL.GL_SPECULAR spiegelnde Lichtfarbe (1.0, 1.0, 1.0, 1.0)GL.GL_POSITION Position in homogenen (0.0, 0.0, 1.0, 0.0)

Koordinaten; w = 0.0bedeutet ein Licht imUnendlichen (Richtungs-licht in Richtung (x, y, z))

GL.GL_SPOT_DIRECTION Richtung eines Spots (0.0, 0.0,−1.0)GL.GL_SPOT_EXPONENT Exponent eines Spots 0.0

(siehe Phong-Modell)

GL.GL_SPOT_CUTOFF halber Offnungswinkel 180◦

in [0◦, 90◦] ∪ {180◦} (d. h. gar kein Spot)GL.GL_CONSTANT Abschwachung mit der 1.0_ATTENUATION Entfernung

GL.GL_LINEAR Abschwachung mit der 0.0_ATTENUATION Entfernung

GL.GL_QUADRATIC Abschwachung mit der 0.0_ATTENUATION Entfernung

Schließlich konnen noch die Materialeigenschaften von Oberflachen eingestelltwerden mit

void gl.glMaterialfv(int face, int pname,

float[] params, int params_offset);

Dabei bestimmt face ∈ {GL.GL_FRONT, GL.GL_BACK, GL.GL_FRONT_AND_BACK},welche Seite eines Polygons von der Materialdefinition betroffen ist. FurparamName gilt folgendes:

23

Parameter setzt Voreinstellung

GL.GL_AMBIENT ambiente Farbe (0.2, 0.2, 0.2, 1.0)GL.GL_DIFFUSE diffuse Farbe (0.8, 0.8, 0.8, 1.0)GL_SPECULAR spiegelnde Farbe (0.0, 0.0, 0.0, 1.0)

GL.GL_EMISSION emittierte Farbe (0.0, 0.0, 0.0, 1.0)GL.GL_SHININESS Spiegelungsexponent 0.0

Manchmal mochte man fur ausgewahlte Materialparameter die aktuelle Zeichen-farbe ubernehmen. Hierzu dient:

void gl.glColorMaterial(int face, int mode)

Fur face bzw. mode sind die gleichen Werte zulassig wie fur face bzw.paramName in glMaterialfv. Die Voreinstellungen sind GL.GL_FRONT_AND_BACK

bzw. GL.GL_AMBIENT_AND_DIFFUSE. Die Moglichkeit, diese Funktion aus-zufuhren, wird mit gl.glEnable/gl.glDisable (GL.GL_COLOR_MATERIAL)

ein/ausgeschaltet.Damit die Beleuchtungsrechnung in JOGL richtig funktioniert, muss die Tiefen-pufferung zur Behandlung verdeckter Flachen eingeschaltet sein.JOGL ubernimmt nur die lokale Beleuchtungsanalyse; globale Analyse muss derBenutzer i. W. selbst programmieren. Allerdings kann zumindest ein globalesLicht angegeben werden mittels

void gl.glLightModelfv(int pname,

float[] params, int params_offset)

Parameter setzt Voreinstellung

GL.GL_LIGHT_MODEL

_AMBIENT

globales ambientes Licht (0.2, 0.2, 0.2, 1.0)

GL.GL_LIGHT_MODEL

_LOCAL_VIEWER

Berechnungsart fur Reflexion:bei Wert 0.0 wird −→v in nega-tiver z-Richtung genommen,unabhangig von der echtenBetrachterposition.

0.0

GL.GL_LIGHT_MODEL

_TWO_SIDE

ein- oder zweiseitige Beleuch-tungsberechnung fur Polygo-ne (0.0 = einseitig). Eckpunk-te werden stets nach den Ma-terialparametern der Vorder-seite gefarbt

0.0

Globales Licht hat den Nachteil, dass es nicht ausgeschaltet werden kann, wah-rend einzelne Lichter jederzeit an- oder ausgeschaltet werden konnen.Zu beachten ist, dass Lichtquellen stets der aktuellen Modelltransformation un-terworfen werden.Schließlich wollen wir noch die Erzeugung von Nebeleffekten besprechen. Siewird ein/ausgeschaltet mit gl.glEnable/gl.glDisable(GL.GL_FOG). Zur De-finition der Nebelart dienen

void gl.glFogfv(int pname,

float[] params, int params_offset)

void gl.glFogf(int pname, float param)

24

Die Nebelberechnung geht so: Wie stark die tatsachliche Farbe eines Objektsvom Nebel abgedampft und verandert wird, bestimmt sich durch die Koordinatez des Objekts im Kamerakoordinatensystem und einen von z abhangigen Nebel-faktor f(z) ∈ [0, 1]. Ist co die eigentliche Objektfarbe und cn die Nebelfarbe, soergibt sich die vernebelte Farbe als con(z) = f(z) · co + (1 − f(z)) · cn. Am rea-listischsten wirkt der Nebel, wenn f(z) eine Exponentialfunktion f(z) = e−dz

mit der Nebeldichte d ist. Weiter verwendet man linearen Nebelverlauf,

f(z) =ze − za

ze − za

,

wobei za den Anfang und ze das Ende des Nebelbereichs bedeutet, sowie dieGauß-Verteilung (auch doppelt exponentielle Verteilung genannt)

f(z) = e−dz2

.

Die JOGL-Parameter dazu folgen.

pname params/param Voreinstellung

GL.GL_FOG_MODE Nebelfaktor f(z)GL.GL_LINEAR, GL.GL_EXP,

GL.GL_EXP

GL.GL_EXP2

GL.GL_FOG_DENSITY Nebeldichte d 1.0GL.GL_FOG_START za 0.0GL.GL_FOG_END ze 1.0GL.GL_FOG_COLOR Nebelfarbe cn

Die Qualitat der Nebelberechnung kann noch mit

void gl.glHint(int target, int mode)

beeinflusst werden. Als target ist GL.GL_FOG_HINT zu wahlen, fur mode

∈ {GL.GL_FASTEST, GL.GL_NICEST, GL.GL_DONT_CARE} ergibt sich niedrigste,hochste, bzw. systemabhangige Qualitat.

9 Texturen

9.1 Erste Beispiele

Zunachst werden zwei Bilddateien auf zwei Quadrate aufgebracht. Das eine wirdin Frontalansicht, das andere um 45◦ nach hinten gedreht gezeigt. In Objekt-koordinaten sind beide Quadrate gleich groß; die Textur wird im zweiten Fallperspektivisch mitverzerrt. Wir geben nur die relevanten Teile des Programm-texts an.

import java.io.*; // zur Behandlung von Bilddateien als Texturen

...

import com.sun.opengl.util.texture.*;

...

public class Beispiel_Text3 {

static class WinRenderer extends GLCanvas

25

implements GLEventListener

{

private GLU glu = new GLU();

private Texture tex0,tex1;

private Texture tex0,tex1;

private Texture loadTexture(String fnm){

String fileName = fnm;

Texture tex = null;

try {

tex = TextureIO.newTexture( new File(fileName), false);

tex.setTexParameteri(GL.GL_TEXTURE_MAG_FILTER,

GL.GL_NEAREST);

tex.setTexParameteri(GL.GL_TEXTURE_MIN_FILTER,

GL.GL_NEAREST);

}

catch(Exception e)

{ System.out.println("Error loading texture " + fileName); }

return tex;

}

public void display(GLAutoDrawable gLDrawable){

GL gl = gLDrawable.getGL();

gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);

gl.glLoadIdentity();

//glu.gluLookAt(0,0,0.5,0,0,0,0,1,0);

glu.gluLookAt(2,0,4,0,0,0,0,-1,0);

gl.glEnable(GL.GL_TEXTURE_2D);

gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE,

GL.GL_DECAL);

tex0.bind();

gl.glBegin(GL.GL_QUADS);

gl.glTexCoord2f(0.0f, 0.0f);

gl.glVertex3f(-2.0f, -1.0f, 0.0f);

gl.glTexCoord2f(0.0f, 1.0f);

gl.glVertex3f(-2.0f, 1.0f, 0.0f);

gl.glTexCoord2f(1.0f, 1.0f);

gl.glVertex3f(0.0f, 1.0f, 0.0f);

gl.glTexCoord2f(1.0f, 0.0f);

gl.glVertex3f(0.0f, -1.0f, 0.0f);

gl.glEnd();

tex1.bind();

gl.glBegin(GL.GL_QUADS);

gl.glTexCoord2f(0.0f, 0.0f);

gl.glVertex3f(1.0f, -1.0f, 0.0f);

26

gl.glTexCoord2f(0.0f, 1.0f);

gl.glVertex3f(1.0f, 1.0f, 0.0f);

gl.glTexCoord2f(1.0f, 1.0f);

gl.glVertex3f(2.41421f, 1.0f, -1.41421f);

gl.glTexCoord2f(1.0f, 0.0f);

gl.glVertex3f(2.41421f, -1.0f, -1.41421f);

gl.glEnd();

gl.glFlush();

gl.glDisable(GL.GL_TEXTURE_2D);

}

public void init(GLAutoDrawable gLDrawable){

GL gl = gLDrawable.getGL();

gl.glClearColor (0, 0, 0, 0);

gl.glShadeModel(GL.GL_FLAT);

gl.glEnable(GL.GL_DEPTH_TEST);

tex0 = loadTexture("pnglogo.png");

tex1 = loadTexture("ausflugschnitt.jpg");

}

...

}

Die Initialisierung fur das Texturieren erfolgt in init(), indem die beiden Bilderin Texturen umgewandelt werden.Die Aufrufe von gl.glTexParameter*() in loadTexture spezifizieren, wie dieTextur umgebrochen wird und wie die Farben gefiltert werden, wenn die Texelund Pixel nicht zusammenpassen.In display() schaltet gl.glEnable() die Texturierung ein. gl.glTexEnv*()setzt die Zeichenart auf GL.GL_DECAL (

”Abziehbild“), so daß die texturierten

Polygone die Farben der Textur statt ihrer Originalfarbe bekommen.Dann werden die beiden Quadrate gezeichnet. Der Aufruf von bind() bedeutet,dass ab dort die jeweilige Textur verwendet wird, solange bis eine andere gebun-den wird. Man beachte, daß die Texturkoordinaten zusammen mit den Punkt-koordinaten spezifiziert werden. Der Aufruf gl.glTexCoord*() setzt die aktu-ellen Texturkoordinaten; alle nachfolgend definierten Punkte bekommen dieseTexturkoordinaten, bis gl.glTexCoord*() nochmals aufgerufen wird.Achtung: Wird das Bild nicht korrekt angezeigt, kann ein Aufruf der Formgl.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT,GL.GL_NICEST) helfen.Im zweiten Beispiel werden keine externen Bilder verwendet, sondern zwei ver-gleichsweise kleine Schachbretttexturen erzeugt und aufgebracht.

...

/* Erzeuge Schachbrettmuster */

private BufferedImage drawCheckerboard

(int bsize, int xSquares, int ySquares){

27

int w = bsize, h = bsize;

BufferedImage img =

new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);

Graphics g = img.getGraphics();

int x = 0, y = 0;

Color c1 = Color.blue;

Color c2 = Color.red;

double squareWidth = (double)w / xSquares;

double squareHeight = (double)h / ySquares;

// Fill background color

g.setColor(c1);

g.fillRect(0,0,w,h);

// Draw squares

g.setColor(c2);

for(int row = 0; row < ySquares; row++){

for(int col = 0; col < xSquares; col++){

if((row + col) % 2 == 0){

x = (int)(row * squareHeight);

y = (int)(col * squareWidth);

g.fillRect(x,y,(int)squareWidth,

(int)squareHeight);

}

}

}

return img;

}

public void init(GLAutoDrawable gLDrawable){

GL gl = gLDrawable.getGL();

gl.glClearColor (0, 0, 0, 0);

gl.glShadeModel(GL.GL_FLAT);

gl.glEnable(GL.GL_DEPTH_TEST);

// erzeuge Texturen aus Pufferbildern

BufferedImage img0,img1;

img0 = drawCheckerboard(128,10,10);

tex0 = TextureIO.newTexture(img0,false);

img1 = drawCheckerboard(64,5,5);

tex1 = TextureIO.newTexture(img1,false);

}

...

28

9.2 Texturobjekte

Ein Texturobjekt speichert Texturdaten und macht sie leicht verfugbar. Mankann damit mehrere Texturen verwalten und zu fruher geladenen Texturen zu-ruckkehren. Benutzung von Texturobjekten ist meist die effizienteste Art, Textu-ren zu verwenden, weil es fast immer schneller ist, ein existierendes Texturobjektzu binden (d.h. wiederzuverwenden), als eine Textur nochmals zu laden.Zusatzlich unterstutzen einige Implementierungen einen begrenzten Satz vonresidenten, besonders schnell benutzbaren Texturen (vgl. Abschnitt 9.13). MitTexturobjekten kann man seine am haufigsten benutzten Texturen in diesenSatz einfugen.Um Texturobjekte zu verwenden, sind folgende Schritte notig.

• Erzeuge Texturnamen.

• Erzeuge Texturobjekte und binde sie erstmals an Texturdaten, einschließ-lich der Bitmap und der Textureigenschaften.

• Unterstutzt die Implementierung residente Texturen, so prufe, ob der Spei-cher fur alle Texturobjekte ausreicht. Wenn nicht, definiere Prioritaten furdie Texturobjekte, so daß haufiger verwendete resident bleiben.

• Binde Texturobjekte erstmalig oder erneut und mache damit ihre Datenfur das eigentliche Texturieren verfugbar.

9.2.1 Benennung von Texturobjekten

Jede vorzeichenlose positive Ganzzahl kann als Texturname dienen. Um Na-menskonflikte zu vermeiden, sollte man Texturnamen konsistent nur ubergl.glGenTextures() erzeugen. Die Funktion

void gl.glGenTextures (int n, int[] texNames, int offset)

liefert n momentan unbenutzte Namen fur Texturobjekte im Feld texNames ab;sie mussen kein zusammenhangendes Intervall bilden. Die Namen werden alsbelegt markiert, aber sie erhalten Texturstatus und -dimensionalitat (1D oder2D) erst, wenn sie erstmals gebunden werden. Der Parameter offset gibt an,ab welcher Indexposition das erzeugte Feld relevant ist.Der Texturname 0 ist reserviert und wird nie als Ergebnis vongl.glGenTextures() geliefert.Die Funktion

boolean gl.glIsTexture (int texName)

pruft, ob texName momentan belegt ist. Wurde texName bereits vongl.glGenTextures() geliefert, aber noch nicht gebunden, liefert der Aufrufgl.glIsTexture(texName) das Ergebnis false.

9.2.2 Erzeugung und Verwendung von Texturobjekten

Ein und die selbe Funktion, namlich

void gl.glBindTexture (int target, int texName),

29

erzeugt und verwendet Texturobjekte. Wird der texName zum ersten Malgebunden, wird ein neues Texturobjekt erzeugt mit Voreinstellungen fur dieTextur und Textureigenschaften. Weitere Aufrufe von gl.glTexImage,gl.glTexSubImage, gl.glCopyTexImage, gl.glCopyTexSubImage,gl.glTexParameter und gl.glPrioritizeTextures speichern dann Da-ten in diesem Texturobjekt. Neben einer Textur und zugehorigen Mipmapskonnen das etwa Breite, Hohe, Randbreite, Internformat, Auflosung vonKomponenten und Textureigenschaften sein; letztere umfassen Filter furMinification und Magnification, Wiederholungsmodi, Randfarbe und Prioritat.Wird das Texturobjekt spater erneut gebunden, erhalt es als Daten den aktuellenTexturzustand, sein fruherer Wert wird uberschrieben. Außerdem wird es damitaktiv.Wird als texName der Wert 0 ubergeben, beendet JOGL die Verwendung vonTexturobjekten und greift auf eine unbenannte Standardtextur zuruck.Der Inhalt des momentan gebundenen Texturobjekts kann auch verandert wer-den; damit andern sich auch die entsprechenden Teile des aktuellen Texturzu-stands.Im vorigen Beispiel ist man nach Beendigung von display() immer noch andie Textur gebunden, die Inhalt von texName[1] ist.Man muss also darauf achten, daß man nicht durch einen Aufruf einer Textur-funktion ungewollt die Daten in der aktuellen Textur verandert.Alle zu einer Textur gehorigen Mipmaps mussen in einem einzigen Texturobjektabgelegt werden (siehe das Beispiel in Abschnitt 9.8).Beim Erstellen einer Textur kann man mit dem AufrufglPixelStorei(GL_UNPACK_ALIGNMENT, 1) notig die Texturdaten am Endejeder Texturzeile auszurichten.

9.2.3 Entsorgen von Texturobjekten

Um die Systemressourcen zu schonen, sollte man Texturen wieder freigeben,wenn sie nicht mehr benotigt werden. Die Funktion

void gl.glDeleteTextures (int n, int[] texNames, int offset)

loscht n Texturobjekte, die im Feld texNames von offset an genannt sind. Siekonnen dann (z.B. von gl.glGenTextures()) wiederverwendet werden.Wird eine momentan gebundene Textur freigegeben, ist die Wirkung wie beieinem Aufruf gl.glBindTexture(0). Versuche, unbelegte Texturnamen oder 0freizugeben, werden ohne Fehlermeldung ignoriert.

9.3 Texturspezifikation

Die Funktion

void gl.glTexImage2D (int target, int level,

int internalFormat,

int width, int height,

int border, int Format,

int type, Buffer pixels);

30

definiert eine 2D-Textur; die analoge fur 1D-Texturen ist gl.glTexImage1D().Wir beschreiben hier ihre wichtigsten Parameter; die weiteren werden in spate-ren Abschnitten behandelt. target kann die Werte GL.GL_TEXTURE_2D oderGL.GL_PROXY_TEXTURE_2D (siehe Abschnitt 9.13) annehmen. Der Parameterlevel wird verwendet, wenn die Textur verschiedene Auflosungen haben soll;bei nur einer Auflosung sollte level = 0 sein (Genaueres spater).Der Parameter internalFormat gibt an, welche der RGBA-, Luminanz- oderIntensitatswerte fur die Texel eines Bildes verwendet werden. internalFormatist eine Ganzzahl von 1 bis 4 oder eine von 38 symbolischen Konstanten (sie-he den Abschnitt Texturfunktionen fur Details), die auch fur internalFormatzulassig sind. Sie lauten

GL.GL_ALPHA, GL.GL_ALPHA4, GL.GL_ALPHA8, GL.GL_ALPHA12, Gl.GL_ALPHA16,

Gl.GL_LUMINANCE, Gl.GL_LUMINANCE4, GL.GL_LUMINANCE8,

Gl.GL_LUMINANCE12, Gl.GL_LUMINANCE16, Gl.GL_LUMINANCE_ALPHA,

GL.GL_LUMINANCE4_ALPHA4, GL.GL_LUMINANCE6_ALPHA2,

GL.GL_LUMINANCE8_ALPHA8, GL.GL_LUMINANCE12_ALPHA4,

GL.GL_LUMINANCE12_ALPHA12, GL.GL_LUMINANCE16_ALPHA16,

GL.GL_INTENSITY, GL.GL_INTENSITY4, GL.GL_INTENSITY8,

GL.GL_INTENSITY12, GL.GL_INTENSITY16,

GL.GL_RGB, GL.GL_R3_G3_B2, GL.GL_RGB4, GL.GL_RGB5, GL.GL_RGB8,

GL.GL_RGB10, GL.GL_RGB12, GL.GL_RGB16,

GL.GL_RGBA, GL.GL_RGBA2, GL.GL_RGBA4, GL.GL_RGB5_A1, GL.GL_RGBA8,

GL.GL_RGB10_A2, GL.GL_RGBA16.

Mit diesen Konstanten stellt man spezifische Komponenten und ggf. deren Auf-losung ein. Z.B. bedeutet internalFormat = GL.GL_R3_G3_B2 den Wunsch,daß in den Texelwerten 3 Bit fur die Rot-, 3 Bit fur die Grun- und 2 Bit furdie Blauinformation dienen sollen. JOGL garantiert das aber nicht, sondern istnur verpflichtet, eine Interndarstellung zu wahlen, die solchen Wunschen nahe-kommt.GL.GL_LUMINANCE, GL.GL_LUMINANCE_ALPHA, GL.GL_RGB und GL.GL_RGBA sindper Definition

”nachsichtig“, weil sie keine spezifische Auflosung verlangen.

Die Parameter width und height geben die Dimensionen der Textur an; borderspezifiziert den Rand (0 = kein Rand, 1 = Rand).Hohe und Breite mussen die Form 2m + 2b haben, wobei m eine nicht-negativeGanzzahl ist (kann fur Hohe und Breite unterschiedlich sein) und b der Wertvon border. Die Maximalgroße einer Textur hangt von der Implementierungvon JOGL ab, muss aber mindestens 64 × 64 (oder 66 × 66 mit Rand) sein.Der Parameter Format hat als mogliche Werte

GL.GL_COLOR_INDEX,

Gl.GL_RGB, GL.GL_RGBA, GL.GL_RED, GL.GL_GREEN, GL_BLUE,

GL.GL_ALPHA, GL_LUMINANCE, GL.GL_LUMINANCE_ALPHA.

Fur type sind zulassig

GL.GL_BYTE, GL.GL_UNSIGNED_BYTE, GL.GL_SHORT, GL_UNSIGNED_SHORT,

GL.GL_INT, GL.GL_UNSIGNED_INT, GL.GL_FLOAT, GL.GL_BITMAP.

31

Schließlich enthalt pixels die eigentlichen Texturdaten, und zwar fur In-neres und Rand des Bilds. Dabei ist Buffer eine abstrakte Oberklasse furdie implementierungsnahen Klassen ByteBuffer, CharBuffer, DoubleBuffer,FloatBuffer, IntBuffer, LongBuffer und ShortBuffer.Das interne Format einer Textur kann die Effizienz von Texturoperationen be-einflussen. Z.B. ist in einigen Implementierungen Texturierung mit GL.GL_RGBAschneller als mit GL.GL_RGB, weil die Farbkomponenten den Speicher besserausnutzen. Man sollte daher die Spezifika der jeweiligen Implementierung vonJOGL prufen.Vom internen Format einer Textur kann auch abhangen, wieviel Speicher sie be-notigt. Z.B. braucht eine Textur mit internem Format GL.GL_RGBA8 32 Bit proTexel, dagegen eine mit internem Format GL.GL_R3_G3_B2 nur 8 Bit pro Texel.Naturlich hat man einen Abtausch zwischen Speicherverbrauch und Farbauflo-sung.Die Anzahl von Texeln fur Breite und Hohe einer Textur (ohne Rand) mussjeweils eine Zweierpotenz sein. Hat das Originalbild andere Dimensionen, kannman sie mit

int glu.gluScaleImage

(int Format, int widthin, int heightin,

int typein, Buffer datain, int widthout,

int heightout, int typeout, Buffer dataout);

andern. Die Parameter Format, typein und typeout verhalten sich wie beigl.glDrawPixels(). Das Bild wird mittels linearer Interpolation und Box-Filterung skaliert (von der Große widthin und heightin auf widthout

und heightout) und das resultierende Bild wird nach dataout gespei-chert und zwar im GL.GL_PACK*-Speichermodus fur Pixel. Der Aufrufer vonglu.gluScaleImage() muss genugend Platz fur den Ausgabepuffer reservieren.Bei Erfolg ist der Ruckgabewert 0; ansonsten wird ein GLU-Fehlercode abgelie-fert.Auch der Bildpuffer selbst kann als Quelle fur Texturen dienen. Die Funktion

void gl.glCopyTexImage2D

(int target, int level,

int internalFormat,

int x, int y, int width, int height,

int border)

liest ein Rechteck von Pixeln aus dem aktuellen GL.GL_READ_BUFFER und ver-wendet es fur eine neue Textur. Die Pixel werden wie mit gl.glCopyPixels()verarbeitet, bis auf die Konversion am Ende. Dabei werden die Einstellungenvon gl.glPixelTransfer*() angewendet.Der Parameter target muss den Wert GL.GL_TEXTURE_2D haben. Die Para-meter level, internalFormat und border haben die selben Effekte wie beigl.glTexImage2D(). Das Texturfeld hat seine linke untere Ecke bei den Pixel-koordinaten (x, y). width und height spezifizieren die Große. Sie mussen, wieoben, die Form 2m + 2b haben.

32

9.4 Die Klassen TextureData und TextureIO

Sehr machtige Hilfsmittel, die die Verwendung von Texturen stark vereinfachen,sind die Klassen TextureData und TextureIO. Wir gehen hier nicht auf alleDetails ein; der interssierte Leser kann sich unterhttp://download.java.net/media/jogl/builds/nightly/javadoc_public/

schlau machen.

9.4.1 Die Klasse TextureIO

Diese Klasse stellt ein riesiges Arsenal von Methoden zum Einlesen undErzeugen von Texturen zur Verfugung. Wir stellen nun einige vor:

Texture TextureIO.newTexture

(BufferedImage image, boolean mipmap)

erzeugt ein neues Texturobjekt aus dem angegebenen BufferedImage mit oderohne MipMapping entsprechend dem mipmap-Parameter

Texture TextureIO.newTexture

(File file, boolean mipmap)

erzeugt ein neues Texturobjekt aus dem spezifizierten File mit oder ohneMipMapping entsprechend dem mipmap-Parameter

Texture TextureIO.newTexture

(TextureData data)

erzeugt ein neues Texturobjekt aus dem angegeben TextureData-Objekt (siehenachster Abschnitt)

Texture TextureIO.newTexture

(URL url, boolean mipmap,

String fileSuffix)

erzeugt ein neues Texturobjekt aus dem angegebenen URL mit oder ohne Mip-Mapping entsprechend dem mipmap-Parameter. fileSuffix gibt einen Hinweisauf das Format, in dem die Ressource vorliegt, z.B. .jpg oder .gif. Hat dieserParameter den Wert null, so wird eine automatische Erkennung versucht.

9.4.2 Die Klasse TextureData

Die Klasse TextureData beinhaltet Informationen uber eine Textur. Laut APIwurde sie von der Texture-Klasse getrennt, zum Beispiel die Verwendung vonTexture-Streams in Hintergrundthreads ohne OpenGL/JOGL-Kontext zu er-moglichen.Der einfachste Konstruktor hat die Form

new TextureData(int internalFormat, int pixelFormat,

boolean mipmap,

BufferedImage image)

33

Die Parameter internalFormat und pixelFormat geben das interne Formatfur die entstehende Textur an; man kann ihnen auch den Wert 0 geben, wobeidann das interne Format vom Bildtyp abgeleitet wird. Skurillerweise wird derpixelFormat-Parameter gegenwartig ignoriert. mipmap gibt an, ob automatischmipmaps erzeigt werden sollen, wahrend image eine kanonische Bedeutung hat.Aus einem solchen TextureData-Objekt kann man nun die verschiedenenEigenschaften mittels get-Methoden abfragen, so liefert zum Beispiel int

TextureData.getHeight() die Hohe in Pixeln. Mit set-Methoden kann mandann die Eigenschaften auch setzen, z.B. void TextureData.setHeight(int

height).

9.5 Ersetzen von (Teil-)Texturen

Eine Textur neu zu erzeugen kann aufwendiger sein, als eine existierende zumodifizieren. JOGL bietet Funktionen zum Ersetzen von (Teilen von) Tex-turen durch neue Information. Das ist z.B. bei Verwendung von Echtzeit-Videobildern als Texturen vorteilhaft. Dazu erzeugt man eine Textur mitgl.glTexSubImage2D(), um die Texturdaten wiederholt durch die neuen Video-bilder zu ersetzen. Außerdem haben die Teilbilder fur gl.glTexSubImage2D()

nicht die Beschrankung, daß Breite und Hohe Zweierpotenzen sein mussen, dievon Videobildern meist nicht erfullt wird.Die Funktion

void gl.glTexSubImage2D

(int target, int level, int xoffset,

int yoffset, int width, int height,

int Format, int type, Buffer pixels)

definiert eine 2D-Textur, die ein zusammenhangendes Teilgebiet (in 2D einRechteck) der aktuellen 2D-Textur ersetzt. Der Parameter target muss aufGL.GL_TEXTURE_2D gesetzt werden. Die Parameter level, Format und type

sind analog zu denen fur gl.glTexImage2D().level gibt das Detailniveau fur das Mipmapping (vgl. Abschnitt 9.8) an. Esist zwar kein Fehler, eine Breite oder Hohe von 0 zu spezifizieren, aber dasTeilbild hat dann keinen Effekt. Format und type sind selbsterklarend. DasTeilbild wird auch von den Modi betroffen, die in gl.glPixelStore*() undgl.glPixelTransfer*() gesetzt werden.pixels enthalt die Texturdaten fur das Teilbild. width und height sind dieDimensionen des Gebiets, das einen Teil der aktuellen Textur ersetzt. xoffsetund yoffset spezifizieren, wo die Teiltextur angebracht wird (mit (0, 0) alslinker unterer Ecke der Textur). Dabei darf man den Bereich der Originaltexturnicht verlassen.Auch hier kann der Bildpuffer selbst als Quelle fur Texturdaten dienen,und zwar fur eine Teiltextur. gl.glCopyTexSubImage2D() liest ein Recht-eck von Pixeln aus dem Bildpuffer und ersetzt einen Teil einer vor-handenen Textur. (gl.glCopyTexSubImage2D() ist eine Art Kreuzung ausgl.glCopyTexImage2D() und gl.glTexSubImage2D().)Die Funktion

void gl.glCopyTexSubImage2D

(int target, int level,

34

int xoffset, int yoffset, int x, int y,

int width, int height)

benutzt Daten aus dem Bildpuffer, um ein zusammenhangendes Teilge-biet der aktuellen 2D-Textur zu ersetzen. Die Pixel werden aus dem ak-tuellen GL.GL_READ_BUFFER gelesen und genau wie bei einem Aufruf vongl.glCopyPixels() verarbeitet, wieder ohne die Schlußkonversion. Dabei wer-den die Einstellungen von gl.glPixelStore*() und gl.glPixelTransfer*()

verwendet.Der Parameter target muss auf GL.GL_TEXTURE_2D gesetzt werden. level istwieder das Mipmap-Detailniveau. xoffset und yoffset spezifizieren, wo dieTeiltextur angebracht wird, width und height die Große des Teilbilds.Ein anderer Weg wird im Beispiel TeilText2.java beschritten: Hier wirdupdateSubImage-Methode der Texture-Klasse verwendet. Sie uberschreibt eineTextur in einem spezifizierten Bereich mit einem neuen Bild. Ihre einfachereSignatur ist

void Texture.updateSubImage

(TextureData data,

int mipmapLevel,

int x, int y)

data ist das TextureData-Objekt, mit dem (besser gesagt, mitt dessen zuge-horiger Textur) die alte Textur uberschrieben werden soll. mipmapLevel gibtan, welche mipmap-Ebene der Textur uberschrieben werden soll. x und y be-schreiben den Ort der linken unteren Ecke der uberschreibenden neuen Textur,gerechnet in Pixeln von der linken unteren Ecke der alten Textur aus.

9.6 1D-Texturen

Manchmal reicht eine 1D-Textur aus — z.B. wenn man texturierte Bander zeich-net, wobei sich das Muster nur in einer Richtung andert. Eine 1D-Textur verhaltsich wie eine 2D-Textur der Hohe 1 und ohne Rander oben und unten.Zu allen 2D-Textur- und Teiltexturfunktionen gibt es 1D-Gegenstucke. Um eineeinfache 1D-Textur zu erzeugen benutzt man

void gl.glTexImage1D

(int target, int level, int internalFormat,

int width, int border, int Format,

int type, Buffer pixels)

Die Parameter haben die selbe Bedeutung wie fur gl.glTexImage2D(), außerdaß das Bild nun ein 1D-Feld von Texeln ist. Wie zuvor muss die Breite 2m(oder 2m + 2, wenn ein Rand vorliegt) mit einer nicht-negativen Ganzzahl m

sein. Auch Mipmaps, Proxies (mit target = GL.GL_PROXY_TEXTURE_1D) unddie selben Filteroptionen sind verfugbar.Fur ein Beispiel mit einer 1D-Textur siehe Abschnitt 9.11.2.Um alle oder einige Texel einer 1D-Textur zu ersetzen, verwendet man

void gl.glTexSubImage1D

(int target, int level, int xoffset,

35

int width, int Format,

int type, Buffer pixels)

Der Parameter target muss auf GL.GL_TEXTURE_1D gesetzt sein. Die ubrigenParameter sind analog denen fur gl.glTexImage1D(). Um den Bildpuffer alsQuelle einer neuen oder Ersatz-1D-Textur zu nehmen, verwendet man

void gl.glCopyTexImage1D

(int target, int level,

int internalFormat, int x, int y,

int width, int border)

void gl.glCopyTexSubImage1D

(int target, int level, int xoffset,

int x, int y, int width)

9.7 Rander

Will man eine großere Textur verwenden als die gegebene Implementierung vonJOGL erlaubt, kann man mit etwas Vorsicht großere Texturen aus Kacheln mitkleineren Texturen zusammensetzen. Braucht man z.B. eine doppelt so großeTextur als erlaubt auf einem Quadrat, zeichnet man das Quadrat als vier Teil-quadrate und ladt jedes Mal eine andere Textur bevor man das nachste Stuckzeichnet.Weil zu jedem Zeitpunkt nur eine einzige Textur verfugbar ist, kann das anden Texturkanten zu Problemen fuhren, besonders bei linearer Filterung. DieTexturwerte fur Pixel an den Randern mussen mit Werten jenseits der Kanteabgeglichen werden, die idealerweise aus der Nachbartextur stammen sollten.In diesem Fall sollte man einen Texturrand definieren, dessen Texelwerte mitdenen am Rand der Nachbartextur ubereinstimmen. Dabei ist zu beachten, daßeine Textur bis zu acht Nachbarn haben kann (vier an den Seiten und vier anden Ecken). Fur Texturen, die selbst am Rand des Gesamtgebiets aufgebrachtwerden, muss man uberlegen, welche Randwerte sinnvoll sind.

9.8 Detailniveaus (Mipmaps)

JOGL unterstutzt Mipmaps und berechnet automatisch, welche Textur gemaßder Pixelgroße des texturierten Objekts gewahlt werden muss. Mipmaps erfor-dern zwar einigen Rechen- und Speicheraufwand; ohne sie konnten aber Textu-ren, die auf zu kleine Objekte aufgebracht werden, flackern oder blitzen, wennsich die Objekte bewegen.Mipmaps funktionieren in JOGL nur, wenn die Texturen fur alle Zweierpotenzenzwischen der großten benotigten Auflosung und 1 bereitgestellt werden. Ist z.B.die hochste Auflosung 64×16, muss man auch Texturen der Auflosungen 32×8,16× 4, 8× 2, 4× 1, 2× 1 und 1× 1 angeben. Die kleineren werden ublicherweisedurch Filtern und Mittelwertbildung aus der großten erzeugt.Um diese Texturen auszuwahlen, ruft man gl.glTexImage2D() fur jede Auf-losung der Textur mit unterschiedlichen Werten fur level, width, height undBild auf. Beginnend mit 0 definiert level, welche Textur in der Folge gemeintist; im vorigen Beispiel wurde die großte Textur mit Auflosung 64 × 16 durchlevel = 0 gewahlt, die mit 32 × 8 durch level = 1 und so weiter.

36

Damit die Mipmaps funktionieren, muss weiter eine der Filtermethoden ausAbschnitt 9.9 verwendet werden.Das Beispiel MipMap.java zeigt die Verwendung einer Folge von sechs Mipmapsder Auflosungen 32 × 32 bis 1 × 1. Das Programm zeichnet ein Rechteck, dassich vom Vordergrund bis weit in die Ferne erstreckt und schließlich ein einemPunkt verschwindet. Mit einem beliebigen Tastendruck lasst sich zwischen einerTexturierung mit und ohne MipMaping umschalten.JOGL enthalt zwei Funktionen zur Erstellung von Mipmaps. Die Textur mitder hochsten Auflosung sei bereits erstellt. Dann erzeugen

int glu.gluBuild1DMipmaps

(int target, int Komponenten, int width,

int Format, int type, void *daten)

int glu.gluBuild2DMipmaps

(int target, int Komponenten, int width,

int height, int Format, int type, Buffer daten)

die Pyramide von Mipmaps bis hinunter zur Auflosung 1 × 1 bzw. 1 (fur 1D-Texturen). Sind die Dimensionen der Ausgangstextur keine Zweierpotenzen, ska-liert glu.gluBuild*DMipmaps() das Bild auf die nachstgelegene Zweierpotenz.Als Ergebnis wird 0 abgeliefert, wenn alle Mipmaps korrekt erzeugt werdenkonnten, ansonsten ein GLU-Fehlercode.

9.9 Filterung

JOGL bietet eine Reihe von Filteroptionen mit unterschiedlichem Abtausch zwi-schen Schnelligkeit und Bildqualitat. Dabei konnen die Filtermethoden fur Ma-gnification und Minification unabhangig gewahlt werden.In einigen Fallen ist nicht klar, ob Magnification oder Minification notig ist.Muß eine Mipmap sowohl in x- wie y-Richtung gestreckt bzw. gestaucht wer-den, dann ist Magnification bzw. Minification zu verwendet. Wird sie aber ineiner Richtung gestreckt und in der anderen gestaucht, trifft JOGL eine Aus-wahl zwischen Magnification und Minification, die in den meisten Fallen dasbestmogliche Ergebnis liefert. Am besten ist es aber, solche Situationen zu ver-meiden.Die folgenden Programmzeilen geben Beispiele, wie mangl.glTexParameter*() zum Spezifizieren von Magnification- und Minification-Filtermethoden verwendet:

gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER,

GL.GL_NEAREST);

gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER,

GL.GL_NEAREST);

Das erste Argument von gl.glTexParameter*() ist entwederGL.GL_TEXTURE_2D oder GL.GL_TEXTURE_1D, je nachdem, ob man mit2D- oder 1D-Texturen arbeitet. Fur unsere Zwecke ist das zweite Argumententweder GL.GL_TEXTURE_MAG_FILTER oder GL.GL_TEXTURE_MIN_FILTER undgibt an, ob die Filtermethode fur Magnification oder Minification spezifiziertwird. Das dritte Argument spezifiziert die Filtermethode; die nachfolgendeTabelle gibt die moglichen Werte an.

37

Parameter Werte

GL.GL_TEXTURE_MAG_FILTER GL.GL_NEAREST oder GL.GL_LINEARGL.GL_TEXTURE_MIN_FILTER GL.GL_NEAREST, GL.GL_LINEAR,

GL.GL_NEAREST_MIPMAP_NEAREST,GL.GL_NEAREST_MIPMAP_LINEAR,GL.GL_LINEAR_MIPMAP_NEAREST,GL.GL_LINEAR_MIPMAP_LINEAR

Bei GL.GL_NEAREST wird das Texel mit Koordinaten am nachsten bei der Pixel-mitte fur Magnification und Minification herangezogen. Das kann zu (schlim-men) Treppeneffekten fuhren. Bei GL.GL_LINEAR wird eine Linearkombinationdes 2×2-Felds von Texeln am nachsten bei der Pixelmitte verwendet. Am Randhangt es davon ab, ob GL.GL_REPEAT oder GL.GL_CLAMP eingestellt ist und einTexturrand definiert ist.GL.GL_NEAREST ist weniger rechenaufwendig als GL.GL_LINEAR, aberGL.GL_LINEAR liefert glattere Ergebnisse.Bei Magnification wird immer die großte Textur (level=0) verwendet, auchwenn Mipmaps definiert sind. Bei Minification kann man eine Filtermetho-de wahlen, die die geeignetsten ein oder zwei Mipmaps verwendet. (BeiGL.GL_NEAREST oder GL.GL_LINEAR und Minification wird allerdings immer diegroßte Textur verwendet.)Wie in obiger Tabelle gezeigt, sind bei Minification von Mipmaps vier zusatz-liche Filtermethoden verfugbar. Innerhalb einer einzelnen Mipmap wahlt mandie nachsten Texelwerte mit GL.GL_NEAREST_MIPMAP_NEAREST oder man inter-poliert linear mit GL.GL_LINEAR_MIPMAP_NEAREST. Verwendung des nachstenTexels ist schneller, liefert aber weniger gute Ergebnisse.Welche spezielle Mipmap gewahlt wird, hangt vom Betrag der Minificati-on ab. Es gibt dazu jeweils Schwellenwerte, wann von einer Mipmap zurnachsten umgeschaltet wird. Plotzliche Ubergange werden durch die Wer-te GL.GL_NEAREST_MIPMAP_LINEAR oder GL.GL_LINEAR_MIPMAP_LINEAR ver-mieden; damit wird linear zwischen den Texelwerten der zwei besten Wahl-moglichkeiten interpoliert. GL.GL_NEAREST_MIPMAP_LINEAR wahlt die nachst-gelegenen Texel in beiden Mipmaps und interpoliert linear zwischen die-sen zwei Werten. GL.GL_LINEAR_MIPMAP_LINEAR benutzt lineare Interpolati-on, um die Werte in zwei Mipmaps zu berechnen, und interpoliert dann li-near zwischen diesen zwei Werten. Erwartungsgemaß liefert im allgemeinenGL.GL_LINEAR_MIPMAP_LINEAR die glattesten Ergebnisse, ist aber am rechen-aufwendigsten.

9.10 Texturfunktionen

In allen bisherigen Beispielen wurden die Texturwerte direkt als Farben aufdie texturierte Flache aufgetragen. Man kann sie aber auch benutzen, um dieFarbe der Flache ohne Texturierung zu modulieren oder die Texturfarbe mit derFlachenfarbe zu mischen. Dazu verwendet man die Funktionen

void gl.glTexEnv{if}(int target, int pname, TYPE param)

void gl.glTexEnv{if}v(int target, int pname, BTYPE param)

38

Dabei steht TYPE fur int bzw. float und BTYPE fur IntBuffer bzw.FloatBuffer. Es gibt auch Uberladungen mit Feld/Offset statt Buffer-parametern. Schließlich muss target = GL.GL_TEXTURE_ENV sein.Hat pname den Wert GL.GL_TEXTURE_ENV_MODE, so hat param die moglichenWerte GL.GL_DECAL, GL.GL_REPLACE, GL.GL_MODULATE oder GL.GL_BLEND, dieangeben, wie die Texturwerte mit den Flachenfarbwerten kombiniert werden.Hat pname den Wert GL.GL_TEXTURE_ENV_COLOR, so ist param ein Feld, daseinen RGBA-Wert darstellt.Die Funktionen wirken nur, wenn GL.GL_BLEND eingestellt ist.Die Kombination hangt auch vom Internformat ab, das als drittes Argumentvon gl.glTexImage*D() angegeben werden muss. Dies ist in den nachfolgendenTabellen dargestellt.Die sechs internen Grundformate sind GL.GL_ALPHA (A), Gl.GL_LUMINANCE (L),GL.GL_LUMINANCE_ALPHA (L und A), GL.GL_INTENSITY (I), GL.GL_RGB (C) undGL.GL_RGBA (C und A) (die Buchstaben in Klammern stehen fur ihre Werte inden Tabellen). Weitere Internformate spezifizieren Auflosungen der Texturkom-ponenten und konnen auf eines dieser sechs internen Grundformate zuruckge-fuhrt werden.In den Tabellen bedeutet ein Index t einen Texturwert, f einen Flachenwert,c den uber GL.GL_TEXTURE_ENV_COLOR zugewiesenen Wert; Großen ohne Indexbezeichnen die schließlich errechneten Werte.

internes Grundformat Ersetzungsfunktion Modulationsfunktion

GL.GL_ALPHA C = Cf , A = At C = Cf , A = AfAt

GL.GL_LUMINANCE C = Lt, A = Af C = CfLt, A = Af

GL.GL_LUMINANCE_ALPHA C = Lt, A = At C = CfLt, A = AfAt

GL.GL_INTENSITY C = Es, A = Es C = CfIt, A = AfIt

GL.GL_RGB C = Ct, A = Af C = CfCt, A = Af

GL.GL_RGBA C = Ct, A = At C = CfCt, A = AfAt

internes Grundformat Deckfunktion Mischfunktion

GL.GL_ALPHA undefined C = Cf

A = AfAt

GL.GL_LUMINANCE undefined C = Cf (1 − Lt) + CcLt

A = Af

GL.GL_LUMINANCE undefined C = Cf (1 − Lt) + CcLt

_ALPHA A = AfAt

GL.GL_INTENSITY undefined C = Cf (1 − Es) + CcIt

A = Af (1 − Es) + AcIt

GL.GL_RGB C = Ct, A = Af C = Cf (1 − Ct) + CcCt

A = Af

GL.GL_RGBA C = Cf (1 − At) + CtAt C = Cf (1 − Ct) + CcCt

A = Af A = AfAt

Die Deckfunktion wird verwendet, wenn eine undurchsichtige Textur auf einObjekt aufgebracht werden soll; die Ersetzungsfunktion ist ahnlich.Ist bei Modulation das interne Grundformat GL.GL_INTENSITY,GL.GL_LUMINANCE oder GL.GL_LUMINANCE_ALPHA, so werden die Farbwer-te mit den selben Werten multipliziert; damit moduliert die Textur zwischender Flachenfarbe (wenn die Luminanz oder Intensitat 1 ist) und schwarz (wenn

39

sie 0 ist). Fur die Internformate GL.GL_RGB und GL.GL_RGBA werden die Fla-chenfarbkomponenten mit entsprechenden (moglicherweise unterschiedlichen)Werten in der Textur multipliziert. Modulation ist im Zusammenhang mitBeleuchtung vorteilhaft, weil die beleuchtete Polygonfarbe benutzt werdenkann, um die Texturfarbe abzuschwachen. Weiße spiegelnde Polygone werdenoft verwendet, um beleuchtete texturierte Objekte darzustellen, wobei dieTextur die diffuse Farbe liefert.Die Mischfunktion ist die einzige, die die durch GL.GL_TEXTURE_ENV_COLOR

spezifizierte Farbe verwendet. Deren Luminanz, Intensitat oder Farbwertwird in etwa wie ein Alpha-Werte benutzt, um die Flachenfarbe mitGL.GL_TEXTURE_ENV_COLOR zu mischen. Genaueres hierzu spater.

9.11 Zuweisen von Texturkoordinaten

Texturkoordinaten konnen aus ein, zwei, drei oder vier Koordinaten bestehen,die meist s, t, r und q heißen um sie von den Objektkoordinaten x, y, z und w

zu unterscheiden.Fur 1D-Texturen benutzt man nur s, fur 2D-Texturen s und t. Die q-Koordinatehat, wie w, typischerweise den Werte 1 und kann zum Definieren homogenerKoordinaten benutzt werden (Genaueres im Abschnitt 9.13).Die Funktion gl.glTexCoord*() zum Spezifizieren von Texturkoordinaten istahnlich zu gl.glVertex*(), gl.glColor*() und gl.glNormal*(). Sie hat ana-loge Varianten und wird ebenfalls zwischen gl.glBegin() und gl.glEnd() be-nutzt. Ublicherweise liegen die Werte von Texturkoordinaten zwischen 0 und1. Werte außerhalb diese Bereichs fuhren zu Effekten, die in Abschnitt 9.11.1beschrieben werden. Die Funktionalitaten sind

void gl.glTexCoord{1234}{sifd}(TYPE coords);

void gl.glTexCoord{1234}{sifd}v(BTYPE coords)

Fur letztere gibt es auch wieder Feld/Offset-Varianten.Nach einem Aufruf dieser Funktionen werden allen durch gl.glVertex*() er-zeugten Punkten die aktuellen Texturkoordinaten zugeordnet, bis wieder einegl.glTexCoord-Funktion aufgerufen wird.Texturkoordinaten werden vor ihrer Verwendung immer mit der aktuellen 4×4-Texturmatrix multipliziert (fur Genaueres siehe Abschnitt 9.13).

9.11.1 Wiederholen und Strecken von Texturen

Man kann auch Texturkoordinaten außerhalb dem Intervall [0, 1] verwenden;dann wird die Textur entweder wiederholt oder in die entsprechende Richtunggestreckt.Beim Wiederholen wird einfach der Ganzzahlteil der Texturkoordinaten gestri-chen, um die Texelwerte fur jeden Punkt abzulesen. Hat man z.B. ein Rechteckund verwendet Texturkoordinaten von 0 bis 10 in jeder Richtung, werden 100Exemplare der Textur kachelartig auf das Rechteck aufgebracht. Meist ist fureinen nahtlosen Verlauf erwunscht, daß die Texel am oberen und unteren undam linken und rechten Rand ubereinstimmen.Beim Strecken werden fur Koordinaten großer als 1 bzw. kleiner als 0 die Tex-turwerte fur 1 bzw. 0 abgelesen und damit die Textur an den Randern konstantauf die gewunschte Breite gedehnt. Verwendet man z.B. Texturkoordinaten von

40

0 bis 10 in jeder Richtung, wird ein Texturexemplar links unten im Rechteckplaziert und nach rechts und oben konstant gedehnt. Wahlt man GL.GL_LINEAR

als Filtermethode, entsteht eine gleich gewichtete Kombination aus Randfarbeund Texturfarbe wie folgt.

• Beim Wiederholen bezieht das 2 × 2-Feld die jeweils andere Kante derTextur ein. Also wird zwischen den Texeln an der rechten und linken Kantegemittelt, ebenso zwischen denen an der oberen und unteren Kante.

• Ist explizit ein Texturrand definiert, werden dessen Texel beim Mitteln be-nutzt. Ansonsten wird GL.GL_TEXTURE_BORDER_COLOR verwendet. (Wahltman GL.GL_NEAREST als Filtermethode, wird die Randfarbe vollig igno-riert.)

Beim Strecken kann man vermeiden, daß der Rest der Flache von der Texturbetroffen wird. Dazu verwendet man Alpha-Werte von 0 fur die Kanten (oderRander, wenn sie spezifiziert sind) der Textur. Die Deck-Texturfunktion verwen-det direkt die Alpha-Werte der Textur. Bei Verwendung der anderen Textur-funktionen kann es notig werden, mit guten Quell- und Zielfaktoren zu mischen(Genaueres siehe spater).Die Effekte werden ausgelost mit

void gl.glTexParameter{if}

(int target, int pname, TYPE param)

void gl.glTexParameter{if}v

(int target, int pname, BTYPE param)

Fur letztere gibt es auch wieder Feld/Offset-Varianten.Der Parameter target ist entweder GL.GL_TEXTURE_2D oderGL.GL_TEXTURE_1D. Die moglichen Werte fur pname und param gibt dienachfolgende Tabelle an.

Parameter Werte

GL.GL_TEXTURE_WRAP_S GL.GL_CLAMP, GL.GL_REPEATGL.GL_TEXTURE_WRAP_T GL.GL_CLAMP, GL.GL_REPEATGL.GL_TEXTURE_MAG_FILTER GL.GL_NEAREST, GL.GL_LINEARGL.GL_TEXTURE_MIN_FILTER GL.GL_NEAREST, GL.GL_LINEAR,

GL.GL_NEAREST_MIPMAP_NEAREST,GL.GL_NEAREST_MIPMAP_LINEAR,GL.GL_LINEAR_MIPMAP_NEAREST,GL.GL_LINEAR_MIPMAP_LINEAR

GL.GL_TEXTURE_BORDER_COLOR beliebige vier Werte in [0, 1]GL.GL_TEXTURE_PRIORITY [0, 1] fur das aktuelle Texturobjekt

Bei GL.GL_REPEAT wird die Textur s- und t-Richtung wiederholt, wenn manfolgende Aufrufe verwendet:

gl.glTexParameteri (GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S,

GL.GL_REPEAT);

gl.glTexParameteri (GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T,

GL.GL_REPEAT);

41

Bei GL.GL_CLAMP fur jede Richtung entsteht ein Exemplar der Textur, dasin Streifen auslauft, wahrend die rechte obere Ecke zu einem großen Viereckwachst. Man kann auch in einer Richtung strecken und in der anderen wieder-holen.

9.11.2 Automatische Erzeugung von Texturkoordinaten

Mit Texturen kann man Hohenlinien erzeugen oder die Spiegelungen einer be-liebigen Umgebung an einer glanzenden Kugel modellieren. Um diese Effekte zuerzielen, kann man JOGL die Texturkoordinaten automatisch erzeugen lassen,statt sie explizit mit gl.glTexCoord*() zu definieren. Dazu dienen die Funk-tionen

void gl.glTexGen{ifd}(int coord, int pname, TYPE param)

void gl.glTexGen{ifd}v(int coord, int pname, BTYPE param)

Fur letztere gibt es auch wieder Feld/Offset-Varianten.Der Parameter coord muss GL.GL_S, GL.GL_T, GL.GL_R oder GL.GL_Q seinund gibt an, ob die Texturkoordinate s, t, r oder q erzeugt werden soll.Der Parameter pname ist GL.GL_TEXTURE_GEN_MODE, GL.GL_OBJECT_PLANE oderGl.GL_EYE_PLANE. Ist er GL.GL_TEXTURE_GEN_MODE, muss param eine Ganzzahlsein (oder, in der Feldversion der Funktion, auf eine Ganzzahl zeigen), die ent-weder GL.GL_OBJECT_LINEAR, GL.GL_EYE_LINEAR oder GL.GL_SPHERE_MAP ist.Diese Konstanten bestimmen, auf welche Art die Texturkoordinate bestimmtwird. Bei einem der anderen moglichen Werte fur pname ist param (fur die Feld-version) ein Zeiger auf ein Feld von Werten, die Parameter fur die Texturerzeu-gung spezifizieren.Die unterschiedlichen Methoden zur Texturkoordinatenerzeugung haben unter-schiedliche Verwendung. Soll eine Textur an einem bewegten Objekt dauerhaftbefestigt sein, spezifiziert man die Bezugsebene am besten in Objektkoordina-ten. Also wurde man GL.GL_OBJECT_LINEAR benutzen, um eine Holzkugel aufeine Tischplatte zu legen. Um dynamische Hohenlinien auf bewegten Objek-ten zu erzeugen, spezifiziert man die Bezugsebene am besten in Kamerakoor-dinaten mit GL.GL_EYE_LINEAR. Diese Option ist z.B. bei der Visualisierungvon Tiefenbohrungen nutzlich. Wenn der Bohrer tiefer eindringt, kann seineFarbe geandert werden, um die verschiedenen Gesteinsschichten darzustellen.GL.GL_SPHERE_MAP wird hauptsachlich fur Environment-Mapping benutzt (sie-he Abschnitt 9.12).

9.11.3 Erzeugung von Hohenlinien

Zu diesem Abschnitt gehort das Beispiel Hohenlinien.java.Sind GL.GL_TEXTURE_GEN_MODE und GL.GL_OBJECT_LINEAR spezifiziert, ist dieErzeugungsfunktion eine Linearkombination der Objektkoordinaten des Punkts(xo, yo, zo, wo):

erzeugte Koordinate = p1xo + p2yo + p3zo + p4wo .

Die Werte p1, . . . , p4 werden im param-Argument an gl.glTexGen*v() uber-geben, wobei pname auf GL.GL_OBJECT_PLANE gesetzt ist. Werden p1, . . . , p4

korrekt normalisiert, ergibt diese Funktion den Abstand des Punkts von einer

42

Ebene. Sind z.B. p2 = p3 = p4 = 0 und p1 = 1, liefert die Funktion den Abstandzwischen dem Punkt und der Ebene x = 0. Der Abstand ist positiv auf der einenSeite der Ebene, negativ auf der anderen und 0, wenn der Punkt auf der Ebeneliegt.Im folgenden Beispiel werden Hohenlinien in gleichen Abstanden auf eine Tee-kanne gezeichnet; sie geben den jeweiligen Abstand von der Ebene x = 0 an.Die Koeffizienten fur die Ebene x = 0 sind in diesem Feld:

static float xequalzero [] = {1.0, 0.0, 0.0, 0.0};

Da nur eine Eigenschaft dargestellt wird (der Abstand von der Ebene), genugteine 1D-Textur. Sie ist eigentlich konstant grun, weist aber an aquidistantenStellen eine rote Unterbrechung auf. Da die Teekanne auf der xy-Ebene steht,verlaufen die Hohenlinien senkrecht zur Grundflache. Druckt man die Taste s,andern sich die Parameter der Bezugsebene zu

static float slanted[] = {1.0, 1.0, 1.0, 0.0};

und die Hohenlinien sind parallel zur Ebene x + y + z = 0, die die Teekanneschrag durchschneidet. Die ursprungliche Ebene stellt man durch Drucken vonx wieder her. Analog konnen durch Drucken von y und z noch andere Ebenengewahlt werden.GL.GL_REPEAT sorgt fur Wiederholung der Hohenlinien.Mit gl.glEnable(GL.GL_TEXTURE_GEN_S) wird die Texturkoordinatenerzeu-gung fur die s-Koordinate eingeschaltet usw.; mit gl.glDisable() wird siewieder ausgeschaltet.Bei GL.GL_OBJECT_LINEAR werden die Texturkoordinaten in Weltkoordinatenberechnet. Im Beispiel wird dies anfangs benutzt; daher bleiben die Hohenliniensenkrecht zur Grundflache der Teekanne, unabhangig von Drehungen oder vomAugpunkt. Druckt man aber die Taste e, andert sich die Art der Koordinatener-zeugung zu GL.GL_EYE_LINEAR, und die Hohenlinien werden im Kamerakoordi-natensystem berechnet. (Die Taste o stellt GL.GL_OBJECT_LINEAR wieder her.)Ist die Bezugsebene x = 0, ergibt sich eine Teekanne mit roten Streifen paral-lel zur yz-Ebene, vom Augpunkt aus gesehen. Mathematisch wird der Vektor(p1 p2 p3 p4) mit der Inversen der Modellmatrix multipliziert, um die Werte furdie Abstandsberechnung von der Ebene zu erhalten. Die Texturkoordinate wirdmit der folgenden Funktion erzeugt:

erzeugt Koordinate = p′1xe + p′

2ye + p′

3ze + p′

4we

wobei (p′1

p′2

p′3

p′4) = (p1 p2 p3 p4)M

−1 und (xe, ye, ze, we) die Augkoordi-naten des Punkts sind, der als p1, . . . , p4 an gl.glTexGen*() uber das param-Argument mit pname = GL.GL_EYE_PLANE ubergeben wird.In diesem Beispiel wurde eine einzige Texturkoordinate benutzt, um die Ho-henlinien zu erzeugen. Die s- und t-Koordinaten konnen aber auch unabhangigerzeugt werden; sie geben dann die Abstande von zwei unterschiedlichen Ebenenan. Mit einer geeigneten 2D-Textur konnen die resultierenden zwei Mengen vonHohenlinien gleichzeitig dargestellt werden. Man kann sogar eine Koordinatemit GL.GL_OBJECT_LINEAR und die andere mit Gl.GL_EYE_LINEAR berechnen.

43

9.11.4 Texturierung von Quadriken

Wie im Beispiel Quadrik_Textur.java gezeigt wird, bietet JOGL die Mog-lichkeit, problemlos Texturen auf Quadriken aufzutragen. Hierzu erzeugt maneinfach ein GLUquadrik-Objekt und bindet mit dem Befehl

GLU.gluQuadricTexture(GLUquadric, boolean)

die vorher mit Texture.bind() verwendete Textur an das GLUquadric-Objekt.Spater kann man aus der GLUquadric dann einen Zylinder, einen Kegel oder wieim Beispiel eine Kugel entstehen lassen. Die Berechnung der Texturkoordinatenwird von JOGL automatisch ubernommen.

9.12 Environment Mapping

Zu diesem Abschnitt liefert SpiegelTorus.zip ein Beispiel.Fur diese Technik muss man eine geeignete Textur definieren und dann dieTexturkoordinaten von JOGL erzeugen lassen. Die verwendete Approximationberuht auf der Annahme, daß die Objekte in der Umgebung weit von den Fla-chen des spiegelnden Objekts sind, d.h. daß ein kleines Objekt in einem großenRaum betrachtet wird.Um eine korrekte Umgebungstextur zu erstellen, mußte man in dieser Umgebungeine große versilberte Kugel aufstellen und sie aus unendlichem Abstand miteiner Linse unendlicher Brennweite fotografieren. Um das zu approximieren,kann man ein Foto der Umgebung mit einer Weitwinkel- oder Fischaugenkameraverwenden.Hat man so eine Textur erzeugt, ruft man den Environment-Mapping-Algorith-mus von JOGL auf. Er bestimmt zu jedem Punkt des dargestellten Objekts denPunkt auf der Kugeloberflache mit der selben Tangentenebene und farbt ihn mitder dort sichtbaren Farbe ein. Um die Texturkoordinaten fur das EnvironmentMapping automatisch zu erzeugen, verwendet man folgende Aufrufe:

gl.glTexGeni(GL.GL_S, GL.GL_TEXTURE_GEN_MODE,

GL.GL_SPHERE_MAP);

gl.glTexGeni(GL.GL_T, GL.GL_TEXTURE_GEN_MODE,

GL.GL_SPHERE_MAP);

gl.glEnable(GL.GL_TEXTURE_GEN_S);

gl.glEnable(GL.GL_TEXTURE_GEN_T);

Die Konstante GL.GL_SPHERE_MAP erzeugt die richtigen Texturkoordinaten furdas Environment Mapping. Wie angegeben, muss man sie jeweils fur s- und t-Richtung spezifizieren. Man braucht aber keine weiteren Parameter anzugeben.

9.13 Weiteres Zubehor

9.13.1 Funktionen fur residente Texturen

Um zu prufen, ob eine Textur momentan resident ist, bindet man zunachst ihrObjekt und liest dann mit gl.glGetTexParameter*v() die mit dem ZustandGL.GL_TEXTURE_RESIDENT assoziierten Werte ab. Will man die Residenz von n

Texturen im Feld texNames prufen, verwendet man

44

boolean gl.glAreTexturesResident

(int n, IntBuffer texNames,

ByteBuffer residences)

Fur letztere gibt es auch wieder Feld/Offset-Varianten. Das Ergebnis wird furjede genannte Textur im Feld residences abgeliefert. Außerdem wird insgesamtnoch GL.GL_TRUE, wenn alle genannten Texturen resident sind und GL.GL_FALSE

sonst.Die Funktion

void gl.glPrioritizeTextures

(int n, IntBuffer texNames,

FloatBuffer priorities)

weist den n im Feld texNames genannten Texturobjekten die Prioritaten im Feldpriorities zu. Deren Werte werden auf das Intervall [0, 1] abgeschnitten. Nullist die niedrigste Prioritat, Eins die hochste. Texturen mit Prioritat Null sindmit geringster Wahrscheinlichkeit resident. Naturlich gibt es auch wieder eineFeld/Offset-Variante.Fur weitere Einzelheiten sei auf die Literatur verwiesen.

9.13.2 Texturproxies

Bei Verwendung von Texturen muss man stets auf deren Große achten, umSpeicherplatzprobleme zu vermeiden. Daher gibt es einen speziellen Texturstell-vertreter (Proxy) target, mit dem man prufen kann, ob genugend Ressourcenvorhanden sind. Die Funktion

gl.glGetIntegerv (GL.GL_MAX_TEXTURE_SIZE,...)

liefert die großte Dimension (Breite oder Hohe, ohne Rand) einer Textur, ty-pischerweise die Große der großten quadratischen Textur, die unterstutzt wird.Allerdings berucksichtigt GL.GL_MAX_TEXTURE_SIZE nicht den Effekt des inter-nen Formats einer Textur. Eine Textur, die Texel im GL.GL_RGBA16-Formatspeichert, kann 64 Bit pro Texel brauchen, so daß sie 16mal kleiner sein mussals eine Textur im GL.GL_LUMINANCE4-Format. (Auch Bilder mit Rand und Mip-maps konnen den verfugbaren Speicher weiter reduzieren.)Zur Benutzung des Proxies verwendet man den Aufruf

gl.glTexImage2D(GL.GL_PROXY_TEXTURE_2D, internalFormat,

level, width, height, border, Format, type, null)

Fur 1D-Texturen verwendet man die entsprechenden 1D-Funktionen und Kon-stanten.Ist der Proxy erstellt, fragt man mit gl.glGetTexLevelParameter*() die Tex-turzustandsvariablen ab. Sind nicht genugend Ressourcen vorhanden, werdendie Texturzustandsvariablen fur Breite, Hohe, Randbreite und Komponenten-auflosungen auf 0 gesetzt.Die Funktion

void gl.glGetTexLevelParameter{if}v(int target, int level,

int pname, BTYPE params)

45

liefert in params Texturparameterwerte fur eine spezifisches Detailniveau, spe-zifiziert als level. target definiert die Zieltextur und hat als mogliche Werte

GL.GL_TEXTURE_1D, GL.GL_TEXTURE_2D,

GL.GL_PROXY_TEXTURE_1D, GL.GL_PROXY_TEXTURE_2D.

Zulassige Werte fur pname sind

GL.GL_TEXTURE_WIDTH, GL.GL_TEXTURE_HEIGHT, GL.GL_TEXTURE_BORDER,

GL.GL_TEXTURE_INTERNAL_FORMAT,

GL.GL_TEXTURE_RED_SIZE, GL.GL_TEXTURE_GREEN_SIZE,

GL.GL_TEXTURE_BLUE_SIZE,

GL.GL_TEXTURE_ALPHA_SIZE, GL.GL_TEXTURE_LUMINANCE_SIZE,

GL.GL_TEXTURE_INTENSITY_SIZE.

Auch GL.GL_TEXTURE_COMPONENTS ist fur pname zulassig, aber nur zur Ab-wartskompatibilitat mit JOGL 1.0 — fur JOGL 1.1 ist der empfohlene WertGL.GL_TEXTURE_INTERNAL_FORMAT.Das folgende Programmstuck zeigt die Verwendung eines Proxies um zu prufen,ob genugend Platz fur eine Textur mit 64×64 RGBA-Texeln von 8 Bit Auflosungvorhanden ist. Im Erfolgsfall speichert gl.glGetTexLevelParameteriv() dasInternformat (hier GL.GL_RGBA8) in der Variablen Format:

int Format;

gl.glTexImage2D(GL.GL_PROXY_TEXTURE_2D, 0, Gl.GL_RGBA8,

64, 64, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE,

NULL);

gl.glGetTexLevelParameteriv(GL.GL_PROXY_TEXTURE_2D, 0,

GL.GL_TEXTURE_INTERNAL_FORMAT,

&Format);

Achtung: Proxies unterliegen einer wichtigen Beschrankung. Der Proxy sagtzwar aus, ob genugend Platz fur die jeweilige Textur da ist, aber nur wenn alleTexturressourcen verfugbar sind (mit anderen Worten, wenn nur diese Textur daist). Verbrauchen auch andere Texturen Ressourcen, so kann die Proxyabfragezwar eine positive Antwort liefern, aber trotzdem nicht genugend Platz da sein,um die Textur resident zu halten.

9.13.3 Der Texturmatrix-Keller

Texturkoordinaten werden vor dem eigentlichen Texturieren immer mit einer4 × 4-Matrix multipliziert. Per Voreinstellung ist das die Identitatsmatrix, sodaß explizite oder automatisch erzeugte Texturkoordinaten zunachst unveran-dert bleiben. Durch Anderung der Texturmatrix wahrend des Auffrischens kannman aber die Textur auf der betreffenden Flache verschieben, drehen, streckenoder stauchen oder Kombinationen davon anwenden. Da die Texturmatrix einebeliebige 4 × 4-Matrix sein darf, kann man sogar Effekte wie perspektivischeVerzerrung erreichen. Nach Transformation werden die Texturkoordinaten alshomogene Koordinaten interpretiert.Wie bei den anderen Matrixarten ist die Texturmatrix das oberste Elementeines Kellers mit Platz fur mindestens zwei Eintrage. Alle Standardfunk-tionen zur Matrixmanipulation, wie gl.glPushMatrix(), gl.glPopMatrix(),

46

gl.glLoadMatrix(), gl.glMultMatrix() sowie gl.glRotate*() usw. konnenauch auf die Texturmatrix angewendet werden. Um die aktuelle Texturmatrixzu modifizieren, muss der Matrixmodus mit

gl.glMatrixMode(GL.GL_TEXTURE);

umgeschaltet werden.

9.13.4 Die q-Koordinate

Die q-Koordinate benutzt man in Fallen, wo mehr als eine Projektion benotigtwird. Als Beispiel diene die Modellierung eines Spotlichts mit ungleichmaßigerLichtverteilung, etwa heller in der Mitte oder oval aufgrund von Flugelklappenoder Linsen, die die Form des Lichtbundels verandern. Man kann die Einwirkungeines solchen Lichts auf eine ebene Flache mit einer Textur simulieren, die derForm und Intensitat des Lichts entspricht, und die dann perspektivisch auf dieFlache projiziert wird. Eine zweite perspektivische Projektion ist notig, weil derBetrachter die Szene von einem unterschiedlichen Standpunkt aus sieht.Ein weiteres Beispiel ist eine Textur, die selbst perspektivisch verzerrt fotogra-fiert wurde.

10 Transparenz und Farbmischung

10.1 Grundbegriffe

Wir haben bereits gelegentlich die Alpha-Werte von Farben erwahnt, ohne sie ge-nau zu erklaren. Das soll nun nachgeholt werden. Der Alpha-Wert (auch Trans-parenz genannt) hat folgende Verwendungen.

• Wenn die Moglichkeit zur Farbmischung (blending) eingestellt ist, wirder benutzt, um die Farbe des gerade bearbeiteten Objektfragments mitder fur das aktuelle Pixel bereits im Bildpuffer eingetragenen Farbe zumischen.

• Er wird zur Spezifikation von Farben und Materialeigenschaften herange-zogen.

• Beim Alphatest werden Flachen nach ihrem Alpha-Wert behalten oder ausder Szenerie gestrichen.

Ohne Mischung uberschreibt jedes neu bearbeitete Fragment alle bisherig Far-binformation im Bildpuffer, so als ware das Fragment vollig undurchsichtig. ImMischungsmodus ist genau kontrollierbar was und wieviel von den vorhandenenFarben mit den Werten fur das neue Fragment kombiniert werden soll. Damitkann man also auch (teilweise) durchscheinende Fragmente erzeugen.Tatsachlich misst der Alpha-Wert nicht die Transparenz, sondern ihr Gegenteil,die Opazitat. Transparente oder durchscheinende Flachen haben daher niedrigeAlpha-Werte. Naturlich konnen auch mehrere Schichten verschieden transparen-ter Flachen entstehen, etwa wenn man durch ein Glasfenster auf die Flussigkeitin einem Trinkglas und eine dahinter stehende Kerze blickt.

47

10.2 Quell- und Zielfaktoren

Eine RGBA-Farbe ist ein Quadrupel (r, g, b, a), wobei (r, g, b)-Farbe und a ∈[0, 1] ein Alpha-Wert ist.Beim Mischen wird die Farbe des neu bearbeiteten Fragments, der Quelle (sour-ce), mit der Farbe des aktuellen Pixels, des Ziels (destination), kombiniert. Da-zu werden die Quell- und Zielfarbe zunachst noch komponentenweise mit eige-nen Faktoren gewichtet, die wieder als RGBA-Farben angegeben werden. Seien(rQ, gQ, bQ, aQ) und (rZ , gZ , bZ , aZ) der Quell- und Zielfaktor und (rq, gq, bq, aq)und (rz, gz, bz, az) die Quell-und Zielfarbe. Dann ist die Mischfarbe

(rQrq + rZrz, gQgq + gZgz, bQbq + bZbz, aQaq + aZaz) .

Die Komponentenwerte werden dann noch auf das Intervall [0, 1]. abgeschnitten.Zunachst muss das Mischen ermoglicht werden mit

gl.glEnable(GL.GL_BLEND);

mit gl.glDisable(GL.GL_BLEND) wird zuruckgesetzt.Die Funktion

void gl.glBlendFunc(int sfactor, int dfactor);

bestimmt mit den Parametern sfactor und dfactor, wie Quell- und Zielfarbekombiniert werden. Deren moglichen Werte werden nachfolgend erlautert.

Konstante betroffener Faktor errechneter Faktorwert

GL.GL_ZERO Quelle oder Ziel (0, 0, 0, 0)GL.GL_ONE Quelle oder Ziel (1, 1, 1, 1)GL.GL_DST_COLOR Quelle (rz, gz, bz, az)GL.GL_SRC_COLOR Ziel (rq, gq, bq, aq)GL.GL_ONE_MINUS_ Quelle (1, 1, 1, 1) − (rz, gz, bz, az)DST_COLOR

GL.GL_ONE_MINUS_ Ziel (1, 1, 1, 1) − (rq, gq, bq, aq)SRC_COLOR

GL.GL_SRC_ALPHA Quelle oder Ziel (aq, aq, aq, aq)GL.GL_ONE_MINUS_ Quelle oder Ziel (1, 1, 1, 1) − (aq, aq, aq, aq)SRC_ALPHA

GL.GL_DST_ALPHA Quelle oder Ziel (az, az, az, az)GL.GL_ONE_MINUS_ Quelle oder Ziel (1, 1, 1, 1) − (az, az, az, az)DST_ALPHA

GL.GL_SRC_ Quelle (f, f, f, 1) mitALPHA_SATURATE f = min(aq, 1 − az)

Verwendet man also GL.GL_ONE fur die Quelle und GL.GL_ZERO fur das Ziel,so entsteht der selbe Effekt wie bei gl.glDisable(GL.GL_BLEND); diese Wertesind voreingestellt.

10.3 Einige wichtige Mischarten

Nicht alle Kombinationen von Quell- und Zielfaktoren sind sinnvoll. Meist ver-wendet man nur einige wenige Kombinationen. Wir geben einige typische An-wendungsfalle ein.

48

• Um ein Bild halftig aus zwei anderen zusammenzusetzen, kann man zu-erst den Quellfaktor auf GL.GL_ONE und den Zielfaktor auf GL.GL_ZEROund zeichnet das erste Bild; dann setzt man den Quellfaktor aufGL.GL_SRC_ALPHA und den Zielfaktor auf GL.GL_ONE_MINUS_SRC_ALPHA

und zeichnet das zweite Bild mit alpha = 0.5. Das ist mit die haufigsteMischart. Soll eine Mischung mit Anteil 0.75 des ersten und 0.25 des zwei-ten Bildes erzeugt werden, so zeichnet man das erste wie oben und daszweite mit alpha = 0.25.

• Um drei Bilder gleichmaßig zu mischen, setzt man den Zielfaktor aufGL.GL_ONE und den Quellfaktor auf GL.GL_SRC_ALPHA. Dann zeichnet manjedes Bild mit alpha = 0.3333333. Damit erhalt allerdings jedes Bild nurnoch ein Drittel seiner ursprunglichen Leuchtkraft, was man dann ein dennicht-uberlappenden Stellen bemerkt.

• In Malprogrammen benotigt man Pinseleffekte: Mit jedem Pinselstrichsoll ein wenig mehr von der Pinselfarbe aufs Bild aufgetragen werden, z.B.10% Farbe im Verhaltnis zur Bildfarbe. Dazu zeichnet man das Bild desPinsels mit alpha = 0.1 und verwendet GL.GL_SRC_ALPHA fur die Quelleund Gl.GL_ONE_MINUS_SRC_ALPHA fur das Ziel. Der Eindruck wird nochrealistischer, wenn der Alpha-Wert sich uber die Pinselflache andert (mehrFarbe in Pinselmitte, weniger ein den Randern). Analog kann man einenRadiergummi darstellen, indem man dessen Farbe auf die Hintergrundfar-be setzt.

• Mischt man unter Verwendung der Quell- oder Zielfarben(GL.GL_DST_COLOR oder GL.GL_ONE_MINUS_DST_COLOR fur den Quellfak-tor und GL.GL_SRC_COLOR oder GL.GL_ONE_MINUS_SRC_COLOR fur denZielfaktor), so kann man jede Farbkomponente individuell beeinflussen.Man kann damit einen einfachen Filtereffekt erreichen. Multipliziert manz.B. den Rotanteil mit 0.8, den Grunanteil mit 0.4 und den Blauanteilmit 0.72, so erscheint das Bild wie durch einen photographischen Filter,der 20% Rot, 60% Grun und 28% Blau entfernt.

• Nun soll ein Bild aus drei durchscheinenden Flachen zusammengesetztwerden, die sich teilweise verdecken und alle vor einem undurchsichtigenHintergrund liegen. Die hinterste Flache laßt 80% des Lichts durch, diemittlere 40% und die vorderste 90%. Dazu zeichnet man zuerst den Hin-tergrund mit den voreingestellten Werten fur die Quell- und Zielfakto-ren. Dann andert man die Faktoren auf GL.GL_SRC_ALPHA (Quelle) undGL.GL_ONE_MINUS_SRC_ALPHA (Ziel). Dann zeichnet man die hinterste Fla-che mit alpha = 0.2, die mittlere mit alpha = 0.6 und die vorderste mitalpha = 0.1.

• Den Effekt eines nicht-rechteckigen Rasterbilds kann man erzeugen, indemman den einzelnen Fragmenten im Bild unterschiedliche Alpha-Werte gibt.Jedes

”durchsichtige“ Fragment erhalt alpha = 0 und jedes undurchsich-

tige alpha = 1. Z.B kann man ein Polygon in Form eines Baums zeichnenund mit einer Laubtextur belegen. Gibt man den Teilen der rechteckigenTextur, die nicht zum Baum gehoren, den Alpha-Wert 0, so sind sie furden Betrachter nicht mehr zu sehen. Diese Methode heißt Billboarding ;

49

sie ist viel schneller als eine Modellierung des Baums mit Polygonen imDreidimensionalen. Ein Beispiel zeigt das nachfolgende Bild.

Abbildung 5. Billboarding

Der Baum ist insgesamt ein einziges Rechteck, das um die Mittelachse desBaumstamms gedreht werden kann, damit es immer zum Betrachter zeigt.

• Schließlich kann Mischen auch zum Ausgleich von Treppeneffekten ver-wendet werden.

10.4 Beispiel

Das nachfolgende Programm zeichnet zwei uberlappende farbige Dreiecke,jedes mit dem Alpha-Wert 0.75. Die Quell- und Zielfaktoren werden aufGL.GL_SRC_ALPHA und GL.GL_ONE_MINUS_SRC_ALPHA gesetzt. Die Taste t schal-tet um, welches Dreieck zuerst gezeichnet wird.

import java.awt.*;

import java.awt.event.*;

import javax.media.opengl.*;

import javax.media.opengl.glu.*;

import com.sun.opengl.util.*;

public class Transp {

static Animator animator = null;

static class WinRenderer extends GLCanvas

implements GLEventListener,

KeyListener

{

private GL gl;

50

private GLU glu;

static boolean leftFirst = true;

public WinRenderer(){

super();

}

public WinRenderer(GLCapabilities cap){

super(cap);

}

void drawLeftTriangle(){

/* zeichne gelbes Dreieck links */

gl.glBegin (GL.GL_TRIANGLES);

gl.glColor4f(1.0f, 1.0f, 0.0f, 0.75f);

gl.glVertex3f(0.1f, 0.9f, 0.0f);

gl.glVertex3f(0.1f, 0.1f, 0.0f);

gl.glVertex3f(0.7f, 0.5f, 0.0f);

gl.glEnd();

}

void drawRightTriangle(){

/* zeichne cyanfarbenes Dreieck rechts */

gl.glBegin (GL.GL_TRIANGLES);

gl.glColor4f(0.0f, 1.0f, 1.0f, 0.75f);

gl.glVertex3f(0.9f, 0.9f, 0.0f);

gl.glVertex3f(0.3f, 0.5f, 0.0f);

gl.glVertex3f(0.9f, 0.1f, 0.0f);

gl.glEnd();

}

public void display(GLAutoDrawable gLDrawable){

gl = gLDrawable.getGL();

glu = new GLU();

gl.glClear(gl.GL_COLOR_BUFFER_BIT);

gl.glLoadIdentity();

glu.gluLookAt(0,0,0.5,0,0,0,0,1,0);

gl.glTranslatef(-0.5f,-0.5f,0f);

if (leftFirst) {

drawLeftTriangle();

drawRightTriangle();

}

else {

drawRightTriangle();

drawLeftTriangle();

}

gl.glFlush();

51

}

public void displayChanged(GLAutoDrawable gLDrawable,

boolean modeChanged, boolean deviceChanged){}

public void init(GLAutoDrawable gLDrawable){

gLDrawable.addKeyListener(this);

gl = gLDrawable.getGL();

glu = new GLU();

gl.glEnable(GL.GL_BLEND);

gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);

gl.glShadeModel(GL.GL_FLAT);

gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

}

public void reshape(GLAutoDrawable gLDrawable, int x, int y,

int width, int height){

gl = gLDrawable.getGL();

glu = new GLU();

gl.glViewport(0, 0,width, height);

gl.glMatrixMode(gl.GL_PROJECTION);

gl.glLoadIdentity();

gl.glOrtho(-1,1,-1,1,-1,1);

//glu.gluPerspective(60, (float)width/(float)height, 1, 100);

gl.glMatrixMode(gl.GL_MODELVIEW);

}

public void keyPressed(KeyEvent e){

switch(e.getKeyChar()){

case ’t’:

case ’T’:

leftFirst = !leftFirst;

break;

case 27: /* Escape-Taste */

System.exit(0);

break;

default:

break;

}

}

public void keyReleased(KeyEvent e){}

public void keyTyped(KeyEvent e) {}

}

public static void main(String[] args) {

52

Frame frame = new Frame("Transparenz");

WinRenderer canvas = new WinRenderer();

canvas.addGLEventListener(canvas);

frame.add(canvas);

animator = new Animator(canvas);

frame.addWindowListener(new WindowAdapter()

{

public void windowClosing(WindowEvent e)

{

animator.stop();

System.exit(0);

}

});

frame.setSize(640, 480);

frame.show();

animator.start();

canvas.requestFocus();

}

}

Die Zeichenreihenfolge beeinflußt die Farbe des Uberlappungsgebiets. Wird daslinke Dreieck zuerst gezeichnet, werden Cyanfragmente aus der Quelle mit gel-ben Zielfragmenten gemischt, ansonsten umgekehrt. Da alle Alpha-Werte 0.75sind, ergibt sich als Quellfaktor 0.75 und 1.0 − 0.75 = 0.25 als Zielfaktor. Alsosind die Quellfragmente leicht durchscheinend, haben aber mehr Einfluß auf dieEndfarbe als die Zielfragmente.

10.5 Dreidimensionales Mischen mit dem Tiefenpuffer

Hat man sowohl durchsichtige wie undurchsichtige Objekte in der Szenerie, istetwas Vorsicht geboten. Flachen, die hinter undurchsichtigen liegen, will manstreichen. Liegen sie aber hinter durchscheinenden Objekten, mussen die Farbengemischt werden. Fur statische Szenerien ist das noch relativ gut zu beherrschen,fur bewegte (oder bewegte Beobachter) nicht mehr.Als Losung schaltet man zwar den Tiefenpuffer ein, setzt aber eine Schreibsper-re solange ein durchscheinendes Objekt gezeichnet wird. Zuerst zeichnet manalle undurchsichtigen Objekte unter normalem Betrieb des Tiefenpuffers. Dannwird die Tiefeninformation gesichert, indem die Schreibsperre gesetzt wird. BeimZeichnen der durchscheinenden Objekte werden ihre Tiefenwerte immer nochmit den Pufferwerten verglichen; damit werden sie nicht gezeichnet, wenn siehinter undurchsichtigen Objekten liegen. Liegen sie aber davor, uberschreibensie sie nicht, sondern konnen mit ihnen gemischt werden. Die Schreibsperre wirdmit glDepthMask(GL.GL_FALSE) gesetzt und mit glDepthMask(GL.GL_TRUE)

wieder aufgehoben. Uberlappen sich mehrere transparente Objekte, erhalt mandie besten Ergebnisse, wenn man sie von hinten nach vorn zeichnet.Das folgende Beispiel zeigt diese Methode. Die Taste a startet eine Animation,in der ein durchscheinender Wurfel durch eine undurchsichtige Kugel fahrt. DieTaste r setzt die Szenerie auf den Anfangszustand zuruck.

53

import java.awt.*;

import java.awt.event.*;

import javax.media.opengl.*;

import javax.media.opengl.glu.*;

import com.sun.opengl.util.*;

// 3D-Transparenz

public class Transp3D {

static Animator animator = null;

static class WinRenderer extends GLCanvas

implements GLEventListener,

KeyListener

{

private GL gl;

private GLU glu;

static boolean anim = true;

static float minz = 0.0f;

static float maxz = 1.0f;

static float solidZ = maxz;

static float transparentZ = minz;

static float zinc = 0.001f;

static int sphereList, cubeList;

public WinRenderer(){

super();

}

public WinRenderer(GLCapabilities cap){

super(cap);

}

void animate(){

if (solidZ <= minz || transparentZ >= maxz)

anim = false;

else {

solidZ -= zinc;

transparentZ += zinc;

}

}

public void display(GLAutoDrawable gLDrawable){

float[] mat_solid = { 0.75f, 0.75f, 0.0f, 1.0f };

float[] mat_zero = { 0.0f, 0.0f, 0.0f, 1.0f };

float[] mat_transparent = { 0.0f, 0.8f, 0.8f, 0.6f };

54

float[] mat_emission = { 0.0f, 0.3f, 0.3f, 0.6f };

gl = gLDrawable.getGL();

glu = new GLU();

gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);

// gl.glLoadIdentity();

// glu.gluLookAt(0,0,0.5,0,0,0,0,1,0);

gl.glPushMatrix ();

gl.glTranslatef (-0.15f, -0.15f, solidZ);

gl.glMaterialfv(GL.GL_FRONT, GL.GL_EMISSION, mat_zero,0);

gl.glMaterialfv(GL.GL_FRONT, GL.GL_DIFFUSE, mat_solid,0);

gl.glCallList (sphereList);

gl.glPopMatrix ();

gl.glPushMatrix ();

gl.glTranslatef (0.15f, 0.15f, transparentZ);

gl.glRotatef (15.0f, 1.0f, 1.0f, 0.0f);

gl.glRotatef (30.0f, 0.0f, 1.0f, 0.0f);

gl.glMaterialfv(GL.GL_FRONT, GL.GL_EMISSION, mat_emission,0);

gl.glMaterialfv(GL.GL_FRONT, GL.GL_DIFFUSE, mat_transparent,0);

gl.glEnable (GL.GL_BLEND);

gl.glDepthMask (false);

gl.glBlendFunc (GL.GL_SRC_ALPHA, GL.GL_ONE);

gl.glCallList (cubeList);

gl.glDepthMask (true);

gl.glDisable (GL.GL_BLEND);

gl.glPopMatrix ();

gl.glFlush();

animate();

}

public void displayChanged(GLAutoDrawable gLDrawable,

boolean modeChanged, boolean deviceChanged){}

public void init(GLAutoDrawable gLDrawable){

gLDrawable.addKeyListener(this);

gl = gLDrawable.getGL();

glu = new GLU();

GLUT glut = new GLUT();

gl.glEnable(GL.GL_BLEND);

gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);

gl.glShadeModel(GL.GL_FLAT);

gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

float[] mat_specular = { 1.0f, 1.0f, 1.0f, 0.15f };

float[] mat_shininess = { 100.0f };

float[] position = { 0.5f, 0.5f, 1.0f, 0.0f };

gl.glMaterialfv(GL.GL_FRONT, GL.GL_SPECULAR, mat_specular, 0);

55

gl.glMaterialfv(GL.GL_FRONT, GL.GL_SHININESS, mat_shininess, 0);

gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, position, 0);

gl.glEnable(GL.GL_LIGHTING);

gl.glEnable(GL.GL_LIGHT0);

gl.glEnable(GL.GL_DEPTH_TEST);

gl.glShadeModel(GL.GL_SMOOTH);

sphereList = gl.glGenLists(1);

gl.glNewList(sphereList, GL.GL_COMPILE);

glut.glutSolidSphere (0.4f, 16, 16);

gl.glEndList();

cubeList = gl.glGenLists(1);

gl.glNewList(cubeList, GL.GL_COMPILE);

glut.glutSolidCube (0.6f);

gl.glEndList();

}

public void reshape(GLAutoDrawable gLDrawable, int x, int y,

int width, int height){

gl = gLDrawable.getGL();

glu = new GLU();

float asp = (float)width/(float)height;

gl.glViewport(0, 0, width, height);

gl.glMatrixMode(gl.GL_PROJECTION);

gl.glLoadIdentity();

gl.glOrtho (-1.5*asp, 1.5*asp, -1.5, 1.5, -10.0, 10.0);

gl.glMatrixMode(gl.GL_MODELVIEW);

gl.glLoadIdentity();

}

public void keyPressed(KeyEvent e){

switch(e.getKeyChar()){

case ’a’:

case ’A’:

solidZ = maxz;

transparentZ = minz;

anim = true;

break;

case ’r’:

case ’R’:

solidZ = maxz;

transparentZ = minz;

break;

case 27:

System.exit(0);

}

}

56

public void keyReleased(KeyEvent e){}

public void keyTyped(KeyEvent e) {}

}

public static void main(String[] args) {

Frame frame = new Frame("3D-Transparenz");

WinRenderer canvas = new WinRenderer();

canvas.addGLEventListener(canvas);

frame.add(canvas);

animator = new Animator(canvas);

frame.addWindowListener(new WindowAdapter()

{

public void windowClosing(WindowEvent e)

{

animator.stop();

System.exit(0);

}

});

frame.setSize(640, 480);

frame.show();

animator.start();

canvas.requestFocus();

}

}

57

Index

1D-Texturen, 35

Bezier, 19Bildausschnitt, siehe Viewport

Farbe, 16Farbmodell

RGB, 16Farbwurfel, 16Frustum, 8

GK.glLineStipple, 15GL.GL_BACK, 15GL.GL_DEPTH_TEST, 18GL.GL_DEPTH_yBUFFER_yBIT, 18GL.GL_FILL, 15GL.GL_FRONT, 15GL.GL_FRONT_AND_BACK, 15GL.GL_LINE, 15GL.GL_LINE_LOOP, 13GL.GL_LINE_STRIP, 13GL.GL_LINES, 13GL.GL_MODELVIEW, 7GL.GL_POINTS, 13GL.GL_PROJECTION, 7GL.GL_QUAD_STRIP, 14GL.GL_QUADS, 14GL.GL_TRIANGLE_FAN, 14GL.GL_TRIANGLE_STRIP, 13GL.GL_TRIANGLES, 13GL.glLineWidth, 15GL.glLoadIdentity, 7GL.glMatrixMode, 7GL.glOrtho, 8GL.glPolygonMode, 15GL.glRotatef, 7GL.glScaleef, 7GL.glTranslatef, 7glBegin, 13glColor3f, 16glEnd, 13GLU.gluLookAt, 7GLU.gluPerspective, 8GLUT, 15GLUT.GLUT_DEPTH, 18GLUT.glutSolidCone, 15GLUT.glutSolidCube, 15

GLUT.glutSolidIcosahedron, 16GLUT.glutSolidOctahedron, 16GLUT.glutSolidSphere, 15GLUT.glutSolidTeapot, 16GLUT.glutSolidTetrahedron, 16GLUT.glutSolidTorus, 15GLUT.glutWireCone, 15GLUT.glutWireCube, 15GLUT.glutWireIcosahedron, 16GLUT.glutWireOctahedron, 16GLUT.glutWireSphere, 15GLUT.glutWireTeapot, 16GLUT.glutWireTetrahedron, 16GLUT.glutWireTorus, 15glutInitDisplayMode, 18glutSolidDodecahedron, 16glutWireDodecahedron, 16glVertex3f, 13Graustufen, 16Grundmatrizen, 7Gruppierung, 13

Korperkomplexe, 15

Kamera, 7

Modellmatrix, 7Modellwelt, 7

Orthogonalprojektion, siehe Projekti-on

Parallelprojektion, siehe ProjektionPixelkoordinaten, 8Platonische Polyeder, 16Polyeder

Polyeder

platonische, 16Polygonzug, 14Projektion, 7, 8Projektionsmatrix, 7Punkt, 13

Raumdiagonale, 16RGB-Farbmodell, 16

Teapot, 16Teekanne, 16

58

Tesselierung, 14TextureData, 33TextureIO, 33Tiefenpufferung, 17Transformation, 7

Viewport, 8

z-buffering, 17Zeichenfarbe, 16Zentralprojektion, siehe Projektion

59