|
Lektion
3
Grundlagen
Richtungslicht
Punktlicht
Spotlight
Beleuchtung Beispielszene
Zum Download des Quellcodes dieser Lektion: Auf das
Bild klicken oder hier.
Grundlagen
In JOGL bestehen Lichter aus einem ambienten, einem diffusen und
einem spiegelnden Anteil. Alle drei Anteile haben eine bestimmte
Farbe. Diese wird im RGBA-Modell angegeben. Es werden mindestens
acht Lichter (Light0 - Light7) unterstützt. Abhängig von
der benutzten JOGL-Implementierung kann diese Zahl nach oben variieren.
Allerdings sinkt mit der Anzahl der benutzten Lichter auch die Performance.
Der ambiente Anteil der Lichtquelle ist der Lichtanteil, der aus
allen Richtungen zu kommen scheint, so daß der Standort des
Lichtes nicht mehr auszumachen ist. Ambientes Licht ist deshalb
ungerichtetes Licht. Mit rein ambienter Beleuchtung erscheinen Objekte
zweidimensional da es in alle Richtungen gleichmässig gestreut
wird, wenn es auf eine Oberfläche trifft.
Bei der diffusen Komponenten kommt das Licht aus einer bestimmten
Richtung. Wenn es auf eine Oberfläche trifft, wird es in alle
Richtungen gleichmässig gestreut. Dadurch erscheint es, unabhängig
von der Position des Betrachters, immer gleichmäßig hell.
Der spiegelnde Lichtanteil kommt genau aus einer Richtung und wird
in eine vorgegebene Richtung reflektiert. Je nach Beschaffenheit
der Oberfläche ergeben die reflektierten Strahlen an einer
bestimmten Stelle ein Glanzlicht (Highlight).
Damit JOGL die Beleuchtung berechnen kann, müssen Normalenvektoren
angegeben werden, die die relative Position eines Objekts zur Lichtquelle
bereitstellen. Wenn vorgefertigte Primitive von GLUT verwendet werden,
müssen die Normalen nicht explizit angegeben werden; bei GLU-Primitiven
werden die Normalen mit gluQuadricNormals(..) berechnet. Die Beleuchtungsmethoden
greifen ausserdem nicht auf die mit glColor*() gesetzten Farben
zurück. Stattdessen müssen mit glMaterial*() Materialien
definiert werden. Dies wird im nächsten Abschnitt besprochen
Um Lichter in OpenGL benutzen zu können, muss zuerst
glEnable(..) mit dem Parameter GL.GL_LIGHTING aufgerufen werden.
Zudem ist es wichtig die Sichtbarkeit der Objekte zu bestimmen bzw.
ihre relative Position zur Kamera, da ansonsten die Objekte in der
Reihenfolge ihrer Erstellung einfach übereinander gezeichnet
werden. Um dies zu verhindern, muss z-Buffering mit glEnable(..)
und dem Parameter GL.GL_DEPTH_TEST aufgerufen werden. Zudem muss
in der Methode glClear zusätzlich das GL.GL_DEPTH_BUFFER_BIT
gesetzt werden. In JOGL könnte der Aufruf von glClear() wie
folgt aussehen:
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
Im Folgenden werden drei Arten von Lichtquellen besprochen: Richtungslicht,
Punktlicht und Spotlight.
nach
oben
Richtungslicht
Ein Richtungslicht beschreibt eine Lichtquelle, die
unendlich weit entfernt ist und deren Licht in eine bestimmte Richtung
strahlt (z.B. die Sonne). Um ein Richtungslicht zu erstellen, muss
in float-Vektoren die Werte für die Position (in eye-Koordinaten)
und für die Farbe der verschiedenen Lichtanteile angegeben
werden. Standardmässig sind die Werte auf {0.0f, 0.0f, 0.0f,
1.0f} gesetzt. Nur das erste Licht (Light0) bildet eine Ausnahme.
Da dort sowohl specular als auch diffuse auf {1.0f, 1.0f, 1.0f,
1.0f} gesetzt sind, muss der entsprechende RGBA-Wert auf {0.0f,
0.0f , 0.0f , 1.0f} gesetzt werden, wenn die Lichtanteile nicht
benutzt werden sollen.
Mit der Methode glLight*(..) müssen die Werte
an das verwendete Licht übergeben werden. Dabei werden als
Parameter die verwendete Lichtquelle, die zu setzende Eigenschaft
und der Vektor, in dem die Werte gespeichert werden, übergeben.
Folgendes Code-Beispiel erzeugt ein Richtungslicht mit blauem Ambient-Anteil,
rotem Diffuse-Anteil und weissem Specular-Anteil:
float light0_pos[] = {0, -4, 5, 0};
float light0_color_am[] = {0, 0, 1, 1};
float light0_color_diff[] = {1, 0, 0, 1};
float light0_color_spec[] = {1, 1, 1, 1};
gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, light0_pos);
gl.glLightfv(GL.GL_LIGHT0, GL.GL_AMBIENT, light0_color_am);
gl.glLightfv(GL.GL_LIGHT0, GL.GL_DIFFUSE, light0_color_diff);
gl.glLightfv(GL.GL_LIGHT0, GL.GL_SPECULAR, light0_color_spec);
nach
oben
Punktlicht
Im Gegensatz zum Richtungslicht beschreibt ein Punktlicht
eine Lichtquelle innerhalb der Szene, die in alle Richtungen gleichmässig
strahlt. Ein Beispiel für ein Punktlicht in der realen Welt
ist eine Glühbirne.
Die Erzeugung eines Punktlichtes erfolgt ähnlich
der Erstellung eines Richtungslichtes, mit der einzigen Ausnahme,
dass die w-Koordinate der Position ungleich 0 sein muß.
Folgender Code demonstriert die Implementierung eines Punktlichtes:
float light1_pos[] = {0, -4, 5, 1};
float light1_color_am[] = {0, 0, 1, 1};
float light1_color_diff[] = {1, 0, 0, 1};
float light1_color_spec[] = {1, 1, 1, 1};
gl.glLightfv(GL.GL_LIGHT1, GL.GL_POSITION, light1_pos);
gl.glLightfv(GL.GL_LIGHT1, GL.GL_AMBIENT, light1_color_am);
gl.glLightfv(GL.GL_LIGHT1, GL.GL_DIFFUSE, light1_color_diff);
gl.glLightfv(GL.GL_LIGHT1, GL.GL_SPECULAR, light1_color_spec);
Desweiteren kann bei einem Punktlicht die Intensität
des Lichtes eingestellt werden. Dies geschieht über die Konstanten
GL.GL_CONSTANT_ATTENUATION, GL.GL_LINEAR_ATTENUATION und GL.GL_QUADRATIC_ATTENUATION.
Diese ergeben zusammen den Abschwächungsfaktor für die
Lichtquelle:
Abschwächungsfaktor =
wobei
d = die Distanz zwischen der Position der Lichtquelle und dem Vertex
kc = GL_CONSTANT_ATTENUATION
kl = GL_LINEAR_ATTENUATION
kq = GL_QUADRATIC_ATTENUATION
Je höher der Wert für die Konstanten gewählt
wird, desto höher ist der Abschwächungsfaktor. Um den
Konstanten Werte zuzuordnen, wird der Befehl glLight(..) mit drei
Parametern benötigt. Der erste Parameter bezeichnet die Lichtquelle,
für die der Abschwächungsfaktor gesetzt werden soll, der
zweite Parameter gibt die zu setzende Konstante an und der dritte
enthält den Wert, der der Konstanten zugeordnet werden soll.
Folgender Codeausschnitt setzt für die zweite
Lichtquelle die Konstanten auf ihre Defaultwerte:
gl.glLightf(GL.GL_LIGHT1, GL.GL_CONSTANT_ATTENUATION,
1.0f);
gl.glLightf(GL.GL_LIGHT1, GL.GL_LINEAR_ATTENUATION, 0.0f);
gl.glLightf(GL.GL_LIGHT1, GL.GL_QUADRATIC_ATTENUATION, 0.0f);
nach
oben
Spotlight
Spotlights sind prinzipiell Punktlichter, deren Lichtkegel
eingeschränkt sind. Dabei wird eine Gerade vom Mittelpunkt
der Lichtquelle zu einem in der Konstanten GL.GL_SPOT_DIRECTION
gesetzten Punkt gezogen. Dann wird mit GL.GL_SPOT_CUTOFF der Winkel
zwischen dieser Geraden und dem Rand des Lichtkegels definiert.
Der Wert für GL.GL_SPOT_CUTOFF sollte kleiner als 180°
sein, da bei 180° das Spotlight einem Punktlicht entsprechen
würde.
Das folgende Beispiel zeigt, wie ein Spotlight implementiert
werden könnte:
float light2_pos[] = {0, -4, 5, 1};
float light2_color_am[] = {0, 0, 1, 1};
float light2_color_diff[] = {1, 0, 0, 0};
float light2_color_spec[] = {1, 1, 1, 1};
float light2_spot_dir[] = {0, 5, -5};
gl.glLightfv(GL.GL_LIGHT2, GL.GL_POSITION, light2_pos);
gl.glLightfv(GL.GL_LIGHT2, GL.GL_AMBIENT, light2_color_am);
gl.glLightfv(GL.GL_LIGHT2, GL.GL_DIFFUSE, light2_color_diff);
gl.glLightfv(GL.GL_LIGHT2, GL.GL_SPECULAR, light2_color_spec);
gl.glLightfv(GL.GL_LIGHT2, GL.GL_SPOT_DIRECTION, light2_spot_dir);
gl.glLightf(GL.GL_LIGHT2, GL.GL_SPOT_CUTOFF, 15);
nach
oben
Beleuchtung Beispielszene
Für das Spiel benötigen wir natürlich
auch eine Lichtquelle. Diese setzen wir in der Methode setLight(..),
die in der init-Methode aufgerufen werden soll. Für unser Spiel
haben wir dabei ein Richtungslicht gewählt, das um zwei Einheiten
nach oben und um fünf in positiver z-Richtung verschoben wurde
und die Szene mit einem weißen Licht ausleuchtet.
Die Methode setLight() sieht wie folgt aus:
private void setLight(GL gl)
{
float light_pos[] = {0, 2, 5, 0};
float light_color_am[] = {1, 1, 1, 1};
float light_color_diff[] = {1, 1, 1, 1};
float light_color_spec[] = {1, 1, 1, 1};
gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION,
light_pos);
gl.glLightfv(GL.GL_LIGHT0, GL.GL_AMBIENT,
light_color_am);
gl.glLightfv(GL.GL_LIGHT0, GL.GL_DIFFUSE,
light_color_diff);
gl.glLightfv(GL.GL_LIGHT0, GL.GL_SPECULAR,
light_color_spec);
gl.glEnable(GL.GL_LIGHTING);
gl.glEnable(GL.GL_LIGHT0);
gl.glEnable(GL.GL_DEPTH_TEST);
gl.glEnable(GL.GL_SMOOTH);
}
Bei eingeschaltetem Licht hat die Funktion glColor*(..)
keine Auswirkungen mehr. Um den Spielfiguren trotzdem Farbe zu verleihen
benötigen wir nun Materialien.
nach
oben
|
|