Home
Über JOGL
  Entstehung
  Eigenschaften
  Installation
Tutorial
  Lektion1
  Lektion2
  Lektion3
  Lektion4
  Lektion5 
JOGL vs.
   OpenGL

Links
Impressum

 

 Lektion 2

Geometrische Primitive mit GLU
Geometrische Primitive mit GLUT
Spielfiguren

Zum Download des Quellcodes dieser Lektion: Auf das Bild klicken oder hier.

Geometrische Primitive mit GLU

Mit der OpenGL Utility Library GLU können verschiedene Arten von geometrischen Primitiven erzeugt werden. Um diese darzustellen, benutzt GLU sogenannte Quadrics (quadric surfaces). Quadrics sind Oberflächen, die durch die quadratische Gleichung:

a1x2 + a2y2 + a3z2 + a4xy + a5yz + a6xz + a7x + a8y + a9z + a10 = 0

beschrieben werden können. Damit lassen sich verschiedenste Oberflächen annähern [WNDS99].

Um Quadrics benutzen zu können, muss zuerst ein Quadrics-Objekt erzeugt werden. Dies geschieht mit dem Befehl gluNewQuadric(..). Danach werden die Rendering-Attribute für das Quadrics-Objekt festgelegt. So kann über den Befehl gluQuadricDrawStyle(..) bestimmt werden, ob Punkte (GL.GL_POINTS), Linien (GL.GL_LINES) oder gefüllte Polygone (GL.GL_FILL) zur Ausgabe des Objekts benutzt werden sollen.

Wenn das Objekt beleuchtet werden soll, müssen die Normalen für das Objekt spezifiziert werden. Dies geschieht mit gluQuadricNormals(..). Dabei kann eine Normale pro Fläche oder pro Vertex erstellt werden. Der Methode wird das erzeugte Quadrics-Objekt übergeben, sowie eine Konstante, mit der sich einstellen lässt, wie die Normalenvektoren erzeugt werden sollen.

Standardmässig ist die Einstellung GLU_NONE. In diesem Fall werden keine Normalenvektoren erzeugt, was sinnvoll ist, wenn keine Beleuchtung verwendet wird. Mit der Konstanten GLU_FLAT wird ein Normalenvektor für jede Facette erzeugt. Dies bietet sich für Flat Shading an, während bei Smooth Shading die Konstante GLU_SMOOTH verwendet werden sollte. Auf Beleuchtung wird in Kapitel 4 näher eingegangen.

Schließlich muss eine von vier Rendering Routinen aufgerufen werden, um den Typ des Quadrics-Objekts festzulegen. Es können Kugeln, Zylinder, Disks und Partial Disks erzeugt werden. Um eine Kugel zu erzeugen, wird die Methode gluSphere(..) verwendet. Dabei wird das erzeugte Quadrics-Objekt übergeben, der Radius der Kugel, sowie die Feinheit der Unterteilung mit Breiten- und Längengraden (Slices bzw. Stacks).

Ein Zylinder kann mit gluCylinder(..) erstellt werden. Es wird der Radius des unteren und des oberen Endes übergeben. Zusätzlich wird die Anzahl der Slices und der Stacks übergeben. Zylinder sind unten und oben offen.

Die Methode gluDisk(..) zeichnet eine Scheibe mit einem inneren Radius und einem äusseren Radius. Auch hier kann die Feinheit der Auflösung eingestellt werden und zwar mittels Slices und der Anzahl der konzentrischen Kreise. Es kann mit gluPartialDisk(..) auch nur ein Teil der Scheibe gezeichnet werden. Dazu wird der Methode zusätzlich zu den Parametern von gluDisk(..) noch der Winkel angegeben, bei dem der Scheibenausschnitt beginnen soll und der Winkel, der die Größe des Scheibenausschnitts bestimmt.

In folgendem Codebeispiel werden drei Quadricobjekte erzeugt, eine Kugel, ein Zylinder und ein Scheibenausschnitt. Diese werden mit jeweils mit verschiedenen Rendering-Attributen erstellt:

GLUquadric qobj0 = glu.gluNewQuadric();
GLUquadric qobj1 = glu.gluNewQuadric();
GLUquadric qobj2 = glu.gluNewQuadric();

gl.glTranslatef(-1, 0, 0);

//Kugel
glu.gluQuadricDrawStyle(qobj0, GLU.GLU_FILL);
glu.gluQuadricNormals(qobj0, GLU.GLU_SMOOTH);
glu.gluSphere(qobj0, 0.4f, 20, 20);
glu.gluDeleteQuadric(qobj0);

gl.glTranslatef(1.3f, -0.4f, 0);
gl.glRotatef(-90, 1, 0, 0);

//Zylinder
glu.gluQuadricDrawStyle(qobj1, GLU.GLU_LINE);
glu.gluQuadricNormals(qobj1, GLU.GLU_SMOOTH);
glu.gluCylinder(qobj1, 0.5f, 0f, 1, 15, 5);
glu.gluDeleteQuadric(qobj1);

gl.glRotatef(90, 1, 0, 0);
gl.glTranslatef(1.5f, 0.4f, 0);

//Scheibenausschnitt
glu.gluQuadricDrawStyle(qobj2, GLU.GLU_POINT);
glu.gluQuadricNormals(qobj2, GLU.GLU_SMOOTH);
glu.gluPartialDisk(qobj2, 0.3, 0.5, 10, 6, 180, 180);
glu.gluDeleteQuadric(qobj2);

nach oben

Geometrische Primitive mit GLUT

Eine andere Möglichkeit geometrische Primitive zu erstellen, bietet das OpenGL Utility Toolkit, kurz GLUT. Da OpenGL plattformunabhängig konzipiert wurde, stellt es keine Routinen zur Verfügung, um mit der graphischen Oberfläche des jeweiligen Betriebssystems interagieren zu können. Auch Benutzereingaben können durch reines OpenGL nicht verarbeitet werden.

Deshalb wurde GLUT entwickelt, um die wichtigsten Routinen zur Fensterverwaltung und zur Interaktion mit dem Benutzer zur Verfügung zu stellen. Diese Funktionen sind für JOGL nicht relevant, da die entsprechenden Methoden mit AWT und Swing implementiert werden. Allerdings enthält GLUT auch Routinen zur Erzeugung von geometrischen Primitiven, auf die nun näher eingegangen werden soll. Um auf GLUT-Routinen zugreifen zu können, muss immer die Bibliothek net.java.games.jogl.util.GLUT importiert werden.

Folgende dreidimensionale Objekte lassen sich mit GLUT erzeugen, und zwar entweder in der Gitternetzdarstellung oder als ausgefüllte Körper: Kegel, Würfel, Dodekaeder, Ikosaeder, Oktaeder, Kugel, Tetraeder und Torus.

Bei Kugeln und Kegeln wird immer ein GLU Objekt übergeben, da die Funktionen glutSolidSphere(..), glutWireSphere(..), glutSolidCone(..) und glutWireCone(..) nichts anderes machen als die entsprechenden GLU-Methode aufzurufen, um die Objekte zu erzeugen. Bis auf die übergebene GLU-Instanz sind die Parameter deshalb gleich mit den Parametern der GLU-Routinen.

Bei der Erzeugung von Mehrecken wird die entsprechende Methode mit einem GL-Objekt als Parameter aufgerufen. Die Größe muss die entsprechenden Skalierungsmethode angepasst werden. Als Beispiel seien die Methoden glutSolidIcosahedron(..) bzw. glutWireIcosahedron(..) genannt.

Um einen Torus zu erzeugen, wird entweder glutSolidTorus(..) oder glutWireTorus(..) eine GL-Instanz, der innere und der äussere Radius sowie die Feinheit der Unterteilung in Seiten und Ringe als Parameter übergeben.
Würfel werden mit den Methoden glutSolidCube(..) und glutWireCube(..) erzeugt, wobei diesen ein GL-Objekt und die Seitenlänge als int übergeben wird.

Mit folgenden Codezeilen werden vier geometrische Primitive, Ikosaeder, Torus, Kugel und Würfel, erstellt und die beiden Möglichkeiten der Darstellung demonstriert:

//Icosaeder in Gitternetzdarstellung
gl.glTranslated(-2, 0, 0);
gl.glScaled(0.6, 0.6, 0.6);
glut.glutWireIcosahedron(gl);

//Rücksetzen auf Einheitsmatrix
gl.glLoadIdentity();

//Torus in Gitternetzdarstellung
gl.glTranslated(-0.5, 0, 0);
glut.glutWireTorus(gl, 0.2, 0.4, 20, 20);

//Kugel ausgefüllt
gl.glTranslated(1.5, 0, 0);
glut.glutSolidSphere(glu, 0.6, 10, 10);

//Würfel ausgefüllt
gl.glTranslated(1.5, 0, 0);
glut.glutSolidCube(gl, 1f);

nach oben

Spielfiguren

Für unser Spiel werden die Figuren ebenfalls mit GLU-Primitiven gezeichnet. Wir benötigen dafür eine Kugel und zwei Zylinder. Die Erzeugung lagern wir in die Methode drawFigure(..) aus, der außer einer Instanz von GL und einer von GLU ausserdem noch die Position im Raum übergeben wird. Um eine Figur zeichnen zu lassen, rufen wir dann einfach in der display-Methode drawFigure(..) auf, beispielsweise:

gl.glColor3f(1f, 0f, 0f);
drawFigure(gl, glu, 0, 0);

gl.glColor3f(1f, 1f, 0f);
drawFigure(gl, glu, -4, 2);

gl.glColor3f(0f, 0f, 1f);
drawFigure(gl, glu, 3, -5);

gl.glColor3f(0f, 1f, 0f);
drawFigure(gl, glu, 3, 2);

gl.glColor3f(1f, 1f, 1f);

Da wir noch nicht mit Materialien und Lichtern arbeiten, benutzen wir glColor3f(..) um den Figuren unterschiedliche Farben zuzuordnen.
Die Methode drawFigure ist wie folgt implementiert:

public void drawFigure(GL gl, GLU glu, float x, float z)
{
    gl.glPushMatrix();

    gl.glTranslated(x, 0, z);
    GLUquadric qobj = glu.gluNewQuadric();

    glu.gluQuadricDrawStyle(qobj, GLU.GLU_FILL);
    glu.gluQuadricNormals(qobj, GLU.GLU_SMOOTH);
    glu.gluSphere(qobj, 0.36f, 30, 30);

    gl.glRotated(90, 1, 0, 0);
    glu.gluQuadricDrawStyle(qobj, GLU.GLU_FILL);
    glu.gluQuadricNormals(qobj, GLU.GLU_SMOOTH);
    glu.gluCylinder(qobj, 0.1, 0.4, 1.2, 30, 30);

    gl.glTranslated(0, 0, 1.2f);
    glu.gluQuadricDrawStyle(qobj, GLU.GLU_FILL);
    glu.gluQuadricNormals(qobj, GLU.GLU_SMOOTH);
    glu.gluCylinder(qobj, 0.4, 0.4, 0.2, 30, 30);

    gl.glPopMatrix();
}

Wir benutzen dabei glPushMatrix(), um die aktuelle Matrix zu speichern. Danach können wir die Transformationen zum Anordnen der einzelnen Teile unserer Spielfigur vornehmen. Damit wir die danach zu zeichnenden Elemente der Szene nicht durch diese Transformationen manipulieren, versetzen wir die Modelview-Matrix mit glPopMatrix wieder in den Zustand, den sie vor dem Zeichen der Spielfigur innehatte.

nach oben