Ob Bildschirmschoner, CAD-System oder Flugsimulator – Programme mit dreidimensionalen Effekten sind auf den meisten Computern zu finden, aber schwer zu schreiben. Ein neues Projekt macht Letzteres leichter und öffnet Skript-Programmierern den Vorhang für 3D-Effekte in ihrer Software.
Programme mit dreidimensionalen bewegten Bildern faszinieren, egal ob sie als Bildschirmschoner die CPU beschäftigen, als CAD-System komplexe Geometrien darstellen oder nur den Spieltrieb befriedigen. Grundlage für die Anzeige unter Linux ist meist die Grafikbibliothek OpenGL [1]. Seit mehr als zehn Jahren dominiert dieser Standard die Szene für professionelles 3D unter Unix und anderen Betriebssystemen.
3D-Grafik für alle
Waren in den Anfangstagen noch teure Workstations von Silicon Graphics notwendig, bieten heutige PCs ausreichend Performance. Da reizt es, selbst 3D-Programme zu entwickeln. Vor den Einsatz von OpenGL haben die Entwickler aber ein umfangreiches C-API gesetzt. Dank Paul Obermeiers neuer Tcl3D-Erweiterung [2] gelingt der Start nun einfacher, sie stellt die meisten OpenGL-Kommandos unter Tcl zur Verfügung.
Tcl und OpenGL sind schon länger verbandelt. Brian Paul entwickelte sein OpenGL-Widget Togl [3] kurz nach der ersten Veröffentlichung von OpenGL. Alle Funktionen zum Erzeugen oder Beleuchten der Modelle mussten Tcl-Entwickler jedoch in C schreiben. Mit Tcl3D ist dies nicht mehr nötig, Skripte haben Zugriff auf den größten Teil des OpenGL-API. Das gilt auch für aktuelle Erweiterungen wie OpenGL 2.0, Nvidias Shader-Bibliothek CG [4] oder den Joystick-Support aus der SDL [5].
Voraussetzungen für die Installation von Tcl3D sind neben dem Tcl-Interpreter eine OpenGL-Library und gegebenenfalls CG und SDL. OpenGL existiert als reine Software-Implementation Mesa [6] oder Hardware-beschleunigt passend zur Grafikkarte. Je nach Hersteller ist das eine offene Implementation im X11-Treiber oder eine proprietäre Variante wie bei ATI oder Nvidia.
Einfache Installation
Die Installation von Tcl3D klappt recht einfach. Auf der Homepage [2] stehen neben den Quellen fertig kompilierte Pakete für Linux bereit, aktuell in der Version 0.3. Wie immer bei Tcl darf die Erweiterung an einer beliebigen Stelle im Filesystem liegen, wenn das Skript den Suchpfad entsprechend erweitert. Einfacher ist es allerdings, wenn die Erweiterung in einem der Standardpfade installiert ist. Diese sind in der Tcl-Variablen » zu finden.
Neben der Tcl3D-Bibliothek empfiehlt es sich, auch das Paket mit den Beispielen zu installieren. Es enthält etwa 100 Programme mit sinnvollen Anregungen für eigene Entwicklungen. Für den Einstieg in OpenGL besonders interessant sind die Unterverzeichnisse »redbook14« und »NeHe«. Das eine enthält die Beispiele aus dem wegen seiner roten Umschlagfarbe Redbook getauften Klassikers OpenGL Programming Guide [7], das andere die Beispiele aus dem OpenGL-Tutorial von [9].
Viele Kommandos
OpenGL und die Begleitbibliotheken umfassen über 300 Kommandos für alle Aufgaben beim Darstellen dreidimensionaler Modelle, die immer aus einzelnen Punkten, Linien, Dreiecken und Vierecken bestehen. Es gibt zwar Routinen für Kugeln oder Quader, aber auch die erstellen nur die Oberfläche. Wer wie in einem CAD-System konstruieren möchte, ist mit fertigen CAD-Systemen oder mit der CAD-Bibliothek Open Cascade [10] wesentlich besser bedient.
Das so genannte Bluebook [8] beschreibt das API von OpenGL, für die meisten Fragen genügt die Onlineversion einer älteren Auflage. Mit Tcl3D stehen die Befehle bis auf wenige Ausnahmen mit gleichen Namen unter Tcl bereit. Die verwendete Abbildung von C auf Tcl ist weitgehend selbsterklärend, eine genaue Beschreibung enthält die Dokumentation. Der Vorteil: Beispiele aus C-Programmen lassen sich ohne großes Nachdenken in Tcl verwenden.
Vorlagen sind am Anfang auch bitter nötig, damit nicht nur ein schwarzes Fenster erscheint. Mit den drei Dimensionen potenzieren sich die Fehlermöglichkeiten: Wer etwa die Beleuchtung vergisst oder die virtuelle Kamera in die falsche Richtung dreht, sieht nichts.
Bühne frei
Der Code in Listing 1 erzeugt das Hello-World-Beispiel von OpenGL, wie in Abbildung 1 zu sehen, ein Drei- und ein Viereck. Zeile 8 fordert die Tcl-Erweiterung an. Falls Tcl3D nicht im üblichen Suchpfad installiert ist, zeigt Zeile 6, wie man den Pfad ergänzt. Das 3D-Widget entsteht dann in Zeile 71 mit dem »togl«-Befehl. Die Optionen »-width« und »-height« sind von anderen Tk-Widgets bekannt, danach kommen die OpenGL-spezifischen.
|
Listing 1: Hello World in |
|---|
01 #!/usr/bin/wish
02 # Einfaches Beispiel für Tcl3d. Basis:
03 # OpenGL-Tutorial von http://nehe.gamedev.net
04
05 # Suchpfad erweitern wenn notwendig:
06 #lappend auto_path /home/cz/tcl3d0.3
07
08 package require tcl3d 0.2
09
10 # Setzt ein paar Startwerte, wird beim Erzeugen des Fensters aufgerufen
11 proc tclCreateFunc {toglwin} {
12 glShadeModel GL_SMOOTH ;# weiche Farbübergänge aktivieren
13 glClearColor 0.1 0.7 1 0.5 ;# die Hintergrundfarbe festlegen
14 }
15
16 # 3D-Modell aufbauen und anzeigen
17 proc tclDisplayFunc {toglwin} {
18 # Farben- und Tiefenspeicher löschen
19 glClear [expr {$::GL_COLOR_BUFFER_BIT | $::GL_DEPTH_BUFFER_BIT}]
20
21 # Anfangsposition setzen
22 glLoadIdentity
23 glTranslatef -1.5 0.0 -10.0
24
25 # Ein rotes Dreieck zeichnen
26 glColor3f 1 1 0
27 glBegin GL_TRIANGLES
28 glVertex3f 0.0 1.0 0.0
29 glVertex3f -1.0 -1.0 0.0
30 glVertex3f 1.0 -1.0 0.0
31 glEnd
32
33 # Viereck mit verschieden farbigen Ecken
34 glTranslatef 3.0 0.0 0.0 ;# Neuer Start
35 glBegin GL_QUADS
36 glColor3f 1.0 0.0 0.0 ;# Erste Ecke rot
37 glVertex3f -1.0 1.0 0.0
38 glColor3f 0.0 1.0 0.0 ;# Zweite grün
39 glVertex3f 1.0 1.0 0.0
40 glColor3f 0.0 0.0 1.0 ;# Dritte blau
41 glVertex3f 1.0 -1.0 0.0
42 glColor3f 1.0 1.0 1.0 ;# Vierte weiß
43 glVertex3f -1.0 -1.0 0.0
44 glEnd
45
46 # Neues Modell anzeigen
47 $toglwin swapbuffers
48 }
49
50 # Passende Ansicht auf das Modell berechnen,
51 # immer wenn sich die Fenstergröße ändert
52 proc tclReshapeFunc {toglwin b h} {
53 # verhindert Teilen durch Null
54 set h [expr {$h<1 ? 1 : $h}]
55
56 # Viewport setzen
57 glViewport 0 0 $b $h
58 glMatrixMode GL_PROJECTION
59 glLoadIdentity
60
61 # Perspektive berechnen und aktivieren
62 set winkel 46
63 set verhaeltnis [expr {double($b)/double($h)}]
64 set von 0.1
65 set bis 100.0
66 gluPerspective $winkel $verhaeltnis $von $bis
67 glMatrixMode GL_MODELVIEW
68 }
69
70 # Fenster aufbauen
71 togl .toglwin -width 640 -height 480
72 -double true -createproc tclCreateFunc
73 -reshapeproc tclReshapeFunc
74 -displayproc tclDisplayFunc
75 pack .toglwin -expand 1 -fill both
|

Abbildung 1: Der Skriptcode in Listing 1 erzeugt ein einfaches 3D-Modell, es besteht aus einem einfarbigen Dreieck und einem mehrfarbigen Viereck. Bei diesem simplen Modell ist die dritte Dimension noch nicht erkennbar, außerdem fehlt Bewegung – dafür ist der Programmaufbau recht gut nachvollziehbar.
Die »-double«-Option sorgt für Double Buffering. Damit zeichnet OpenGL ein neues Bild erst im Hintergrund und tauscht die fertige Grafik dann gegen die bisher dargestellte aus. Das vermeidet Flackern beim Bildaufbau. Zuletzt übergeben die Parameter »-createprop«, »-reshapeproc« und »-displayproc« drei Tcl-Prozeduren an das Widget. Dies wiederum ruft die Createproc einmal beim Initialisieren auf, die Reshapeproc bei jeder Größenänderung und die Displayproc bei jedem Bildaufbau.
Die erste Prozedur heißt »tclCreateFunc« (Zeilen 11 bis 14). Sie initialisiert OpenGL; das ist pro Programmlauf nur einmal nötig. Eine der Entwurfsideen ist, dass Einstellungen immer so lange wie möglich gelten. Ist die Farbe für Geometrie einmal auf Rot gesetzt, färbt OpenGL jedes nachfolgende Objekt rot, egal ob das Programm zehn oder 10 000 Objekte einfügt. Die in Zeile 13 mit »glClearColor« gesetzte Hintergrundfarbe gilt auch für jeden Bildaufbau.
Als Nächstes definiert das Skript von Zeile 17 bis Zeile 48 die Prozedur »tclDisplayFunc«. Tcl3D ruft dieses Callback bei jedem Bildaufbau auf. Nachdem »glClear« in Zeile 19 den bisherige Bildinhalt gelöscht hat, entstehen hier das Drei- und das Viereck. Zudem löscht »glLoadIdentity« den bisherigen Startpunkt sowie die Rotation, »glTranslatef« setzt einen neuen Startpunkt. Das Koordinatensystem entspricht dem in Abbildung 2, die x- und y-Achse bilden die Bildschirmebene, die z-Achse zeigt aus dem Bildschirm heraus.

Abbildung 2: Alles Ansichtssache: Ohne den korrekten Viewport sieht der Betrachter in OpenGL nichts. Der Viewport bildet eine Kamera im virtuellen Raum nach, zeigt aber nur Objekte zwischen den beiden Ebenen.
Zeile 26 setzt mit »glColor3f« die Objektfarbe auf Rot, die Farbwerte stammen aus dem RGB-Modell mit Werten zwischen 0 und 1 (additive Farbmischung aus Rot, Grün und Blau). Danach definiert der Block in den Zeilen 27 bis 31 mit »glBegin GL_TRIANGLES« das erste Dreieck mit den drei Eckpunkten (Vertex) bis hin zum »glEnd«. Zwischen »glBegin« und »glEnd« darf das Skript auch mehrere Dreiecke angegeben.
Die meisten Oberflächen lassen sich mit Dreiecken wunderbar darstellen, aber OpenGL kennt noch weitere Grafik-Primitive wie Punkte, Linien oder Vierecke. Als Beispiel dient das Viereck in den Zeilen 34 bis 44. Nach dem per »glTranslatef« definierten Startpunkt gibt es wieder eine mit »glBegin« und »glEnd« eingefasste Punkteliste. Bei Vierecken ist darauf zu achten, dass alle vier Punkte in einer Ebene liegen. Andernfalls stellt OpenGL sie nicht korrekt dar und es gibt Löcher in der Geometrie.
Objekt mit Farbverlauf
Die Beschreibung der Vierecke enthält zusätzlich eine Farbdefinition für jede Ecke, sodass OpenGL einen Farbübergang auf das Viereck malt. Ist die Geometrie komplett definiert, dann kopiert »swapbuffers« das im Hintergrund gerenderte Bild in das Fenster und die erste mit OpenGL gezeichnet Geometrie erscheint (siehe Abbildung 1).

Abbildung 3: Die Kugel aus Listing 2 besteht aus 2600 Elementen. Trotzdem kann der Benutzer sie mit Maus und Tastatur ruckelfrei bewegen.
Die Prozedur »tclReshapeFunc« (Zeilen 52 bis 68) kommt bei jeder Größenänderung des Widget an den Start. Sie definiert die Ansicht auf das Modell. Die OpenGL-Utility-Bibliothek (Glu) enthält die Funktion »gluPerspective«, die dies bequem erledigt. Die Ansicht heißt Viewport (siehe Abbildung 2). Der Betrachter steht im Koordinatenursprung und erblickt einen durch Höhe und Breite des Fensters begrenzten Ausschnitt des 3D-Modells. Der in Zeile 62 gesetzte Winkel von 46° entspricht dem Blickwinkel des menschlichen Auges.
Der Viewport zeigt nur Modellteile, die im angegebenen Abstand (Zeilen 64 und 65) vom Betrachter liegen. Anders als bei der realen Fotografie werden Teile davor und dahinter nicht unscharf, sondern komplett unsichtbar.
Das Listing 1 hat aber zwei wesentliche Mängel. Einerseits bleibt die dritte Dimension unsichtbar, denn der Betrachter sieht das Modell immer aus der gleichen Perspektive. Das zweite Problem ist die Performance. Die Prozedur »tclDisplayFunc« definiert das 3D-Modell bei jedem Bildaufbau neu. Bei kleineren Modellen klappt das noch problemlos, aber schon mit wenigen 100 Dreiecken wird der Ablauf einfach zu langsam.
Bewegung
Abhilfe schafft ein zweites Beispiel, das eine Kugel aus etwa 2600 Dreiecken erzeugt (Abbildung 3). Diese Kugel bei jedem Bildaufbau neu erzeugen würde aber viel zu viel Zeit kosten. Deshalb legt die Prozedur »tclCreateFunc« (Listing 2a) eine so genannte Display-Liste an. Sie enthält fertige Geometrien, die das Programm später beliebig oft aufrufen darf. Die Verwendung der Display-Liste verbessert die Performance erheblich, denn die Geometrie zu erzeugen dauert je nach Anzahl der Dreiecke 10- bis 1000-mal länger, als sie anzuzeigen.
|
Listing 2a: |
|---|
01 # Startwerte setzen und Display-Liste erzeugen.
02 # Wird beim Erzeugen des Fensters aufgerufen
03 proc tclCreateFunc {toglwin} {
04 # Schwarzer Hintergrund
05 glClearColor 0.0 0.0 0.0 0.0
06
07 # Ein bischen Tuning
08 glClearDepth 1.0
09 glEnable GL_DEPTH_TEST
10 glShadeModel GL_FLAT
11 glDepthFunc GL_LEQUAL
12 glHint GL_PERSPECTIVE_CORRECTION_HINT GL_NICEST
13
14 kugel 100 ;# Display-Liste einmalig erzeugen
15 }
|
Die Prozedur »kugel« (Listing 2b) erzeugt erst eine neue Display-Liste und füllt sie dann – ähnlich wie im ersten Beispiel – mit Dreiecken. Dabei haben die Dreiecke abhängig vom Breitengrad jeweils eine andere Farbe, hierzu verwendet das Skript die Prozedur »hls2rgb« (zu finden auf dem FTP-Server des Linux-Magazins [13]).
|
Listing 2b: Kugelmodell |
|---|
01 proc kugel {radius} {
02 set kante 10
03 set ::displayliste [glGenLists 1]
04 glNewList $::displayliste GL_COMPILE
05 for {set l 0} {$l <= 360} {incr l 5} {
06 for {set b -90 } {$b <= 90} {incr b 5} {
07 # Position im Bogenmaß
08 set lr [expr {$l/180.0 * $::PI}]
09 set br [expr {$b/180.0 * $::PI}]
10 # Farbe für das nächste Element setzen
11 set hue [expr {sin($br/3.0)}]
12 eval glColor3f [hls2rgb $hue 1 1]
13
14 # Dreieck einfügen
15 glBegin GL_TRIANGLES
16 glVertex3f
17 [expr {$radius*cos($lr)*cos($br)}]
18 [expr {$radius*sin($lr)*cos($br)}]
19 [expr {$radius*sin($br)}]
20 glVertex3f
21 [expr {$radius*cos($lr)*cos($br)}]
22 [expr {$radius*sin($lr)*cos($br) +$kante}]
23 [expr {$radius*sin($br)}]
24 glVertex3f
25 [expr {$radius*cos($lr)*cos($br)}]
26 [expr {$radius*sin($lr)*cos($br)}]
27 [expr {$radius*sin($br) +$kante}]
28 glEnd
29 }
30 }
31 glEndList
32 }
|
Transformation
In der »tclDisplayFunc« gibt es zwei Änderungen (Listing 2c) gegenüber dem ersten Beispiel. Statt die Kugel an einer bestimmten Stelle einzufügen, legen die beiden Tcl3D-Funktionen »glTranslatef« und »glRotatef« eine neue Anfangsposition und -orientierung fest (Zeilen 6 bis 9). Ändern sich die Variablen für die Position und Orientierung, dann befindet sich die Kugel an einer anderen Stelle. Statt die Position des Betrachters zu verändern, bewegt das vollständige Skript das ganze Modell.
|
Listing 2c: Kugelmodell |
|---|
01 proc tclDisplayFunc {toglwin} {
02 # Screen und Depth Buffer löschen
03 glClear [expr {$::GL_COLOR_BUFFER_BIT | $::GL_DEPTH_BUFFER_BIT}]
04
05 # Anfangsposition setzen
06 glLoadIdentity
07 glTranslatef $::Posx $::Posy $::Posz
08 glRotatef $::Rotx 1.0 0.0 0.0
09 glRotatef $::Roty 0.0 1.0 0.0
10 glRotatef $::Rotz 0.0 0.0 1.0
11
12 # Display-Liste abrufen
13 glCallList $::displayliste
14
15 $toglwin swapbuffers
16 }
|
Um die Aktionen des Benutzers zu interpretieren, verwendet das Skript die Tk-übliche Binding-Technik zusammen mit Callback-Funktionen. Die Callbacks reagieren auf Events der Maus und der Tastatur. Ein Druck auf die Cursortasten verschiebt die Kugel. Drehen am Scrollrad ändert den Abstand, bei gedrückter linker Maustaste dreht dieselbe Aktion die Kugel. Die Callbacks rufen zusätzlich ».toglwin postredisplay« auf, um nach getaner Arbeitet einen Neuaufbau des Bildes zu erzwingen.
Wer die Ergebnisse aus Tcl3D hochauflösend drucken möchte, kann dies dank Ian Gay und dessen Projekt »tclgl2ps« [11]. Das Programm erzeugt echte, skalierbare Postscript-Dokumente. Damit gelingt es, die 3D-Ansichten in voller Qualität aufs Papier zu bringen.
Weiter geht’s

Abbildung 4: Statt jedes Element eines 3D-Modells per Skript anzulegen, können OpenGL-Programme auch fertige 3D-Modelle laden und darstellen.
Mit diesen Beispielen sind die ersten Schritte in OpenGL gemacht. Sie zeigen, wie ein Skript die Geometrie und die Ansichten definiert und ändert. In den wenigsten Fällen wird das Programm aber einzelne Dreiecke erzeugen, viel sinnvoller ist es, fertige Geometrien für Gebäude oder Fahrzeuge aus Modellierwerkzeugen wie Blender [12] zu verwenden (Abbildung 4). Dies und Weiteres zum Thema OpenGL wird eine der nächsten Ausgaben des Linux-Magazins vorstellen. (fjl)
|
Infos |
|---|
|
[1] OpenGL: [http://www.opengl.org] [2] Tcl3D: [http://www.tcl3d.org] [3] Togl: [http://togl.sourceforge.net] [4] Stephan Siemen, “Glänzende Effekte – 3D-Grafiken mit Shader”: Linux-Magazin 02/04, S. 98 sowie [http://developer.nvidia.com/object/cg_toolkit.html] [5] SDL: [http://www.libsdl.org] [6] Mesa: [http://www.mesa3d.org] [7] Mason Woo, Jackie Neider und Tom Davis, “OpenGL Programming Guide” (Redbook): Addison-Wesley [8] Dave Shreiner, “OpenGL Reference Manual” (Bluebook): Addison-Wesley sowie [http://www.rush3d.com/reference/opengl-bluebook-1.0] [9] Nehe: [http://nehe.gamedev.net] [10] Open-Cascade-Bibliothek: [http://www.opencascade.org] [11] Tclgl2ps: [http://www.sfu.ca/~gay/tclgl2ps.zip] [12] Blender: [http://www.blender.org] [13] Downloads zum Artikel: [ftp://ftp.linux-magazin.de/pub/listings/magazin/2006/07/3D-Skripting] |
|
Der Autor |
|---|
|
Carsten Zerbst lebt in Hamburg und arbeitet im Bereich der Produktdatenverwaltung bei einem großen Dienstleister. |





