Mal ein kleines Update der Demo …
… das meiste hat sich unter der Haube getan, aber zu den deutlich erkennbaren Veränderungen gehört:
- Sonne und Mond enthalten
- Sonne, Mond und Sterne bewegen sich auf realistischen Bahnen
- Beschleunigen und bremsen der Zeit (z.B. für Motion Blur, Mondphasen im Zeitraffer und Rotation der Sterne um die Himmelspole)
- Größerer Leistungshunger auf Direct3D-10.1- und -11-Chips
- neu: Glare
Schnellstart:
- Raum verdunkeln ;)
- Direct3D-10- oder 11-taugliche Grafikkarte benötigt, außerdem die DirectX-Runtime Juni 2010 (falls kein aktuelles SDK installiert, Download hier)
- Die Demo zeigt den Himmel zur aktuellen Systemzeit von Deutschland (genauer: NRW) aus
- Mausrad hoch und runter macht heller und dunkler
- Bild auf / Bild ab zoomt hinein oder weg
- Plus- und Minustaste auf dem Numpad beschleunigen und verlangsamen die Zeit
- Drücken und Halten der rechten Maustaste schaltet von menschlichem auf digitales Tonemapping um (farbenfroh, ohne Glare & ohne Griesel)
neu: D3D-11-only-Version mit Glare:
Alte D3D-11-only-Version ohne Glare:
Alte D3D-10-Version:
Ausführlich:
Im letzten halben Jahr hat sich viel an der Engine getan und ich wollte die kleinen Fortschritte am Sternenhimmel nutzen, um sie mal wieder „in der Wildnis“ zu testen. Die Sonne ist nur als einfarbige Scheibe implementiert; der Mond deutlich liebevoller, aber z.B. fehlt noch das von eXile vorgeschlagene Beleuchtungsmodell. Die Demo benutzt die aktuelle Systemzeit für Chronologie und Himmelsmechanik – leider nicht präzise genug, um z.B. Sonnenfinsternisse wiederzugeben, aber für den Spielealltag mehr als ausreichend. Da kein künstlicher Horizont drin ist, ist ernsthafte Navigation anstrengend, aber möglich – der Betrachter schaut beim Start genau nach Norden; die Y-Achse findet man durch hin- und herbewegen der Maus und der Nordstern ist relativ einfach zu finden, indem man die Zeit beschleunigt und schaut, worum die Sterne rotieren (falls man nicht fachkundig ist und ihn sowieso sofort erkennt).
Steuerung:
- Alt+Eingabe zum Umschalten zwischen Vollbild- und Fenstermodus; Alt+F4 zum Beenden.
- Maus zum Umsehen (manchmal ungünstig, wenn sie das Fenster verlässt. Ist mir als Fenstermodus-Fan aber lieber, als sie einzusperren).
- Mausrad hoch/runter ändert die Belichtungszeit, macht also das Bild heller oder dunkler.
- Drücken und Halten der rechten Maustaste deaktiviert menschliches Tonemapping und schaltet auf digitales um – die menschlichen Limitierungen entfallen (bei geringer Helligkeit Farben- statt Schwarz-Weiß-Sehen, kein Rauschen mehr) und man sieht den Himmel wie auf Fotos.
- Bild-auf- und Bild-ab-Taste steuern den Zoom.
- Plus- und Minustaste auf dem Numpad lassen die Zeit schneller und langsamer vergehen. Sehr nützlich, um den Motion Blur zu betrachten, den Nordstern zu finden, sich den Mond während verschiedener Phasen anzuschauen oder zu beobachten, wie die Sonne im Sommer aufsteigt. Ich habe die Maximalgeschwindigkeit auf drei Tage pro Sekunde – also ein halbes Jahr pro Minute – begrenzt, damit das Ganze auch bei niedrigen Frame-Raten kontrollierbar bleibt.
Schnellhilfe:
Alles ist weiß! (in der Version
mit Glare)
Du schaust in die Sonne. Mausrad hoch / runter ändert die Helligkeitsempfindlichkeit.
Alles ist schwarz! (in den Versionen
ohne Glare)
Mausrad hoch / runter ändert die Helligkeitsempfindlichkeit.
Ich sehe nur blauen Griesel!
Das ist die untere Helligkeitsgrenze der menschlichen Wahrnehmung; rechte Maustaste drücken und halten, um auf „Digitalkamera“-Tonemapping umzuschalten.
Sonne und Mond fehlen! (wahrscheinlich nur in den Versionen
ohne Glare)
Höchstwahrscheinlich nicht; sie sind nur
kleiner, als wir denken. Ranzoomen und genau hinschauen. Helligkeit runterdrehen, damit die meisten Sterne verschwinden und nur Sonne und Mond übrig bleiben. Zeit beschleunigen und ein paar Tage überspringen, um sicher zu gehen, dass nicht Neumond oder Sonnenfinsternis ist.
Probleme:
Ich habe die Demo auf Windows 7 mit einer GeForce 8600M GT und einer Radeon HD 5770 getestet, aber es kann natürlich immer mal vorkommen, dass was nicht läuft. In dem Fall schickt mir bitte ein Log und Eckdaten zum System – ist ja auch in meinem Interesse, dass die Engine überall wie erwartet funktioniert. Bekannt:
- Gedrückte rechte Maustaste verträgt sich nicht mit dem Verlassen des Fensters. In dem Fall rechte Maustaste außerhalb des Fensters drücken und gedrückt haltend zurückkehren ;)
Bei hohen Auflösungen kann die Performance massiv einbrechen (von eigentlich 30 auf 4 fps), nachdem man das Fenster vergrößert hat. Die Ursache habe ich noch nicht gefunden, aber Maximieren und Wiederherstellen hilft meistens. Letzter Ausweg ist der Vollbildmodus, der funktioniert immer reibungslos. Ist in der Glare-Version behoben.
Die Performance ist schwach. Auf D3D-10.1- und 11-Karten schalte ich 2- bzw. 4-fach MSAA zu, um Mond und Sonne zu glätten. Bei Full-HD-Auflösungen bringt das aktuelle Karten schnell an die Grenze … es ist wirklich lächerlich, die Performance wegen der zwei Pünktchen auf ein Viertel (2×MSAA) / Achtel (4×MSAA) einbrechen zu lassen – aber ich sehe es positiv; für alles, was in nächster Zeit an Effekten und Geometrie nachkommt muss ich diesen Schritt früher oder später eh wagen und ist die Performance erst ruiniert, lebt es sich ganz ungeniert. Und so lange da nur Sonne, Mond und Sterne sind, sollte es nicht unspielbar langsam werden. Die D3D-11-Versionen sind mittlerweile ziemlich gut optimiert.
- Beim Schalten in den Vollbildmodus verschiebt sich die Maus. Leider macht Direct3D das automatisch.
Technik:
Hachja, das ist wieder mal alles so wunderbar overengineered ;)
Bewegung in die Sterne zu bringen ist eigentlich ganz einfach. Man braucht nur das derzeitige
Julianische Datum in koordinierter Weltzeit (ich empfehle
GetSystemTimeAsFileTime()) und rechnet es dann auf die
Epoche J2000.0 um (d.h., subtrahiert 2451545; lässt sich mit
GetSystemTimeAsFileTime() kombinieren). Daraus berechnet man die derzeitige
Sternzeit in Greenwich (GMST). Die Transformation der Sterne ist dann eine Rotation um die Y-Achse um
GMST ÷ 24 × 2 × π + Länge des Beobachters gefolgt von einer Rotation um die X-Achse um
90 - Breite des Beobachters.
Mit der Sonne wird es schon schwieriger – aber auch hier liefert Wikipedia
alles, was man braucht, inklusive
Beispiel.
Kniffliger wird es mit dem Mond. Die Formel für die Mondbahn habe ich dem exzellenten Paper
A Physically-Based Night Sky Model entnommen, und sie funktioniert zufriedenstellend.
Komplett selber gemacht ist das Rendern von Sonne und Mond. Textur und Normal-Map des Mondes habe ich, wie schon weiter vorne im Thread geschrieben, aus verschiedensten Quellen selber zusammenkombiniert. Gerendert werden die Gestirne als Sprites – dafür gibt es mehrere Gründe:
- 50 MiB Texturdaten (Albedo und Normalen beim Mond, allerdings nicht in dieser Demo, um Bandbreite zu sparen) sind auch heute noch ziemlich viel; erst recht für einen letztendlich meist 15×15 Pixel kleinen Punkt.
- Auf weniger Details kann man aber leider nicht ausweichen, weil sonst die Krater verloren gehen und die Qualität des Renderings zu leiden beginnt (erst recht, falls sich tatsächlich mal zufällig jemand den Mond angucken will).
- Die Gestirne verändern sich sehr, sehr langsam – von Vollmond bis Neumond vergehen 14 Tage. Warum also 50 Mal pro Sekunde neu zeichnen?
Lange Rede, kurzer Sinn – die Gestirne stecken bei mir in einer 256² Pixel großen Sprite. Sie wird frühstens dann aktualisiert, wenn eine Stunde in-game-Zeit vergangen ist (obwohl man den Zeitraum sicher noch ausdehnen könnte). Obwohl eine riesige Menge Details im Mond steckt, von hochauflösenden Texturen bis – bald – einem detaillierten Beleuchtungsmodell, besteht die per-Frame-Arbeit also nur im Runterrendern einer stinknormalen Sprite. Hier ist mal das Exemplar von heute mittag:
- Moon Sprite.png (337.82 KiB) 45222 mal betrachtet
Man sieht, dass gerade Vollmond ist :) Man beachte den Kranz: Der ist notwendig, um durch Texturfilterung bedingte Ränder zu vermeiden, wenn die Textur auf eine runde Sprite gerendert wird. Für eine runde Sprite habe ich mich aus dem schlichten Grund entschieden, dass mit einer eckigen Sprite und einem Alpha-Kanal kein Anti-Aliasing möglich ist – es handelt sich schließlich um ein HDR-Bild, was bedeutet, dass ein geantialiaster Rand in einem anderen Helligkeitsraum vorliegen würde als das finale Bild und nach dem Tonemapping wieder eckig aussehen würde (was sich auch nicht vermeiden lässt, da man beim Rendern der Sprite nicht weiß, mit welcher Helligkeitskurve das Bild letztendlich ausgegeben werden wird). Erzwingt man hingegen Antialiasing durch Multisampling, das nach dem Tonemapping aufgelöst wird, erhält man ein klares Bild wie dieses hier:
- Moon.png (74.97 KiB) 45222 mal betrachtet
Zu guter Letzt noch ein Tipp, wie man so eine Sprite
am effizientesten rendert (auch, wenn es unter Einsatzbedingungen wahrscheinlich keinen Unterschied macht ;) ). Achja, da ist auch meine größte Sauerei: Um mir Input-Layout und Vertex-Buffer zu sparen, habe ich den Winkel jedes Vertex als Gleitkommazahl in einen 32-Bit-Index-Buffer gesteckt, im Vertex-Shader
SV_VertexID entgegengenommen und die ID wieder zu
float uminterpretiert, um per Sinus und Kosinus die Position zu berechnen. Boah, war ich stolz und mein Schlüpfer nass, als das funktionierte.
Das alles erscheint wie ein bisschen viel Arbeit für einen Himmelskörper, der auf dem Bildschirm gerade mal ein paar Pixel einnimmt – aber imo ist
gerade das ein Grund, so viel zu investieren: Wenn man es nicht tut, hat man nämlich einfach ein paar helle Pixel in der Luft hängen, die keiner als irgendwas erkennt.