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