Einführung in die Echtzeit-3D-Grafik mit Java - Bachelorarbeit Daniel Egger September 2005
←
→
Transkription von Seiteninhalten
Wenn Ihr Browser die Seite nicht korrekt rendert, bitte, lesen Sie den Inhalt der Seite unten
Department für Informatik Universität Fribourg, Schweiz http://diuf.unifr.ch/ Einführung in die Echtzeit-3D-Grafik mit Java Bachelorarbeit Daniel Egger September 2005 Unter der Aufsicht von: Prof. Dr. J. PASQUIER -ROCHA und Dr. P. F UHRER Software Engineering Group
ii Zusammenfassung Das Ziel dieser Bachelorarbeit ist es, wie der Titel eigentlich schon treffend ausdrückt, eine Einführung in die Echtzeit-3D-Graphik mit Java zu geben. Als erster Schritt wurden dazu die verfügbaren Optionen untersucht und angeschaut. Nach Auswahl der Java 3D-Engine jMonkey Engine auch jME genannt [36] wurden deren Einsaztmöglichkeiten getestet. Im dritten und letzten Teil der Arbeit wurde ein Tutorial ge- schrieben, um den Lesern zu zeigen, wie man mit Hilfe von jME Echtzeit-3D-Grafik auf den Bildschirm bringen kann. Es ist vor allem das Resultat dieses Tutorials, was man im Rapport finden kann. Schon diese Einleitung beinhaltet einige Wörter, die den einen oder anderen Leser vielleicht bereits verwirren, ich hoffe aber im Verlauf des Textes die meisten Unklarheiten klären zu können. Es wird aber voraus- gesetzt das die Leser zumindest einige Grundkenntnisse in Java haben oder zumindest gute Kenntnisse in einer anderen objektorientierten Programmiersprache. Falls sie noch einige Probleme mit Java haben, kann ich das Buch Thinking in Java von Bruce Eckel[Eck02] empfehlen, das mir einen hervorragenden Einstieg in die objektorientierte Programmierung mit Java bereitet hat. Schlüsselwörter: 3D-Graphik, 3D-Engine, Echtzeit-Rendering, Java, jMonkey Engine, OpenGL,
Inhaltsverzeichnis I. Einleitung 2 1. Bachelorprojekt 3 1.1. Beschreibung der Aufgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 1.2. Projektablauf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 1.2.1. 1. Schritt: Auswahl einer 3D-Engine . . . . . . . . . . . . . . . . . . . . . . . . 3 1.2.2. 2. Schritt: Machbarkeitstest . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 1.2.3. 3. Schritt: Tutorial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 1.3. Gliederung der Dokumentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 1.4. Konventionen und Notationen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 2. 3D-Grafik 8 2.1. Was ist 3D-Grafik? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 2.2. Was bedeutet Echtzeit-3D-Grafik? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 2.3. Was ist eine 3D-Engine? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 2.4. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 II. Tutorial 14 3. Erste Schritte mit jME 15 3.1. Unser erstes jME-Programm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 3.2. Scenegraph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 3.2.1. Was ist ein Scenegraph? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 3.2.2. Zustände im Kontext von jME . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 4. Wie funktioniert 3D-Grafik 22 4.1. Was macht eine 3D-Engine? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 4.2. Das 3D-Koordinatensystem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 4.2.1. 2D-Koordinatensystem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 4.2.2. 3D-Koordinatensystem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 4.2.3. Model-Space vs. World-Space . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 4.3. Transformationen im Raum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 4.3.1. Verschiebungen (engl. translation) . . . . . . . . . . . . . . . . . . . . . . . . . 25 4.3.2. Rotationen (engl. rotation) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 4.3.3. Skalierungen (engl. scaling) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 4.3.4. Alle Bewegungen zusammen . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 4.4. Perspektive und Projektion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 4.5. Kamera . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 iii
Inhaltsverzeichnis iv 4.6. jME, OpenGL, DirectX, 3D-Pipeline: Was ist das? Was machen die? . . . . . . . . . . . 29 4.7. Ressourcen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 5. Interaktion 31 5.1. Einfache Interaktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 5.2. Ein objektorientierter Ansatz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 6. Simple Formen und Modelle 37 6.1. Einfache Formen und warum die Kugeln nicht rund sind . . . . . . . . . . . . . . . . . 37 6.2. Komplexere Modelle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 6.2.1. Modell-Formate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 6.2.2. Modelle laden in jME . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 7. Es werde Licht 44 7.1. Lichtquellen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 7.2. Lichter einsetzen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 8. Texturen 50 8.1. Was sind Texturen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 8.2. Texturen einsetzen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 8.3. Wie geht es weiter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 9. Landschaften 56 9.1. Einführung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 9.2. Heightmaps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 9.3. Landschaften darstellen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 9.4. Landschaften mit Texturen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 9.5. Skybox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 10. Final Island 68 10.1. Der Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 10.2. Fazit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 A. CD-ROM 73 B. Ein jME-Demo in der Konsole starten und kompilieren 75 B.1. Demos starten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 B.2. Demos kompilieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 C. Verwendete Software 77 C.1. Entwicklung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 C.1.1. Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 C.1.2. Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 C.2. Dokumentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 C.2.1. LATEX und Co . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 C.2.2. Violet UML-Tool . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 C.2.3. Dia Diagramm-Editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
Abbildungsverzeichnis 1.1. Screenshot aus dem finalen Projekt des Gameversity Kurses . . . . . . . . . . . . . . . 6 2.1. Ein Screenshot von Blender . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 2.2. Ein Ausschnitt aus “Geri’s Game” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 2.3. Ausschnitt aus dem Film Madagascar . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 2.4. Zeitlinie Echtzeit-3D-Spiele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 3.1. Screenshot aus HelloWorld . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 3.2. Screenshot des Einstellungsdialogs der bei jedem Start erscheint . . . . . . . . . . . . . 17 3.3. Eine Hierarchie von einem Haus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 3.4. UML-Diagramm der Scenegraph Elemente . . . . . . . . . . . . . . . . . . . . . . . . 20 3.5. UML Diagramm der RenderStates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 4.1. Ein rechthändiges 3D-Koordinatensystem . . . . . . . . . . . . . . . . . . . . . . . . . 23 4.2. Ein Würfel rotiert, bewegt und skaliert . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 4.3. Projektionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 4.4. 3D-Pipeline . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 4.5. jME und OpenGL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 5.1. Die verschiedenen InputActions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 6.1. Ein Screenshot aus der Demo SimpleGeometry.java . . . . . . . . . . . . . . . . . . . . 38 6.2. Milkshape 3D mit dem Ferrari Modell . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 6.3. Screenshot von ModelLoader.java mit Blick auf das Gittermodell . . . . . . . . . . . . . 41 7.1. Eine Szene mit Licht (links) und ohne Licht (rechts) . . . . . . . . . . . . . . . . . . . . 45 7.2. Ein Punktlicht . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 7.3. Screenshot aus der Licht Demo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 8.1. Eine Stadt mit und ohne Textur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 8.2. Textur vom Ferrari Modell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 8.3. Screenshot von der Texturen Demo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 8.4. Beispiel für Bump-Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 9.1. Screenshot aus Oblivion von Bethesda Softworks . . . . . . . . . . . . . . . . . . . . . 57 9.2. Eine sehr einfache 3D-Landschaft . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 9.3. Ein Beispiel für eine Heightmap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 9.4. Eine Landschaft generiert aus der Heightmap aus Abbildung 9.3 . . . . . . . . . . . . . 59 9.5. Screenshot aus TerrainDemo.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 v
Abbildungsverzeichnis vi 9.6. Unsere Landschaft mit einigen saftigen, grünen Hügeln . . . . . . . . . . . . . . . . . . 61 9.7. Rechts die Grastextur, links die Detailtextur . . . . . . . . . . . . . . . . . . . . . . . . 62 9.8. Würfelbild mit der Skybox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 9.9. Screenshot von der Skybox Demo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 10.1. Final Island: Blick auf das Wasser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 10.2. Final Island: Blick auf das Jeep-Modell . . . . . . . . . . . . . . . . . . . . . . . . . . 69 10.3. Beispiel für eine Particle Engine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 A.1. CD-Rom Inhalt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
Listings 3.1. HelloWorld.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 3.2. HelloWorld.java, simpleInitGame . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 4.1. SimpleTransformation.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 5.1. InputDemo.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 5.2. InputActions.java simpleInitGame and simpleUpdate . . . . . . . . . . . . . . . . . . 34 5.3. InputActions.java KeyNodeUpAction . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 6.1. SimpleGeometry.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 6.2. ModelLoader.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 7.1. SimpleLight.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 7.2. Lichter aktivieren mit LightStates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 8.1. TextureDemo.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 9.1. TerrainDemo.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 9.2. TerrainWithTexture.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 9.3. SkyBoxTest.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 9.4. SkyBox.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 10.1. FinalIsland.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 1
Bachelorprojekt 1 1.1. Beschreibung der Aufgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 1.2. Projektablauf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 1.2.1. 1. Schritt: Auswahl einer 3D-Engine . . . . . . . . . . . . . . . . . . . . . . . 3 1.2.2. 2. Schritt: Machbarkeitstest . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 1.2.3. 3. Schritt: Tutorial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 1.3. Gliederung der Dokumentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 1.4. Konventionen und Notationen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 1.1. Beschreibung der Aufgabe Ziel dieser Bachelorarbeit ist es, Leuten mit Programmiererfahrung, die 3D-Grafikprogrammierung nä- her zu bringen. Es wird sogar angenommen das die Zielgruppe keinerlei spezielle vorherige Kenntnis- se der 3D-Grafik hat. Das bringt natürlich einige Einschränkungen mit sich. Einerseits will man zwar schnell einige sehenswerte Ergebnisse haben, deshalb wurde auch eine Engine ausgewählt, mit der man schnell etwas auf den Bildschirm “zaubern” kann. Andererseits sollte man trotzdem ein wenig die Theo- rie verstehen. Das ist keine leichte Aufgabe, wenn man bedenkt wie breit und zum Teil auch komplex allein der Teilbereich des 3D-Echtzeitrendering ist. Ich selbst bin häufig auf Schwierigkeiten gestossen, ein bestimmtes Teilgebiet dieser Materie zu erklären. Aber ich hoffe trotz allen Problemen und Schwie- rigkeiten ist das Tutorial immer noch verständlich. 1.2. Projektablauf Das ganze Bachelorprojekt ist in drei Hauptschritten angegangen worden. Im ersten Schritt wurden ver- schiedene 3D-Engines evaluiert um dann eine von diesen auszuwählen. Im zweiten Schritt wurde eine kleine Machbarkeitsstudie ausgeführt um zu sehen. Es wurde eine grössere Beispielanwendung ent- wickelt, die auch das Ziel des eigentlichen Tutorials ist. Im dritten und letzten und bei weitem auch umfangreichsten Schritt wurde das Tutorial selbst mit sehr vielen kleinen Beispielanwendungen ge- schrieben. Aber gehen wir auf die einzelnen Schritte doch noch ein wenig näher ein. 1.2.1. 1. Schritt: Auswahl einer 3D-Engine Ziel des ersten Abschnitts meiner Bachelorarbeit bestand darin einen Überblick über die fast schon unzähligen 3D-Engines zu gewinnen und danach eine 3D-Engine für den weiteren Verlauf des Projektes auszuwählen. 3D-Engines gibt es wie Sand am Meer möchte man fast meinen. Allein eine Suche auf Google mit dem Stichwort “3D Engine” ergibt 469’000 Ergebnisse (Stand: August 2005) und die Zahl 3
1.2. Projektablauf 4 ist steigend. Man kann sich vorstellen, dass unter diesen Bedingungen schon dieser erste Schritt nicht ganz leicht ist. Um die Auswahl etwas zu vereinfachen und zu systematisieren wurden einige Kriterien erstellt. Im einzelnen sind das folgende Kriterien: 1. Die 3D-Engine muss mit Java benutzt werden können und auf verschiedenen Plattformen laufen, nicht nur auf Windows sondern zum Beispiel auch auf Linux oder MacOSX. 2. Die 3D-Engine sollte frei verfügbar und gratis benutzbar sein. Am besten wäre eine Engine die unter einer Open-Source-Lizenz veröffentlicht wurde, die dieses Kriterium langfristig garantiert. 3. Die 3D-Engine sollte ein gutes objektorientiertes Design vorweisen und ein “sauberes” Frame- work zur Verfügung stellen. 4. Die Engine sollte aktuelle Techniken des Echtzeit-Rendering unterstützen. 5. Die 3D-Engine sollte auch ausreichend dokumentiert sein. Die oben genannten Kriterien ergeben nun einige Konsequenzen, die ich kurz aufgreifen möchte: • Punkt eins und drei verlangen, dass die Engine in einer objektorientierten Sprache geschrieben ist. (Falls möglich in Java, aber dazu weiter unten noch mehr.) Engines die noch in C, oder anderen prozeduralen Sprachen geschrieben sind fallen weg. • Punkt zwei schliesst von vorne Herein viele im Spielesektor bekannte, aber auch sehr teure (die Lizenzkosten belaufen sich zum Teil auf mehrere hunderttausend Dollar) Engines aus wie die Doom3-Engine von id-Software [29], die Source-Engine von Valve [58], die CryEngine [16] von Crytek, die Unreal3-Engine [57] von Epic, etc... • Punkt vier erfordert schlussendlich eine moderne, hardwarebeschleunigte Engine. Das bedeutet, dass die Engine entweder Direct3D [20] oder OpenGL [47] unterstützen muss, wobei Direct3D als Windows-only gleich wieder von der Liste verschwindet. Als Konsequenz davon müssen Software-Renderer gleich wieder von der Liste verschwinden. Der in Java-Kreisen bekannteste Vertreter eines Softwarerenderers dürfte dabei Java3D [33] von Sun sein. Als Randnotiz muss man aber beachten, dass seit der kürzlichen Open-Source Veröffentlichung von Sun auch von einer 3D-Beschleunigung durch OpenGL diskutiert wird. • Punkt fünf filtert schon viele Engines aus, die erst in den Kinderschuhen stecken oder gar nie richtig aus der Konzeptphase herausgekommen sind. Mit Hilfe dieser Kriterien wurde nun eine Internetrecherche durchgeführt und die ersten Engines zur näheren Betrachtung ausgewählt. Eine grosse Hilfe war dabei die 3D Engines Database [1]. Die Daten- bank ist sehr umfangreich und man kann die Engines sogar anhand von bestimmtem Kriterien sortieren und filtern. Java gegen C++ In der Spieleindustrie ist immer noch C++, die am weitesten verbreitete Sprache. Das hat zur Folge, dass auch die meisten 3D-Engines in C++ geschrieben sind. Es gibt viele Gründe dafür. Die am wei- testen verbreiteten 3D-APIs1 , OpenGL und Direct3D, sind selbst C-APIs. Wie oben bereits erwähnt ist OpenGL-Unterstützung eine Bedingung für die 3D-Engine. Als Java Programmierer können wir nun zwei Wege gehen. Einerseits gibt es OpenGL-Wrapper für Java, wie JOGL von Sun [37] oder die Light- weight Java Game Library LWJGL [40]. Andererseits bieten viele C++-Engines eine Java Unterstützung, die auf dem Java Native Interface (JNI) basiert. 1 API = Aplication Programming Interface. Eine API stellt ein Interface zur Verfügung, dass man als Programmierer benutzen kann.
1.2. Projektablauf 5 C++-Engines Als erstes habe ich einige C++-Engines untersucht. Diese sind: • Irrlicht Engine [30] • OGRE Engine [46] • Crystal Space Engine [17] Diese Engines sind alle in C++ geschrieben. Sie sind alle Open Source und haben eine beeindruckende Feature Liste. Mit der OGRE Engine wurde sogar ein kommerzielles Spiel programmiert. Leider haben aber auch alle drei Engines eine entscheidenden Nachteil. Es existiert zwar bei allen eine Java Unterstüt- zung, der Wrapper ist aber bei allen drei Engines sehr vernachlässigt worden und sogar die Entwickler selbst raten davon ab ihn zu benutzen. Das heisst nun konsequenterweise, das alle diese Engines von der Liste gestrichen werden. Java-Engines Glücklicherweise gibt es aber auch noch einige Engines, die direkt mit Java geschrieben wurden. Leider führt Java im Spielentwicklungsbereich immer noch ein Mauerblümchendasein. Einerseits ist das immer noch auf das alte und eigentlich ausgemerzte Performanceproblem zurückzuführen, das sich in einigen Kreisen aber immer noch sehr hartnäckig weiter vertreten wird. Ehrlicherweise muss man aber auch gestehen, dass sich Java in Sachen Grafikperformance bis vor kurzer Zeit nicht gerade mit Ruhm bekle- ckert hat. Obwohl Sun das Problem zuerst dementiert und relativiert hat, arbeiten sie aber in letzter Zeit direkt an der bereits angesprochenen Unterstützung von OpenGL. Es gibt auch einige Bücher die sich direkt mit der Spiele- und Grafikprogrammierung unter Java beschäftigen wie [BBV03] und [Dav05]. Ich habe drei Java 3D-Engines in die nähere Betrachtung gezogen: • Aviatrix3D [9] • Espresso3D [24] • jME jMonkey Engine [36] Leider muss man bei Aviatrix3D einige Abstriche machen, da die Dokumentation einiges zu wünschen übrig lässt. Die Installation ist ausserdem sehr aufwändig. Espresso3D ist eine Engine, die noch nicht lange in Entwicklung ist. Es gibt noch nicht sehr viele Features. Sehr überzeugt hat mich hingegen jME. Die Website von jME [36] enthält schon eine ziemlich umfangreiche Dokumentation und auch die Demos sehen sehr gut aus. Entscheidung Nach den Tests habe ich mich schlussendlich für jME entschieden. Sie hat alle Kriterien, die ich am Anfang aufgestellt habe erfüllt. • jME ist komplett in Java geschrieben. • jME steht unter der BSD-Lizenz [13]. Diese Open-Source Lizenz ist sehr liberal und erlaubt sogar den kommerziellen Einsatz. Die Engine ist also frei für jedermann und kann von allen herunter- geladen, benutzt und sogar verändert werden. • Die Engine stellt ein gutes Framework zur Verfügung und ist leicht zu benutzen. • jME verwendet OpenGL [47] mit LWJGL [40]. Die Engine unterstützt also hardwarebasiertes Echtzeit-Rendering.
1.2. Projektablauf 6 Abbildung 1.1.: Screenshot aus dem finalen Projekt des Gameversity Kurses • jME hat bereits jetzt eine umfangreiche Dokumentation, was für ein Open Source Projekt doch sehr positiv bemerkbar ist. In diesem ersten Schritt habe ich also jME ausgewählt. Die weiteren Schritte basieren natürlich auf dieser Entscheidung. 1.2.2. 2. Schritt: Machbarkeitstest Nachdem nun eine 3D-Engine ausgewählt worden ist, hat sich die Frage gestellt, was man damit anfan- gen soll. Nach einigen Besprechungen mit den verantwortlichen Lehrpersonen wurde beschlossen ein Tutorial zum Thema 3D-Grafik zu schreiben, das auch Anfänger mit Programmierkenntnissen verstehen sollten. Ich musste selbst noch meine 3D-Kenntnisse erweitern und habe dazu online Kurse besucht bei Game- versity [27] und Game Institute [26]. Konkret habe ich bei Game Institute den Kurs Graphics Program- ming with DirectX 9 - Module I und bei Gameversity den Kurs DirectX Graphic Programming besucht, einige Scripts aus diesen Kursen können Sie auf der CD (siehe Anhang A) im Verzeichnis dateien be- gutachten. Die Kurse werden sogar in einen amerikanischen Universitäten anerkannt. Das erfolgreiche Bestehen dieser Kurse, ist ungefähr äquivalent mit 10 ECTS Punkten hier in der Schweiz. Diese Kurse sind wirklich sehr empfehlenswert und jeder, der sich noch tiefer und grundlegender mit der Materie in dieser Arbeit beschäftigen will, kann ich diese Kurse sehr ans Herz legen. Diese Erfahrung hat mit gezeigt, dass man sehr wohl in Online basierten Kursen hervorragendes Wissen vermitteln kann. Lange Rede kurzer Sinn. Beim Gameversity Kurs, erlernte man das Anwenden und die Implementation einer 3D-Engine und musste als letzte Aufgabe eine kleine Demo mit einer Insel, Wasser und noch einigem mehr machen. Eine Abbildung dieser Demo sehen Sie auf Screenshot 1.1. Die ganze Demo finden Sie auch auf der CD zum Projekt, sehen Sie sich dazu Anhang A an. Da die Demo Direct3D benutzt, läuft sie nur unter Windows-Betriebssystemen. In dieser Bachelorarbeit geht es hauptsächlich um das Anwenden der 3D-Engine jME, ich habe mir also gedacht, das man die gleiche Demo in jME implementieren könnte.
1.3. Gliederung der Dokumentation 7 Gesagt, getan. In einer Coding-Session von gut einer Woche wurde eine ähnliche Demo in jME pro- grammiert. Es ist also durchaus möglich eine Applikation, die in C++ geschrieben wurde und Direct3D benutzt in Java zu schreiben und eine plattformübergreifende Engine zu benutzen. Der 2. Schritt ist damit abgeschlossen. 1.2.3. 3. Schritt: Tutorial Nachdem nun die Engine ausgwählt und ein Endziel festgelelgt wurde, ging es im dritten und letzten Schritt darum die einzelnen Zwischenschritte zu erstellen, jeweils mit Bespielapplikationen, im Verlauf des Dokuments Demos gennant, und dem jeweiligen Begleittext. Obwohl die Arbeit klar strukturiert und das Resultat klar umschrieben werden konnte, handelte es sich bei diesem Teil, dennoch um den mit Abstand zeitaufwändigsten und arbeitsintensivten Schritt. Einige fast unüberwindbar scheinende Hürden, mussten umschifft werden, bis wir zu diesem Endresultat gelangten. Ich hoffe Sie als Leserinnen und Leser können mit dieser Arbeit nun etwas sinnvolles anfangen. 1.3. Gliederung der Dokumentation Dieses Dokument ist in zwei Hauptteile gegliedert: 1. Im ersten Teil befindet sich eine kurze allegemeine Einführung in die 3D-Computergrafik. Diese Arbeit beschäftigt sich mit einem Unterteil dieses Themas. Desweiteren gibt es einen Überblick über das Bacholorprojekt im allgemeinen. 2. Der zweite Teil dieses Dokumentes beschäftigt sich mit der 3D-Engine jME[36]. In jedem Ka- pitel wird im Tutorialstil Schritt für Schritt ein weiters Element der Engine jME und der 3D- Grafikprogrammierung beschrieben, bis wir am Schluss in der Lage sind die Demo in Kapitel 10 nach zu vollziehen. Am Ende des Dokumentes befinden sich noch einige Anhänge. Sie zeigen unter anderem den Inhalt der mitgelieferten CD auf (siehe Anhang A) und beschreiben wie Sie die jeweiligen Demos starten, bearbeiten und neu kompilieren können (siehe Anhang B). 1.4. Konventionen und Notationen • Dateiname: wird benutzt um Dateinamen, Dateierweiterungen und Pfade anzugeben. • Wichtig wird benutzt um wichtige Namen hervorzuheben. • Variable wird benutzt um Klassennamen und Variablennamen und weiter Quellcodeextrakte im Text anzuzeigen. • Längere Quellcodeabschnitte und Listings werden folgendermassen angezeigt: System . out . println ( " Hallo schöne jME - Welt !" ); • Abbildungen und Listings sind innerhalb eines Kapitels nummeriert. • Referenzen zu einem Objekt innerhalb des Literaturverzeichnisses sieht so aus: [AMH02]. Das komplette Literaturverzeichnis befindet sich am Ende dieses Dokumentes. • Referenzen auf Webseiten sehen folgendermassen aus: [36]. Das gesamte Verzeichnis mit den Web Ressourcen befindet sich ebenfalls am Ende dieser Bachelorarbeit. • Bei den einzelnen Klassendiagrammen in diesem Dokument handelt es sich um herkömmliche UML-Klassendiagramme. Mehr zu UML können Sie im Buch UML Distilled[Fow04] lesen.
3D-Grak 2 2.1. Was ist 3D-Grafik? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 2.2. Was bedeutet Echtzeit-3D-Grafik? . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 2.3. Was ist eine 3D-Engine? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 2.4. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 3D-Grafik oder genauer gesagt 3D-Computergrafik ist ein sehr breites Thema und es kann leicht sein, dass man sich zu schnell von den Details erschlagen lässt. Deshalb will ich in den folgenden Unter- kapiteln eine kurze allgemeine Einführung in das Thema bereiten. Die Einführung wird sehr kurz sein und sich nur auf das Wesentliche konzentrieren. Aber ich hoffe dennoch, dass Sie damit einen kurzen Überblick über das Thema gewinnen. Ich hoffe vor allem Leute, die sich noch nie mit dieser Thema- tik beschäftigt haben, werden einiges dazulernen, aber auch alle andern sollte dieser Überblick und die dazugehörige Abgrenzung der Themen helfen. 2.1. Was ist 3D-Grafik? Was ist 3D-Grafik? Das ist vielleicht die grundlegendste Frage, die man im Zusammenhang mit 3D- Grafik überhaupt stellen kann. Und die Beantwortung ist gar nicht so einfach. Grundsätzlich gibt es zwei Teilbereiche in der 3D-Grafik: das Modellieren und das Rendern von räumlichen Daten. Mit der Modellierung bezeichnet man das Erstellen von räumlichen Daten, so genannten 3D-Objekten. Diese 3D-Objekte werden weitgehend von Hand mit spezialisierter Software erstellt. Diese Software wird 3D-Modelliersoftware oder auch nur “3D-Modeller” genannt. Einige der bekanntesten unter ihnen sind “3D-Studio Max” von Autodesk [3] , Maya von Alias [5] und SOFTIMAGE|XSI von Softima- ge [54] . Die meisten dieser Applikationen sind sehr teuer, zum Teil mehrere zehntausend Franken1 . Glücklicherweise gibt es auch einige freie Open-Source Alternativen wie Blender [12] (siehe auch Ab- bildung 2.1) oder Wings3D [62] . Abschliessend bleiben noch einige günstige Shareware Programme zu erwähnen, die auch ihre Daseinsberechtigung haben, zum Beispiel Milkshape 3D [43] oder AC3D [4] . Die Modellierung von 3D-Objekten ist sehr aufwändig, braucht viel Zeit und Geduld und auch einiges künstlerisches Geschick, wie ich aus eigener missglückter Erfahrung berichten kann. Wir werden im weiteren Verlauf nicht mehr auf das Modellieren eingehen, wer sich dafür interessiert findet unzählige Tutorials und WebSites auf dem Internet dazu, wie zum Beispiel auf 3D Links [2] und den Seiten der einzelnen oben genannten Programme. Die einzelnen 3D-Objekte werden dann unter Umständen noch zusammengefasst zu grösseren Szenen. Man kann sich zum Beispiel eine Stadtszene vorstellen, in der 1 Es gibt aber meistens auch eine gratis Learning- bzw. Personal-Edition für Heimanwender und Studenten, die im Funktions- umfang eingeschränkt ist. Beispiele dafür sind “gmax” von den 3D Studio Max Entwicklern Autodesk [28] und die “Maya Personal Learning Edition” von Alias [42]. 8
2.1. Was ist 3D-Grafik? 9 Abbildung 2.1.: Ein Screenshot von Blender
2.2. Was bedeutet Echtzeit-3D-Grafik? 10 Abbildung 2.2.: Ein Ausschnitt aus “Geri’s Game” wir einige Autos sehen, im Hintergrund hat es Geschäfte und Hochhäuser, das sind alles einzelne 3D- Objekte die in einer Szene zusammen kommen. Der zweite wichtige Teilbereich in der 3D-Grafik ist das Rendern. Unter dem Rendern verstehen, wir das Erzeugen eines zweidimensionalen Bildes aus den räumlichen 3D-Daten. Es geht also darum nun die erzeugten 3D-Objekte auf dem Bildschirm anzuzeigen. Man kann es vergleichen mit dem Fotografieren oder dem Filmen einer Szene, wir betrachten also unsere 3D-Szene aus einer virtuellen Kamera. Dazu gibt es auch viele verschiedene Methoden und Techniken. In diesem Tutorial beschäftigen wir uns mit dem Echtzeit-Rendern. Wie in der Einleitung erwähnt, wollen wir ja Bilder mit Hilfe von Java in Echtzeit auf den Bildschirm bringen. Auf der Abbildung 2.2 können wir einen Ausschnitt aus dem Pixar Kurzfilm Geri’s Game [51] sehen. Links ist das Drahtmodell (engl. wireframe) zu sehen, das in einem 3D-Modeller modelliert wurde. Rechts ist eine fertig gerenderte Szene mit dem gleichen Kopf aus dem Film zu sehen. 2.2. Was bedeutet Echtzeit-3D-Grafik? Wie der Titel schon andeutet, behandeln wir hier Echtzeit-3D-Grafik. Unter Echtzeit verstehen wir, dass wir eine 3D-Szene in Echtzeit rendern wollen. Viele von ihnen kennen vielleicht die bekannten Animationsfilme von Pixar [50] oder Dreamworks Ani- mation [21], wie “Shrek”, “The Incredibles”, “Findet Nemo” oder den neusten Titel “Madagascar”. Und obwohl diese Filme auch mit Hilfe von 3D-Grafik erstellt wurden (und keineswegs, wie manche viel- leicht meinen mit klassischen Trickfilmzeichnungen), handelt es sich eben gerade nicht um Echtzeit 3D-Grafik. In solchen Filmen wird jeder einzelne Frame2 vorher gerendert und später zu einem Film zu- sammengefügt. Dieses Rendering einer einzelnen Szene kann aufgrund der verwendeten Effekte, Licht- einstellungen und anderen speziellen Einstellungen mitunter Stunden dauern. Das Ziel ist es das Bild möglichst realistisch erscheinen zu lassen, natürlich in einem gewissen Rahmen, der für die Computer noch machbar ist. Ein Oberbegriff für diese Art des Renderns ist Raytracing. Beim Raytracing werden Lichtstrahlen simuliert und die Lichtverteilung errechnet um möglichst realistische Schatten und Farb- schattierungen zu erhalten. Die Methoden sind dabei auch für heutige Rechner sehr zeitaufwändig und lassen sich noch nicht in Echtzeit ausführen. 2 Ein Frame ist ein einzelnes Bild aus einem Trickfilm oder eben einer 3D-Animation.
2.2. Was bedeutet Echtzeit-3D-Grafik? 11 Abbildung 2.3.: Ausschnitt aus dem Film Madagascar Wenn wir von Echtzeit sprechen, meinen wir nun, dass eben genau diese Rendering nur Bruchteile von Sekunden dauern darf, damit wir uns auch frei in einer 3D-Welt umher bewegen können. Diese Echtzeit- Rendering wird in 3D-Simulationen und auch 3D-Spielen verwendet. Um aber das ganze in Echtzeit zu bekommen, muss man auch einige Kompromisse eingehen. Die erzeugten Echtzeit-3D-Welten sind in der Regel nicht so realistisch, wie die vorgerenderten 3D-Welten. Das primäre Ziel ist die erzielbare Geschwindigkeit, mit der ein Frame gerendert werden kann, möglichst zu minimieren. Die Echtzeit-3D- Grafik wird aber immer realistischer. Das hängt auch mit der Einführung der so genannten 3D-Karten zusammen, die man mittlerweile als Standardausrüstung, sogar auf Mittelklasse PCs zählen kann. Der Markt für 3D Karten war in den letzten Jahren sehr dynamisch, im Moment gibt es aber nur zwei Firmen, die Karten für den End-User Markt herstellen: ATI [8] und NVidia [44] Auf der Abbildung 2.4 können Sie die Entwicklung der Echtzeit-3D-Grafik in Spielen kurz, aber keines- falls wirklich repräsentativ, mitverfolgen. Auf dem Teilbild oben links sehen Sie einen Ausschnitt aus dem Spiel Battlezone [10] aus dem Jahre 1980. Das ist das erste Spiel, das eine Art pseudo 3D-Grafik enthielt. Oben rechts ist ein Ausschnitt aus dem Spiel Wolfenstein3D [63] der Firma id Software [29] zu sehen. Das ist eines der ersten 3D-Spiele für den PC und auch der erste “First-Person-Shooter” und somit auch der Vater aller anderen Spiele dieser Art. Es wurde 1992 veröffentlicht. Aus dem Jahr 1999 stammt der dritte Screenshot unten links. Es handelt sich um das Spiel Quake 3 [53], wiederum von id Software. Man kann hier schon gut die ersten eingesetzten Lichteffekte erkennen. Der letzte Screenshot ist von Far Cry [25] . Ein Spiel das 2004 herausgeben wurde. Auf diesem Bild kann man einige inter- essante Wassereffekte erkennen. Auch die Darstellung der Bäume und Pflanzen ist bemerkenswert und der Detailreichtum der Umwelt. Dieses Echtzeit-Rendering war zuerst nur auf spezieller Hardware möglich, die vor allem militärischen 3D-Simulationen dienten. Im Jahre 1996 hat aber die Firma 3dfx [61] eine erste 3D-Beschleuniger Kar- te in bezahlbaren Regionen für den Endkundenmarkt hergestellt. Erst mit Hilfe dieser Karten ist das einigermassen realistische 3D-Echtzeit Rendern auf normalen Computern möglich geworden. Die Ent-
2.2. Was bedeutet Echtzeit-3D-Grafik? 12 Abbildung 2.4.: Zeitlinie Echtzeit-3D-Spiele oben links Battlezone [10] , 1980 oben rechts Wolfenstein 3D [63] , 1992 unten links Quake 3 [53] , 1999 unten rechts Far Cry [25] , 2004
2.3. Was ist eine 3D-Engine? 13 wicklung der 3D-Grafik-Karten verläuft immer noch rasant. Sie übertrifft sogar das Moorsche Gesetz3 . Die Geschwindigkeit der neuesten 3D-Grafikkarten verdoppelt sich fast alle sechs Monate und ein Ende ist nicht abzusehen. Und das obwohl sich, nach einigen Konkursen und Übernahmen nur zwei grosse Player, ATI [8] und NVidia [44], auf dem Endkundenmarkt tummeln. Es wird also nicht mehr lange dauern, bis wir auch 3D-Echzeitgrafik in der gleichen Qualität, wie die aktuellen Pixar und Dreamworks Animation Filme geniessen können. Was es mit diesen 3D-Grafik-Karten auf sich hat und wie sie intern arbeiten, werden wir ein bisschen genauer im Kapitel 4 unter die Lupe nehmen. 2.3. Was ist eine 3D-Engine? Wir wollen mit Hilfe der 3D-Engine jME [36] eine 3D-Welt aufbauen und schlussendlich auf den Bild- schirm bringen. Aber was genau ist eine 3D-Engine? Eine 3D-Engine verwaltet die ganzen 3D-Objekte und die Szenen, die ein 3D-Künstler oder wir selbst vorher angefertigt haben. Sie verwalten also kurz ge- sagt die ganzen 3D-Daten. Des weiteren ist eine 3D-Engine dafür zuständig, was wir auf den Bildschirm bringen und wie wir das ganze Zeichnen wollen. Diese Entscheidungen werden auf einer unteren und einer oberen Ebene gemacht. Die Entscheidung auf der oberen Ebene werden von unserem Spiel oder unserer Anwendung mit Hilfe eines Scenegraphs auf Softwareebene gemacht, das ganze wird im Ab- schnitt 3.2 erläutert. Die Entscheidungen der unteren Ebene werden vom Renderer selbst gemacht. Das bedeutet das wir die meiste Arbeit an die Hardware oder genauer gesagt an die Grafik-Karte delegieren können. Wie das genau funktioniert wird im Kapitel 4 näher erklärt. 2.4. Zusammenfassung Ich hoffe, dass Sie als Leser nun ein bisschen mehr Ahnung von der 3D-Grafik haben. Was Sie behal- ten sollten, ist das wir uns hier im Tutorial auf das Rendern mit der Java 3D-Engine jME beschränken werden. Wir werden uns genauer gesagt sogar auf das interaktive Echtzeit-Rendern beschränken. Wie das abläuft, werden wir im Tutorial sehen. Im Tutorial werden wir uns auf die Handhabung von jME aus Sicht eines Endbenutzers beschränken und nur wo nötig auf implementationstechnische Details einge- hen. 3 AlsMooresches Gesetz wird die Beobachtung bezeichnet, dass sich durch den technischen Fortschritt die Komplexität von integrierten Schaltkreisen etwa alle 24 Monate verdoppelt.
Teil II. Tutorial 14
Erste Schritte mit jME 3 3.1. Unser erstes jME-Programm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 3.2. Scenegraph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 3.2.1. Was ist ein Scenegraph? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 3.2.2. Zustände im Kontext von jME . . . . . . . . . . . . . . . . . . . . . . . . . . 21 3.1. Unser erstes jME-Programm Jetzt geht es los liebe Leser! Statt lange um den heissen Brei zu reden, tauchen wir gleich ein in un- ser erstes jME-Programm. Wie jedes anständige Programmiertutorial beginnt auch dieses Tutorial mit einem HelloWorld Programm. Zu sehen ist aber nicht ein kurzer Textstring sondern ein einfacher Wür- fel. Sie finden den Quellcode und die Klasse selbst im Verzeichnis demos/firststeps mit dem Namen HelloWorld.java. Wie die CD genau aufgebaut ist, wird in Anhang A erklärt. Im Unterverzeichnis de- mos der CD finden Sie ausserdem zwei Dateien mit dem Namen firststeps_helloworld.bat und first- steps_helloworld.sh. Es handelt sich dabei um Startdateien, mit denen Sie die Demo unter Windows bzw. unter Linux und MacOSX direkt starten können. Wie sie die Demo direkt in der Konsole starten können, wird im Anhang B erläutert. Listing 3.1: HelloWorld.java 1 package firststeps ; 2 3 import helper . TutorialGame ; 4 5 import com . jme . math . Vector3f ; 6 import com . jme . scene . shape . Box ; 7 8 public class HelloWorld extends TutorialGame 9 { 10 public static void main ( String args [] ) 11 { 12 new HelloWorld () ; 13 } 14 15 protected void simpleInitGame () 16 { 17 // set the title of our application 18 display . setTitle ( " Hello World !!! " ); 19 15
3.1. Unser erstes jME-Programm 16 Abbildung 3.1.: Screenshot aus HelloWorld 20 // create a simple cube 21 Box b = new Box ( " box " , new Vector3f ( 0, 0, 0 ) , new Vector3f ( 1, 1, 1 ) ); 22 23 // put the box in the scene graph 24 rootNode . attachChild ( b ); 25 } 26 } Dieses einfache Programm beinhaltet bereits alles was auch ein grösseres jME-Programm beinhalten muss. Wir erben von einer Klasse namens TutorialGame. In TutorialGame werden verschiedene Din- ge aufgesetzt, die in jedem unseres jME-Programme nützlich sind: • Eine einfache Kamera wird erzeugt, die wir mittels der in Spielen üblichen Manier bewegen kön- nen. Das heisst konkret: mit der Maus verändern wir unsere Blickrichtung, während wir uns mit der ’W’- und ’S’-Taste vorwärts und rückwärts bewegen. Mit ’A’ und ’D’ bewegen wir und links bzw. rechts seitwärts. Das wird auch für die meisten anderen, kommenden Demos, die Standard- methode sein, mit der wir uns fortbewegen können. Wer bereits einen modernen 3D-Shooter ge- spielt hat, wird sich gleich zu Hause fühlen. • Ein simples Licht wird aufgesetzt. In Kapitel 7 werden wir genauer darauf eingehen. • Ein Scenegraph wird eingerichtet. Was ein Scenegraph genau ist werden wir am Ende dieses Kapitels genauer erklären. • Einige simple Tastenkommandos werden definiert wie zum Beispiel: – ’T’ und den Wireframe-Modus ein- bzw. auszuschalten – ’C’ um die Position der Kamera in der Konsole auszugeben – ’L’ um die Lichter zu deaktivieren bzw. wieder zu aktivieren
3.1. Unser erstes jME-Programm 17 Abbildung 3.2.: Screenshot des Einstellungsdialogs der bei jedem Start erscheint – ESCAPE um das Programm zu beenden • TutorialGame stellt auch zwei Methoden namens simpleInitGame und simpleUpdate zur Ver- fügung, die wir in unseren eigenen Programmen überschreiben können, wenn wir etwas initiali- sieren wollen oder etwas in jedem Frame ändern wollen. Für uns ist es nicht so wichtig zu wissen, wie TutorialGame genau seine Arbeit macht, wichtig zu wissen ist aber, das jedes unserer Programme von TutorialGame erben wird. Beschreiben wir nun unser erstes Programm ein bisschen genauer. Die Dialogbox mit dem Uni-Fribourg Logo, die bei jedem Start gezeigt wird, ist sicher auch schon einigen aufgefallen, auf der Abbildung 3.2 können Sie einen Screenshot davon sehen. Der Konstruktor von TutorialGame, ist dafür verantwortlich, dass dieser Dialog immer erscheint. In diesem Dialog kann man die Bildschirmauflösung des folgenden Programms auswählen und bestimmen, ob die Applikation in einem Fenster oder als Vollbildanwendung ausgeführt wird. Es ist also durchaus sinnvoll diesen Dialog vor jedem Start anzuzeigen. In Zeile 12 beginnt unser Programm dann richtig. Mit dem Aufrufen des Konstruktors wird auch der Konstruktor von TutorialGame aufgerufen, der wiederum eine Endlosschleife ausführt, die erst been- det wird, wenn wir auf ’Fenster schliessen’-Kreuz drücken oder die Taste Escape betätigen. Bevor die Schleife startet wird das System initialisiert und unter anderem auch simpleInitGame aufgerufen. In der Schleife selbst werden danach in jedem Iterationsschritt zwei Dinge gemacht: zunächst erhält jedes Objekt, denn Befehl sich zu bewegen und simpleUpdate wird aufgerufen, als zweites wird alles auf den Bildschirm gerendert. Unser main wird in jeder Demo gleich aussehen, die eigentliche Arbeit werden wir immer in den Metho- densimpleInitGame und zum Teil auch in simpleUpdate machen. Besprechen wir also im folgenden Abschnitt simpleInitGame etwas genauer: Listing 3.2: HelloWorld.java, simpleInitGame 15 protected void simpleInitGame () 16 { 17 // set the title of our application
3.2. Scenegraph 18 18 display . setTitle ( " Hello World !!! " ); 19 20 // create a simple cube 21 Box b = new Box ( " box " , new Vector3f ( 0, 0, 0 ) , new Vector3f ( 1, 1, 1 ) ); 22 23 // put the box in the scene graph 24 rootNode . attachChild ( b ); 25 } Schon beim betrachten des Codes sehen wir, das genau drei Dinge passieren: 1. Wir geben unserer Demo einen Titel. Der Titel wird auf dem Fenster angezeigt und wird auch der Name sein, mit dem das jeweilige Betriebssystem unsere Applikation kennt. 2. Als nächstes erstellen wir einen Würfel mit Box. Der Würfel ist, dieses Ding, das man auf dem Bildschirm sehen konnte. 3. Wir fügen unseren neu erstellten Würfel der Wurzel unseres Scenegraphen an. Die Wurzel des Scenegraphen heisst rootNode und wird uns von TutorialGame zur Verfügung gestellt. Wie wir oben sehen können, benutzen wir drei Argumente um einen Würfel zu kreieren. Als erstes geben wir dem Objekt mittels eines String Objektes einen Namen. Allen Objekte, die wir irgend einmal einem Scenegraph anhängen wollen müssen wir einen Namen geben. Dieser Würfel wurde in diesem Beispiel box genannt, wir hätten aber auch irgend einen anderen Namen wählen können. Die Nächsten zwei Argumente geben zwei Ecken unseres Würfels an. Der Würfel hat eine Ecke im Ursprung (0; 0; 0) und eine Ecke im Punkt (1; 1; 1) es handelt sich also um einen Einheitswürfel. Jetzt haben wir also einen Würfel erstellt, wir wollen diesen Würfel aber auch sehen. Deshalb müssen wir den Würfel mittels rootNode.attachChild unserem Scenegraph anhängen. Alle Objekte, die ge- rendert werden sollen, müssen wir dem Scenegraphen anhängen. rootNode ist dabei die Wurzel des Scenegraph, der in TutorialGame definiert wurde. In diesem Beispiel besteht unser ganzer Scenegraph aus der Wurzel rootNode mit dem angehängten Würfel box. Wenn nun von TutorialGame der Befehl kommt rootNode zu zeichnen wird automatisch auch der Würfel mitgerendert. 3.2. Scenegraph 3.2.1. Was ist ein Scenegraph? Kommen wir zurück zu der grundlegenden Frage, was eine 3D-Engine eigentlich macht. Wir haben be- reits gesagt, dass es die Hauptaufgabe einer 3D-Engine ist verschiedene 3D-Objekte zu verwalten und auf den Bildschirm zu bringen. Die einfachste Möglichkeit diese Objekte zu verwalten, wäre eine ver- kette Liste mit allen Objekte, die wir dann eines nach dem anderen auf den Bildschirm zeichnen. Das ist eine einfache aber leider nicht sehr effiziente Methode Objekte zu verwalten. Wenn man sich näher mit einem modernen 3D-Spiel beschäftigt, erkennt man, dass die 3D-Welten aus tausenden von 3D-Objekten bestehen. Diese tausenden von Objekte in einer Liste zu speichern und nacheinander zu verarbeiten wür- de viele Spiele wohl zu einer eher langweiligen Dia-Show ausarten lassen. Denn selbst Objekte die nicht auf dem Bildschirm erscheinen würde man in diesem Falle einer zeitraubenden Bearbeitung unterziehen müssen und wertvolle Ressourcen rauben. Die 3D-Hardware weiss noch nicht einmal, dass viele Objek- te nicht zu zeichnen sind, und schmeisst solche Objekte erst sehr spät aus der Pipeline, deshalb spricht man auch davon, dass die 3D-Beschleuniger auf einem sehr tiefen sogenannten low-level arbeiten. Als Programmierer haben wir aber sehr wohl eine grössere high-level Ahnung von den 3D-Objekten auf dem Bildschirm und wie sie miteinander in Beziehung stehen. Die ganze Welt aus den 3D-Objekten wird, wie wir bereits einmal erwähnt haben als Szene (engl. scene) bezeichnet. Wenn wir nun unser Wissen von den Beziehung dieser 3D-Objekten einbringen erhalten
3.2. Scenegraph 19 Abbildung 3.3.: Eine Hierarchie von einem Haus wir einen Scenegraphen1 . Als Scenegraph wird in jME ein Baum verwendet. In einem Baum gibt es eine Wurzel und jedes Element kann mehrere Kindobjekte enthalten, besitzt aber nur ein Elternelement. jME stellt uns mit rootNode bereits die Wurzel eines Scenegraphen zur Verfügung, an den wir weitere Blätter anfügen können. Mit einem Baum können wir als Benutzer einer Engine eine Hierarchie von 3D- Objekten aufbauen. Dieser Ansatz gibt uns viele Vorteile, wie Sie in [Ebe00] auch nachlesen können: • Stellen wir uns vor wir haben eine 3D-Welt, die aus vielen verschiedenen Räumen besteht. Wenn wir nun ein Licht einsetzen, wie wir es in Kapitel 7 zeigen, dann wollen wir das dieses Licht nur diesen Raum betrifft und beleuchtet auch aus Performancegründen. Mit einem Scenegraph ist das einfach in dem wir das Licht einfach im Raum einsetzen den wir beleuchten wollen. Das Licht beleuchtet dann automatisch auch alle Kindobjekte von diesem Raum. • Zweitens, in einem Scenegraphen kann man leicht lokale Gruppierung darstellen. Das hilft beson- ders, weil man mit dieser Methode schnell ganze Objektgruppen eliminieren kann, die nicht auf dem Bildschirm zu sehen sind. Nehmen wir als Beispiel an wir befinden uns im Raum 2 wie auf der Abbildung 3.3 zu sehen. Der Renderer kann den ganzen Unterbaum von Raum 1 direkt von der 1 Für diesen Begriff scheint es leider keine geläufige deutsche Übersetzung zu geben, deshalb werden wir uns im Verlaufe des Dokuments auf den englischen Begriff Scenegraph beschränken.
3.2. Scenegraph 20 Abbildung 3.4.: UML-Diagramm der Scenegraph Elemente Bearbeitung ausschliessen, weil dieser Raum nicht zu sehen ist. Wenn man Objekte von der Be- arbeitung ausschliesst, die nicht auf dem Bildschirm zu sehen sind spricht man vom sogenannten Frustum Culling. Das ist ein Konzept, das sehr wichtig ist im Echzeitrendern. • Viele 3D-Objekte die wir darstellen wollen sind schon von Natur aus auf hierarchische Weise aufgebaut. Das ist ein dritter Vorteil, den wir mit Scenegraphen haben. Das gilt besonders für humanoide Objekte. Die Lage und die Rotation von einer Hand hängt auf natürliche Weise ab von der Lage und der Rotation des Ellbogens, der Schulter und der Hüfte. Mit einem Scenegraphen ist es leicht solche Abhängigkeiten darzustellen. Wenn wir das ganze von Hand machten müssten, würde es sehr schnell kompliziert werden, wie Sie im Abschnitt 4.2.3 selbst sehen können. Kommen wir nun zum Scenegraphen zurück den jME für uns bereitstellt. In jME gibt es Objekte von drei Klassen, die Elemente eines Scenegraphen sein können, die Klassen Spatial, Geometry und Node. Wie auf der Abbildung 3.4 zu sehen ist, ist die Klasse Spatial die Oberklasse. Man kann Spatial nicht instanzieren, da es sich um eine abstrakte Klasse handelt. In Spatial werden aber die Lage und die Rotation gespeichert, jedes Element des Scenegraphen hat also seine eigenen Standort, der immer relativ zum Elternelement ist. Man kann auch sogenannten RenderStates setzen, der die Lichter und Texturen beschreibt, dazu gibt es im weiteren Verlauf des Tutorials mehr. Die Klasse Geometry und ihre Unterklassen beinhalten all die geometrischen Daten, das heisst die Drei- ecke, die ein 3D-Objekt enthalten. Das heisst jedes Objekt, das wir am Schluss auf dem Bildschirm sehen ist ein Geometry-Objekt. Die Box, die wir im ersten Demo benutzt haben (siehe Listing 3.1 und 3.2 Zeile 21), ist auch im UML-Diagramm zu erkennen. Bei den Geometry-Objekten handelt es sich aber nur um die Endblätter unseres Scenegraph-Baumes. Die Knoten werden durch die Klasse Node verwaltet, der
3.2. Scenegraph 21 Abbildung 3.5.: UML Diagramm der RenderStates man beliebig viele Kindknoten und Kind Geometry-Objekte anfügen kann, dazu können Sie noch einmal die Abbildung 3.3 betrachten. Für alles das wir in jME auf den Bildschirm sehen existieren also eigene Geometry-Unterklassen oder spezialisierte Node-Klassen. Solche spezialisieren Klassen sind schwierig auf eine gute Art und Weise zu implementieren und das ist eine der eigentlichen Schwierigkeiten, wenn man eine 3D-Engine entwickeln will. Man kann ohne zu übertreiben sagen, dass der Scenegraph das Rückgrat der 3D-Engine von jME ist. Das Design und die Implementierung von jME ist dabei sehr komplex. Der Autor von jME sagt in seiner In- ternetseite, dass er das Design von jME an die beiden Bücher von David H. Eberly angelehnt hat [Ebe00] und [Ebe04]. In [Ebe00] wird die Implementierung einer Scenegraph basierten Engine beschrieben. Der Text ist dabei sehr mathematisch gehalten. In [Ebe04] wird die Architektur dieser weiterentwickelten Scenegraph-Engine aus einem etwas höheren Level beschrieben. Beide Bücher sind über 500 Seiten dick, das sollte nur ein kleiner Hinweis sein, wie komplex eine moderne 3D-Engine ist. Der ganze Scenegraph entspricht ausserdem dem Composite Pattern, wie er aus dem allseits bekann- ten Buch der Gang of Four, Design Patterns [GHJV95] bekannt ist. Im Scenegraphen, dessen Wurzel rootNode in unserem Programm verwendet wird, wird einmal pro Frame die Methode draw aufgerufen. Dieser Aufruf bringt die ganze Engine zum Laufen. Im Artikel [BZ] von Avi Bar-Zeev finden Sie noch einige weiter Anmerkungen zu einem Scenegraphen allgemein. Die meisten modernen 3D-Engines benutzen eine Scenegraph-Implementierung, die der von jME ähnlich ist. 3.2.2. Zustände im Kontext von jME Wir werden im Verlauf des Tutorials auch einige Zuständen verwenden. Vor allem Lichter in Kapitel 7 und Texturen in Kapitel 8. Ein Zustand den wir in jME verwenden können ist immer eine Unterklasse von RenderState. Abbildung zeigt ein UML-Diagramm mit einigen der Zustände. Sehen Sie sich nun noch einmal die Abbildung mit den Scenegraph-Elementen (Abbildung 3.4) an. Wie Sie auf dieser Abbildung sehen können, können wir zu jedem Element unseres Scenegraphen, das heisst zu jedem Spatial, einen RenderState also einen Zustand setzen, mit der treffenden Methode setRenderState. Der Zustand, den wir setzen, wirkt sich dann im Scenegraph auf alle Kinder, des jeweiligen Spatials aus dessen Zustand wir erweitern. Betrachten wir dazu noch einmal die Hierarchie auf Abbildung 3.3. Falls wir im Raum1 ein Licht setzen, beleuchtet das Licht alle Elemente der Tischgruppe und auch den Stuhl, Raum2 merkt aber nichts vom Licht.
Wie funktioniert 3D-Grak 4 4.1. Was macht eine 3D-Engine? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 4.2. Das 3D-Koordinatensystem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 4.2.1. 2D-Koordinatensystem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 4.2.2. 3D-Koordinatensystem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 4.2.3. Model-Space vs. World-Space . . . . . . . . . . . . . . . . . . . . . . . . . . 24 4.3. Transformationen im Raum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 4.3.1. Verschiebungen (engl. translation) . . . . . . . . . . . . . . . . . . . . . . . . 25 4.3.2. Rotationen (engl. rotation) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 4.3.3. Skalierungen (engl. scaling) . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 4.3.4. Alle Bewegungen zusammen . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 4.4. Perspektive und Projektion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 4.5. Kamera . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 4.6. jME, OpenGL, DirectX, 3D-Pipeline: Was ist das? Was machen die? . . . . . . . . 29 4.7. Ressourcen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 4.1. Was macht eine 3D-Engine? Man kann kurz sagen, eine 3D-Engine ist verantwortlich dafür die 3D-Daten zu verwalten und zu bestim- men, was auf den Bildschirm kommt und wie es zu zeichnen ist. Wir haben oben im Kapitel vor allem das was beschrieben, das wie ist dabei meistens die Aufgabe der sogenannten Rendering-Pipeline. Das Ziel der Rendering-Pipeline ist es ein zweidimensionales Bild auf dem Bildschirm zu erzeugen, aus dreidi- mensionalen Objekten, einer virtuellen Kamera, Texturen, Lichtern und noch einigem mehr. Als Benut- zer einer 3D-Engine haben wir es dabei glücklicherweise um einiges leichter. Die 3D-Engine ist meistens ein Wrapper um eine gegebene 3D-Schnittstelle, wie OpenGL oder Direct3D und abstrahiert dement- sprechend die so genannte Rendering-Pipeline. Die Rendering-Pipeline selbst ist heutzutage auch direkt in der Hardware implementiert, in den sogenannten 3D-Beschleunigern. Das sind 3D-Grafikkarten, die mittlerweile wohl jeder Computer enthält. Wir müssen uns also um die meisten Low-Level Angelegen- heiten nicht mehr selbst kümmern und können ganz einfach die 3D-Engine walten lassen. Dennoch ist es von Vorteil, zumindest ein grundlegendes Wissen zu haben, was in dieser ominösen Pipeline passiert. Und genau deshalb werden wir uns in diesem Teil des Tutorials ein wenig um die theoretischen Grundlagen kümmern. Ich hoffe, dass ich damit ein wenig Licht in das bereits jetzt von Fachwörtern gespickte Tutorial bringen kann. Gezwungenermassen kann ich hier keinen tiefen Einblick in das Thema geben. Ich verweise die interessierten Leser aber auf die kommentierte Bibliographie am Ende dieses Kapitels in Abschnitt 4.7. 22
4.2. Das 3D-Koordinatensystem 23 Abbildung 4.1.: Ein rechthändiges 3D-Koordinatensystem Trotz allem wird hier ein wenig Vorwissen vorausgesetzt. Der Leser sollte wissen, was ein Vektor und eine Matrix ist. Grundlegende Vektor- und Matrix-Operationen sollten, deshalb auch schon verstanden werden. Wer noch ein wenig Mühe mit dieser Materie hat oder sein mathematisches Wissen ganz all- gemein ein bisschen auffrischen will findet im Buch von Fletcher und Parberry 3D Math Primer for Graphics and Game Development [DP02] eine hervorragende Einführung. 4.2. Das 3D-Koordinatensystem 4.2.1. 2D-Koordinatensystem Fast jeder wird wohl schon von einem kartesischen 2D-Koordinatensystem gehört haben und es wohl sogar selbst benutzt haben. Ein Koordinatensystem besteht aus einer oder mehreren Zahlengeraden. Jeder dieser Zahlengeraden heisst Achse. Die Anzahl Achsen in einem System entspricht der Anzahl Dimensionen, die in diesem System repräsentiert werden. In einem 2D-Koordinatensystem sind das nor- malerweise die x- und die y-Achse. Diese Achsen entspringen dem Ursprung (engl. origin) des Systems. Dieser Ursprung entspricht dem Punkt (0; 0) in einem 2D-System, deshalb wird der Ursprung häufig auch Nullpunkt genannt. 4.2.2. 3D-Koordinatensystem Ein 3D-System fügt nun dem 2D-System eine dritte Tiefendimension hinzu. Diese neue Achse wird normalerweise als z-Achse bezeichnet. Alle drei Achsen in einem 3D-Koordinatensystem stehen im rechten, 90 Grad, Winkel zueinander. Es gibt zwei Versionen des 3D-Koordinatensystem, die häufig benutzt werden. Das linkshändige System (engl: left-handed system) und das rechtshändige System (engl: right-handed system). Der Unterschied zwischen den beiden ist die Richtung in welche die z-Achse zeigt. In einem linkshändigen System zeigt die positive z-Achse gegen vorwärts und negative Zahlen zeigen sozusagen hinten von uns weg. In einem
4.2. Das 3D-Koordinatensystem 24 rechtshändigen System ist das genau umgekehrt. jME benutzt ein rechtshändiges Koordinatensystem, da auch das darunterliegende OpenGL ein rechtshändiges Koordinatensystem benutzt. Das linkshändige Koordinatensystem wird von der anderen bekannten 3D-API Direct3D benutzt. Auf der Abbildung 4.1 sehen Sie ein rechtshändiges Koordinatensystem. 4.2.3. Model-Space vs. World-Space Im Bereich der 3D-Grafik benutzt man oft verschiedene Koordinatensysteme. Im Besonderen unter- scheidet man zwischen dem sogenannten World Space, dem Model- oder Object Space und dem Ca- mera Space. Man benötigt verschiedene Koordinatensysteme, weil einige Informationen nur in einem bestimmten Kontext (das bedeutet in unserem Fall in einem bestimmtem Koordinatensystem) von Nut- zen sind. Das ganze Konzept ist am Anfang vielleicht ein bisschen schwierig zu verstehen aber die Konzepte sind grundlegend. Am besten wir beginnen mit dem World Space. Das World Space ist ein absolutes Koordinatensystem. Jede Position auf der Welt hat seine eigenen Koordinaten, die unverwechselbar sind. Am besten kann man sich das mit einer normalen Weltkarte verbildlichen. Auf einer Weltkarte ist die Welt in Längen- und Breitengrade aufgeteilt. Jeder Ort auf der Welt lässt sich nun eindeutig mit diesen Koordinaten beschrei- ben. Die Koordinaten von Freiburg sind zum Beispiel, 46,8◦ nördliche Breite, 7,15◦ östliche Länge. Auch jede 3D-Welt, besitzt nun ein solches absolutes Koordinatensystem, in dem jeder Ort eindeutig durch die x-, y-, z-Koordinaten beschrieben werden kann. Um dieses Konzept besser zu illustrieren, sind in den meisten Demos, die dieses Kapitel begleiten die Achsen des World Space zu sehen. Jedes Objekt in unserer 3D-Welt hat hingegen sein eigenes lokales Koordinatensystem, besitzt seinen eigenen Achsen und hat seinen eigenen Nullpunkt. Der Nullpunkt kann beispielsweise in der Mitte des Objekts liegen. Die Achsen zeigen an, welche Richtungen für das Objekt “oben”, “unten”, “rechts”, “links”, “vorne” und “hinten” sind. Das ist genau so in der “echten” Welt. Für mich bedeutet zum Bei- spiel “links” etwas anderes als für jemanden, der vis-à-vis von mir sitzt. Diese lokale Koordinatensys- tem wird Object Space genannt. Ausserdem bewegt sich das lokale Koordinatensystem mit dem Objekt. Wenn sie zum Beispiel den linken Arm ausstrecken, wird dieser linke Arm immer einen Meter links von ihnen sein, egal wie sie sich im Raum umher bewegen. Wenn sie sich aber bewegen, wird sich ihre Position in der Welt verändern. Die Position, die sie inneha- ben, wenn sie sich umher bewegen wird in Weltkoordinaten ausgedrückt. Natürlich hat auch ihr linker Arm, den sie immer noch ausgestreckt haben eine Position im World Space. Es genügt nun aber, wenn wir nur die Position ihres Nullpunktes kennen (nehmen wir an der Nullpunkt von ihnen befindet sich in der Körpermitte). Die Position ihres linken Armes kann man nun leicht ausrechnen, weil man weiss, dass sich ihr linker Arm ein Meter links von ihrem Nullpunkt entfernt befindet. Weil sich ein 3D-Objekt mitsamt seinem lokalen Koordinatensystem im absoluten globalen Koordina- tensystem bewegt, kann es hilfreich sein, das globale Koordinatensystem (World Space) als Eltern-Space und das lokale Koordinatensystem als Kind-Space zu verstehen. Ausserdem ist es sehr nützlich, die 3D- Objekte in weiter Subobjekte zu unterteilen. Der Roboter hat zum Beispiel einen Kopf mit einer Nase, zwei Arme und zwei Beine. Falls der Roboter nun nicken will, bewegt sich sein Kopf mitsamt Nase relativ zum Roboterkörper. Um den ganzen Roboter zu bewegen müssen, wie aber den Kopf und die Nase mit bewegen. Wir erhalten also ein Hierarchie von Objekten, die sich alle relativ zueinander bewe- gen. Genau diese Hierarchie kann man nun mit einem Scenegraph implementieren und das macht es uns leicht in jME1 solche Hierarchien von Objekten zu benutzen. 1 ImGegensatz zur “herkömmlichen” 3D-Programmierung mit OpenGL und Direct3D, in denen man solche Hierarchien und relative Bewegungen mühsam von Hand selbst verwalten muss. Ausser man implementiert seinen eigenen Scenegraph natürlich.
Sie können auch lesen