Seminar Echtzeit-Rendering SS 2008 DirectX - Eine
Transcription
Seminar Echtzeit-Rendering SS 2008 DirectX - Eine
Fachbereich 4: Informatik Seminar Echtzeit-Rendering SS 2008 DirectX - Eine praktische Einführung vorgelegt von Stefan Stümper Betreuer: Prof. Dr. Stefan Müller (Institut für Computervisualistik, AG Computergrafik) Koblenz, 10. Juni 2008 Inhaltsverzeichnis 1 2 3 Einleitung 1.1 Was ist DirectX [Wik08] . . . . . . . . . 1.2 Komponenten . . . . . . . . . . . . . . . 1.3 DirectX – technisch . . . . . . . . . . . . 1.4 Component Object Model (COM)[Rie06] 1.5 Fachbegriffe . . . . . . . . . . . . . . . . 1.5.1 Adapter . . . . . . . . . . . . . . 1.5.2 Device . . . . . . . . . . . . . . 1.5.3 Modus . . . . . . . . . . . . . . 1.5.4 Format . . . . . . . . . . . . . . 1.5.5 Buffer . . . . . . . . . . . . . . . 1.5.6 Front– und Backbuffer . . . . . . 1.5.7 Depthbuffer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 2 2 2 2 3 3 3 3 3 3 3 4 Transformationen in Direct3D [Sch06] 2.1 Transformationspipeline . . . . . 2.1.1 World–Matrix . . . . . . . 2.1.2 View–Matrix . . . . . . . 2.1.3 Projection–Matrix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 5 5 5 5 Programmieren mit Direct3D [Rie06] 3.1 Vorbereitungen . . . . . . . . . . . 3.2 Direct3D initialisieren . . . . . . . 3.3 Zeichnen einfacher Formen . . . . . 3.3.1 Rendern in 2D . . . . . . . 3.3.2 Der Viewport3 . . . . . . . 3.4 Flexible Vertex Format – FVF . . . 3.5 Geometrie erzeugen – 2D . . . . . . 3.6 3D . . . . . . . . . . . . . . . . . . 3.7 Szene Initialisieren . . . . . . . . . 3.8 Geometrien in 3D . . . . . . . . . . 3.9 Animation durch Transformationen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 8 8 9 9 9 9 10 11 11 12 12 Kamerabeschreibung . . . . . . . . . . . . . . . . . . . . . . . . ViewVolume . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Der Viewport . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 6 10 Abbildungsverzeichnis 1 2 3 1 1 1.1 Einleitung Was ist DirectX [Wik08] DirectX ist eine Sammlung von COM-basierten Programmierschnittstellen (englisch: Application Programming Interface, kurz API) für multimedia-intensive Anwendungen (besonders Spiele) auf der Windows-Plattform und kommt auch auf der Spielekonsole Xbox zum Einsatz. Die DirectX-Sammlung von Software-Komponenten deckt nahezu den gesamten Multimediabereich ab. Vorrangig kommt es zum Einsatz bei der Darstellung komplexer 2D- und 3D-Grafik, bietet aber auch Unterstützung für Audio, diverse Eingabegeräte (z. B. Maus, Joystick) und Netzwerkkommunikation. 1.2 Komponenten • DirectX Graphics / Direct3D Beinhaltet alles, was benötigt wird, um Grafiken zu erzeugen. • DirectX Input Ist zuständig für die Ansteuerung von Eingabegeräten, wie zum Beispiel Maus und Tastatur, aber auch Joysticks, Gamepads oder Lenkräder. Des weiteren kann hiermit auch die Force–Feedback–Technik von Geräten genutzt werden. • DirectX Sound Dient der Ausgabe von Sound und Musik in allen Variationen. Im Rahmen dieser Seminararbeit wird DirectX Graphics / Direct3D behandelt. 1.3 DirectX – technisch DirectX liegt, auf Dateiebene betrachtet, als dll–Dateien im Verzeichnis WINDOWS/system32. Es handelt sich aber nicht um normale dll–Dateien, sondern um COM–Komponenten weshalb auch ein kurzer Einblick in diese genommen wird. DirectX basiert fast vollständig auf COM. 1.4 Component Object Model (COM)[Rie06] Ein COM–Objekt ist eine Art Black–Box, welche bestimmte Funktionalitäten beinhaltet. Von außen her ist nicht ersichtlich, was im inneren passiert. Jedes COM– Objekt hat mindestens eine Schnittstelle (beispielsweise IDirect3D9) über die auf seine Funktionalitäten zugegriffen werden kann. Der Zugriff auf eine COM–Schnittstelle ist erst möglich, nachdem ein Objekt des COM erzeugt wurde. Jede Methode eines COM–Objektes liefert als Rückgabe einen Wert vom Typ HRESULT. Erfolgreiche Meldungen haben dabei das Präfix S und Fehler E (S OK, E FAIL) 2 Dieser gibt Aufschluss über die Fragen ob der Methodenaufruf funktioniert hat, und wenn nicht, warum. Wenn ein COM–Objekt nicht mehr benötigt wird, muss es über die Methode Release() freigegeben werden. 1.5 1.5.1 Fachbegriffe Adapter Ein Adapter ist eine Grafikkarte. 1.5.2 Device Jeder Adapter beinhaltet mindestens ein 3D–Device, welches für die Berechnung von Operationen zuständig ist. Ein solches Device kann sowohl ein Hardwareelement (HAL–Device) oder aber ein Softwareelement (Ref–Device). Das HAL– Device ist der tatsächliche Grafikchip des Adapters. Das Ref–Device ist eine reine Software–Referenz–Implementierung, die auf der CPU ausgeführt wird und somit natürlich langsamer ist als der speziell auf Grafikoperationen ausgelegte 3D–Chip der Grafikkarte. 1.5.3 Modus Als Modus bezeichnet man die Auflösung mit Bildwiederholfrequenz und Farbtiefe. 1.5.4 Format Jeder Grafikprozessor benutzt eine gewisse Menge RAM (Random Access Memory). In diesem Speicher werden 3D–Daten, Texturen sowie der aktuelle Bildschirminhalt abgelegt. Die Art und Weise, wie diese Daten im Speicher liegen, nennt man Format. 1.5.5 Buffer Direct3D verfügt über verschiedene Buffer. In ihnen werden aktuelle Rechenergebnisse sowie das aktuelle Bild, das nächste Bild, Tiefeninformationen und ähnliches abgelegt. Die drei wichtigsten Buffer nennt heißen Frontbuffer, Backbuffer und Depthbuffer. 1.5.6 Front– und Backbuffer Der Frontbuffer ist der Grafikspeicher, in dem sich ein Abbild des aktuell dargestellten Bildes befindet. Dieser Speicher wird kontinuierlich ausgelesen und an den Monitor gesendet. Würde man nun unmittelbar in diesen Speicher schreiben, 3 so würde es zwangsläufig dazu kommen, dass der Monitor beispielsweise die obere Hälfte des alten Bildes bereits angezeigt hat, dann würde (in den Puffer) gezeichnet werden und dann die untere Hälfte des Bildes angezeigt. Damit dies nicht geschieht, wird nicht in den Frontbuffer gezeichnet sondern in einen zweiten Buffer gleicher Größe, den sogenannten Backbuffer. Sobald das vollständige Bild im Backbuffer steht werden Back– und Frontbuffer getauscht und das neue Bild angezeigt. 1.5.7 Depthbuffer Im Depthbuffer oder auch Tiefenpuffer werden Tiefeninformationen für jedes Pixel gespeichert. Konkret existiert also für jedes Pixel ein Eintrag in diesem Depthbuffer, der die Entfernung des konkreten Pixels zur Kamera angibt. 4 2 Transformationen in Direct3D [Sch06] 2.1 Transformationspipeline Für die Transformation und Beleuchtung wird eine sogenannte Pipeline benutzt. In einzelnen Schritten werden die Dinge abgearbeitet, die benötigt werden, um ein abstraktes Modell als etwas grafisches am Monitor darzustellen. Hier wird näher auf die hierzu benötigten Transformationsmatrizen eingegangen. Wichtig zu beachten ist die Tatsache, dass bei Direct3D das Koordinatensystem immer im Linkssystem angegeben wird. (im Gegensatz zum Rechtssystem bei OpenGL) Standartmäßig befindet sich die Kamera also im Koordinatenursprung mit Blick entlang der positiven z–Achse. 2.1.1 World–Matrix Die World–Matrix transformiert von den Modellkoordinaten in die Weltkoordinaten. Der dazugehörige Direct3D–Funktionsaufruf lautet SetTransform(D3DTS_WORLD, &TransformationsMatrix); 2.1.2 View–Matrix Die Welt wird durch die Kamera wahrgenommen. Diese ist definiert durch ihre Position und ihre Blickrichtung. 1 Die Objekte werden mit Hilfe einer View–Matrix in das Kamerakoordinatensystem transformiert. SetTransform(D3DTS_VIEW, &ViewMatrix); Anstatt die Kamera zu bewegen, wir diese im Ursprung mit Blick entlang der positiven Z–Achse positioniert und die Objekte um sie herum werden in inverser Kamerarichtung bewegt. Soll beispielsweise die Kamera um 50◦ rotieren, so rotiert das Objekt einfach um –50◦ . Die View–Matrix entspricht der inversen Kameratransformationsmatrix 2.1.3 Projection–Matrix Durch die Projektionstransformation wird die 3D–Szene auf 2D–Dimension herunterprojeziert und so auf dem Bildschirm darstellbar. SetTransform(D3DTS_PROJECTION,&ProjectionMatrix); Der sichtbare Ausschnitt einer 3D–Szene (abhängig von der Kamera) wird durch das Viewing Volume 2 beschrieben. 5 Abbildung 1: Kamerabeschreibung Abbildung 2: ViewVolume 6 Direct3D erzeugt durch folgende Funktion die Projektions–Matrix D3DXMatrixPerspectiveFovLH( &matProj, 45*D3D_PI/180, Aspect, 1.0f, SizeOfUniverse); Funktionsparameter D3DXMATRIX * D3DXMatrixPerspectiveFovLH( D3DXMATRIX * pOut, //AusgabeMatrix FLOAT fovy, //Blickwinkel FLOAT Aspect, //SeitenVerhältnis FLOAT zn, //Near-Clipping-Plane FLOAT zf //Far-Clipping-Plane); 7 3 Programmieren mit Direct3D [Rie06] Da Direct3D sprachenunabhängig ist kann jede Programmiersprache genutzt werden um damit zu arbeiten. Am häufigsten wird hierzu C++ verwendet. Auch in dieser Einführung sind alle Codebeispiele in C++ implementiert. 3.1 Vorbereitungen Um mit Direct3D arbeiten zu können, muss zunächst ein ganz normales Windows– Fenster erzeugt werden. Dieses kann durch die normalen Befehle der Windows– API geschehen und wird hier nicht näher erläutert. Nähere Informationen dazu sind in diesem[del] Tutorial zu finden. • Initialisiertes Windowsfenster (Pre DirectX Tutorial) • Installiertes Microsoft DirectX SDK (442 MB) (1) • Programmierumgebung zu gewählter Programmiersprache (Sprachunabhängig, bevorzugt C++ mit Microsoft Visual Studio) • DirectX–Bibliotheken aus SDK einbinden (d3d9.lib, d3dx9.lib) • DirectX–Header aus SDK einbinden (d3d9.h, d3dx9.h) 3.2 Direct3D initialisieren Mit dem Aufruf der Funktion Direct3DCreate9 wird ein Direct3D–Objekt erzeugt und eine Zeiger auf die Schnittstelle IDirect3D9 zurückgeliefert. Diese Schnittstelle ermöglicht den Zugriff auf das Direct3D–Hauptobjekt und das Erstellen eines Direct3D–Device–Objekts. In diesem Fall wird der Einfachheit halber mit Standardwerten initialisiert. //Zeiger auf das Interface IDirect3D9 LPDIRECT3D9 pxD3D; //Erstellen des Direct3D-Hauptobjekts pxD3D = Direct3DCreate9(D3D_SDK_VERSION); //Zeiger auf das Direct3D-Device LPDIRECT3DDEVICE9 pxD3DDevice; //Parameter der Anzeige D3DPRESENT_PARAMETERS xD3DPresentParameters; //Nun müssen noch die Parameter der Anzeige gesetzt werden //Fenster-Modus xD3DPresentParameters.Windowed = true; //Anzahl der Backbuffer xD3DPresentParameters.BackBufferCount = 1; //Backbuffer durch Vertauschen zum Frontbuffer machen 8 xD3DPresentParameters.SwapEffect = D3DSWAPEFFECT_FLIP; //Backbuffer kann verriegelt werden, d.h. man kann darauf zeichnen xD3DPresentParameters.Flags =D3DPRESENTFLAG_LOCKABLE_BACKBUFFER; //Erstellen des Direct3D Devices if(FAILED(pxD3D -> CreateDevice( D3DADAPTER_DEFAULT, //Adapter D3DDEVTYPE_HAL, //Device-Typ hMainWindow, //Handle des Fensters D3DCREATE_HARDWARE_VERTEXPROCESSING, &xD3DPresentParameters, //Parameter &pxD3DDevice))) //Ergebnis { ...Fehlerbehandlung } 3.3 3.3.1 Zeichnen einfacher Formen Rendern in 2D Alle zu zeichnenden Objekte müssen Direct3D als Liste von Punkten übergeben werden und werden dann entsprechend der angegebenen Art“ zu Primitiven ver” bunden. (z.B. TriangleStrip für Dreiecke) Primitive sind Dreiecke, Linien und Punkte. 3.3.2 Der Viewport3 Der Viewport ist der Teil des Bildschirms, in dem die Szene dargestellt wird – in dem also gezeichnet werden muss. (das muss nicht unbedingt der ganze Bildschirm sein). Alle Attribute zum Viewport werden in einer Struktur namens D3DVIEWPORT9 gespeichert. Der Zugriff auf diese Struktur erfolgt mit GetViewport und SetViewport. In dieser Struktur enthalten sind die Kordinaten der linken oberen Ecke (X und Y) und die Größe des Viewport (Width, Height) in Bildschirmkoordinaten. 3.4 Flexible Vertex Format – FVF Punkte werden in Direct3D als Vertexe definiert. Hierbei besteht eine Vielzahl an Möglichkeiten, welche Informationen in einem solchen Vertex enthalten sein können. Um möglichst effektiv zu arbeiten, hat man in Direct3D die Möglichkeit mit Hilfe des Flexible Vertex Formats eigene Vertex–Strukturen zu definieren. Dies hat den Vorteil, dass keine unnötigen Informationen zu einem Vertex gespeichert und verarbeitet werden müssen. Beispiel – Eine einfach Vertexstruktur in 2D, bei der zu jedem Vertex die Position und Farbe gespeichert wird. 9 Abbildung 3: Der Viewport struct D3DVERTEX_2D { float x, y, z; //Punktkoordinaten D3DCOLOR xColor; //Punktfarbe }; Hat man nun eine eigene (FVF–)Struktur beschrieben, muss man deren Aufbau noch in Direct3D definieren. #define D3DFVF_2D (D3DFVF_XYZ | D3DFVF_DIFFUSE) 3.5 Geometrie erzeugen – 2D Hier werden exemplarisch 3 Vertexe im vorher selbst definierten FVF D3DVERTEX 2D initialisiert um sie anschließend zu einem Dreieck zu verbinden. D3DVERTEX_2D ax2DVertices[] = { {0.0, 600.0, 0.0, 1.0, D3DCOLOR_XRGB(255,0,0)}, {400.0, 0.0, 0.0, 1.0, D3DCOLOR_XRGB(0,255,0)}, {800.0, 600.0, 0.0, 1.0, D3DCOLOR_XRGB(0,0,255)} } 10 //links unten //mitte oben //rechts unten Um nun ein Dreieck anhand der vorher definierten Geometrie(D3DVERTEX 2D) und FVF (D3DFVF 2D) zu erzeugen, müssen folgende Schritte auf dem D3DDevice ausgeführt werden pxD3DDevice->BeginScene(); pxD3DDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(255, 255, 255), 1.0f, 0); //Vertex-Format setzen pxD3DDevice->SetFVF(D3DFVF_2D); //Dreieck zeichnen pxD3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLELIST, 1, &ax2DVertices, sizeof(D3DVERTEX_2D)); pxD3DDevice->EndScene(); //Szene anzeigen pxD3DDevice->Present(NULL, NULL, NULL, NULL); 3.6 3D Im dreidimensionalen Raum befinden sich alle sichtbaren Elemente im sogenannten ViewFrustum, ähnlich einem Pyramidenstumpf. Die Kamera befindet sich hierbei an der Spitze der Pyramide“. ” 3.7 Szene Initialisieren Punkte aus 3D werden durch Transformationen so geändert, dass sie in ViewportKoordinaten vorliegen. 1. Transformation Die lokalen Koordinaten werden in die absoluten Weltkoordinaten umgewandelt. (Transformation wird durch Weltmatrix angegeben – Koordinaten werden verschoben, skaliert oder gedreht damit sie an der gewünschten Position in der Szene liegen) 2. Transformation Beschreibt um die Kamera durch Position und Richtung 3. Transformation Die Projektionsmatrix wandelt die Kamera-Koordinaten in Viewport-Koordinaten um. Diese drei Transformationen werden in einer Matrix vereint. Berechnet wird diese Matrix mit der Funktion D3DXMatrixPerspectiveFovLH. Diese erstellt anhand des Sichtfeldes eine Projektionsmatrix im Linkshand–Koordinatensystem Parameter von D3DXMatrixPerspectiveFovLH 1. Zeiger auf eine Matrix – hier wird das Ergebis rein geschrieben 11 2. Sichtfeld des Radians 3. Seitenverhältnis Breite/Höhe (Wert kommt aus Viewport – GetViewport()) 4. Abstand Kamera – Near Clipping Plane 5. Abstand Kamera – Far Clipping Plane Die so erzeugte Matrix wird anschließend mit der Methode SetTransform() an Direct3D übermittelt. 3.8 Geometrien in 3D Im dreidimensionalen Raum wird auch hier wie bereits oben beschrieben zunächst ein FVF definiert, diese an Direct3D übergeben und anschließend mit Werten gefüllt. struct D3DVERTEX_3D { float x, y, z; //Punktkoordinaten D3DCOLOR xColor; //Punktfarbe }; #define D3DFVF_3D (D3DFVF_XYZ | D3DFVF_DIFFUSE) D3DVERTEX_3D ax3DVertices[] = { {-0.75, -1.0, 3.0, D3DCOLOR_XRGB(255,0,0)}, {0.0, 1.0, 3.0, D3DCOLOR_XRGB(0,255,0)}, {0.75, -1.0, 3.0, D3DCOLOR_XRGB(0,0,255)}, }; 3.9 Animation durch Transformationen In diesem Abschnitt wird exemplarisch gezeigt, wie man das soeben erstellte Dreieck um die eigene Achse rotiert. 1. Erzeugen der Weltmatrix Die Transformations-Matrix wird aus verschiedenen einzelnen Matrizen erstellt. D3DMATRIX xMatrixRotY; D3DMATRIX xMatrixTranslate; D3DMATRIX xWorldMatrix; //Einheitsmatrix //Einheitsmatrix //Einheitsmatrix D3DXMatrixRotationY(&xMatrixRotY, fAngle); D3DXMatrixTranslation(&xMatrixTranslate, 0.0f, 0.0f, 3.0f); 12 //D3DXMatrixMultiply erzeugt die fertige Transformationsmatrix D3DXMatrixMultiply( &xWorldMatrix, &xMatrixRotY, &xMatrixTranslate); Hier werden erst 3 Matrizen erstellt. Als nächstes wird xMatrixRotY mit der Methode D3DXMatrixRotationY mit Werten zum Drehen um die Y-Achse gefüllt. Danach wird die zweite Matrix mit Translationswerten gefüllt. Zum Schluss wird das Produkt der beiden Matrizen in die Matrix xWorldMatrix geschrieben. D3DXMatrixMultiply( &zu schreibende Matrix, &M1, &M2); Die Reihenfolge der Multiplikation ist ausschlaggebend – in diesem Fall wird erst gedreht und dann verschoben. 2. Setzen der Weltmatrix Die oben erzeugte Weltmatrix (xWorldMatrix) wird nun an Direct3D übergeben pxD3DDevice->SetTransform(D3DTS_WORLD, &xWorldMatrix); 13 Literatur [del] [email protected]. Pre directx tutorial - einführung in die microsoft winapi. http://www.codeworx.org/directx_tuts.php. [Rie06] Malte Ried. Programmieren mit directx. http:// homepages.fh-giessen.de/˜hg13419/lehre/ProgDX/ ProgDX-Skript.pdf, November 2006. [Sch06] David Scherfgen. Transformationen. In 3D-Spieleprogrammierung mit DirectX 9 und C++. Hanser Fachbuchverlag, 2006. [Wik08] Wikipedia. Directx. DirectX, 2008. http://de.wikipedia.org/wiki/ 14