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

Links
Impressum

 

 Lektion 1

Basiskomponenten von JOGL
Der GLEventListener
Fenstererzeugung

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

Basiskomponenten von JOGL

Im Gegensatz zu OpenGL verwendet JOGL keine eigenen Datentypen. Es werden die vorgegebenen Javadatentypen benutzt. Dabei sollte vor allem die Benutzung von float beachtet werden, da Kommazahlen in Java standardmässig von Typ double sind. Soll float verwendet werden, muss an die Zahl ein 'f' oder ein 'F' angehängt werden. Eine weitere Möglichkeit ist das Casten, wobei der Kommazahl "(float)" vorangestellt wird.

Oft werden Konstanten benutzt, um den Quellcode leserlicher zu machen. Aussagekräftige Namen sind deshalb von Vorteil. Ausserdem bestehen Konstanten, nach den bekannten Java Namens-konventionen, immer aus Großbuchstaben.

Obwohl Funktionen in Java bei verschiedenen Argumenten, z.B. verschiedene Datentypen, Arrays oder unterschiedliche Anzahl der Parameter, überladen werden können, müssen in JOGL teilweise parameterspezifische Funktionen aufgerufen werden, da diese auf C basieren und es dort keine Möglichkeit des Überladens von Funktionen gibt. Als Beispiel sei hierbei die Methode glColor* genannt, die sich u.a. mit drei float-Zahlen (glColor3f), drei Bytezahlen (glColor3b) oder auch mit einem Array vom Typ int (glColor3iv) aufrufen lässt.
Dies kann für einige Programmierer eine Umstellung darstellen, jedoch wird dies durch die Eindeutigkeit und Eingängigkeit der Methodennamen kompensiert, so dass das Prinzip schnell zu verstehen und anzuwenden ist.

Funktionen der verschiedenen OpenGL-Bibliotheken werden durch Objekte aufgerufen. Dies geschieht hauptsächlich mit den Interfaces GL und GLU. Diese werden auf der Basis der verwendeten GLDrawable gebildet, z.B. einem GLCanvas, auf dem das Rendering erfolgt.
Auf die GLUT-Bibliothek kann ebenfalls zugegriffen werden, allerdings erfolgt dies nicht über ein Interface. GLUT ist eine eigene Klasse, deren Objekte mit new() gebildet werden.

Weitere Basisklassen der JOGL-API sind GLCanvas, GLJPanel, GLCapabilities, GLDrawableFactory, GLDrawable und GLEventListener.

GLCanvas ist eine AWT-Komponente, die Hardware-Beschleunigung unterstützt. Während GLJPanel zwar eine 100%ige Swing Integration für die Fälle garantieren soll, wenn GLCanvas nicht benutzt werden kann, gewährleistet es jedoch keine Hardwareunterstützung. GLCanvas und GLJPanel werden über die Factory-Methoden der GLDrawableFactory erstellt.

Die GLDrawableFactory erlaubt es dem Benutzer bei neuen Widgets (GUI-Element) bestimmte Parameter von OpenGL einzustellen, die im Zusammenhang mit dem Rendering stehen. Dies geschieht mittels eines GLCapabilities Objekts. GLCapabilities Objekte werden verwendet, um die Parameter für das Rendering festzulegen, z.B. das Ein- oder Ausschalten der Hardwarebeschleunigung oder des Double-Bufferings.

GLDrawable bietet Zugang zu GL- und GLU-Objekten, um die OpenGL Routinen aufzurufen. Ausserdem wird über GLDrawable das OpenGL Rendering per GLEventListener zur Verfügung gestellt (addGLEventListener(GLEventListener listener)). Des weiteren kann unabhängig von Swing oder AWT die Größe des Widgets über getSize() und setSize(Dimension d) ausgelesen und manipuliert werden.

Der GLEventListener wird im nächsten Abschnitt besprochen.

nach oben

Der GLEventListener

In Java werden Eingaben des Benutzers, z.B. über I/O-Geräte, durch das sogenannte Event-Listener-Modell abgefangen und verarbeitet. Auch die Kommunikation zwischen Threads kann über dieses Modell gewährleistet werden.

Beim Event-Listener Modell gibt es zwei Teilnehmer, das Empfängerobjekt und die Ereignisquelle.
Das Empfängerobjekt ist eine Instanz einer Klasse, die ein spezielles Interface, die Empfängerschnittstelle implementiert. In dieser Klasse wird festgelegt, wie ein auftretendes Ereignis behandelt werden soll.
Die Ereignisquelle ist ein Objekt, das Empfängerobjekte registriert und diesen Ereignisobjekte senden kann. In der Ereignisquelle werden Benutzereingaben oder Threadereignisse an das Empfängerobjekt delegiert. Dies geschieht über die Ereignisobjekte, die Informationen bzgl. des auftretenden Ereignisses sammeln und so die aufzurufende Methode im Empfängerobjekt festlegen.
Der GLEventListener wird immer dann aufgerufen, wenn OpenGL Rendering ausgeführt werden soll. Jede Klasse, die das Interface GLEventListener benutzt, muss die folgenden Methoden implementieren: init(..), display(..), reshape(..) und displayChanged(..). Dabei ist immer eine Instanz der Klasse GLDrawable, ein GLCanvas oder ein GLJPanel, zu übergeben, auf der das Rendering ausgeführt werden soll.

Die init-Methode wird beim ersten Initialisieren des OpenGL Kontextes aufgerufen. In ihr werden allgemeine Parameter zum Rendern der Szene eingestellt, die sich nicht verändern sollen. Dies können z.B. Voreinstellungen bezüglich der Lichter oder der Display Lists sein.

Die Methode display(..) wird immer dann aufgerufen, wenn tatsächlich gerendert werden soll. Das kann z.B. durch einen Aufruf von repaint() geschehen oder bei Erstellung einer Animation durch eine Instanz der Klasse Animator (siehe Kapitel 5). In dieser Methode werden u.a. die zu zeichnenden Elemente der Szene beschrieben oder Methoden aufgerufen, denen dies übertragen wird. Reshape(..) wird aufgerufen, wenn die verwendete Instanz der Klasse GLDrawable, ein GLCanvas oder ein GLJPanel, initialisiert oder in ihrer Größe verändert wird. Es bietet sich an, in dieser Methode die Kameraeinstellungen vorzunehmen.

Schließlich wird in der Methode displayChanged(..) der Code implementiert, der immer dann ausgeführt werden soll, wenn die Auflösung des Bildschirms während der Laufzeit der JOGL-Applikation verändert wird.

nach oben

Fenstererzeugung

m folgenden Abschnitt erzeugen wir ein einfaches Fenster, in dem ein weißes Viereck auf schwarzem Grund zu sehen ist. Es stellt die Basis für das Spielfeld dar. Dazu benötigen wir zwei Klassen: Beispielszene.java, die von JFrame abgeleitet wird und die eigentliche Fenstererzeugung übernimmt und BeispielszeneView.java, die das Interface GLEventListener implementiert und in der das Zeichnen vorgenommen wird. Um auf die Klassen und Interfaces von JOGL zugreifen zu können, müssen wir die Bibliothek net.java.games.jogl importieren.

Die Klasse Beispielszene.java:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import net.java.games.jogl.*;

/**
* @author Melanie Klein & Stefan Jouaux
*/

public class Beispielszene extends JFrame
{
    public Beispielszene()
    {
        GLCapabilities glcaps = new GLCapabilities();
        GLCanvas canvas = GLDrawableFactory.getFactory().
        CreateGLCanvas (glcaps);

         BeispielszeneView view = new BeispielszeneView();
        canvas.addGLEventListener(view);

        setSize(500,500);
        setTitle("CAV-Projekt: JOGL - Beispielszene");
        setResizable(false);

        getContentPane().add(canvas,BorderLayout.CENTER);

        addWindowListener(new WindowAdapter()
        {
            public void windowClosing(WindowEvent e)
            {
                System.exit(0);
            }
        });
    }

    public static void main(String[] args)
    {
        final Beispielszene app = new Beispielszene();

        SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                app.setVisible(true);
            }
        });
    }
}

Das Neue an dieser Klasse sind insgesamt vier Zeilen JOGL-Code:

GLCapabilities glcaps = new GLCapabilities();
GLCanvas canvas = GLDrawableFactory.getFactory().createGLCanvas (glcaps);

Bei der Initialisierung eines GLCanvas werden zuerst die plattformabhängigen Parameter zur Erzeugung ermittelt, z.B. die Farbtiefe. Dies geschieht mittels eines GLCapabilities-Objektes. Um trotz der unterschiedlichen Möglichkeiten, der verschiedener Plattformen, wozu auch die verwendete JOGL- bzw. JVM-Version gehört, Plattformunabhängigkeit zu gewährleisten, muss ein GLCanvas-Objekt über die GLDrawableFactory erzeugt werden. Der Methode createGLCanvas(..) werden dabei die durch das GLCapabilities-Objekt spezifizierten Parameter der jeweiligen Plattform übergeben.

In den letzten beiden Zeilen JOGL-Code:

BeispielszeneView view = new BeispielszeneView();
canvas.addGLEventListener(view);

Wird der GLEventListener an die GLCanvas übergeben. Damit gibt es eine Verbindung zwischen der GLDrawable, in diesem Fall canvas, auf der gezeichnet werden soll und den Funktionen, die definieren was und wie gerendert werden soll. Dies geschieht im GLEventListener.

Die Klasse BeispielszeneView.java:

import net.java.games.jogl.*;

/**
* @author Melanie Klein & Stefan Jouaux
*/

public class BeispielszeneView implements GLEventListener
{
    public void init(GLDrawable arg0)
    {
        GL gl = arg0.getGL();
        GLU glu = arg0.getGLU();

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

        gl.glMatrixMode(GL.GL_MODELVIEW);
    }

    private void setCamera(GL gl, GLU glu)
    {
        int w = 500, h = 500;

        gl.glViewport(0, 0, w, h);

        gl.glMatrixMode(GL.GL_PROJECTION);
        gl.glLoadIdentity();

        glu.gluPerspective(50.0, 1, 2.0, 40.0);
    }

    public void display(GLDrawable arg0)
    {
        GL gl = arg0.getGL();
        GLU glu = arg0.getGLU();

        gl.glClear(GL.GL_COLOR_BUFFER_BIT);

        gl.glLoadIdentity();

        glu.gluLookAt(0, 12, 19,
                      0, 0, 0,
                      0, 1, 0);

        gl.glTranslated(0, 1, 0);

        drawField(gl, glu);
    }

    public void drawField(GL gl, GLU glu)
    {
        gl.glBegin(GL.GL_QUADS);
            gl.glVertex3f(-6.5f, -1.5f, -6.5f);
            gl.glVertex3f(-6.5f, -1.5f, 6.5f);
            gl.glVertex3f(6.5f, -1.5f, 6.5f);
            gl.glVertex3f(6.5f, -1.5f, -6.5f);
        gl.glEnd();
    }

    public void reshape(GLDrawable arg0, int arg1, int arg2, int arg3,     int arg4)
    {
    }

    public void displayChanged(GLDrawable arg0, boolean arg1, boolean     arg2)
    {
    }
}

Die Methoden reshape(..) und displayChanged(..) werden für dieses Beispiel nicht benötigt.

In der init-Methode wird zuerst die Farbe des GLCanvas bestimmt, die nach dem Löschen der Szene angenommen werden soll. Dies geschieht in der Zeile

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

Der Methode glClearColor(..) werden vier float-Zahlen übergeben. Die ersten drei Parameter stellen die Farbe im RGB-Modell dar. Zulässige Werte sind Zahlen zwischen 0.0 und 1.0. Der letzte Parameter bestimmt den Alpha-Wert.

In der zweiten Zeile rufen wir die Methode setCamera(..) auf, in der wir die Kameraerzeugung ausgelagert haben. Zuerst bestimmen wir mit glViewport(..) wie das gerenderte Bild in das Fenster eingepasst werden soll. Die Parameter geben dabei die obere linke Ecke in Bildschirmkoordinaten an, während die beiden letzten Parameter die Weite und Höhe des gerenderten Bildes angeben:

int w = 500, h = 500;
gl.glViewport(0, 0, w, h);

Nun folgt unsere Kameradefinition. Dafür wird der GL_PROJECTION Matrix Stack verwendet. Für Transformationen bezüglich Objekten und Kamera sollte dagegen der GL_MODELVIEW Matrix-Stack verwendet werden, d.h. auch auch die Positionierung der Kamera muss auf dem GL_MODELVIEW Stack durchgeführt werden [Ker02]:

gl.glMatrixMode(GL.GL_PROJECTION);
gl.glLoadIdentity();

Die letzte Zeile der Methode definiert eine perspektivische Projektion, um sicherzustellen, dass Objekte nach ihrer Position im Raum perspektivisch verzerrt werden:

glu.gluPerspective(50.0, 1, 2.0, 40.0);

Der Methode wird zuerst der Öffnungswinkel der Kamera übergeben, dann das Verhältnis zwischen Höhe und Breite des Viewing-Volumens, sowie die Größe des Viewing-Volumens. Kurzgefasst wird die Information übergeben, ab welchem Punkt im Raum Objekte angezeigt werden (der dritte Paramter near) und ab welcher Distanz Objekte nicht mehr zu sehen sein sollen (der letzte Paramter far).

In der Methode display(..) löschen wir zuerst das Color-Buffer-Bit, dann wechseln wir in die Modelview-Matrix, da nun das Zeichnen der Objekte, in diesem Fall eines Vierecks, das später einmal das Spielfeld für unser "Mensch ärgere dich nicht"-Spiel darstellt, erfolgen soll. Da wir die Kamera leicht nach oben versetzt und schräg auf das Spielfeld sehen lassen wollen, manipulieren wir mit der Methode gluLookAt(..) die Kamerapostion. Auf gluLookAt(..) wird in dem Kapitel über Interaktion im 3D-Raum näher eingegangen. Schließlich setzen wir mit glTranslate(..) die Position des Spielfeldes um eins nach oben und lassen dann von der Methode drawField(..) das Viereck zeichnen.

nach oben