DirectX Graphics - Der erste Kontakt - Softwaretechnologie II - Master of Ceremony: Christian Daum
←
→
Transkription von Seiteninhalten
Wenn Ihr Browser die Seite nicht korrekt rendert, bitte, lesen Sie den Inhalt der Seite unten
DirectX Graphics – Der erste Kontakt – Softwaretechnologie II Master of Ceremony: Christian Daum Sidekick: Patrick Gunia
Was ist DirectX? Eine Sammlung von Application Programming Interfaces (APIs) für Multimediaprogramme zur schnellen Ansteuerung von Hardware. o insbesondere Grafikkarte, Soundkarte, Netzwerk, Speicher. Anwendungen für DirectX werden mit Hilfe des DirectX SDK erstellt. unterstützte Plattformen: o Windows-Plattformen seit Windows 95. o Spielekonsole Xbox. DirectX ermöglicht direkte Zugriffe auf die Hardware des Systems, ohne die Programme von der Hardware abhängig zu machen. o Spiele-Entwicklern wird eine Hardware-Abstraktions-Schicht (HAL) für die Spieleprogrammierung zur Verfügung gestellt, > Mithilfe der HAL können langsame Schnittstellen umgangen werden. o Funktionen, die von der Hardware und damit nicht von der HAL bereitgestellt werden können, werden in der Hardware-Emulations-Schicht (HEL) emuliert. Die einzelnen DirectX-Komponenten sind COM-Objekte; Vorteile: o Abwärtskompatibilität von DirectX (über alle Versionen hinweg). o Sprachenunabhängigkeit von DirectX (Freiheit für Entwickler).
DirectX-Komponenten 1. DirectX Graphics (Unterstützung von 2D- & 3D-Grafik). ehemals Direct3D & DirectDraw. ermöglicht schnellen (weil direkten) Zugriff auf die Grafikkarte. also vorbei an den langsamen Schnittstellen GDI & DDI (Graphics & Display Device Interface). 2. DirectX Audio (Unterstützung von Soundeffekte & 3D-Raumklang). ehemals DirectSound & DirectMusic. Wiedergabe und Aufnahme von Soundeffekten & Hintergrundmusik (MIDI, allerdings kein mp3). unterstützt Raumklang (d.h. Positionierung der Klänge im 3D-Raum). 3. DirectInput (Unterstützung von Eingabegeräten zur Spielsteuerung). wie Tastatur, Maus, Joysticks usw. Der Zugriff über DirectInput umgeht das Windows Message System (d.h. Ereignis-, Melde- & Warteschlagen) & erfolgt direkt auf die Hardware. Ermöglicht Force-Feedback-Effekte (z.B. vibrierendes Gamepad) & ActionMapping (größere Freiheit für Spieler bei Auswahl & Konfiguration von Eingabegeräten). 4. DirectPlay (Unterstützung von Multiplayer-Netzwerk-/Online-Games). Protokoll auf Anwendungsschicht. Vorteil: unabhängig von konkret genutzten Protokollen der Transport- & Übertragungsebene. 5. DirectShow (Unterstützung von Streaming Media). Verarbeitung von Video- und Audio-Dateien (mp3. AVI, MPEG). 6. DirectSetup (Unterstützung bei der DirectX-Installation).
Arbeitsweise einer 3D-Engine Die Transformations- & Beleuchtungspipeline 1. Welttransformation = Transformation vom Modell- ins 3DWeltkoordinatensystem DirectX Graphics-Funktionsaufruf: SetTransform(D3DTS_WORLD, &Transformationsmatrix); Is klar: Erst nach Berechnung der Transformationsmatrix
Arbeitsweise einer 3D-Engine Die Transformations- & Beleuchtungspipeline 2. Sichttransformation I - Überblick = Transformation eines 3D-Objektes in den Kameraraum o Kameraraum: Bereich, in dem sich alle sichtbaren Objekte einer 3D-Szene befinden a) Berechnung einer Sichtmatrix (View-Matrix) b) Sichttransformation DirectX Graphics-Funktionsaufruf: SetTransform(D3DTS_WORLD, &ViewMatrix); (Funktionsaufruf bei jeder Änderung der View-Matrix notwendig)
Arbeitsweise einer 3D-Engine Die Transformations- & Beleuchtungspipeline 2. Sichttransformation II – Die View-Matrix Bei jeder Spielerbewegung ändern sich die Kamera- & damit Blickperspektive auf die entsprechenden 3DObjekte Eine Anpassung der Perspektive erfolgt nun nicht durch Bewegung oder Drehung der Kamera o stattdessen wird die Kamera im Koordinatenursprung fixiert (mit Blickrichtung entlang der positiven z-Achse) o und alle Objekte gemäß der inversen Kamerabewegung transformiert Statt also die Kamera auf ein Objekt zu zu bewegen/um ein Objekt zu drehen etc. wird stattdessen das Objekt auf die Kamera zu bewegt/um die Kamera gedreht usw. Damit entspricht die View-Matrix (na klar – wem sonst?) der inversen Kamera-Transformationsmatrix
Arbeitsweise einer 3D-Engine Die Transformations- & Beleuchtungspipeline 2. Sichttransformation III – Berechnung der View-Matrix a) Definition von 4 Vektoren zur Beschreibung der Position & Ausrichtung der Kamera (Pos, Look, Right, Up) b) Berechnung der View-Matrix auf Basis dieser Vektoren: DirectX-Graphics-Funktionsaufruf: (View-Matrix für ein linkshändiges Weltkoordinatensystem)
Arbeitsweise einer 3D-Engine Die Transformations- & Beleuchtungspipeline 2. Sichttransformation IV – Attentione! (Kameraverschiebung) Empfehlung: Man sollte aus Performance-Gründen die inverse Verschiebung der Kamera (bereits) während der Welttransformation aller Objekte berücksichtigen – und nicht erst in der View-Matrix o Grund: Eine Verschiebung der Kamera entspricht in der View- Matrix einer Verschiebung der Objekte o Würde die Kamera-Verschiebung also erst in der View-Matrix vorgenommen werden, würden sich (z.B. bei einer 3D-Szene mit 2000 Fixsternen) die Objekte ungewollt mitverschieben o Problem: Vor dem eigentlichen Rendern müsste dann eine erneute Welttransformation aller betroffenen Objekte (hier: 2000 Sternsken) vorgenommen werden, um diese (ungewollte) Verschiebung wieder rückgängig zu machen o s.a. Billboardmatrizen für 2D-Grafiken (Rauch. Explosionen etc.): Die Kameraverschiebung sollte auch hier bereits bei der Welttransformation der Matrizen vorgenommen werden
Arbeitsweise einer 3D-Engine Die Transformations- & Beleuchtungspipeline 3. Projektionstransformation I = Projektion der 3D-Szene auf den 2D-Bildschirm a) Berechnung einer Projektionsmatrix b) DirectX Graphics-Funktionsaufruf: SetTransform(D3DTS_Projection, &ProjectionMatrix); o Dieser Funktionsaufruf bei jeder Änderung der Projektions-Matrix notwendig
Arbeitsweise einer 3D-Engine Die Transformations- & Beleuchtungspipeline 3. Projektionstransformation II - Das Viewing Volume (Bildraum) = der vom Betrachter sichtbare Ausschnitt einer 3D-Szene Dabei wird unterschieden nach o Far Clipping Plane Æ maximale Sichtweite o Near Clipping Plane Æ minimale Sichtweite o Fiel of View (FOV) Æ Blickfeld des Betrachters Das Viewing Volume Die Projektionsmatrix
Arbeitsweise einer 3D-Engine Die Transformations- & Beleuchtungspipeline 3. Projektionstransformation III - Das Viewing Volume (Bildraum) Gepriesen sei der Herr Æ auch hierfür gibt´s eine entsprechende DirectX-Funktion: (Funktionsaufruf zur Erzeugung einer Projektionsmatrix für ein linkshändiges Weltkoordinatensystem) Arbeitsweise der Projektionsmatrix Die Projektionsmatrix bildet jedes Objekt der Szene auf der Projektionsfläche ab o Attentione: Um Projektionsfehler zu vermeiden, muss der Abstand zw. Projektionsfläche & Near Clipping Plane >=1.0 sein!
Arbeitsweise einer 3D-Engine Die Transformations- & Beleuchtungspipeline 3. Projektionstransformation IV – Umrechnung von Welt- in Bildschirmkoordinaten Für die Transformation der Weltkoordinaten eines Objektes (über die Viewportkoordinaten) in die entsprechenden Bildschirmkoordinaten ist mal wieder eine Matrix zuständig: o ViewProjectionMatrix = matView*matProj; o Diese Matrix muss für jedes Frame berechnet werden o Durch anschließende Matrizenmultiplikation wird der Ortsvektor eines Objektes in die entsprechenden Viewportkoordinaten transformiert Zu beachten ist, dass Viewport & Bildschirm zwar denselben Ausschnitt darstellen, ihre Koordinatensysteme jedoch divergieren: o Viewportkoordinaten (-1/-1) linke obere Bildschirmecke o Viewportkoordinaten 1/1 rechte untere Bildschirmecke o Viewportkoordinaten 0/0 Zentrum Während der Ursprung des Viewportkoordinatensystems also bei (-1/-1) liegt, findet sich der Ursprung des Bildschirmkoordinatensystem in der linken oberen Ecke o Aus diesem Grund müssen die Viewportkoordinaten noch in die Bildschirmkoordinaten umgerechnet werden
Arbeitsweise einer 3D-Engine Die Transformations- & Beleuchtungspipeline 3. Projektionstransformation V Umrechnung von Welt- in Bildschirmkoordinaten Und so sieht´s als Funktion aus: (berechnet unter Verwendung der entsprechenden Sicht- & Projektionsmatrizen die Bildschirmkoordinaten eines Objektes)
Arbeitsweise einer 3D-Engine Die Transformations- & Beleuchtungspipeline 4. Clipping Nach der Projektionstransformation werden die Polygone gegen alle Flächen des Viewing Volumes geclippt o Clipping: Die nicht sichtbaren Bereiche eines Polygons werden weggeschnitten o Für ein effizienteres Clipping wird der Viewing Raum zuvor in ein Quader transformiert Grund: Das Clipping gegen die Flächen eines Quaders ist erheblich einfacher als gegen die Flächen einer spitzwinkligen Pyramide
Arbeitsweise einer 3D-Engine Die Transformations- & Beleuchtungspipeline 5. Object-Culling I Um unnötige Durchläufe von (später ohnehin nicht sichtbaren) 3DObjekten durch die Transformationspipeline zu sparen, setzt man Objekt-Culling ein: o Ein Verfahren, um später nicht sichtbare Objekte auszusondern, bevor sie rechenintensiv durch die Pipeline gejagt würden 1. Test auf „reales“ Blickfeld von 180° (Einfachste Variante des Object-Culling): Wird sich das Objekt vor oder hinter dem Betrachter befinden? (Annahme: Blickfeld des Betrachters beträgt 180°) Vorgehensweise: Bilden des Skalarprodukts aus Ortsvektor & Blickwinkel des Betrachters Effekt: frappierende Objektdiskriminierung! Æ Durchschnittlich 50 % aller Objekte wird der Zutritt zur Pipeline verwehrt 2. Test auf Blickfeld von 90° (von –45° bis +45°) [= FOV (Field of View)?] Nun wird der Rest der Objekte darauf getestet, ob sie sich jeweils nicht nur „irgendwie vor den Ohren“ des Betrachters - sondern auch in dessen direktem Blickfeld befinden Vorgehensweise: Division des Skalarprodukts durch den Betrag des Ortsvektors & Check auf Blickwinkel zw. –45° bis +45°: Sicher ist sicher: In der Praxis wäre ein etwas größerer Blickwinkel realistischer:
Arbeitsweise einer 3D-Engine Die Transformations- & Beleuchtungspipeline 5. Object-Culling II – Effizienz Es gibt allerdings noch eine effizientere Methode, um alle beschriebenen Schritte des Object Culling durchzuführen o Grund: Die Berechnung es Vektorbetrags ist eigentlich überflüssig o Stattdessen können der quadrierte Betrag & das quadrierte Skalarprodukt verwendet werden, um sich eine Wurzelberechnung zu sparen:
Arbeitsweise einer 3D-Engine Die Transformations- & Beleuchtungspipeline 6. Bilderzeugung – Doppelpufferung (Double Buffering) Bilder werden im RAM bzw. VRAM in Form eines linearen Arrays gespeichert o Dabei gilt: 1 Arrayelement = 1 Pixel o Der Speicherbedarf der Arrayelemente ist abhängig von der Farbtiefe; wir erinnern uns: > Pseudo: Bei 256 Farben/Pixel = 8 Bit / Arrayelement > High Color: Bei 65536 Farben/Pixel = 16 Bit / Arrayelement > True Color: Bei 16,7 Mio. Farben/Pixel = 24/32 Bit / Arrayelement Display Surface (auch Primary Surface) o = Speicherbereich des VRAM (Videospeicher), auf den bei der Bilderzeugung direkt zugegriffen wird o Jede Änderung am Display Surface wird umgehend auf dem Bildschirm umgesetzt Problem: Durch schrittweisen Aufbau eines Frames im Display Surface kommt es zu Bildschirmflackern & Doppelbildern o Gründ: der Schrittweise Aufbau von Frames braucht eine gewisse Zeit; andauernde Bildveränderung generell; asynchroner Bildaufbau (Speicher vs. Bildschirm) Solution: Double Buffering with Backbuffer and Surface Flipping (yeah)!!! o Der Backbuffer ist ein zweiter Speicherbereich zum Bildaufbau > Dort wird das entsprechende Frame quasi vorbereitet o Nun gibt es 2 Möglichkeiten, das vorbereitete Frame in den Display Surface zu bekommen > Schlichtes Kopieren vom Backbuffer in den Display Surface Æ böse weil langsam > Durch Surface-Flipping: Pointer-Austausch Æ toll, weil schnell: Der Austausch des Backbuffer-Zeigers mit demjenigen des Display Surface ist erheblich schneller -> ergo wird der Backbuffer nun ad hoc zum neuen Display Surface & umgekehrt
Arbeitsweise einer 3D-Engine Die Transformations- & Beleuchtungspipeline 7. Tiefenpufferung (Depth Buffering). Generell: Verschiedene Verfahren, um zu ermitteln, welche Pixel sich (auf Basis der Betrachter-Perspektive und der Tiefe der Bildpunkte) überlagern und damit für den Betrachter sichtbar sein sollen oder nicht. o Effekt: Ist die Entfernung zweier sich überlagernder Bildpunkte verschieden, wird der weiter entfernte aus dem Bildspeicher durch den näher liegenden Punkt überschrieben. o Diese Verfahren müssen in DirectX Graphics nicht explizit initialisiert werden. z-Buffering-Algorithmus. Auf Grafikkarten implementiert (entspricht einem Array mit Dimensionen der Bildschirmauflösung); Ablauf: Zunächst werden alle Elemente des Bildes in den Bildspeicher projiziert. Danach werde die Tiefeninformation der Polygone auf Pixelbasis gespeichert. Vor dem Rendern wird der maximal mögliche Tiefenwert (des gesamten Bildes) ermittelt und auf alle Elemente des z-Buffers übertragen (Der z-Buffer arbeitet dabi mit Tiefenwerten von 0 bis 1). Beim Rendern eines Polygons erfolgt dann pixelweise eine Tiefenprüfung. o Tiefenwert Z-Buffer > Tiefenwert Pixel = Z-Buffer-Tiefenwert wird durch Piexel-Tiefenwert überschrieben. > Das entsprechende Pixel ist dem Betrachter also näher & damit sichtbar. o Tiefenwert Z-Buffer < Tiefenwert Pixel = Z-Buffer-Tiefenwert bleibt bestehen. > Das entsprechende Pixel ist ohnehin nicht sichtbar. w-Buffering-Algorithmus. Speichert im Gegensatz zum z-Buffer statt „künstlichen“ Tiefenwerten direkt die z-Komponente der einzelnen Vertex-Positionen.
Das Anwendungsgerüst 1. Gliederung des Anwendungsgerüsts 2. Weitere Dateien: Workshop Gunia Wenn es Mr. Sidekick genehm wäre, könnte er hier zur Erläuterung des Anwendungsgerüstes gerne das 1. Programmbeispiel („FirstContact“) verchecken? (ach ja: nach 19 Charts wär nun auch ein Kaffee angebracht... Danke.)
Das Anwendungsgerüst 3. Einzubindende Bibliothek-Dateien: Zum Kompilieren des Anwendungsgerüsts sind folgende Bibliotheken einzubinden: 4. DirectX-Ersetzungen (Substitutions) Problem: Jedes DirectX-Update bringt versionsspezifische DirectX-Typen & -Funktionsaufrufe mit sich -> ergo hoher Anpassungsaufwand bei jedem Versionswechsel Lösung: Deshalb Headerdatei DXSubstitutions.h einbinden (also eigene Bezeichnungen definieren und durch den Präprozessor ersetzen lassen):
Das Anwendungsgerüst 5. Die zentrale Headerdatei der Anwendung: space3D.h Standard-Includes Funktionsspektrum dieser Header-Dateien: DirectX-Makros & Helper-Funktionen Textausgabe eine lauffähige DirectX-Graphics-Anwendung mathematische Funktionen Funktionen zur Billboard-Ausrichtung Spielsteuerung & Sound vordefinierte Vertex-Formate Klassen für Generierung & Handling von Texturen Funktionsprototypen
DirectX Graphics – Initialisierungsschritte Die Game-Initialisierungs-Routine I (Modul: GameRoutines.cpp) Wird bei Programmstart aufgerufen und führt die wichtigsten Initialisierungsschritte durch: o InitRenderOptions(); (Modul: GameRoutines.cpp) ¾ s. Tag 1: Ruft die Ressource RenderingAndMore.txt auf (Art der Vertexverarbeitung? Frame od. Time Based Rendering?) o InitSpieleinstellungen(); (Modul: Space3D.cpp) ¾ Ruft die Ressource Spieleinstellungen.txt auf (enthält Grafikeinstellungen – hier: Sichtweite der 3D-Szene) o InitD3D(); (Modul: Space3D.cpp) ¾ Initialisierung von DirectX-Graphics (Details Folge-Charts) o Init_View_and_Projection_Matrix(); (Modul: Space3D.cpp) ¾ Erzeugung von Sicht- & Projektionsmatrizen & Transformation
DirectX Graphics – Initialisierungsschritte Die Game-Initialisierungs-Routine II Initialisierung von DirectX-Graphics mit InitD3D() 1. Erzeugen eines Direct3D-Objekt mit direct3DCreate9(); Als Parameter wird die SDK-Version als Konstante übergeben: D3D_SDK_VERSION 2. Ermitteln des aktuellen Display-Modus mit GetAdapterDisplayMode(); Rückgabewert wird in einer Variablen vom Typ D3DDISPLAYMODE gespeichert 3. Abfrage von Display- & Backbuffer-Formaten mit CheckDeviceType(); Für den Fullscreen-Modus ist die Abfrage der von der Grafikkarte unterstützten Display- & Backbuffer-Formate notwendig: Displayformat bei einer Farbtiefe von 32 Bit: D3DFMT_X8R8G8B8 R8G8B8 -> 8 Bit für die Darstellung von jeweils 256 R-/G-/B-Tönen X8 -> repräsentiert die 8 unbelegten Bits o Formate bei einer Farbtiefe von 16 Bit: Definition der Farbformate: D3DFMT_R5G6B5 / _X1R5G5B5 / _X4R4G4B4 Verschiedene Kombinationen möglich; jedoch sind 2 Regeln zu beachten: Display- & Backbuffer-Format müssen Backbuffer-Formate bei einer Farbtiefe von 1. ... die gleiche Farbtiefe haben! 32 Bit: D3DFMT_X8R8G8B8 & D3DFMT_A8R8G8B8 2. ... die gleiche Bitanzahl/Farbwert aufweisen! A8 -> 8 Bit für den Alphakanal (definiert den Transparenzwert eines Pixels) Unterstützung bei der Suche nach dem korrekten Farbformat o Formate bei einer Farbtiefe von 16 Bit: D3DFMT_R5G6B5/_X1R5G5B5/_X4R4G4B4/ o Mithilfe der beiden DirectX-Makros SUCCEDED() FAILED() _A1R5G5B5/_A4R4G4B4 o Der Rückgabewert HRESULT gibt Auskunft über einen erfolgreichen oder fehlgeschlagenen Funktionsaufruf
DirectX Graphics – Initialisierungsschritte Die Game-Initialisierungs-Routine II Initialisierung von DirectX-Graphics mit InitD3D() 4. Definition der Präsentationsparameter: Fullscreen oder Window? Wie viel Backbuffer? Backbuffer-Formt & - Auflösung? Tiefenpuffer (z-Buffer) verwenden? Flipping/Swap-Effect: Art des Wechselns zw. Backbuffer & Display-Surface? 5. Initialisierung eines Grafikkarten-Objekt mit CreateDevice(): Mit dem so erzeugten Objekt (HAL-Gerät) kann auf die Treiber der Grafikkarte zugegriffen werden o Das HAL-Gerät fungiert hier gleichsam als „Ersatz“ für den direkten Zugriff auf die Hardware 6. Definition der Vertexverarbeitung: verschiedene Arten des Transform & Lightning (Beleuchtung) der Vertices: Hardware TnL (schnellste Variante, weil von GPU durchgeführt) o Spezielle Graphic Processing Unit auf der Grafikkarte o Vorteile: CPU-Entlastung & keine überflüssigen Grafikkarten CPU-Transfers o Hier ist ggf. eine Verwendung der Grafikkarte als sog. pure device (reines Gerät) möglich Vorteil: Schnellstmöglicher Gerätetyp; Nachteile: Keine volle Direct3D-Funktionalität (z.B. keine Get*()-Aufrufe; keine Emulation nicht unterstützter Funktionalitäten durch die HEL; instabil bei unerlaubten Aufrufen etc. Mixed TnL (irgendwas dazwischen) Software TnL (langsamste Variante, weil durch CPU durchgeführt)
DirectX Graphics – Initialisierungsschritte Die Game-Initialisierungs-Routine II Initialisierung von DirectX-Graphics mit InitD3D() 7. Render States (Renderzustände) definieren mit SetRenderState() States dienen der detaillierten Steuerung des Rendering o Beispiel: o Texture Stage States definieren mit SetTextureStageState() > Steuerung des Texture Mappings (Auswahl der Texturkoordinaten; Erzeugung von Texture Blending Effekten) o Sampler States definieren mit SetSamplerState() > Ebenfalls zur Steuerung des Texture Mappings (Texturfilterung; Textur- Adressierungsmodus)
DirectX Graphics – Initialisierungsschritte Die Game-Initialisierungs-Routine II Initialisierung von DirectX-Graphics mit InitD3D() 8. CD3DFONT-Schriftklassen definieren mit der Klasse CD3DFont Dienen der Textausgabe einer 3D-Szene o Der auszugebende Text wird als Textur auf ein sog. Quad gemappt > Quad: viereckiges Polygon, das später gerendert wird o Initialisierung & Deklaration in D3DGlobals.h o Implementierung der Klasse in d3dfont.cpp
DirectX Graphics – Initialisierungsschritte Die Game-Initialisierungs-Routine II 9. View- & Projektionsmatrizen erstellen & transformieren mit Init_View_and_Projection_Matrix(); Ende der Initialisierungsarbeiten
DirectX Graphics Anzeige des 3D-Szenarios mit Show_3D_Szenario() Step 1: Löschen der vorangegangenen Szene Æ durch Clear(): Durch Clear wird der Backbuffer mit einer Hintergrundfarbe gefärbt der z-Buffer mit maximalen Tiefenwerten initialisiert der Stencil-Buffer mit Werten initialisiert Funktionsprototyp: Step 2 & 3: Eine neue Szene beginnen & beenden durch BeginScene() & EndScene(): BeginScene() „informiert“ Direct X Graphics vom Beginn des Szenenaufbaus EndScene() Szenenaufbau fertig – Szene bereit zur Anzeige! Step 4: Neue Szene anzeigen durch Present(): Ausgabe von 2D-Text durch DrawtextScaled(): Funktionsprototyp später Viewportkoordinaten (Vorteil „Vpk“: Textposition bleibt immer identisch - unabhängig von d. jew. Resulotion Textskalierung (B & H), bilineare Filterung mit D3DFONT_FILTERED (für bessere Textqualität)
DirectX Graphics Beenden von DirectX Graphics - Aufräumarbeiten mit CleanUpD3D() CleanUpD3D() wird durch die Methode GameCleanUpRoutine() aufgerufen SAFE_DELETE: Æ Zerstören der beiden CD3DFont-Instanzen SAFE_RELEASE: Æ Freigabe der beiden DirectX-Graphics-Objekte
Punkte, Linien & Kreise Aus diesen geometrischen Grundelementen lassen sich beliebige Objekte generieren (vergleichbar mit den Grundfarben...) Zudem lassen sie sich besonders fix rendern Workshop Gunia Die Klasse C3DScenario: Es ist mal wieder soweit: 2. Programmbeispiel („Punkte, Linien & Kreise“) bitte! UND Kaffee.
Punkte, Linien & Kreise - Anwendungsgerüst Workshop Gunia Anwendungsgerüst: Einbinden der Datei 3D_ScenarioClasses.h:
Punkte, Linien & Kreise - Initialisierung Workshop Gunia Initialisierungsschritte:
Punkte, Linien & Kreise - Aufräumarbeiten Workshop Gunia Aufräumarbeiten:
Punkte, Linien & Kreise rendern Workshop Gunia Punkte, Linien & Kreise rendern mit Show_3D_Scenario():
Punkte, Linien & Kreise FVF: Flexible Vertexformate Vertexdaten: Für jeden Eckpunkt (Vertex) eines Objektes müssen (je nach Verwendungszweck) verschiedene Daten gespeichert werden o Position o Normale (für die Lichtberechnung) o Farbe (dann aber nicht Lichtberechnung) o Texturkoorinaten (Texture Mapping, Multitexturing) Zu diesem Zweck existiert das Modul VertexFormats.h mit dem struct POINTVERTEX: o Dort werden für jeden Vertex Position & Farbe gespeichert > Konstante D3DFVF_XYZ: Vertices müssen noch durch DirectX-Graphics transformiert werden > Konstante D3DFVF_DIFFUSE: Vertices sind bereits beleuchtet
Punkte, Linien & Kreise – Vertexbuffer I Vertexbuffer: Geschützter Speicherbereich der Grafikkarte zur effizienten Vertexverarbeitung o Vertexverarbeitung: Transformation und Rendering der Vertexdaten Initialisierung eines Vertexbuffers o Beispiel: o Parameter: > 1. Parameter definiert die Größe des Vertexbuffers (Übergabe der Anzahl an Punkten & des Speicherbedarfs eines POINTVERTEX) > 2. Parameter definiert den Verwendungszweck durch das Flag D3DUSAGE_WRITEONLY (Zwar Lesezugriff untersagt aber dafür effizientere Schreib- & Render-Operationen) > 3. Parameter definiert das zu verwendende Vertexformat des Buffers > 4. Parameter weist DirectX-Graphics an, sich um die Aufräumarbeiten zu kümmern > 5. Parameter definiert Adresse des auf den Vertexbuffer verweisenden Zeigers > 6. Parameter „soll uns nicht weiter stören“
Punkte, Linien & Kreise – Vertexbuffer II Zugriff auf Vertexbuffer (Zugriffschutz) o Der Buffer muss – als geschützter Speicherbereich – vor jedem Zugriff mit der Methode Lock() verriegelt werden o Nach Durchführung des Schreibvorgangs wird er mit Unlock() wieder entriegelt
Punkte, Linien & Kreise – Vertexbuffer III Vertexdaten transformieren. o Die Vertexdaten werden durch die Transformations- & Beleuchtungspipeline geschickt. o Zu diesem Zweck muss mit der Methode SetFVF() ein entsprechender Vertex-Shader ausgewählt werden. > Bei Pointvertices: g_pd3dDevice->SetFVF(D3DFVF_POINTVERTEX); > Bei programmierbaren Vertex-Shadern: SetVertexShader(); o Danach werden mit SetStreamSource die später zu rendernden Vertextdaten ausgewählt. > g_pd3dDevice->SetStreamSource(0, PunkteVB, 0, sizeof(POINTVERTEX)); > Bei programmierbaren Vertex-Shadern können die Vertexdaten auch aus mehreren Sources stammen (ein Vertex-Buffer für Texturkoordinaten, einer für Normalen, einer für Positionen etc.
Punkte, Linien & Kreise – Vertexbuffer IV Vertexdaten rendern mit DrawPrimitive() o „Primitive“: definiert, auf welche Weise die Vertices in im Vertexbuffer miteinander verbunden werden sollen o Mit DrawPrimitive() können bis zu 6 verschiedene Grafikprimitiven gerendert werden: o Punkte & Linien rendern > Point List rendern (Startindex = 0, da alle Vertexdaten gerendert werden sollen): > Eine einzelne Linie rendern:
Punkte, Linien & Kreise – Vertexbuffer V Einen Kreis rendern o Eigentlich handelt es sich nicht um Kreise – sondern um einen Polyeder > Polyeder: regelmäßiges Vieleck > Der angestrebte „Kreis“ wirkt umso realistischer, je größer die Anzahl der Polyedersegmente ist o Berechnung der Vertexkoordinaten > s. Tag 3: 2dimensionale Polarkoordinatendarstellung (Kreiskoordinaten) > Attentione! Will man beispielsweise einen 8eckigen Polyeder generieren, müssen 9 (und nicht 8!) Eckpunkte definiert werden! > Grund: Der letzte Eckpunkt muss immer mit dem ersten identisch sein, damit der Polyeder überhaupt geschlossen ist! o Das Rendern des „Kreises“ erfolgt aufgrund seiner Natur als Polyeder dann auch mit einem Line Strip > RingSegmente-1 entspricht dabei der Anzahl der zu rendernden Grafikprimitiven > Grund: Ein Line Strip besteht zwar aus n Punkten, jedoch nur aus n-1 Linien
Texturen Texturen sind „einfache“ Bitmapgrafiken, welche die Oberflächen eines 3D-Körpers definieren und im VRAM gespeichert sind. o Durch Texturen wird eine „realistischere“ Darstellung der Objekte erreicht, ohne die Anzahl der Polygone erhöhen zu müssen. o Ein Pixel der Textur wird als Texel bezeichnet. Texture Mapping meint dabei das Verfahren zur Ausstattung dreidimensionaler Objektoberflächen mit zweidimensionalen Texturen. o Beim Texture Mapping werden die Polygone eines Objektes (nach dessen Transformation) gemäß der in den Vertices gespeicherten Texturkoordinaten mit Texturen überzogen. o Dabei werden die Texturen in einem rechenintensiven Verfahren Texel für Texel auf die entsprechenden Polygone gemappt (aufgetragen). o Erschwerender Aspekt beim Texture Mapping: > Frei bewegliche & in ihrer Größe variierende 3D-Objekte...
Texturen - Generierung Die Textur-Helperklasse CTexturPool I (Implementierung: TextureClasses.h) CreateTexture(); Erzeugt eine Textur inklusive vollständiger Mipmap-Kette mit transparentem Bereich o Mipmap-Textur: verkleinerte Version einer Textur (wird bei Texturfilterung eingesetzt) o Transparenz: Bei aktiviertem Alpha- Blending werden schwarze Bereiche transparent dargestellt CreateNonTransparentTexture(); Erzeugt eine Textur inklusive vollständiger Mipmap-Kette ohne transparenten Bereich Erzeugung einer Textur in 3 Schritten: CreateTexture_WithoutMitmps & 1. Ein Texturklassen-Objekt erzeugen CreateNonTransparentTexture_without CursorTextur = new CTexturPool Mitmaps(); Erzeugen Texturenohne Mitmap-Ketten 2. Speicherpfad der Textur übergeben sprintf(CursorTextur->Speicherpfad, „Cursor.bmp“); 3. Textur erzeugen CursorTextur->CreateTextur();
Texturen - Generierung Die DirectX-Wunderfunktion D3DXCreateTextureFromFileEx() o Funktionsprototyp: Parameter 3,4,5: Die Konstante D3DX_DEFAULT für Breite, Höhe & Anzahl der Mipmaps Erzeugt eine Textur inklusive kompletter Mipmap-Kette Die Maße der Textur entsprechen dabei denjenigen der Quelldatei (also Original-Bitmap) Parameter 6: 0 Erzeugt eine normale Textur-Surface Möglichkeit des Shadowmapping: Textur-Surface wird als Render Target eingesetzt Parameter 7: Die Konstante D3DFMT_UNKNOWN Übernimmt das Original-Format der Bitmap-Quelldatei Parameter 8: Die Konstante D3DPOOL_MANAGED DirectX-Graphics übernimmt die Verwaltungsarbeiten Parameter 9-14: Filter für die Textur- & Mipmap-Erstellung o Beispiel: D3DX_FILTER_TRIANGLE: wird verwendet, wenn Textur- & Quellbitmap-Maße identisch sein sollen D3DX_FILTER_BOX: wird verwendet, wenn die Textur-Maße nur die Hälfte der Originalmaße (Bitmap) betragen sollen Parameter 15: Die Konstante D3DCOLOR_XRGB definiert Farbe (hier schwarz) & Transparenz bei eingeschaltetem Alpha-Blending Parameter 18: &pTexture übergint Adresse des Zeigers pTexture, der auf die Textur-Surface zeigt
Texturen - Generierung Die Textur-Helperklasse CTexturPool II (Implementierung: TextureClasses.h) Breit schaut die Klasse einfach besser aus ...
Texturen - Generierung Die Texturklasse CTexturPoolEx I (Implementierung: TextureClasses.h). o Mit Hilfe dieser Klasse lassen sich mehrere Texturen in einem Schritt erzeugen. o Ausgangssituation: Für ein 3D-Objekt sollen drei verschiedene Texturen unterschiedlichen Detailgrads verwendet werden: > SternenkreuzerTextur1M1.bmp > SternenkreuzerTextur1M3.bmp > SternenkreuzerTextur1M3.bmp o Um nun alle 3 Texturen in einem Schritt zu erzeugen ... > ... muss lediglich der Texturstammname (hier also SternenkreuzerTextur1) an ein Objekt der Klasse CTexturPoolEx übergeben ... > ... & anschließend mit der so erzeugten Instanz die Methode InitTextures() aufgerufen werden:
Texturen - Generierung Die Texturklasse CTexturPoolEx II (Implementierung: TextureClasses.h)
Texturen - Koordinaten Texturkoordinaten Als Texturkoordinaten werden die Parameter tu & tv verwendet o Koordinaten 0/0 = linke obere Ecke des als Textur verwendeten Bitmaps o Koordinaten 1/1 = rechte untere Ecke des als Textur verwendeten Bitmaps Um also ein Bitmap vollständig als Textur auf ein Objekt zu mappen, müssen Texturkoordinaten in einem Bereich von 0 bis 1 verwendet werden Texture Wrapping (mehrfaches mappen von Polygonen): o Analog funktioniert dies bei doppelter Texturierung eines Objektes – hier müssen dann Texturkoordinaten >1 (bzw.
Texturen – weitere Vertexformate COLORREDVERTEX: Bereits beleuchtete Vertices mit Texturkoordinaten o Wird verwendet, wenn DirectX Graphics das darzustellende Objekt zwar transformieren aber nicht beleuchten soll Parameter tu & tv: is klar -> Texturkoordinaten Konstante D3DFVF_TEX1: Hier soll nur ein einzelner Satz von Texturkoordinaten verwendet werden SCREENVERTEX: Sowohl bereits transformierte als auch beleuchtete Vertices mit Texturkoordinaten o Wird verwendet, wenn DirectX Graphics das darzustellende Objekt weder transformieren noch beleuchten soll Konstante D3DFVF_XYZRHW: Die Vertices wurden bereits transformiert (was der 4dimensionale Vektor D3DXVECTOR4 ebenfalls verdeutlicht) • XY: enthalten die x- & y-Koordinaten des Vertex (da der Vertex bereits transformiert wurde, entsprechen diese den Bildschirmkoordinaten) • Z: Tiefenwert (s. z-Buffer: Wertebereich von 0 bis 1) • RHW-Wert: die „reziproke w-Komponente“ (s. Tag 3!); wird für Nebelberechnung & perspektivisch korrektes Texture-Mapping verwendet)
Texturen – texturierte Rechtecke (Quads) I Quads werden für die Darstellung von 2D-Objekten verwendet. o Wie Partikel, Feuer, Explosionen, Wolken, Cursor, Displayelemente etc. . Einen Quad rendern mit DrawPrimitive(): o Zu diesem Zweck wird ein aus 2 Dreiecken bestehender Triangle Strip verwendet. > alternativ wäre auch eine Triangle List möglich – jedoch müssten in diesem Fall 6 statt 4 Vertices definiert werden; Grund: 2 Dreiecke = 6 Vertices. o Beim Funktionsaufruf muss die Anzahl der Dreiecke im Triangle Strip mit übergeben werden. > Attentione: Ein Triangle Strip beinhaltet immer 2 zusätzliche Vertices; Grund: 2 Dreiecke = 4 Vertices, 4 Dreiecke = 6 Vertices usw.
Texturen – texturierte Rechtecke (Quads) II Cullmode definieren o Der Cullmode legt fest, welche Dreiecke (z.B. bei einem Triangle Strip) beim Rendern auszusondern (zu cullen) sind > Grund: Bei der Definition von Dreiecken müssen deren Eckpunkte entweder im Uhrzeigersinn oder entgegen dem Uhrzeigersinn angegeben werden > Beim Triangle Strip gibt man die Eckpunkte entgegen dem Uhrzeigersinn an > Dies muss DirectX-Graphics mitgeteilt werden: > Hierbei wird zwischen den Cullmodi CW (clockwise) & CCW (counter-clockwise) unterschieden > Diese Modi definieren jeweils, welche Dreiecke auszusondern sind (hier: diejenigen, deren Eckpunkte im Uhrzeigersinn angegeben wurden) o Vorteil des Verfahrens: Performancegewinn > Beispiel: Wären die vorderen Dreiecksflächen eines Objekte im Uhrzeigersinn definiert, würden die die hinteren Dreiecksflächen aus Betrachtersicht entgegen dem Uhrzeigersinn definiert erscheinen > In diesem Fall würde man den Cullmode CCW wählen > Effekt: Alle hinteren Dreiecksflächen würden nicht gerendert werden (auch backface culling genannt) Æ Performancegewinn
Texturen – texturierte Rechtecke (Quads) III Vertexdaten für ein beleuchtetes Quad definieren
Texturen – texturierte Rechtecke (Quads) IV Vertexdaten für ein beleuchtetes & transformiertes Quad definieren
Texturen – texturierte Rechtecke (Quads) V Zu verwendende Textur festlegen & Quad rendern 1. Textur festlegen DirectX-Graphics benötigt zur Verwendung einer Textur einen Zeiger auf das entsprechende Textur-Objekt. Dies geschieht mit Hilfe der Funktion SetTexture(); o Beispiel: o Erläuterung > Cursor-Texture Instanz der Textur-Helperklasse CTexturPool > pTexture Zeiger auf das eigentliche Texturobjekt > Parameter 0 Texture Stage Number (=Nummer der Verwendeten Textur-Stufe) – DirectX-Graphics kann bis zu acht Texturen gleichzeitig verarbeiten (s. Tag 12: Multitexturing) – 0 entspricht also der „nullten Textur-Stufe“ (zur Verwendung von lediglich 1 Textur) 2. Transformations- & Beleuchtungspipeline mit SetStreamSource() Nun werden die Vertexdaten des Quads durch die Transformations- & Beleuchtungspipeline gejagt 3. Vertex-Shader festlegen mit SetFVF() 4. Rendern mit DrawPrimitive() 5. Die verwendete Textur wieder aus der Texturstufe entfernen mit SetTexture()
Texturen – Demoprogramm Workshop Gunia Überblick über die einzubindenden Programmmodule, Klassen, Funktionen Einbinden der Module in Space3D.h BuildIdentityMatrices() Initialisiert bei Programmstart die global verwendeten Matrizen als Einheitsmatrizen (s. Folgechart) Zusätzlich DirectInput-Bibliothek fürgegen CursorSpass mit einbinden!
Texturen – Demoprogramm Workshop Gunia BuildIdentityMatrices(): o Initialisiert global verwendete Matrizen als Einheitsmatrizen o Attentione: Wird von der Game-Initialisierungs-Routine aufgerufen > Deshalb: Funktionsprototyp als External in GameRoutines.h einbinden!
Texturen – Demoprogramm Workshop Gunia Die Klasse C3DScenario Konstruktor: initialisiert alle Elemente des 3DSecenarios Destruktor: na? New_Scene(): Wird zum Rendern verwendet & muss zur Darstellung der Scene durch Show_3D-Scenario aufgerufen werden Danach werden nacheinander Sternenfeld, Cursor & Bildschirmschablone gerendert
Texturen – Demoprogramm Workshop Gunia Die Klasse CSchablone CSchablone definiert ein Screen Quad (Bildschirmschablone) Im einfachsten Fall: Ein simples beleuchtetes & transformiertes Quad von der Größe der Bildschirmauflösung Zweck: Einbau von Cockpit- od. anderen Anzeigeelementen
Texturen – Demoprogramm Workshop Gunia Die Klasse CCursor Also die Klasse passt hier nur wirklich nicht mehr auf eine Folie – deshalb: Siehe SimpleCursorClass.h CCursor ist für Initialisierung, Darstellung & Zerstörung des Cursor-Quads zuständig o Cursor-Quad: Ein beleuchtetes Quad, dass für die Cursordarstellung verwendet wird Attentione: Der Cursor muss immer an der aktuellen Mausposition gerendert werden o Deshalb muss vor dem Rendern eine Welttransformation durchgeführt werden:
Texturen – Demoprogramm Workshop Gunia Die Klasse CStarfield Erzeugt eine Point List & gibt diese auf dem Bildschirm aus:
Simulationen Workshop Gunia: „Nach eigenem Dafürhalten“
Sie können auch lesen