Miguel Angel Sepulveda Über den Autor: Miguel machte seinen Abschluß an der Washingtoner Universität im Jahre 1993. Danach arbeitete er als Wissenschaftler in Spanien, Japan und den USA. Mit Linux traf er das erste Mal zusammen, als der Kernel noch bei Version 0.98 stand, es war Liebe auf den ersten Blick. Gegenwärtig ist er in seiner Freizeit Chefredakteur des LinuxFocus. Inhalt |
Zusammenfassung:
Parallel zu der Artikelserie über die OpenGL Programmierung läuft
eine Reihe über GLUT, eine GUI Hilfsbibliothek, auf die häufig
bei der OpenGL Programmierung zurückgegriffen wird. Im ersten Teil der
Serie werden Fenstersteuerung und Animation behandelt.
Aus konzeptionellen Gründen wurden in OpenGL alle Abhängigkeiten von irgendeinem Fenstersystem vermieden. Das Resultat ist eine portable, schlanke und effiziente 2D/3D Rendering Bibliothek. Es bleibt dem jeweiligen Fenstersystem überlassen, Fenster zu öffnen und darzustellen. Zwischen dem Fenstersystem und OpenGL vermitteln weitere Hilfsbibliotheken, zum Beispiel dient die GLX Hilfsbibliothek dem Zusammenspiel von OpenGL und dem X Window System.
Das OpenGL Utility Toolkit (GLUT) ist eine Programmierschnittstelle, mit deren Hilfe man unabhängig von einem bestimmten Fenstersystem, OpenGL Programme schreiben kann. GLUT bietet C und FORTRAN Schnittstellen an. Geschrieben wurde die Bibliothek von Mark J. Kilgard. Sie schließt die Lücke, die in der OpenGL Spezifikation (bewußt) aufgelassen worden war. OpenGL Anwendungen, die GLUT verwenden, können einfach zwischen verschiedenen Plattformen portiert werden,ohne daß große Änderungen am Quelltext vorgenommen werden müssen. GLUT vereinfacht ohne Zweifel die Programmierung von OpenGL Software und vervollständigt die OpenGL Bibliothek.
GLUT ist relativ klein und die Programmierung einfach zu erlernen. Die Bibliothek ist nicht nur sehr schön designed worden, der Autor hat für sie auch eine erstklassige Dokumentation erstellt. Deswegen mag es ein wenig überflüssig erscheinen, im LinuxFocus eine Artikelserie über GLUT zu veröffentlichen. Jedem Programmierer, der ernsthaft an der GLUT Programmierung interessiert ist, sollte Marks Dokumentationen lesen. Anliegen dieser GLUT Kolumne ist es, die Bibliothek und ihre Verwendung anhand von Beispielen Schritt für Schritt vorzustellen, und zwar in Verbindung mit der Artikelserie über OpenGL. Wir hoffen, dadurch einen brauchbaren Beitrag zu leisten, und so mehr Programmierer dazu annimieren zu können, auf den OpenGL-Linux Zug aufzuspringen. Auf jeden Fall sollte der Leser sich Marks Beschreibungen besorgen, da sie eine ausgezeichnete Referenz darstellen.
Die GLUT API ("Application Programming Interface" - Bibliothek) ist wie OpenGL ein Zustandsautomat. Dies bedeutet, daß es eine Anzahl von Variablen in GLUT gibt, die während der Ausführung einer Anwendung existieren. Die Anfangszustände des GLUT Automaten wurden mit Bedacht so gewählt, daß sie für die meisten Programme brauchbar sind. Die Programme können diese Variablen ihren Zielen entsprechend manipulieren. Wann immer eine GLUT Funktion aufgerufen wird, wird ihre Handlung von entsprechend den jeweiligen Werten der Zustandvariablen verändert. GLUT Funktionen sind recht einfach und erwarten meist nur wenige Parameter. Es werden keine Zeiger zurückgegeben, und die wenigen Zeiger, die an die Funktionen übergeben werden, verweisen auf Zeichenketten und transparente Font-Handler.
Aufgrund ihrer Funktionalität können die GLUT Funktionen in verschiedene Bereiche unterteilt werden:
Jedes OpenGL Programm, daß GLUT nutzt, muß zu Beginn den GLUT Zustandsautomaten initialisieren. Die Funktionen füt die GLUT Initialisierung fangen mit dem Präfix glutInit- an. Die Hauptroutine ist glutInit.
Syntax:
glutInit(int **argcp, char
**argv);
argcp ist ein Zeiger auf die
(unveränderten) Parameter argc der main Funktion
des Programmes. Nach Beendigung ist der Wert, auf den argcp zeigt verändert, da
glutInit jeden Kommandozeilen-Parameter, der für GLUT relevant ist, extrahiert.
Zum Beispiel unter X jeden Parameter, der für das X Fenster relevant ist,
das mit dem GLUT Fenster assoziiert ist.
argv
ist die (unveränderte) argv Variable der main Funktion.
glutInit übernimmt die Initialisierung des GLUT Zustandsautomaten und handelt eine Session mit dem Fenstersystem aus. Es können einige wenige Funktionen vor glutInit aufgerufen werden, und zwar solche, die mit glutInit- anfangen. Durch diese Routinen können die Standardwerte der Initialsierungszustände für das Fenster gesetzt werden.
Beispiel::
glutInitWindowPosition(int x,
int y);
glutInitWindowSize(int width,
int height);
x,y =
Bildschirmkoordinaten des Fensters (seiner oberen linken Ecke)
width,height =
Breite, Höhe des Fensters in Pixel.
es gibt eine weitere Initialisierungsroutine, die in jeder OpenGL Anwendung zu finden ist, glutInitDisplayMode():
Syntax:
glutInitDisplayMode(unsigned int mode);
mode
ist der Darstellungsmodus, gebildet aus GLUT Darstellungsmodi, die bitweise
mittels OR verknüpft sind. Mögliche Werte für Verknüpfungen sind:
GLUT_RGBA | Wählt den RGBA Modus für das Fenster aus. Dies ist der Standardmodus, falls weder GLUT_RGBA noch GLUT_INDEX gesetzt wurden. |
GLUT_RGB | Wie GLUT_RGBA. |
GLUT_INDEX | Auswahl des Farbindex Modus des Fensters. Überschreibt GLUT_RGBA. |
GLUT_SINGLE | Single Buffer Modus des Fensters. Standard Einstellung. |
GLUT_DOUBLE | Double Buffering des Fensters. Überschreibt GLUT_SINGLE. |
GLUT_ACCUM | Auswahl eines Fensters mit Akkumulation Puffer. |
GLUT_ALPHA | Wählt ein Fenster mit Alpha-Komponente des Farbpuffers aus. |
GLUT_DEPTH | Auswahl eines Fensters mit einem Tiefenpuffer. |
GLUT_STENCIL | Wählt ein Fenster mit einem Puffer für Schablonen aus. |
GLUT_MULTISAMPLE | Wählt ein Fenster mit Multisampling Unterstützung aus. |
GLUT_STEREO | Auswahl eines Stereofensters. |
GLUT_LUMINANCE | Ein Stereofenster mit einem Beleuchtungs (luminance) Farbmodell. |
#include <GL/glut.h> void main(int argcp, char **argv){ /* Set window size and location */ glutInitWindowSize(640, 480); glutInitWindowPosition(0, 0); /* Select type of Display mode: Single buffer & RGBA color */ glutInitDisplayMode(GLUT_RGBA | GLUT_SINGLE); /* Initialize GLUT state */ glutInit(&argcp, argv); .....more code }; |
Hier nun die Initialisierung einer Animation:
#include <GL/glut.h> void main(int argcp, char **argv){ /* Set window size and location */ glutInitWindowSize(640, 480); glutInitWindowPosition(0, 0); /* Select type of Display mode: Double buffer & RGBA color */ glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE); /* Initialize GLUT state */ glutInit(&argcp, argv); .....more code }; |
Diese Beispiele werden später nochmals benutzt werden, um mehr über GLUT zu zeigen. Hauptunterschied ist, daß im zweiten Beispiel die Darstellung im Double Buffer Modus initialisiert wird, welcher ideal für Animationen ist, da so das Flackern des Bildes bei dem Wechsel zwischen den einzelnen Bildern der Animation eliminiert wird.
Wie schon zuvor erwähnt, ist GLUT ein Zustandsautomat. Nun wird gezeigt, daß
es auch ereignisgesteuert ist. Das bedeutet, daß es einen "Timer" oder
eine Endlosschleife gibt, der/die nach dem Einsatz der jeweiligen Initialisierungsfunktionen
gestartet wird und alle Ereignisse (Events), die während der Initalisierung ausgwählt
und GLUT mitgeteilt worden sind, eines nach dem anderen verarbeitet. Solche Ereignisse sind:
Ein Mausklick, das Schließen eines Fensters, die Umgestaltung eines Fensters (Reshape),
eine Cursorbewegung, ein Tastendruck, und, um so merkwürdiger, das "idle" Ereignis
("idle" = unbeschäftigt), d.h. ein Ereignis, daß auftritt, wenn sich nichts ereignet hat!
Jedes dieser Ereignisse muß in einer der GLUT Zustandsdvariablen registriert werden,
damit der "Timer" oder die Verarbeitungs-Schleife periodisch nachprüft, ob dieses
Ereignis durch den Benutzer ausgelöst wurde.
Zum Beispiel könnte das Ereignis "Mausklick" als Ereignis registriert
werden, damit GLUT danach Ausschau hält. Ereignisse werden durch die Registrierung von
Callback Funktionen ausgwählt. Alle diese Funktionen haben die Syntax glut{jeweiliges Ereignis}Func,
im Falle des Mausklickes wäre es glutMouseFunc.
Durch die Registrierung der Callback Funktion wird dem GLUT Automaten mitgeteilt, welche benutzerdefinierte
Funktion aufgerufen werden soll, falls das entsprechende Ereignis eingetreten ist. Wird also die
Funktion MyMouse geschrieben, welche beschreibt, was bei einem Mausklick passieren soll,
kann diese als Callback Funktion nach glutInit in main() mittels der Anweisung
"glutMouseFunc(MyMouse);" registriert werden.
Später wird genauer betrachtet, welche Callback Funktionen und Ereignisse in GLUT möglich sind. Wichtig ist, daß nach der Registrierung aller wichtigen Ereignisse im Programm, die entsprechende Routine zur Verarbeitung der Ereignisse von GLUT, namentlich glutMainLoop() aufgerufen wird. Diese Funktion wird nicht mehr beendet, das Programm startet quasi eine Endlosschleife. Diese wird, wenn notwendig, jede Callback Funktion aufrufen, die vorher registriert worden ist. Jede main() Funktion einer OpenGL Anwendung muß also mit der Anweisung glutMainLoop() enden.
Im Fall des Beispieles der Animation:
#include <GL/glut.h> void main(int argcp, char **argv){ /* Initialize GLUT state */ glutInit(&argcp, argv); glutInitWindowSize(640, 480); glutInitWindowPosition(0, 0); /* Open a window */ glutCreateWindow("My OpenGL Application"); /* Select type of Display mode: Double buffer & RGBA color */ glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE); /* Register Callback Functions */ ..... /* Start Event Processing Engine */ glutMainLoop(); }; |
Man beachte, daß hier Kode auftritt, der vorher nicht beschrieben worden ist. Es handelt sich hierbei um eine der Rountinen zur Fenstersteuerung in GLUT, glutCreateWindow(char **name). Dies zeigt wieder sehr schön die Designphilosophie von OpenGL & GLUT, der Name der Funktion macht schon ganz klar, was diese tut! Sie kümmert sich ebenfalls darum, daß die Ordnung an das zugrundeliegende Fenstersystem weitergereicht wird und ein Fenster für das OpenGL Programm geöffnet wird. Das Fenster trägt den Namen name, der als (Zeiger auf eine) Zeichekette übergeben wird. Unter X wird dieser Name in der oberen linken Ecke des Fensters angezeigt. Der Teil von GLUT, der für die Fensterverwaltung zuständig ist, besitzt noch viele weitere Funktionen, die zum Teil noch diskutiert werden. Für den Augenblick reicht diese eine hier aus. Die Routinen zur Initialisierung wurden umplaziert, damit gezeigt wird, daß diese hinter glutInit() stehen können.
Zurück zu den Ereignissen...es werden nun zwei Funktionen zur Registrierung von Callback Routinen vorgestellt, die fundamental für jede Animation sind: die glutDisplayFunc, die die Funktion zur Darstellung für das gegenwärtige Fenster einsetzt und die glutIdleFunc, die die "Idle" Callback Routine registriert. Beide Funktionen erwarten eine Funktion vom Typ void *(void) als Parameter. Angenommen, es werden zwei Callback Funktionen zusätzlich zu dem Animationsbeispiel erstellt. void MyDisplay(void) kümmert sich um die Aufrufe der jeweiligen OpenGL Funktionen,welche die aktuelle Szene im Fenster realisieren sollen und void MyIdle(void), die jedes Mal aufgerufen wird, wenn keine Benutzereingabe vorliegt. Jedes Mal also, wenn der Ereignisgsteuerte Automat von GLUT die Endlosschleife durchlaufen hat (glutMainLoop) und kein Ereignis aufgetreten ist, wird MyIdle ausgeführt. Warum sollte eine Idle Callback Funktion in einer Animation eingesetzt werden ? Nun, dadurch kann jedes Bild (Frame) der Animation unabhängig von jeglicher Benutzereingabe während des Ablaufes der Animation modifiziert werden. Dafür muß es aber eine Funktion geben, die immer wieder während des Ablaufes des OpenGL Programmes aufgerufen wird und das Bild ändert, bevor es mittels myDisplay dargestellt wird. Und das ist die Idle Callback Routine.
Zum Schluß noch ein einfaches Beispiel einer Animation:
#include <GL/glut.h> void MyIdle(void){ /* Some code to modify the variables defining next frame */ .... }; void MyDisplay(void){ /* Some OpenGL code that draws a frame */ .... /* After drawing the frame we swap the buffers */ glutSwapBuffers(); }; void main(int argcp, char **argv){ /* Initialize GLUT state */ glutInit(&argcp, argv); glutInitWindowSize(640, 480); glutInitWindowPosition(0, 0); /* Open a window */ glutCreateWindow("My OpenGL Application"); /* Select type of Display mode: Double buffer & RGBA color */ glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE); /* Register Callback Functions */ glutDisplayFunc(MyDisplay); glutIdleFunc(MyIdle); /* Start Event Processing Engine */ glutMainLoop(); }; |
Am Ende von MyDisplay wird eine neue GLUT Routine, gltSwapBuffers(), aufgerufen. Die ist vor allem in Animationen recht nützlich. Wird im DOUBLE Buffer Modus gearbeitet, so wird immer ein Puffer angezeigt, während der andere unsichtbar ist. Die zeichnenden OpenGL Befehle arbeiten immer im versteckten Puffer. Der Aufruf glutSwapBuffers vertauscht die Puffer, wodurch der Pufferinhalt so in einem Zug im Fenster dargestellt wird. Dies ist ein übliches Verfahren bei Computeranimationen, da so dem menschlichen Auge der stückweise Bildaufbau verborgen bleibt.
Der Leser sollte nun genug Wissen haben, um anzufangen, eigene OpenGL Programme zu schreiben. Das Einzige, was fehlt, sind die OpenGL Befehle in MyDisplay welche das Zeichnen selbst übernehmen...das ist aber eine andere Geschichte ;-).
Der nächste Artikel über die Programmierung von GLUT geht verstärkt auf die Möglichkeiten der Fenstersteuerung ein, und wie man mehrere Szenen im selben Fenster realisiert. Auch der Einsatz von Menüs, sowie ihre Vor- und Nachteile unter dem Gesichtspunkt der Portabilität, werden beleuchtet.
This website is maintained by Miguel Angel Sepulveda © Miguel Angel Sepulveda 1998 LinuxFocus 1998 |