Das Gebiet der 3D-Echtzeitprogrammierung hat sich in den letzten Jahren rasant entwickelt. Durch die ständige Verbesserung der Grafikhardware wird die Echtzeitgrafik zunehmend realistischer und schneller. Außerdem wurden Grafikschnittstellen wie DirectX und OpenGL geschaffen, die eine einheitliche Programmierumgebung bieten und durch die direkte Kommunikation mit dem Grafiktreiber große Geschwindigkeitsvorteile bringen. Sie stellen Funktionen zur Verfügung, mit denen man die Fähigkeiten der Hardware direkt verwenden kann und so schnell zu ansehnlichen Ergebnissen kommt. Prinzipiell bilden sie die komplette klassische 3D-Pipeline ab und verwenden die Grafikhardware, wo es möglich ist. Mit ihrer Hilfe ist es auch nicht mehr notwendig, die eigene Software an jede mögliche Grafikhardware anzupassen, wie es noch zu DOS-Zeiten notwendig war. Sie abstrahieren von der konkreten Hardware und sind so weitgehend hardwareunabhängig.Mit der Einführung des .NET-Frameworks wurde das Programmieren mit DirectX weiterhin vereinfacht. Man benötigt keine komplizierten COM-Objekte mehr, die man meist in Verbindung mit C oder C++ verwendet hat, sondern kann mit Objekten im .NET-Framework arbeiten. Durch das .NET-Framework wurde ein weiterer Schritt in Richtung Plattformunabhängigkeit getan. Alle Sprachen, die es unterstützt, werden in eine Zwischensprache übersetzt und dann von einer Laufzeitumgebung interpretiert, wie dies auch bei Java der Fall ist. Die DirectX-Bibliotheken werden in das .NET-Framework integriert und sind auch in diese Zwischensprache übersetzt. Übersichtlicherer DirectX-Quellcode, der sich besser in die Programmiersprache einfügt, ist das Resultat. Diese Variante von DirectX heißt Managed-DirectX. Überall, wo die Laufzeitumgebungen des .NET-Frameworks von DirectX installiert sind, sollte so das Programm laufen.

 

Zielsetzung

Ziel dieser Arbeit ist es, den Studenten der Universität Trier in einer Vorlesung über 3D-Programmierung den Einstieg in die Materie zu vereinfachen, indem eine flexible, leicht erweiterbare Umgebung geschaffen wird. Managed-DirectX wurde als API gewählt, weil dadurch der Zugang zur 3D-Programmierung strukturierter und schneller möglich ist. Außerdem gibt es durch den CodeDOM die Möglichkeit, Quellcode zur Laufzeit zu kompilieren, was die Anwendungsmöglichkeiten des Programms WaveGen enorm erweitert und wodurch das Aufrufen von beliebigen Funktionen zur Wellengenerierung erst effizient möglich wird.

Der Lerneffekt soll einerseits durch das Programm selbst zustande kommen, indem man auf einfache Weise 3D-Objekte hinzufügen, Shader-Effekte integrieren und Befehle zur Manipulation der Umgebung einbinden kann. Außerdem gibt es die Möglichkeit, beliebige mathematische Funktionen auf die Wasseroberfläche anzuwenden, die dann als Wellenbewegungen deutlich gemacht werden. Begleitend dazu werden in dieser Arbeit zunächst in Kapitel 2 die Grundlagen der 3D-Programmierung erklärt. Kapitel 3 befasst sich mit der aktuellen Grafikhardware und erklärt ihre Funktionsweise am Beispiel der GeForce 6 Serie. In Kapitel 4 wird dann anhand des Programm-Quellcodes eine Einführung in die 3D-Programmierung gegeben, wobei sowohl die grundlegenden als auch fortgeschrittenere Techniken beschrieben werden. In Kapitel 5 werden durch ein Anwendungsbeispiel die Funktionsweise und Erweiterungsmöglichkeiten des Programms erläutert. Alle Listings, die verwendet werden, sind im Quellcode gekennzeichnet. Dies stellt vor allem einen Bezug des Quellcodes zu Kapitel 4 her, welches man so als Tutorial verwenden kann, um den Einstieg in die 3D-Programmierung zu finden.

Da die Flexibilität und Erweiterungsmöglichkeit des Programms im Vordergrund stehen, habe ich versucht, den Quellcode möglichst strukturiert und offen zu gestalten und gut zu kommentieren. Jedes 3D-Objekt, das in der Szene erscheinen kann, wird von einer Oberklasse Object3D abgeleitet, in der die wichtigsten Methoden schon implementiert sind bzw. überschrieben werden müssen. Diese Objekte bilden den Kern des Programms und enthalten die wesentlichen DirectX-Operationen wie Matrix-Transformationen, Zeichenbefehle und Zustandsänderungen. Einige Methoden, die für die Grundversion des Programms nicht erforderlich waren, wie die Rotation eines Objektes, wurden noch nicht implementiert, um den Studenten die Möglichkeit zu bieten, selbst aktiv zu werden. Shader-Programme kann man außerhalb des Projektes in der HLSL-Sprache von Microsoft oder der cg-Sprache von NVIDIA schreiben und per XML-Interface in das Programm integrieren. So kann man diese Hochsprachen auf einfache Weise erlernen und das Ergebnis sofort kompilieren und Anzeigen lassen. Weil es trotzdem noch viele Verbesserungsmöglichkeiten gibt, bin über jedes Feedback froh, das mir mitgeteilt wird. Am Schluss der Arbeit werde ich noch auf eigene Vorschläge eingehen und Möglichkeiten der Weiterentwicklung aufzeigen.

Darstellung von Wasser

Als nächstes möchte ich noch kurz auf die Techniken eingehen, die bei WaveGen verwendet werden und ihren Gebrauch bei der Darstellung von Wasser erläutern. Das Erste, was einem beim Anblick einer Wasseroberfläche auffällt ist die Reflexion der Umwelt. Hierbei muss man zwischen globalen und lokalen Reflexionen unterscheiden. Globale Reflexionen sind Reflexionen des Horizontes. Da dieser unendlich weit entfernt ist, fällt es nicht ins Gewicht, wenn der Betrachter sich bewegt. So scheinen globale Reflexionen sich mit dem Betrachter zu bewegen. Dies fällt bei WaveGen auf, wenn man sich mit „w“ oder „a“ nach links oder rechts bewegt: Die Wolken auf der Wasseroberfläche ziehen mit in die jeweilige Richtung. Eine solche Situation kann man mit Cube-Maps realisieren.

Lokale Reflexionen, wie die einer nahen Landschaft, dürfen dieses Verhalten natürlich nicht zeigen. Hier behilft man sich, indem man die Landschaft spiegelverkehrt in eine Textur rendert und dann auf die Wasseroberfläche projiziert. Diese Techniken nennen sich Texture-Rendering und projektives Texturing.

Selbst bei einer relativ ruhigen Wasseroberfläche fällt auf, dass sehr kleine Wellen die perfekte Ruhe stören. Um dies mit der Geometrie zu erreichen, bräuchte man eine sehr kleine Auflösung, also viele Dreiecke. Dies ist aus Performance-Gründen nicht erwünscht. Durch das Bump- oder Normal-Mapping kann man auf eigentlich glatten Oberflächen eine scheinbare Struktur auf Pixel-Ebene schaffen. Wenn man diese noch animiert, hat man den Effekt von kleinen Wellen und kann damit man auf eine hohe Auflösung der Geometrie verzichten.

Die Transparenz von Wasser ist schnell realisiert, indem man Alpha-Blending verwendet. Allerdings brechen sich die Lichtstrahlen im Wasser und erzeugen eine leichte Verzerrung des Untergrunds. Dies erreicht man durch Refraction-Maps, welche die Geometrie, die unter Wasser liegt in eine Textur rendern und durch Erzeugung von Texturkoordinaten nach dem Brechungsgesetz eine realistische Refraktion erzeugen.

Reflexionen und Refraktionen kombiniert man durch den so genannten Fresnel-Term. Dieser beschreibt das Verhältnis von Reflexion zu Refraktion für ein bestimmtes Material (in diesem Fall Wasser): Je größer der Winkel zwischen Betrachter und Wasseroberfläche ist, desto höher ist der Anteil der Refraktion, also des durchscheinenden Wassers. Wenn man also in einem kleinen Winkel auf das Wasser schaut, sieht man mehr Reflexionen. Refraktionen sind bei WaveGen bisher noch nicht implementiert, der Fresnel-Effekt wird beim Effekt Effekt1.xml angewandt.

Eine weitere optische Eigenschaft von Wasser sind Kaustiken. Sie entstehen bei klarem Wasser durch die Spiegelung der Sonne auf dem Grund. Man kann sie z.B. im Schwimmbad beobachten. Kaustiken werden als Beispiel einer Texturanimation in Kapitel 5.13 implementiert.

Große, weiträumige Wellen wie beispielsweise die eines Ozeans werden durch die Geometrie der Wasseroberfläche dargestellt und ergeben sich, wie schon beschrieben, durch die Eingabe einer Wellenfunktion auf der Konsole.

Das Benutzerinterface

Das Benutzerinterface verzichtet auf grafische Eingabemöglichkeiten. Es basiert allein auf der Texteingabe an der Konsole. Auf diese Weise ist es einfach, neue Befehle einzubinden, und das Fenster mit der Grafikausgabe bleibt übersichtlich. Es existieren drei Arten von Fenstern: Das Statusfenster enthält nur Textmeldungen. Dies kann die Hilfe sein oder eine Fehlermeldung. Das Statusfenster wird mit F1 ein- und ausgeblendet. Im Programmierfenster (F2) gibt man beliebigen Programmcode ein, der dann durch den Befehl „compile“ auf die Wasseroberfläche angewendet wird. Das wichtigste Fenster ist die Konsole. Hier gibt man alle existierenden Befehle ein. Dieses Fenster wird mit der Tabulator-Taste ein- und ausgeschaltet. Durch die Möglichkeit der Ausblendung der einzelnen Fenster wirkt die Grafikausgabe immer aufgeräumt und ist nicht durch Buttons oder Menüs überladen.

Lizenz

Managed DirectX Copyright © Andreas Zimmer. Alle Rechte vorbehalten.

Dieses Buch teilen