Interaktive 3D-Stellarkartographie Studienarbeit vorgelegt von Thomas Jegust Institut für ComputerVisualistik Arbeitsgruppe Computergraphik Betreuer: Dipl.-Inform. Matthias Biedermann Prüfer: Prof. Dr.-Ing. Stefan Müller Mai 2005 2 Inhaltsverzeichnis 1. Einleitung 04 2. Theoretische Grundlagen 2.1 Sterne vs. Planeten 2.2 die solare Nachbarschaft 2.3 Sternörter und Koordinaten 2.4 Entfernungsmessung über große Distanzen 2.5 Spektralklassen 2.6 Binär- und Trinärsternsysteme 05 05 07 07 08 09 09 3. Billboards 3.1 Rotationsachsen 3.2 Darstellung der Sterne als VABB 3.3 Zwei Varianten zur Darstellung 3.3.1 Variante 1 3.3.2 Variante 2 10 10 11 12 12 13 4. Beschreibung des Programms 4.1 Daten 4.2 Star-Objekte 4.2.1 Umrechnung in kartesische Koordinaten 4.3 Parsen der Textdateien 4.4 Aufbau des Szenegraphen 4.4.1 Die Methode createStar 4.4.2 Die Methode createSystem 4.4.3 Skalierung der Sterne 4.5 Einfügen eines Skybackgrounds 4.6 Navigation 4.6.1 Oben/Unten im Weltraum 4.6.2 Einbinden einer eigenen Navigation 4.7 Picking 4.8 Einblenden von Informationen 4.9 Diverse Effekte 4.9.1 FTL-Effekt 4.9.2 Animierte Billboards 4.9.3 Selektierte Sterne 4.9.4 Bildschirmschoner 14 14 15 15 16 18 19 21 24 26 28 28 29 31 33 35 35 37 39 39 5 Einbinden als Stereoapplikation 40 6 Ausblick 41 7 Fazit 42 8 Quellenverzeichnis 43 3 1. Einleitung Der Blick in den Nachthimmel offenbart dem Betrachter ein Bild der Sterne und anderer Himmelskörper jenseits der Grenzen unseres Sonnensystems. Allerdings gehen hier aufgrund der überaus hohen räumlichen Distanzen zu den Nachbargestirnen die dreidimensionalen Informationen für das menschliche Auge verloren. Die Entfernung eines Sterns zum Betrachter kann ohne weitere technische Hilfsmittel nicht eingeschätzt, geschweige denn mit anderen bekannten Entfernungen in Beziehung gebracht werden. Mit Hilfe der Computergrafik ist es möglich, ein Modell des interstellaren Raumes im Umkreis von 20 Lichtjahren (ca. 188 Billionen km) in so stark verkleinertem Maßstab anzufertigen, dass die dreidimensionalen Informationen für den Betrachter leicht ersichtlich werden und somit diesem eine neue Sicht und ein neues Verhältnis zu unserer Lage im Weltraum offenbaren. Ziel der Arbeit ist es eine für den Endanwender leicht und intuitiv zu bedienende, ansprechende Software zu gestalten. Aufbauend auf Informationen aus der Astronomie soll jene Software einerseits korrekte Daten der umliegenden Gestirne liefern, andererseits diese aber in publikumswirksamer Form präsentieren – die Zielgruppe sind astronomische Laien sowie Hobby-Astronomen. Die Umsetzung erfolgt mittels der Programmiersprache C++ unter Verwendung von OpenSG. 4 2. Theoretische Grundlagen In den folgenden Abschnitten sollen zunächst einige theoretische Grundlagen der Astronomie dargelegt und ihre jeweilige Umsetzung im Programm beschrieben werden. 2.1 Sterne vs. Planeten Immer wieder vom Laien missverstanden werden die Ausdrücke „Planeten“ und „Sterne“ wild durcheinander geworfen, ohne sich des gravierenden Unterschiedes dieser im Klaren zu sein. Das beste und verständlichste Beispiel für einen Planeten wäre die Erde. Die Erde ist nur einer von mehreren Planeten, die unser Zentralgestirn Sonne umkreisen. Für einen Umlauf um die Sonne braucht die Erde ziemlich genau 365 Tage. Die anderen Planeten des Sonnensystems, nämlich die „inneren“ Planeten Merkur, Venus, (Erde) und Mars sowie die „äußeren“ Planeten Jupiter, Saturn, Uranus und Neptun kreisen der Erde gleich in konzentrischen aber leicht ellipsenförmigen Bahnen um die Sonne. Die „inneren“ Planeten sind ähnlich der Erde Gesteinsplaneten, nur dass sie aufgrund ihrer geringen (Merkur, Venus) oder hohen Entfernung (Mars) zur Sonne ein für Menschen zu heißes bzw. kaltes Klima bieten. Die „äußeren“ Planeten sind allesamt Gasplaneten, d.h. sie besitzen keine wirklich feste Oberfläche sondern bestehen aus Zusammensetzungen verschiedener Gase. Zwischen den inneren und äußeren Planeten befindet sich im Sonnensystem eine Zone mit tausenden kleiner, nur einige Kilometer Durchmesser messende Gesteinsbrocken, welche als Planetoiden bezeichnet werden. Alle diese Planeten umkreisen die Sonne in konstanten Abständen. Merkur als sonnennächster Planet ist 58 Mio. km von der Sonne entfernt, Neptun als sonnenfernster1 Planet 4498 Mio. km. Wir können mit einem guten Fernrohr die Planeten unseres Sonnesystems betrachten. Das von ihnen ausgehende Licht ist das Sonnenlicht, welches von diesen reflektiert wird. 1 Pluto gilt nur noch bei Sentimentalisten und Historikern als Planet unseres Sonnensystems. Aufgrund seiner extremem Bahnabweichungen zur Ekliptik und der Tatsache, dass er zeitweise der Sonne näher ist als Neptun, dann wieder ferner, wird er mittlerweile von vielen Astronomen nur noch als großes Objekt des Kuiper-Gürtels angesehen. 5 Das beste Beispiel für einen Stern wäre unsere Sonne. Es gibt im Universum unzählige Gebilde, die unserer Sonne ähneln – Sterne. Viele der Sterne, welche wir bei Nacht sehen sind der Sonne sehr ähnlich, einige sogar bedeutend größer und heller als unsere Sonne. Nur die Tatsache, dass diese so unglaublich weit entfernt sind, lässt sie auf Punktgröße am Nachthimmel schrumpfen. Die Entfernungen zu den nächsten Sternen sind wie gesagt „unglaublich weit“, in Zahlen zwar auszudrücken, jedoch nicht für den täglichen Gebrauch empfehlenswert. Deshalb haben sich Astronomen die Distanzangabe Lichtjahr ausgedacht. Ein Lichtjahr ist die Strecke, welche ein Lichtstrahl in der gegebenen Zeit von genau einem Jahr zurücklegt, etwa 9.4 Billionen km. Im Vergleich dazu: ein Lichtstrahl von der Sonne braucht bis zur Erde etwa 8 Minuten, bis zum Neptun 4.16 Stunden. Die durchschnittliche Entfernung von Sternen in unserem Teil der Galaxis beträgt 4-5 Lichtjahre! Der nächste Stern zur Sonne ist Proxima Centauri, dieser liegt 4,2 Lichtjahre von der Sonne entfernt. Sterne reflektieren nicht wie die Planeten das Licht unserer Sonne. Da Sterne an sich „Sonnen“ sind, strahlen sie ihr eigenes Licht aus. Die vorliegende Studienarbeit befasst sich ausschließlich mit Sternen, nicht mit den sie umkreisenden Planetensystemen. Folglich werden keine Planeten, Monde oder Asteroiden behandelt. 6 2.2 Die solare Nachbarschaft In klaren, dunklen Nächten können wir ohne Einsatz eines Teleskops mit dem bloßen Auge etwa 6000 Sterne sehen. Schon ein flüchtiger Blick an den Nachthimmel offenbart folgende Merkmale: Die Verteilung der Sterne ist nicht gleichförmig, die Sterne haben nicht alle die gleiche Helligkeit, und es gibt ein schwach leuchtendes, unregelmäßiges Band, das den Himmel zweiteilt. Das diffuse Lichtband, das sich über den ganzen Himmel erstreckt, ist die Milchstraße. (Der Ausdruck Galaxis ist aus dem griechischen Wort für „Milch“ abgeleitet.) Mit Hilfe von kleinen Fernrohren oder sogar schon mit Feldstechern kann das Band in eine Unzahl von einzelnen Sternen aufgelöst werden. Sie sind Mitglieder einer großen Galaxis, die von schätzungsweise 1011 gravitativ aneinander gebundenen Sternen gebildet wird. Die meisten der mit dem bloßen Auge sichtbaren Sterne sind Mitglieder unserer Galaxis, der Milchstraße. Sie sind nah genug, so dass wir sie mit dem Auge als Einzelsterne auflösen können. Unsere Galaxis hat einen geschätzten Durchmesser von 70.000 Lichtjahren. Alle Sterne und Objekte in weniger als 20 Lichtjahren Entfernung zur Sonne liegen in unmittelbarer Nachbarschaft unseres Sonnesystems. Die durchschnittliche Entfernung zweier Sterne in solarer Nachbarschaft beträgt etwa 5 Lichtjahre. Das Programm stellt alle Sterne in solarer Nachbarschaft zur Sonne dar. Hierbei ist zu beachten, dass die meisten Sterne in solarer Nachbarschaft mit dem bloßen Auge eigentlich nicht zu erkennen sind, da sie als relativ kühle, rote Zwergsterne nicht genügend starkes Licht emittieren. 2.3 Sternörter und Koordinaten Unter Sternörter versteht man in der Astronomie die Angabe eines Satzes von Koordinaten von Gestirnen. Alle Sternörter beziehen sich auf eine astronomische Epoche, den Zeitpunkt ihrer Messung. Der Zeitpunkt der Messung ist relevant, da auch Sterne sich mit der Zeit bewegen, allerdings (im astronomischen Maßstab gesehen) so langsam, dass es dem menschlichen Auge nicht auffällt. Da die Geschwindigkeiten nur einige hundert Kilometer die Sekunde betragen fällt die Eigenbewegung nicht so sehr ins Gewicht als dass sie im vorliegendem Programm Beachtung finden. Meistens sind diese Sternörter zweidimensional – d.h. auf die übliche (gedachte) Himmelskugel bezogen. Die Erde befindet sich im Zentrum dieses Koordinatensystems, welches auch als äquatoriales System bezeichnet wird. Von der Erde aus betrachtet erhält ein Stern seine Koordinaten durch die Angabe zweier Werte, der Rektaszension (RA) und der Deklination (Delta). Die Rektaszension ist die Entsprechung der geografischen Längenkreise auf der (imaginären) Himmelskugel. Als Nullpunkt der Rektaszension dient dabei der Frühlingspunkt. Die Rektaszension, die bei der Positionsangabe von Himmelsobjekten verwendet wird, wird von Norden aus betrachtet im Gegenuhrzeigersinn gemessen. In der Astronomie hat es sich durchgesetzt, die Rektaszension nicht in Grad, sondern in Stunden anzugeben, wobei 24h = 360° gesetzt werden. 7 Die Deklination entspricht der Projektion der Breitenkreise der Erde auf die Himmelskugel. Die Deklination gibt somit den Winkelabstand eines Objekts vom Himmelsäquator an. Werte nördlich des Äquators sind positiv, Werte südlich davon negativ. Die minimale Deklination beträgt somit –90°, die maximale Deklination +90°. 2.4 Entfernungsmessungen über große Distanzen Um die Entfernung zu sonnennahen Sternen zu messen hat sich das Verfahren der Parallaxenverschiebung in der Astronomie bewährt. In diesem recht einfachen trigonometrischen Verfahren wird der Winkel θ gemessen unter den man einen Stern zu einem festen Zeitpunkt betrachtet; diese Messung wird genau ein halbes Jahr später wiederholt [siehe Zeichnung]. Dieses halbe Jahr später befindet sich die Erde an einer Position 300 Mio. km von der ersten Messung entfernt. Anhand der gemessenen Winkel zu dem Stern ergibt sich ein Dreieck mit extrem großer Seitenlänge. Die Höhe dieses Dreiecks berechnet sich nach Pythagoras. Die jährliche Parallaxe ist der Winkel, unter dem die große Halbachse der Erdbahn vom Stern aus erscheint. Beträgt die Parallaxe eine Bogensekunde (1/3600 eines Grades) so entspricht das einer Entfernung von 3,26 Lichtjahren oder rund 31 Billionen Kilometern. Diese Entfernung wird auch als Parsec (parallax arcsecond) bezeichnet. Die Parallaxe ist selbst bei nahen Sternen so klein, dass man sie lange nicht messen konnte. Selbst beim sonnennächsten Stern Proxima Centauri beträgt die Parallaxe nur 0,772 Bogensekunden. In den 1990ern gelangen mit dem Astrometriesatelliten HIPPARCOS genaue Parallaxenmessungen für viele Sterne. Basierend auf den von HIPPARCOS gemessenen Daten werden die Koordinaten der Sterne dreidimensional durch das Programm dargestellt. 8 2.5 Spektralklassen Dem menschlichen Auge erscheinen die meisten Sterne als weiße Pünktchen am Nachthimmel; nur bei wenigen Sternen wie z.B. Beteigeuze im Sternbild Orion erkennt der Betrachter deren Farbe. Spektroskopische Messungen ergeben jedoch, dass nicht alle Sterne sonnenähnlich sind, sprich gelbliches Licht ausstrahlen. Die meisten Sterne in solarer Nachbarschaft sind so genannte rote Zwergsterne, d.h. relativ kühle, kleine und für das bloße Auge meist gar nicht mehr sichtbare Sterne, welche rotes Licht ausstrahlen. Dann wiederum gibt es auch sehr heiße, blau leuchtende Sterne. Die verschiedenen Typen werden durch ihre Spektralklasse klassifiziert. Als Spektralklasse bezeichnet man das System der Harvard-Klassifikation nach der alle Sterne nach ihrer Oberflächentemperatur und chemischen Zusammensetzung eingruppiert werden. Es wurde von Edward Charles Pickering, dem Direktor des Harvard College Observatory und seinen Mitarbeitern zwischen 1890 und 1901 erarbeitet. Klasse O B A F G K M Farbe Blau Blau-weiß Weiß Weiß-gelb Gelb Orange Rotorange Temperatur [in K] 28.000 – 50.000 9.900 – 28.000 7.400 – 9.900 6.000 – 7.400 4.900 – 6.000 3.500 – 4.900 2.000 – 3.500 Beispielsterne Mintaka Rigel, Spica Wega, Sirius Prokyon, Canopus Capella, Sonne Arcturus, Aldebaran Beteigeuze, Kapteyns Stern Die Spektralkassen mit ihren 7 Grundtypen (O,B,A,F,G,K,M) machen rund 99% aller Sterne aus, weshalb die anderen Klassen oft vernachlässigt werden. Diesem Beispiel folgend existieren auch in vorliegendem Programm nur jene 7 Grundtypen, zusätzlich aber noch eine achte Klasse (T) für braune Zwergsterne. 2.6 Binär- und Trinärsternsysteme Oft bilden sich in Sternentstehungsprozessen gleich mehrere Sterne, die örtlich nah zusammen stehen und gravitationell gebunden sind. Diese Sterne umkreisen einen gemeinsamen Schwerpunkt und bilden Binärsternsysteme (für je zwei sich umkreisende Sterne) oder Trinärsternsysteme (für drei sich umkreisende Sterne). Viele Mehrfachsternsysteme sind mit bloßem Auge nicht von einfachen Sternsystemen zu unterscheiden. Handelt es sich bei einem System um ein Binär- bzw. Trinärsternsystem, so stellt das Programm die jeweiligen Sterne versetzt zueinander dar und lässt sie umeinander rotieren. Binärsternsystem mit gemeinsamen Rotationspunkt 9 3. Billboards Unter einem Billboard versteht man einfach gesprochen eine Grafik, die sich stets nach dem Betrachter ausrichtet. Hierbei kann man dem Billboard zusätzlich eine Rotationsachse mitgeben um die Ausrichtung einzuschränken. 3.1 Rotationsachsen In folgender Szenerie sieht man einen Tannenbaum [Bild 1], dargestellt über ein Billboard. Bewegt sich der Betrachter horizontal um diesen herum [Bild 2], so verändert sich der Tannenbaum scheinbar nicht, da sich das Billboard zu dem Betrachter hin ausrichtet. Bild 1 Bild 2 Dies liegt daran, dass einem Billboard eine feste Rotationsachse mitgegeben ist. Die Rotationsachse des Billboards ist in folgenden Bildern die y-Achse, welche es dem Billboard weiterhin erlaubt in x/z-Ebene zu rotieren [Bild 4], nicht aber in der x/y- oder z/yEbene. Bild 3 Bild 4 Allerdings beschränkt diese Rotationsachse nun die Rotation des Billboards wie gesagt allein auf die x/z-Ebene. Der Billboard-Tannenbaum rotiert nur in der x/z-Ebene, weshalb er von einem Betrachtungspunkt weiter oberhalb nur noch als flache, eindimensionale Linie wahrgenommen wird. Bild 5 10 Wird die Rotationsachse des Billboards jedoch nicht gesetzt – es wird also speziell der Nullvektor als Rotationsachse genommen – so kommt dieser Effekt nicht zustande. Bild 6 Der Tannenbaum dreht sich jetzt stets mit der Kamera mit [Bild 6]. Scheinbar „liegt“ der Baum nun auf dem Boden. Sich nach der Kamera ausrichtende Billboards nennt man „View-aligned-Billboards“ (VABB). VABBs sind demnach sehr praktisch für Szenen, in denen Objekte vorkommen, die keiner festen Ausrichtung bedürfen. Sterne bieten sich an für den Einsatz von VABB. 3.2 Darstellung der Sterne als VABB Die Sterne des vorliegenden Programms benötigen keine feste Verankerung. Sie stehen an einer fixen Position im Raum, müssen sich nach nichts ausrichten außer dem Benutzer. Von allen Seiten betrachtet sollen sie gleich aussehen, weshalb ein Effekt wie in Bild 5 zu sehen äußerst ungünstig wäre. Sterne werden nicht als 3D-Objekte, sondern als View-aligned-Billboards (VABB) dargestellt. Die Entscheidung zur Darstellung der Sterne mittels VABBs wurde nicht aus Performance-Gründen getroffen – hierzu werden zu wenig Sterne in das Modell übernommen um einen merklichen Geschwindigkeitsvorteil zu erlangen -, sondern ergab sich aus der einfachen Begebenheit heraus, dass zum einen ein Stern von allen Seiten gleich aussieht und zum anderen VABB bis zu diesem Zeitpunkt selten benutzt worden sind und somit eine Herausforderung darstellten. Astronomische Spitzfindigkeiten wie z.B. die Radialgeschwindigkeit der Sterne, welche diese an den Polen abflachen, oder Gebiete mit Temperaturschwankungen auf der Sternoberfläche, die unregelmäßige Flecken hervorrufen, werden in vorliegendem Modell nicht beachtet. 11 3.3 Zwei Varianten zur Darstellung Aufgrund der äußerst spärlichen OpenSG-Dokumentation gestaltete sich die Ausrichtung der Billboards zum Benutzer bzw. zur Kamera hin als eine mittlere Odyssee, welche letztendlich aber zu einem überaus simplen doch sehr zufrieden stellendem Ergebnis kam. Der erste im Ergebnis suboptimale Ansatz basierte auf der Annahme, dass jedem Billboard eine Rotationsachse mitgegeben werden muss. Somit ließ sich dass Problem, dass sich das Billboard aus bestimmten Positionen betrachtet nur als schmale Linie darstellt, auf 2 verschiedene Arten umgehen. 3.3.1 Variante 1 Variante 1 stellt die Sterne dar über den Einsatz zweier zueinander um 90° gekippter Billboards. Sterne mittels zweier gekippter Billboards darzustellen ist technisch gesehen sehr einfach, liefert jedoch kein wirklich zufrieden stellendes Ergebnis, da dem ungeübten Auge schnell die eigentliche Täuschung auffällt: 2 um 90° zueinander gekippte Billboards Man beachte das ringförmige Halo um den Stern sowie die 3D-Kugel, welche zusätzlich als Geometrie an die Position des Sterns gesetzt worden ist. Das Halo ist zweimal sichtbar, da es von beiden Billboards benutzt wird und diese um 90° zueinander gekippt sind. Die Kugel ist nicht vollständig abgebildet, man sieht, dass sie von dem waagerechten Billboard zerschnitten wird. 12 3.3.2 Variante 2 Variante 2 stellt die Sterne dar über eine dynamische Berechnung der Rotationsachse zur Laufzeit Die zweite Möglichkeit ist, den Stern nur durch ein einziges Billboard darzustellen. Die Rotationsachse des Billboards muss aber stets im Winkel von 90° zu dem Vektor stehen, der die direkte Verbindungslinie des Sterns und der Kamera beschreibt. Die Achse zur Laufzeit dynamisch zu berechnen ist umständlich und soll nicht weiter vertieft werden, da die im folgenden beschriebene Lösung des Problems weitaus einfacher ist. Billboard mit dynamischer Rotationsachse Letztendlich führte ein Programmierfehler in zweitem Lösungsansatz zu der eigentlichen Erkenntnis, dass eine Rotationsachse in vorliegendem Fall unnötig ist. Durch umständliche Berechnungen hatte es sich ergeben, dass die Rotationsachse stets der Nullvektor war. Somit ist es möglich, ein Billboard nach der Kamera auszurichten, indem man einfach seine Rotationsachse gleich dem Nullvektor setzt und auf alle anderen Berechnungen verzichtet. 13 4. Beschreibung des Programms Im folgenden Kapitel wird beschrieben, wie Daten für die Erstellung der Sterne eingelesen und verwaltet werden, wie mittels OpenSG der Szenengraph aufgebaut und ein eigenes Navigationsmodell eingesetzt wird. Zu guter letzt werden zusätzliche Features des Programms beschrieben. 4.1 Daten Um die Sterne unserer solaren Nachbarschaft darzustellen benötigt man zu aller erst die korrekten astronomischen Koordinaten dieser. Die HIPPARCOS-Mission der 90er-Jahre führte zu einem umfassenden Katalog sehr präziser Sternörter der umliegenden Gestirne. Die für das Programm relevanten Daten wurden in einzelne Textdateien geschrieben, wobei jede Textdatei für einen Stern steht. Dies erlaubt dem User eine einfache und unkomplizierte Bearbeitung der Daten. Die Textdateien sind im Verzeichnis /stardata abgelegt. Der Aufbau einer Textdatei ist im folgenden Beispiel beschrieben: 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 Sirius -1.612 -2.474 8.078 6.0 45.0 8.871 -16.0 42.0 57.99 8.601 2.03 0.02 0 2.35 0.98 0 B A 0 Sirius ist ein blaues Doppelsternsystem. Obwohl SiriusB, der kleine Begleiter von SiriusA, nur etwa ein hundertstel dessen Durchmesser misst, fasst er dennoch ein Drittel der Gesamtmasse des Systems. Sirius A und B umkreisen einen gemeinsamen Schwerpunkt. Name des Systems Kartesische Koordinaten Rektaszension Deklination Entfernung zu Sol Durchmesser (Sol=1) Masse (Sol=1) Spektralklasse Zusätzliche textuelle Informationen Hinweis: Beim Auslesen der Textdatei (s. 4.3) wird die erste Zeile komplett als Name des Sterns ausgelesen. Es ist also darauf zu achten, die kartesischen Koordinaten sowie anderen Daten unbedingt in nachfolgende Zeilen zu schreiben. 14 4.2 Die Klasse Star Die Klasse Star verwaltet Objekte, die einen Stern bzw. Binär- oder Trinärstern beschreiben. Ein Stern setzt sich aus folgenden relevanten Daten zusammen: Name des Systems [string] Kartesische Koordinaten X [double] Y [double] Z [double] Astronomische Koordinaten Rektaszension RA [double] RAhour [double] Ramin [double] RAsec [double] Astronomische Eigenschaften Größe Size [double] SizeBi [double] SizeTri [double] Spektralklasse [char] Deklination DEC [double] DECgrad [double] DECmin [double] DECsec [double] Masse Mass [double] MassBi [double] MassTri [double] Textuelle Informationen [string] Star-Objekte werden dazu benutzt, die aus den Textdateien stammenden Sterndaten zu verwalten. An Methoden bietet die Klasse Star nur gewöhnliche Getter&Setter, lediglich die Methoden Star::calcXYZ() sowie Star::getInfo() führen Operationen aus. Star::calcXYZ() berechnet aus einem Satz gegebener astronomischer Koordinaten die korrekten kartesischen Koordinaten (s. 4.2.1), nach denen ein Stern vom Programm positioniert wird; Star::getInfo() liefert einen string zurück, welcher die textuellen Informationen in einer für die Anzeige formatierten Form enthält. 4.2.1 Umrechnung in kartesische Koordinaten Die Umrechnung der astronomischen Koordinaten in kartesische Koordinaten erfolgt mittels folgender Umrechnungsformel: ( ) ( ) dist * cosDEC * cosRA = dist * cosDEC * sinRA dist * sinRA x y z Hinweis: Unter OpenSG ist die Y-Achse mit der Z-Achse vertauscht! Hierzu müssen vorerst aber die in Winkelstunden, Winkelminuten und Winkelsekunden vorliegenden astronomischen Koordinaten ins Gradmaß umgerechnet werden. Es entsprechen: Winkelstunde Winkelminute Winkelsekunde = = = 15 15° 0.25° 0.00416° 4.3 Die Klasse Parser Über die Klasse Parser erfolgt das Einlesen der benötigten txt-Dateien. Relevant ist hier die Methode Parser::searchDirectory(), welche alle im Verzeichnis /stardata befindendlichen txt-Dateien öffnet, deren Inhalt in Star-Objekten speichert und diese Star-Objekte wiederum in einem Vektor ablegt. Parser::searchDirectory() wird in stars-main.cpp innerhalb der Methode createScenegraph() aufgerufen und liefert den benötigten Vektor mit allen darzustellenden Star-Objekten. Im folgenden wird beschrieben, wie Parser::searchDirectory() die in dem Verzeichnis /stardata liegenden txtDateien öffnet und ihren Inhalt in Star-Objekten speichert. 01 vector<Star>* Parser::searchDirectory(){ 02 vector<Star>* stars = new vector<Star>; // Pointer to Vector 03 // storing our star-objects 04 struct _finddata_t c_file; 05 long hFile; 06 07 if( (hFile = _findfirst( "stardata/*.txt", &c_file )) == -1L ) 08 cerr << "No *.txt files in current directory" << endl; 09 else { 10 cout << "Searching directory /stardata/..." << endl; 11 do { 12 Star aktuell; // object star 13 ifstream datei; // input filestream Zuerst wird der Vektor stars angelegt, welcher später alle Star-Objekte enthalten soll [Zeile 02]. Dann folgt ein Test, ob das angegebene Verzeichnis überhaupt gesuchte Dateien enthält. Ist das Verzeichnis leer oder enthält keine txt-Dateien, so wird eine Fehlermeldung ausgeworfen, andernfalls ein temporäres star-Objekt aktuell erzeugt und ein input filestream für die erste zu lesende txt-Datei. Das ganze steht zudem in einer do-while-Schleife [Zeile 11], damit jede txt-Datei ausgelesen wird. Zu beachten ist außerdem, dass in Zeile 07 stets _findfirst( "stardata/*.txt", &c_file ) ausgeführt wird, also schon hier die erste gefundene .txt-Datei in dem Strukt c_file abgelegt wird. Folgende temporären Variablen speichern die ausgelesenen Informationen, bevor diese an das Star-Objekt aktuell überschrieben werden. 14 15 16 17 18 19 20 21 22 string starname, textInfo, textInfoLine;// unsigned char colorCode, colorCodeBi, // colorCodeTri; // double x, y, z, // size, sizeBi, sizeTri, mass, massBi, massTri, RAh, RAmin, RAsec, DECgrad, DECmin, DECsec, distance; temporary variables storing all the stuff relevant to object star Jetzt wird die erste txt-Datei in einem input filestream geöffnet und ausgelesen. Zu beachten ist, dass das Strukt c_file allerdings nur den einfachen Dateinamen, nicht aber das eigentliche Verzeichnis in dem die Datei liegt speichert. Deshalb ist es nötig, zuerst das Verzeichnis als Präfix im eigentlichen Dateinamen anzugeben. 16 23 24 25 26 27 string filename( "stardata/" ); filename += string( c_file.name ); datei.open(filename.c_str()); if (!datei) cerr << "mayday!"; else cout << c_file.name << " opened" << endl; Nun beginnt das eigentliche Auslesen der Informationen. Da die erste Information in jeder Textdatei der Name des Sternensystems ist, dieser jedoch auch Leerzeichen enthalten kann (z.B. das Sternensystem „DX Cancri“), erfolgt hier das Auslesen mittels der Methode getline. 28 29 30 getline(datei, starname); // here getline, so we can use //starnames with whitespaces aktuell.setName(starname); Die folgenden Daten werden wie folgt ausgelesen: 28 29 30 31 32 33 34 35 datei >> x >> y >> z; aktuell.setXYZ(x,y,z); datei >> RAh >> RAmin >> RAsec >> DECgrad >> DECmin >> DECsec >> distance; aktuell.setRA(RAh,RAmin,RAsec); aktuell.setDEC(DECgrad,DECmin,DECsec); aktuell.setDistance(distance); aktuell.calcXYZ(); Zeile 35 berechnet die kartesischen Koordinaten des Sterns neu, sollte die Distanz zur Sonne nicht 0 Lichtjahre betragen. Die Abfrage hierzu erfolgt in der Methode Star::calcXYZ(). Die letzten Informationen der Textdatei - die textuellen Informationen, welche später bei der Selektion des Sterns angezeigt werden sollen - werden aus Gründen der Performance wieder mittels getline ausgeslesen. Zuletzt wird die aktuelle Datei geschlossen [Zeile 36], das mit Daten gefüllte Star-Objekt in den Vektor stars verschoben [Zeile 39] und auf die nächste txt-Datei zugegriffen [Zeile 42]. 36 datei.close(); // close txt-file 37 cout << c_file.name << " closed" << endl; 38 39 stars->push_back(aktuell); // push starObject into vector 40 cout<<endl; cout<<endl; 41 } 42 while (_findnext(hFile, &c_file)==0); 43 } 44 return stars; 45 }// END searchDirectory Die Methode beendet ihre Aktivitäten mit Rückgabe des Vektors stars [Zeile 44]. 17 4.4 Aufbau des Szenegraphen Der Szenegraph des Programms präsentiert sich wie folgt: An die Stelle „ STAR-SYSTEM “ werden die verschiedenen Sterne bzw. Sternensysteme gehangen, eine genauere Erläuterung hierzu folgt in 4.4.1 und 4.4.2. 18 4.4.1 Die Methode createStar Im folgenden wird beschrieben, wie ein Billboard-Stern unter OpenSG modelliert wird. Die Methode NodePtr createStar(NodePtr BillboardNode, string name, double size, unsigned char colorCode) in stars-main.cpp erzeugt die für den Stern notwendige Geometrie, belegt diese mit einer Textur und hängt sie unter einen im ersten Argument angegebenen Billboard-Knoten. Zuallererst wird ein Knoten benötigt, der die Geometrie beinhalten soll, welche das darzustellende Bild als Textur besitzt. Für die Geometrie empfiehlt sich eine Ebene. 01 GeometryPtr planegeo = makePlaneGeo(size,size,1,1); // 1st geometry-core Im Core planegeo befindet sich nun die Beschreibung für eine Ebene mit den Ausmaßen size x size. Size ist für jeden Stern unterschiedlich, je nachdem wie groß er ist. Da die Ebene bis jetzt aber noch keine Textur hat, wird die Ebene mit einer Textur belegt die beim Start des Programms bereits global kreiert worden ist. 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 beginEditCP(planegeo, Geometry::MaterialFieldMask); switch(colorCode){ case ‘O’: {planegeo->setMaterial(blue);} break; case ‘B’: {planegeo->setMaterial(bluewhite);} break; case ‘A’: {planegeo->setMaterial(white);} break; case ‘F’: {planegeo->setMaterial(yellowwhite);}break; case ‘G’: {planegeo->setMaterial(yellow);} break; case ‘K’: {planegeo->setMaterial(orange);} break; case ‘M’: {planegeo->setMaterial(red);} break; case ‘T’: {planegeo->setMaterial(brown);} break; case ‘E’: {planegeo->setMaterial(sol);} break; case ‘R’: {planegeo->setMaterial(redflare);} break; case ‘S’: {planegeo->setMaterial(bab5);} break; default: {planegeo->setMaterial(bab5);} break; } endEditCP(planegeo, Geometry::MaterialFieldMask); Da für die Sterne je nach Spektralklasse verschiedene Texturen benötigt werden, erfolgt deren Zuweisung in einer switch-Anweisung über ihren Farbcode. Nun befindet sich im Core planegeo die Beschreibung einer Ebene mit den Ausmaßen size x size und einer Texturierung. Jetzt muss planegeo an einen Knoten gehangen werden. 18 19 20 21 22 23 NodePtr geometryNODE = Node::create(); beginEditCP(geometryNODE, Node::CoreFieldMask); geometryNODE ->setCore(planegeo); endEditCP(geometryNODE, Node::CoreFieldMask); // ... give a name to the node, so we can easily find it later on... setName(geometryNODE, name); Der Knoten geometryBillboard stellt jetzt im Programm eine fest im Raum stehende texturierte Fläche dar. Bei einer Rotation um diese dreht sie sich natürlich noch nicht mit der Kamera mit, da sie kein Billboard ist. Dies wird dahingehend verändert, indem über den geometryBillboard-Knoten einen Billboard-Knoten gehangen wird. Hinweis: Es ist wichtig, der Geometrie des Billboards einen Namen mitzugeben. Später wird anhand dieses Namens auf den Vektor mit den Star-Objekten zugegriffen werden müssen. 19 Aber das wichtige zuerst: der Core für den Billboard-Knoten. 24 25 26 27 28 29 30 // ... above the geometryBillboard-Nodes shall be BillboardNodes, // so the geometry becomes a flattened image always facing the camera beginEditCP(billyBoy); // core already created in createSceneGraph() billyBoy->setAlignToScreen(true); billyBoy->setAxisOfRotation(Vec3f(0,0,0)); billyBoy->setFocusOnCamera(false); endEditCP(billyBoy); //Billboards now face the camera Der Core billyBoy wird, wie in Zeile 26 beschrieben, bereits in NodePtr createSceneGraph(void) erstellt: billyBoy = Billboard::create();// create core for star-billboards Somit teilen sich alle im folgenden erstellten Billboard-Knoten für die Sterne ein und denselben Core billyBoy. Wichtig ist nun, dass dem Core keine Rotationsachse mitgegeben wird [Zeile 28]. Nur der Einsatz des Nullvektors bewirkt, dass sich das zukünftige Billboard von jeder Kameraposition aus zum Bildschirm hin ausrichtet (s. 3.1). Eine Rotationsachse beschränkt hier die Ausrichtung des Billboards! Standardmäßig sind die Methode setAlignToScreen [Zeile27] und setFocusOnCamera [Zeile 29] auf true gesetzt. Die erste Methode bewirkt, dass sich die Billboards zum Bildschirm hin ausrichten. Die zweite Methode lässt die Billboards auf die Kamera ausrichten, d.h. sie stehen orthogonal zu dem Vektor zwischen Kamera und ihrer eigenen Position. Dies führt gerade in den Fällen, in denen die Sterne an den Rand des Bildschirms dargestellt werden zu einem unrealistischem Erscheinungsbild, wie folgende Bilder zeigen: Bild 1 Bild2 Beide Bilder zeigen die untere rechte Ecke des Fensters, in dem das Programm läuft. Der Billboard-Knoten von Bild 1 hat die Methode setFocusOnCamera mit dem Parameter true aufgerufen, der Billboard-Knoten von Bild 2 mit false. In der Endversion des Programms werden alle Billboards nur auf den Bildschirm, nicht aber auf die Kamera ausgerichtet, somit ergibt sich das gewünschte Bild [Bild2]. Anzumerken sei außerdem, dass sich auf die Kamera fokussierte Billboards [Bild 1] nicht weiter zu dieser hin ausrichten, sollten sie einer Rotation unterworfen werden (sprich der Billboard-Knoten hängt unter einem Rotationsknoten). 20 Aber bevor obige Bilder zu sehen sind, muss natürlich erst der Core an einen Knoten gehangen und dieser über den GeometryNODE-Knoten gesetzt werden. 31 beginEditCP(BillboardNode, Node::CoreFieldMask|Node::ChildrenFieldMask); 32 BillboardNode->setCore(billyBoy);// set the core of BillboardNode 33 BillboardNode->addChild(geometryNODE); 34 endEditCP(BillboardNode, Node::CoreFieldMask|Node::ChildrenFieldMask); 35 return BillboardNode;} Im Szenegraphen zeigt sich der zum Billboard gewandelte Stern wie folgt: 4.4.2 Die Methode createSystem Ein Sternensystem kann aus bis zu 3 Sternen bestehen. Diese Sterne umkreisen einen gemeinsamen Schwerpunkt. Der Schwerpunkt dieses Systems liegt auf den eigentlichen Koordinaten des Sternensystems. Die ihn umkreisenden Sterne rotieren um den Schwerpunkt in geringer Entfernung. Es gibt nun folgende drei Fälle: a) Singulärsystem Es wird genau einmal die Methode createStar aufgerufen. Der hieraus resultierende Teilszenegraph wird in den Gesamtszenegraphen eingefügt, wobei ein Knoten zwischen die beiden Szenegraphen geschaltet wird der die Translation des Sterns zu seinen Koordinaten fasst. 21 b) Binärsystem Es werden zwei Singulärsysteme in dichtem Abstand zueinander erzeugt – der Abstand der beiden Sterne zueinander wird über jeweils einen Translationsknoten oberhalb des Singulärsystems gewährleistet. Jene Singulärsysteme werden unter einen gemeinsamen Rotationsknoten gehangen und letztendlich wie in a) mit einem dazwischengeschalteten Translationsknoten an den Gesamtszenegraphen angefügt. 22 c) Trinärsystem Es wird ein Binärsystem erzeugt, ein dritter Stern wird in geringen Abstand zu jenem gesetzt (vgl. Erzeugung des Binärsystems). Das Binärsystem und das Singulärsystem werden unter einen gemeinsamen Rotationsknoten gehangen und wie in a) mit einem dazwischengeschalteten Rotationsknoten an den Gesamtszenegraphen angefügt. 23 Der Quellcode der Methode createSystem soll hier nicht im genauen erläutert werden. Dem aufmerksamen Auge fällt allerdings bei obigen drei Szenegraphen eine gewisse Redundanz auf: Der Szenegraph für das Singulärsternsystem ist auch Teil der Szenegraphen für Bi- bzw. Trinärsternsysteme, der Szenegraph für das Binärsternsystem ist Teil des Szenegraphen für Trinärsternsysteme. Durch geschicktes Erzeugen und Verbinden der verschiedenen Teilszenegraphen können hiermit im Quellcode ein paar Zeilen eingespart werden. Grob gesehen besteht createSystem aus zwei aufeinander folgenden switch/caseAnweisungen mit jeweils drei Fällen. Die erste switch/case-Anweisung erzeugt je nachdem um welchen der obigen Fälle es sich handelt die benötigten Nodes und Cores, die zweite switch/case-Anweisung setzt diese zu einem Teilszenegraphen zusammen. 24 4.4.3 Skalierung der Sterne Eine Einheit im System beträgt ein Lichtjahr. Die Sterne werden deshalb nicht in originalgetreuer Größe abgebildet, da sie sonst im Subpixelbereich lägen. Die Sonne wird auf einer Ebene mit 1E Kantenlänge abgebildet – dies entspricht einem Lichtjahr –, die restlichen Sterne skaliert dargestellt. Die Skalierung macht in dem Fall Sinn, da die meisten Sterne in 20 LJ Entfernung zur Sonne weniger als 20% des Sonnendruchmessers messen und somit kaum mehr sichtbar wären. Die Sterne, welche größer als die Sonne sind, werden ein wenig kleiner dargestellt um die Aufmerksamkeit des Benutzers nicht auf eine handvoll weniger, sehr großer Sterne zu fokussieren. Es gilt für Sterne <Sol: Darstellung = 3/4 * Größe + ¼ Es gilt für Sterne >Sol: Darstellung = ½ * Größe + ½ Die Skalierung der Sterne findet sich im Code innerhalb der Methode createSystem() wieder. 25 4.5 Einfügen eines Sky-Backgrounds Zu den Anfängen der Studienarbeit befand sich das Programm in folgendem Zustand: Das Modell der Sterne ließ sich zwar schon von allen Seiten mittels Kamerarotation aus betrachten, allerdings blieb der Hintergrund monoton schwarz. Nachdem ein Hintergrundbild eingefügt wurde bekam der Betrachter der Szene sehr schnell das Gefühl, sich nicht selbst mit der Kamera um die Sterne zu drehen sondern die Szene bzw. die Sterne an sich zu drehen, da das Hintergrundbild fest verankert sich nicht mit der Szene bewegte. Es wurde der Eindruck erweckt, dass nur einige Sterne der Milchstraße (welche im Hintergrundbild dargestellt war) bewegt wurden; bei dieser Bewegung wurden die Sterne aber offensichtlich aus der Milchstraße „herausgerissen“, da sich diese im Hintergrund nicht mitbewegte. Um das Gefühl des Betrachters, sich tatsächlich in einem dreidimensionalen Raum zu befinden, zu erhöhen, empfiehlt sich der Einsatz eines SkyBackgrounds. Unter einem SkyBackground versteht man eine Reihe von Texturen, welche auf einen Würfel, der die gesamte Szene beinhaltet, gezeichnet werden. Die verschiedenen Seiten des Würfels lassen sich durch Rotation der Kamera anschauen, nie aber kann sich der Betrachter diesen nähern, sie befinden sich stets in einem konstanten Abstand zur Kamera. Die Bilder, welche auf die verschiedenen Seiten des Würfels gemalt werden, sollten natürlich an den benachbarten, zusammenstoßenden Kanten ähnliche Bildinformationen beinhalten, um den nicht den Eindruck zu vermitteln, sich in gerade demselbigen Würfel zu befinden. Es empfiehlt sich daher zuerst den kompletten Skybackground in einem Bild zu malen und dieses nachher in Einzelbilder zu zerschneiden. Skybox (4096x3072 Pixel) Die Seiten des Skybackgrounds werden wie folgt mit Bildern belegt: Zuerst benötigt man für jede der 6 Seiten des Würfels einen ImagePtr, welcher die Bildinformationen enthält: 01 ImagePtr skyfront = Image::create(); Diesem ImagePtr muss nun ein Bild zugewiesen werden: 02 beginEditCP(skyfront); 03 skyfront->read("skybackground/sky-front.jpg"); 04 endEditCP(skyfront); Der Skybackground selbst benötigt allerdings sogenannte TextureChunks in denen die ImagePtr ihre Bilder übergeben. Des weiteren sind TextureChunks dazu in der Lage die 26 6 Bilder des Würfels an ihren Kanten zu falten [Zeile 09], so dass eventuelle Artefakte (siehe Bildvergleich) unterdrückt werden. 05 06 07 08 09 10 11 12 13 TextureChunkPtr FrontTexture = TextureChunk::create(); // then we store it in a TextureChunkPtr beginEditCP(FrontTexture); FrontTexture->setImage(skyfront); FrontTexture->setWrapS(GL_CLAMP_TO_EDGE); // to avoid edges FrontTexture-setMinFilter(GL_LINEAR_MIPMAP_LINEAR); FrontTexture->setMagFilter(GL_LINEAR); endEditCP(FrontTexture); Der Einsatz eines Filters zur Faltung der Bilder an den Würfelkanten lohnt sich, wie folgende Screenshots zeigen: links: ohne Filter rechts: mit Filter Nun verfügt der TextureChunk “FrontTexture” über den ImagePtr „skyfront“ und somit über das Bild „skybackground/sky-front.jpg“. Jetzt muss der TextureChunk nur noch einem SkybackgroundPtr übergeben werden. Hierzu muss dieser erst einmal erzeugt werden: 14 SkyBackgroundPtr skyBkg = SkyBackground::create(); Dann übergibt man in der gewohnten beginEditCP..endEditCP-Schreibweise den TextureChunk: 15 beginEditCP(skyBkg); 16 skyBkg->setFrontTexture(FrontTexture); 17 endEditCP(skyBkg); 27 Diese Prozedur wiederholt sich 5 Mal für die restlichen Seiten des Skybackgrounds. Den fertigen Skybackground muss man dann in der main-Funktion seinem Viewport zuweisen [Zeile 05]: 01 02 03 04 05 06 07 08 SkyBackgroundPtr skyBkg = makeSky(); myViewport = Viewport::create(); beginEditCP(myViewport); myViewport->setCamera(myCamera); myViewport->setBackground(skyBkg); myViewport->setRoot(scene); myViewport->setSize(0,0,1,1); endEditCP(myViewport); 4.6 Navigation Als eine Herausforderung stellte sich das Einbinden einer eigenen Navigation dar mit gleichzeitigem Verzicht auf den SimpleSceneManager von OpenSG. Der SimpleSceneManager erlaubt dem Programmierer das einfach Einbinden verschiedenster Navigationsmöglichkeiten wie den Fly-Navigator oder TrackballNavigator. Alle Variationen aber bringen ein schwerwiegendes Problem mit sich, dass im folgenden erläutert werden soll. 4.6.1 Oben/Unten im Weltraum Die Begriffe „oben“ und „unten“ sind im Weltraum ohne Bedeutung; aufgrund der Schwerelosigkeit merkt beispielsweise ein Astronaut nicht, ob er sich kopfüber bewegt oder in Schieflage befindet. Nur an der Ausrichtung bekannter Landmarken (wie zum Beispiel einer Raumstation oder einem Planeten) kann sich der Astronaut orientieren. Ein Programm, welches die Lage der Sterne zueinander darstellt ohne dabei aber im weiteren auf bekannte Objekte und Landmarken wie der Erde einzugehen, beherbergt nun eine ganze Fülle von Möglichkeiten, dem User die Orientierung zu rauben. Die dargestellten Sterne sind gerade beim allerersten Betrachten einem Laien nicht sehr vertraut. Vollführt der laienhafte User eine Bewegung um diese Sterne herum, so vermag er vielleicht diese Bewegung zu ihrem Ursprung zurück zu führen, sprich sich wieder in eine Lage in dem System zu versetzen, die er zu Beginn eingenommen hat – doch nach einigen weiteren und teils komplizierten Bewegungen um das Sternenmodell wird er seine Ursprungslage verloren haben. Kommen für den User noch Drehungen um die eigene Achse hinzu, so stünde das ganze Modell irgendwann auf dem Kopf – spätestens jetzt verliert der User seine Orientierung, liefern doch einige bisher unbekannte Sterne keine konkreten Anhaltspunkte über den eigentlichen Aufenthaltsort im Weltraum. Navigationsmodelle wie der Fly-Navigator oder Trackball-Navigator besitzen gerade diese Schwäche, dass sich der User „kopfüber“ durch das Modell bewegen kann. 28 Erste Abhilfe verschafft hier der Einsatz eines geeigneten Skybackgrounds, an dem sich der User orientieren kann. Das im folgenden beschriebene Navigationssystem umgeht dies und bietet dem User eine intuitivere Navigation welche ihm zusätzlich nicht die Orientierung raubt sondern vielmehr ein Gefühl für „oben“ und „unten“ im Weltall gibt. Das System gleicht dem polarem System der Bestimmung von Sternörtern (s. 2.4) und wird im folgenden als „Polar-Navigator“ (PN) umschrieben. 4.6.2 Einbinden einer eigenen Navigation Die Kamera navigiert im PN um einen fixen Punkt. Dabei rotiert sie um diesen Punkt in zwei Richtungen, der Horizontalen sowie Vertikalen. Für die horizontale Rotation dreht sich die Kamera um die y-Achse, für die vertikale Rotation um eine zur y-Achse orthogonal verlaufende Achse, die x-Achse. Die Rotation um die Horizontale ist ohne Einschränkungen, jedoch ist die Rotation um Vertikale auf 180° beschränkt. Dies bewirkt, dass sich die Kamera zwar um ein Objekt in horizontaler Ebene herumbewegen, dieses zusätzlich auch von oben oder unten betrachten kann, sich aber niemals bei einer Kamerabewegung über das Objekt hinüber „überschlägt“. Rotationen des PN Sämtliche Rotationen werden im Core myCamRotation der Node naviTransNode vollzogen, der Zoom findet im perspectiveCameraPtr myCamera innerhalb der Node myCamBeacon statt (s. 4.4). 29 Folgende Variablen sind in stars-main.cpp global definiert: 01 02 03 04 05 06 07 float oldX, oldY, zoom = 8, // variables for mouse movement... angleRA, angleDE; // ... and the angle of camera Quaternion qRA, qDE; // rotation Quaternions Matrix camM, // camVec (starting[0 0 8]) for zoom in camM[3] starRotation; // Matrix for the rotation of binary stars Vec3f xAxis = Vec3f(1,0,0), // x-axis yAxis = Vec3f(0,1,0); // y-axis Die Variablen oldX und oldY sind Hilfsvariablen um die Bewegung der Maus abzufangen. angleRA ist der Winkel für die Rotation in der horizontalen Ebene – hierfür wird auch das Quaternion qRA benötigt -, angleDE und qDE werden benötigt für die Rotation in vertikaler Ebene. Innerhalb der Methode void motion(int x, int y) wird nun der Polar-Navigator zum Leben erweckt: 01 02 03 04 05 06 07 08 09 10 11 12 13 14 float dX = x - oldX; float dY = y - oldY; if (motionState == ROTATE){ // RIGHT_BUTTON is pressed Matrix naviMat = myCamRotation->getMatrix(); angleDE -= 0.5 * dY; // must stand before DE-locking angleRA += 0.5 * dX; // must stand before DE-locking // _DECLINATION LOCK_ [important for orientation in 3D] if (angleDE >= 90){ // locks DE to +90 degrees angleDE=90; // camera stays straight above sun } else if (angleDE <= -90){ // locks DE to -90 degrees angleDE=-90; // now camera stays right below sun } Zuerst wird in dX und dY gespeichert, wie sehr der User die Maus in x- bzw. y-Richtung bewegt hat [Zeile 1+2]. Bei gedrückter rechter Maustaste ist motionState auf ROTATE gesetzt (dies geschieht innerhalb der Funktion void mouse(int button, int state, int x, int y)). In der Matrix naviMat wird nun der Zustand der Kamera gespeichert [Zeile 5]. Hierzu wird aus dem Core myCamRotation mittels getMatrix() die aktuelle Matrix, welche die Kameraposition enthält, geholt. Nachdem die Bewegung der Maus auf die Rotationswinkel addiert wurde [Zeile 6+7] folgt ein clamping für angleDE um das oben beschriebene „Überschlagen“ der Kamera um den Rotationspunkt zu vermeiden. 15 16 17 18 19 20 21 22 23 qRA.setValueAsAxisDeg(yAxis, angleRA ); qDE.setValueAsAxisDeg(xAxis, angleDE); qRA.mult(qDE); naviMat.setRotate(qRA); beginEditCP(myCamRotation, Transform::MatrixFieldMask); myCamRotation->setMatrix(naviMat); endEditCP(myCamRotation, Transform::MatrixFieldMask); 30 Jetzt kommen die Quaternionen zum Einsatz! In Zeile 15 wird die Drehung in der horizontalen Ebene in das Quaternion qRA eingerechnet, in Zeile 16 die Drehung in der vertikalen Ebene in das Quaternion qDE. Die beiden Quaternionen müssen jetzt noch miteinander multipliziert werden [Zeile 18], das Ergebnis dieser Multiplikation wird in qRA gespeichert, welches dann in Zeile 19 in die Matrix naviMat eingeht, die die Position der Kamera speichert. Zu guter letzt muss naviMat noch zurück in den Core myCamRotation eingefügt werden [Zeile 22]. Bis jetzt ist es also möglich, die Kamera um einen Stern zu rotieren, es fehlt noch der Zoom auf diesen. Auch dieser befindet sich in void motion(int x, int y) 24 25 26 27 28 29 30 31 32 33 34 if (motionState == ZOOM){ zoom += 0.1* dY; if (zoom<=1) zoom = 1; else if (zoom>=45) zoom = 45; beginEditCP(myCamTrans); camM.setTransform(Vec3f(0,0,zoom)); myCamTrans->setMatrix(camM); endEditCP(myCamTrans); } Bei gedrückter mittlerer Maustaste [Zeile 24] wird nun der Zoomfaktor mit der Bewegung der Maus in vertikaler Richtung aufaddiert und geclampt [Zeile 26-28]. Der minimale Zoom beträgt 1 Lichtjahr, der maximale 45 Lichtjahre. Der für den Zoom verantwortliche Core befindet sich nicht in myCamRotation sondern in myCamTrans, dem Core von myCamBeacon. 31 4.7 Picking Dem Benutzer soll es möglich sein, Sterne per Mausklick zu selektieren. Hierbei soll der selektierte Stern hervorgehoben und zusätzlich Information zum Stern eingeblendet werden. Im folgenden Abschnitt wird erklärt werden, wie das Picking, d.h. der Vorgang, mit dem der User einen Stern selektiert, im Programm umgesetzt worden ist. Das Hervorheben des Sterns wird genauer in 4.9.3 erklärt; die Einblendung von Informationen in 4.8. Die Methode void singleClick(int x,int y) prüft, ob bei einem Linksklick ein Stern angeklickt worden ist: 01 02 03 04 05 06 07 08 09 Line l; myCamera->calcViewRay(l,x,y,*myViewport); IntersectAction *act = IntersectAction::create(); act->setLine(l); act->apply(myLightNode); // did we hit something? if (act->didHit())// yes!! { . . . Zuerst wird eine Gerade erzeugt, anhand derer auf einen Schnitt mit der vorhandenen Geometrie geprüft wird [Zeile 01]. Hierzu wird die von OpenSG mitgelieferte Methode calcViewRay(...) angewendet. Jene Methode benötigt die aktuellen Koordinaten des Mauszeigers über dem Fenster (hier x und y) sowie den zugehörigen Viewport. Mittels der Mauskoordinaten und dem Viewport berechnet calcViewRay() nun diejenige Gerade, welche von der virtuellen Kamera aus den Punkt des Bildschirms bei den Mauskoordinaten durchstößt und weiter in die Szenerie hineinragt. Die Ergebnisgerade wird in der in Zeile 1 erzeugten Geraden gespeichert. Nun wird eine IntersectAction erstellt [Zeile 03]. Diese IntersectAction benötigt die eben erzeugte Schnittgerade [Zeile 04] und testet, ob jene Gerade eine im Szenegraph vorhandene Geometrie schneidet. Hierbei ist es aber unnötig, den Szenegraph von der Wurzel aus auf einen Schnitt zu testen, deswegen reicht ein Test ab dem Knoten myLightNode vollkommen [Zeile 05]. Jetzt wird nur noch geprüft, ob das getroffene Objekt – was hier nur die Geometrie eines Billboards sein kann – einen Namen hat (vgl. die Methode createStar). Da die einzig vergebenen Namen diejenigen sind, welche auch die Star-Objekte innehaben, kann man nun den Vektor stars (vgl. 4.3) nach dem star-Objekt durchsuchen, welches die Informationen des angeklickten Sterns enthält. 32 4.8 Einblenden von Informationen Bei Selektion eines Sterns werden die zu dem Stern gehörigen Informationen auf dem Bildschirm präsentiert. Da OpenSG nicht über ausreichende Mittel verfügt Text schnell und unkompliziert darzustellen, muss hier ein kleiner Umweg gegangen werden. Im folgenden wird das grobe Vorgehen beschrieben: Klickt der User auf einen Stern, so wird in dem zugehörigen Star-Objekt die textuelle Information ausgelesen. Dann wird für jeden einzelnen ausgelesenen Buchstaben eine separate Ebene erzeugt, auf die die Textur dieses Buchstabens gelegt wird. Somit ist es möglich, durch aneinanderhängen verschieden texturierter Ebenen den kompletten Text darzustellen. Alle diese Ebenen werden unter einem Teilszenegraphen zusammengefasst, welcher dann in geringer Distanz direkt vor die Kamera gestellt, bzw. unter den Kameraknoten gehangen wird. Somit bleibt der Text stets im Sichtfeld des Users auf dem Bildschirm. Zu beachten ist hier nur noch die Formatierung des Textes, d.h. ab wann im Programm z.B. ein Zeilenumbruch stattfindet. Dies soll verhindern, dass sehr große Texte in Bereiche geschrieben werden, die von der Kamera her nicht mehr einsehbar sind; umgangssprachlich formuliert könnte man sagen, dass der Text nicht aus dem Bildschirm „herauswandert“. Ein manueller Zeilenumbruch ist innerhalb der Textdateien schon mittels des Sonderzeichens „ | “ zu setzen. Die Methode Star::getInfo() bringt die textuellen Informationen derart in Form, dass sie vor den eigentlichen anzuzeigenden Text die Basisinformationen zum angeklickten Stern setzt, beispielsweise den Namen des Sterns, seine Koordinaten, Größe, Masse etc.. Star::getInfo() erzeugt noch nicht den Teilszenegraphen welcher dann letztendlich vor die Kamera gehangen werden muss. Hierfür sorgt die Klasse Info mit der Methode Info::renderInfo(...). InfoGroupNODE wird mit dem unter ihr liegendem Teilszenegraphen von der Methode zurückgegeben und später dann unter den Kameraknoten gehangen. TexTransNODE hält die Transformationen für die einzelnen Ebenen, sprich die Positionen, an denen die Buchstaben auf dem Bildschirm erscheinen sollen. TexGeoNODE enthält die eigentliche Ebene, auf der als Textur der darzustellende Buchstabe gezeichnet ist. 33 Informationseinblendung 34 4.9 Diverse Effekte In den folgenden Abschnitten werden kleinere Effekte beschrieben, die sozusagen als Feinschliff dem Programm mehr Dynamik geben. 4.9.1 FTL-Effekt Unter FTL versteht man den in englischsprachiger Science-Fiction geprägten Ausdruck von Reisen mit Überlichtgeschwindigkeit (Faster Than Light). Beim Zoom auf einen Stern wird die Lichtgeschwindigkeit vom Betrachter drastisch überschritten, da in der Regel kein User sich die Zeit nimmt, für 1E im Modell ein ganzes Jahr lang – ohne Unterbrechung – zu zoomen. Um die Geschwindigkeit zu verdeutlichen verzerrt sich bei einer vorwärts-rückwärts Bewegung der Kamera das Bild. Realisiert wird dies durch das „Field of View“ (FOV) der Kamera, welches den überschaubaren Bereich derselben angibt. Standardmäßig ist das Field of View auf 60° gesetzt, d.h. der Betrachter überblickt in einem Winkel von 60° die Szene. Bewegt sich nun der Betrachter mit Überlichtgeschwindigkeit nach vorne, so „überholt“ er quasi Lichtstrahlen, die von hinter ihm liegenden Gegenständen an ihm vorbeistrahlen. Dieses Licht sieht er, womit sich sein Blickwinkel dermaßen erhöht, dass er diese hinter ihm liegenden Gegenstände sehen kann. Das FOV wird auf einen Wert >60° gesetzt und der Blickwinkel offenbart dem Betrachter nun Gegenstände, die sich seitlich hinter ihm befinden. Umgesetzt wird dies void motion(int x, int y): 01 02 03 04 05 06 07 08 09 10 im Code innerhalb der Funktion // The following stuff is for the FTL-effect if (dY<0){// if moving forward fov+=2;// our field of view gets wider if (fov>=145) fov =145;// until it is really wide if (fov<=45) fov =45; beginEditCP(myCamera); myCamera->setFov(deg2rad(fov));// setting field of view endEditCP(myCamera); } Bewegt sich der Betrachter mit Überlichtgeschwindigkeit nach hinten, so dürfte er eigentlich nichts mehr sehen, da sämtliche auf seine Augen gerichteten Lichtstrahlen „nur“ mit Lichtgeschwindigkeit diesen hinterherjagen, sie aber nicht berühren. Da ein schwarzer Bildschirm allerdings die wenigsten User begeistert, wurde hierauf verzichtet und statt dessen das FOV bei einer Rückwärtsbewegung auf einen Wert kleiner 60° gesetzt: 35 11 12 13 14 15 16 17 18 19 if (dY>0){// if moving backward fov-=0.1;// our field of view gets smaller if (fov<= 45) fov = 45; // until it is really small... if (fov>=145) fov = 145; beginEditCP(myCamera); myCamera->setFov(deg2rad(fov));// setting the field of view endEditCP(myCamera); } Beendet der User den Zoom (loslassen der mittleren Maustaste), so fällt er wieder unter die Lichtgeschwindigkeit. Das FOV wird zurück auf 60° gesetzt. Um einen allzu deutlichen Sprung bei einem Abbremsen von der Lichtgeschwindigkeit zu vermeiden, wird das FOV schrittweise auf 60° zurückgesetzt – dem Betrachter wird der Eindruck vermittelt, dass er sacht abbremse. Umgesetzt wird dies im Code innerhalb der Funktion void display(void): 01 02 03 04 05 06 07 08 09 if (waiting == 1){// FTL-Effect if (fov>=63)fov-=3; else if (fov<57) fov+=3; else fov=60; beginEditCP(myCamera); myCamera->setFov(deg2rad(fov)); endEditCP(myCamera); } Jener Code muss in der display-Funktion stehen, da diese laufend vom Programm aufgerufen wird durch glutIdleFunc in der main-Funktion. Da das Hauptaugenmerk der Studienarbeit nicht auf der korrekten Darstellung visueller Informationsübermittlung bei überlichtschnellen Geschwindigkeiten liegt, erhebt der Ansatz mittels Veränderung des Blickwinkels die Überlichtgeschwindigkeit zu simulieren keinen Anspruch auf physikalische Korrektheit. Einzig und allein die Tatsache dem Benutzer zu verdeutlichen, dass dieser sich mit extrem hohen Geschwindigkeiten bewegt liefert den Grund für die Implementierung dieses Features. Bei der Rotation der Kamera in der Szene wurde auf den FTL-Verzerrungseffekt bewusst verzichtet. Ausnahmslos alle Testpersonen beurteilten den FTL-Verzerrungseffekt in einer frühen Version des Programms als äußerst irritierend, da dieser bei der Rotation angewandt den zusätzlichen Anschein eines Zooms auf oder von den Sternen weg suggerierte. Dies liegt daran, dass durch das langsame, schrittweise Zurücksetzen des FOV – Winkels die äußeren Ränder des sichtbaren Bereichs hinter den Betrachter rücken (und damit nicht weiter sichtbar sind), die mittleren Bereiche dehnen sich scheinbar zurück zu ihren ursprünglichen Maßen. Dem Betrachter wird der Eindruck vermittelt, er bewege sich während der Rotation von den Sternen weg, nach Beenden der Rotation wieder zu den Sternen hin. 36 FTL-Effekt bei Zoom vorwärts Obige Abbildung zeigt den FTL-Effekt bei einem vorwärts gerichteten Zoom. Das grüne Rechteck zeigt den sichtbaren Bildausschnitt bei einem FOV von 60°, der maximale Bildausschnitt ist sichtbar bei einem FOV von 145°. 4.9.2 Animierte Billboards Das Einbinden von Animationen ist nötig um FLARE-Sterne grafisch hervorzuheben. Ein FLARE-Stern ist ein Stern, welcher in unregelmäßigen Abständen seine Leuchtkraft um ein Vielfaches erhöht. Der damit verbundene Strahlungsausbruch lässt den Stern für extrem kurze Zeit sehr hell strahlen. Im Programm werden FLARE-Sterne durch eine Animationsabfolge dargestellt. Das eigentliche Bild des Sterns wird in zufälligen Intervallen durch ein wesentlich helleres Bild des Sterns ersetzt. Da die verschiedenen Bilder der Animationsfolge zur Laufzeit ständig im Speicher vorliegen müssen, ist es notwendig, die Image-Pointer, welche die Bilddaten speichern, global in stars-main.cpp zu definieren. 01 02 ImagePtr Iredflare0, Iredflare1, Iredflare2, Iredflare3, Iredflare4, Ihud0, Ihud1, Ihud2, Ihud3, Ihud4, Ihud5, Ihud6, Ihud7; Innerhalb der display-Funktion – welche laufend vom Programm aufgerufen wird durch glutIdleFunc in der main-Funktion – wird nun zu bestimmten Zeitpunkten der zu einem FLARE-Stern gehörige Image-Pointer durch einen anderen Image-Pointer ersetzt. 37 01 02 03 04 05 06 07 08 … 28 29 30 31 32 33 34 35 36 37 if (flaretime>250){ // animation of red flare stars if (flaretime == 255) { beginEditCP(redflare); addRefCP(Iredflare0); redflare->setImage(Iredflare1); redflare->imageChanged(); endEditCP(redflare); } if (flaretime == 260) {…} if (flaretime == 275) { beginEditCP(redflare); addRefCP(Iredflare4); redflare->setImage(Iredflare0); redflare->imageChanged(); endEditCP(redflare); flaretime = rand()%250; } } flaretime++; Die Variable flaretime zählt stets bis 250 hoch, dann folgen in 5er-Abständen die verschiedenen Animationen. Bei Ende der Animation [Zeile 34] wird flaretime auf einen zufälligen Wert <250 gesetzt, dies bewirkt, dass sich die Animation für den FLARE-Stern nicht in konstanten Intervallen wiederholt. Hinweis: Bei Austausch des Image-Pointers setzt OpenSG den internen Reference-Counter für den alten Image-Pointer herunter und löscht somit diesen aus dem Speicher. Um dies zu verhindern muss der Reference-Counter manuell hochgesetzt werden [Zeile 04+30]! 38 4.9.3 Selektierte Sterne Bei Selektion eines Sterns wird dieser hervorgehoben durch eine Animation. Ein rotierender Kreis über dem Stern zeigt dem User, welchen Stern er soeben angeklickt hat. Diese Animation ist wie in 4.9.2 beschrieben umgesetzt worden und als Ergänzung innerhalb der Methode createStar() eingebunden (vgl. 4.4.1). Jeder Stern ist an eine Switch-Node gekoppelt. Unter dieser Switch-Node hängt zum einen der Stern selbst, zum anderen ein texturierte Ebene, welche die Animation des rotierenden Kreises abspielt. Erst bei Selektion eines Sterns wird jene Animation über den Switch freigeschaltet. Wählt der User einen anderen Stern oder klickt er auf den Hintergrund, so wird über den Switch die Animation wieder ausgeblendet. 4.9.4 Bildschirmschoner Der Bildschirmschoner lässt nach einer vorgegebenen Zeitspanne, in der keine Aktionen vom User vollzogen wurden, die Kamera um die y-Achse rotieren. Mit jedem Aufruf der display-Funktion wird die globale Variable screensaver hochgezählt. Sollte der Wert dieser Variablen größer sein als 3000, so wird bei jedem folgendem Aufruf der display-Funktion der Kamerawinkel innerhalb von myCamRotation um 1° hochgezählt (vgl. 4.6.2). Bei einer Aktion des Users – einem Mausklick – wird der Wert der Variablen screensaver auf 0 gesetzt. 39 5. Einbinden als Stereoapplikation Um das Programm stereofähig zu gestalten ist wie in [3, Kapitel Clustering] beschrieben vorgegangen worden. Um einen guten 3D-Eindruck zu gewährleisten ist es wichtig, den Abstand der Sterne zueinander nicht zu hoch zu setzen. Es empfiehlt sich ein Abstand von einer Einheit für die reale Distanz von einem Lichtjahr zu nehmen. Die eingeblendeten Informationen werden wie in 4.8 beschrieben durch einen Szenegraphen realisiert, welcher in sehr geringem Abstand (~0.1 Einheiten) vor die Kamera gestellt, bzw. unter den Kameraknoten gehangen wird. Dies bringt jedoch bei Einsatz des Programms auf einer Stereoleinwand einen Nachteil mit sich: Die eingeblendeten Informationen befinden sich in so naher Parallaxe zum User, dass die von den Renderservern erzeugten Bilder sehr stark in ihrer Position variieren. Um diesen unangenehmen und auch nicht optisch ansprechenden Effekt zu verhindern muss besagter Szenegraph in größerem Abstand (~10 Einheiten) vor die Kamera gehangen werden. 40 6. Ausblick Das Programm bietet einige Möglichkeiten einer zukünftigen Erweiterung. So wäre der nächste Schritt die Einbindung einer Datenbank und somit Verzicht auf die Textdateien zur Speicherung der Daten. Interessant wäre das Einblenden von Linien zwischen den Sternen, zum einen um Entfernungen benachbarter System zu visualisieren, zum anderen um ggf. Sternbilder ersichtlich zu machen. Auch könnte hiermit ein 3D-Voronoi-Diagramm realisiert werden, das zeigt, welche Bereiche des Weltraums von der Schwerkraft der Sterne beeinflusst werden. GENAUER, wegen versch. Schwerkräften... In den Sterndaten sind zwei wichtige Punkte bisher nicht beachtet worden, die Leuchtkraft eines Sterns sowie dessen Eigengeschwindigkeit. Viele sehr erdnahe Sterne leuchten so schwach, dass man sie eigentlich nicht sehen bzw. erst bei geringer Distanz sehen kann. Des weiteren könnte man die Zeit mit in das Programm einfließen lassen, ein Zeitraffer von einigen Tausend Jahren würde bei eingerechneter Eigenbewegung der Sterne eine interessante Simulation der solaren Nachbarschaft ergeben. Hierbei müssten aber auch die verschiedenen Massen der Sterne sowie deren gegenseitige Anziehungskräfte mit einbezogen werden. 41 7. Fazit In vorliegender Studienarbeit wurde ein Programm erstellt, welches die stellare Nachbarschaft unseres Sonnensystems darstellt. Mittels eines neuen Navigationsmodells ist es dem User vereinfacht, die Orientierung in einem solchen System zu behalten. Sämtliche Sterne sind als Billboards dargestellt und erlauben es dem User durch einfaches Austauschen der dazugehörigen Bilddateien komplett neue Skins für das Programm zu entwerfen. Auch sind die sternrelevanten Daten in einer für den User leicht zu modifizierenden Form abgelegt. Die Applikation ist darüber hinaus stereofähig, eine publikumswirksame Präsentation über 2 Beamer an einer Stereoleinwand ist somit möglich. Die 6-monatige Arbeit an vorliegendem Programm hat sich als extrem erfahrungsreich erwiesen. Das Thema war nicht vorgegeben, sondern ist von mir selbst aufgestellt worden. Somit war maximale Motivation meinerseits gegeben, da Vorkenntnisse in der Astronomie vorhanden waren und schnell umgesetzt werden konnten. Dennoch blieb auch viel Raum für neue Erkenntnisse und Erweiterung meines eigenen zugegebenermaßen teilweise laienhaften Wissens über Astronomie. Als OpenSG-Anfänger gab es natürlich öfters Punkte, an denen die Arbeit ins Stocken geraten ist, so zum Beispiel bei dem Einbinden einer eigenen Navigation oder beim Verwenden von Billboards – zu denen es bisher keine ausreichende Dokumentation gab. Wie so oft übersieht man am Ende dann die eigentliche Arbeit, die in einem solchen Projekt steckt und man stellt sich eigens die kritische Frage, ob man nicht hätte mehr erreichen können. Hierbei sollte man sich aber immer vor Augen halten, dass innerhalb der 6 Monate ein großer Teil vorerst unbekannten Basiswissens erarbeitet und „in Fleisch und Blut“ übergehen musste bevor etwas greifbares auf dem Bildschirm erscheinen konnte. 42 8. Literaturverzeichnis [1] Ulrich Kaiser. C/C++ Von den Grundlagen zur professionellen Programmierung. Galileo Press. 2000. [2] Paul A. Tipler. Physik. Spektrum, Akademischer Verlag. 1998. [3] OPENSG. Open Source Scenegraph (http://www.opensg.org/doc-1.4.0/index.html) [4] WIKIPEDIA. (http://www.wikipedia.de) 43