Kapitel 8: DirectX Graphics
←
→
Transkription von Seiteninhalten
Wenn Ihr Browser die Seite nicht korrekt rendert, bitte, lesen Sie den Inhalt der Seite unten
Kapitel 8: DirectX Graphics Nochmal etwas Theorie: ● Arbeitsweise einer 3D Engine ● Umrechnen von Welt- in Bildschirmkoordinaten ● Objekt-Culling ● Doppel- und Tiefenpufferung Yipieh! Es geht los: ● Anwendungsgerüst für das Projekt ● Initialisierung von DirectX Graphics ● Textausgabe ● Punkte, Linien und Kreise zeichnen ● Einfaches Texture Mapping
Kapitel 8: Die 3 Anwendungen Teil 1 Beispiel 'FirstContact': Das modularisierte Anwendungsgerüst Beispiel 'Punkte, Linien und Kreise': Die Implementierung von Primitiven in das Gerüst Teil 2 Beispiel 'Texturen': Die Implementierung von Textur-Funktionalität
Was ist DirectX? ● ist eine Sammlung von Komponenten, mit denen sich unter Windows lauffähige Multimediaanwendungen entwickeln lassen ● ist sprachunabhängig ● besitzt einzelne Komponenten (Component Object Model): ● DirectX Graphics (zusammengefaßt aus den früheren Komponenten Direct3D und DirectDraw) ● DirectX Audio ● DirectInput ● DirectPlay ● DirectShow ● DirectSetup In Kapitel 8 befassen wir uns mit DirectX Graphics.
Am Rande erwähnt: Das DirectX SDK Es könnte hilfreich sein, in der mitgelieferten Dokumentation des SDK zu stöbern... Microsoft schenkt uns den DirectX Sample Browser ...
Am Rande erwähnt: Das DirectX SDK ... und die vollständige Dokumentation. Dort findet man z.B. Beschreibungen von Funktionen ...
Am Rande erwähnt: Das DirectX SDK ... oder Erklärungen für interne Funktionsweisen.
Die Transformations- und Beleuchtungspipeline Für die Transormation 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.
Welttransformation Die Transormation von den Modellkoordinaten in die Weltkoordinaten ist die Welttransformation. Matrix multiplication that converts vertices from model space to world space. Die Welttransformation wird von DirectX durch diesen Aufruf erzeugt: SetTransform(D3DTS_WORLD, &TransformationsMatrix); HRESULT SetTransform ( D3DTRANSFORMSTATETYPE State, CONST D3DMATRIX * pMatrix ); Header: Declared in D3d9.h
Sichttransformation Die gesamte 3D Szene wird durch die Kamera (= Player) wahrgenommen: ● Kameraposition ● Blickrichtung Die Objekte werden mit Hilfe einer Sichtmatrix (View- Matrix) in den Kameraraum transformiert. Die Sichttransformation wird (wie die Welttransformation auch) von DirectX durch diesen Aufruf erzeugt: SetTransform(D3DTS_View, &ViewMatrix); Man beachte, dass hier wieder SetTransform, allerdings mit anderem Transformationsstatetyp, aufgerufen wird. siehe in der SDK-Dokumentation: D3DTRANSFORMSTATETYPE bzw. SetTransform (Idirect3DDevice9)
Sichttransformation: Prinzipielle Funktionsweise Anstatt die Kamera zu bewegen, wird die Kamera im Ursprung des Weltkoordinatensystems mit Blickrichtung in die positive z-Achse aufgestellt. Die Kamera wird nicht bewegt oder gedreht, sondern die Objekte um sie herum in inverser Kamerabewegung. Beispiel: ● Die Kamera soll sich eigentlich um 30° drehen, also wird das Objekt um - 30° gedreht. ● Die Kamera soll sich eigentlich auf ein Objekt zubewegen, also wird das Objekt auf die Kamera zubewegt. Die View-Matrix entspricht der inversen Kamera- Transformationsmatrix.
Sichttransformation: Vorgehen 1/2 Im Prinzip wird die Sichttransformation so durchgeführt: ● die Kamera-Transformationsmatrix berechnen ● die Kamera-Transformationsmatrix invertieren ● Sichtransformation durchführen Aber das ist rechenaufwendig. Darum geht man etwas anders vor ... Die Kamera wird durch 4 Vektoren für deren Position und Ausrichtung beschrieben. Mit diesen Vektoren kann man dann die View-Matrix erstellen.
Sichttransformation: Vorgehen 2/2 DirectX erzeugt mit folgender Funktion eine (linkshändige) View-Matrix: D3DXMatrixLookAtLH(&ViewMatrix, &PlayerPosition, &PlayerFlugrichtung, // Look &PlayerVertikale // Up ); D3DXMATRIX * D3DXMatrixLookAtLH( D3DXMATRIX * pOut, CONST D3DXVECTOR3 * pEye, CONST D3DXVECTOR3 * pAt, CONST D3DXVECTOR3 * pUp ); Header: Declared in D3dx9math.h Setting Up a View Matrix The D3DXMatrixLookAtLH and D3DXMatrixLookAtRH helper functions create a view matrix based on the camera location and a look-at point. The following example creates a view matrix for left-handed coordinates. D3DXMATRIX out; D3DXVECTOR3 eye(2,3,3); D3DXVECTOR3 at(0,0,0); D3DXVECTOR3 up(0,1,0); D3DXMatrixLookAtLH(&out, &eye, &at, &up);
Sichttransformation: Optimierung Die (inverse) Verschiebung der Kamera kann auch direkt in die Welttransformation aller Objekte berechnet werden. Beispiel 1: Man hat eine Szene mit 2000 eigentlich nicht bewegten Objekten, z.B. fixe Sterne im Hintergrund. Die Sterne werden durch die Kamerabewegung bewegt, aber das will man garnicht. Also muß man diese Bewegung wieder zurückberechnen und das ist schlecht. Beispiel 2: 2D Objekte (Rauch, Partikel etc.) werden anhand von sogenannter Billboard-Matrizen berechnet. Auch hier: weniger Rechenaufwand, wenn die Kamerabewegung der Billboard-Matrix nicht in der View-Matrix berechnet wird, sondern direkt in der Welttransformation der Billboard- Matrix.
Projektionstransformation In der Projektionstransformation wird die 3D-Szene auf 2 Dimensionen herunterprojeziert und so auf dem Bildschirm darstellbar. Diese Projektion wird wieder durch SetTransform (hier mit dem State D3DTS_PROJECTION) durchgeführt: SetTransform(D3DTS_PROJECTION, &ProjectionMatrix); You can think of the projection transformation as controlling the camera's internals; it is analogous to choosing a lens for the camera. Bevor man die Projektionstransformation durchführen kann, muß eine Projektionsmatrix erstellt werden.
Projektionstransformation: Arbeitsweise der Projektionsmatrix 1/3 Der sichtbare Ausschnitt einer 3D-Szene (abhängig von der Kamera) wird durch das Viewing Volume beschrieben. Es Viewing Volume besitzt: ● eine maximale Sichtweite: Far Clipping Plane ● eine minimale Sichtweite: Near Clipping Plane ● ein Blickfeld (der Sichtwinkel): Field of View (FOV)
Projektionstransformation: Arbeitsweise der Projektionsmatrix 2/3 Durch die Projektionsmatrix wird jedes Objekt der 3D Szene auf der Projektionsfläche abgebildet. Die Projektionsmatrix: ● z-Achse ist Blickrichtung ● die Eckpunkte der Clipping Planes als Vertices verwenden ● ein Punkt auf der z-Achse als Vertex verwenden ● für das FOV wird als Spezialfall 180° festgelegt ● der Abstand der Far Clipping Plane strebt gegeb unendlich ● der Abstand zwischen Projektionsfläche und Near Clipping Plane muß >= 1.0 sein, sonst kommt es zu Projektionsfehlern
Projektionstransformation: Arbeitsweise der Projektionsmatrix 3/3 DirectX erzeugt durch folgende Funktion die Projektionsmatrix: D3DXMatrixPerspectiveFovLH(&matProj, 45*D3D_PI/180, Aspect, 1.0f, SizeOfUniverse); siehe SDK-Dokumentation: D3DXMATRIX * D3DXMatrixPerspectiveFovLH( D3DXMATRIX * pOut, FLOAT fovy, FLOAT Aspect, FLOAT zn, FLOAT zf ); pOut [in, out] Pointer to the D3DXMATRIX structure that is the result of the operation. fovy [in] Field of view in the y direction, in radians. Aspect [in] Aspect ratio, defined as view space width divided by height. zn [in] Z-value of the near view-plane. zf [in] Z-value of the far view-plane.
(Projektionstransformation) Clipping Clipping bedeutet, dass die nicht sichtbaren Bereiche eines Polygons weggeschnitten werden. Nach der Projektionstransformation werden die Polygone gegen die 6 Flächen des Viewing Volumes geclippt. Anmerkung: Aus Effizienzgründen wird das Viewing Volume in einen Quader transformiert.
Umrechnung von Welt- in Bildschirmkoordinaten 1/3 Hier wird eine Funktions vorgestellt, mit der die Bildschirmkoordinaten eines Objektes berechnet werden können. Beispiel: Ein Angriffsziel im 3D Raum wird mit Mauszeiger ausgewählt. ● 1.Schritt: Bildschirmkoordinaten des Mauszeigers und des Angriffziels ermitteln. ● 2.Schritt: diese Koordinaten miteinander vergleichen ● 3.Schritt: Bei Übereinstimmung wird das Objekt selektiert Diese Umrechnung geschieht unter Verwendung der zugehörigen Sicht- und Projektionsmatrizen.
Umrechnung von Welt- in Bildschirmkoordinaten 2/3 Wie geht das? Für jedes Frame muss diejenige Matrix berechnet werden, die für die Transformation der Weltkoordinaten eines Objektes in die entsprechenden Bildschirmkoordinaten verantwortlich ist: ViewProjectionMatrix = mathView*mathProj; (Diese Anweisung wird später im letzten Beispiel 'Simulation' in 3D_ScenarioClasses.h eingebaut.) Um den Ortsvektor eines Objektes in die Viewportkoordinaten zu transformieren, bedarf es einer Matrizenmultiplikation. Weil der jeweilige Ursprung der Viewportkoordinaten und der Bildschirmkoordinaten nicht übereinstimmt, muss anschließend ein klein wenig umgerechnet werden. (siehe Funktion auf nächster Folie)
Umrechnung von Welt- in Bildschirmkoordinaten 3/3 Die Funktion wird später in CoolMath.h implementiert und sieht so aus: inline void Calculate_Screen_Coordinates( float* pScreenX, float* pScreenY, D3DXVECTOR3* pVec) { tempX = pVec->x; tempY = pVec->y; tempZ = pVec->z; tempX2 = ViewProjectionMatrix._11*tempX + ViewProjectionMatrix._21*tempY + ViewProjectionMatrix._31*tempZ + ViewProjectionMatrix._41; tempY2 = ViewProjectionMatrix._12*tempX + ViewProjectionMatrix._22*tempY + ViewProjectionMatrix._32*tempZ + ViewProjectionMatrix._42; tempW2 = ViewProjectionMatrix._14*tempX + ViewProjectionMatrix._24*tempY + ViewProjectionMatrix._34*tempZ + ViewProjectionMatrix._44; tempInvW2 = 1.0f/tempW2; *pScreenX = (1.0f + (tempX2*tempInvW2))*0.5f*screenwidth; *pScreenY = (1.0f - (tempY2*tempInvW2))*0.5f*screenheight; }
Object-Culling 1/3 Es braucht Rechenzeit, um ein Objekt durch die Transformations- und Beleuchtungspipeline zu schicken und anschließend zu clippen. Mit Object-Culling sondert man daher vorher die sowieso nicht sichtbaren Teile aus. Die einfache Variante des Culling prüft, ob ein Objekt vor oder hinter dem Betrachter (bzw. der Kamera) ist: tempFloat = D3DXVec3Dot(&Ortsvektor,PlayerBlickrichtung); Der Rückgabewert ist das Skalarprodukt der reingegebenen Werte. if(tempFloat < 0.0f) ... und prüfen visible = FALSE; Objekt hinter dem Spieler Statistisch sind jetzt schonmal etwa 50% der Objekte ausgesondert. Und nun weg mit dem Rest ...
Object-Culling 2/3 Jetzt werden die Objekte ermittelt, die uns durch die Lappen gegangen sind. Es geht entweder so: Den Betrag des Ortsvektors ermitteln: Betrag = Calculate3DVectorLength(&Ortsvektor); So erhält man den Kosinus des Blickwinkels: tempFloat = tempFloat/Betrag; Bei einem Blickfeld von angenommenen 90° sind Objekte nur in einem Blickwinkel von +45° bis – 45° sichtbar. Aus Sicherheitsgründen nehmen wir einen höheren Wert, nämlich jeweils 53°. (Sonst kann es zu vorzeitigem Culling kommen und das ist nicht schön.) if(tempFloat < 0.6f) visible = FALSE; 0.6f ist cos(53°) bzw. cos(-53°). Aber die Berechnung ist aufwendig, also optimierbar. Alles Beschriebene geht aber zusammengefaßt auch anders, sodass man eine schnellere Berechnung erhält. Und das geht so ...
Object-Culling 3/3 Um die Wurzelberechnung einzusparen, kann man mit dem quadratischen Betrag und dem quadratischen Skalarprodukt arbeiten. (Das ist einfach schneller.) Der Wert der if-Abfrage muss natürlich auch der quadratische Wert von cos(53°) sein, also nicht 0.6f sondern 0.36f.
Doppelpufferung Ein Bild muss irgendwie auf den Monitor gelangen. Im Speicher (RAM oder VRAM) wird ein Bild als linearer Array gespeichert. Jedes Array-Element entspricht einem bestimmten Bildpunkt und speichert als Inhalt den dazugehörigen Farbwert. Darum ist der Speicherbedarf abhängig von der eingestellten Farbtiefe. Bei der Bilderzeugung wird direkt auf einen bestimmten Bereich des VRAM zugegriffen. (siehe InitD3D(): Abfrage der Farbtiefe ff.) Zur Vermeidung von Doppelbild- oder Flacker-Effekten wird die sogenannte Doppelpufferung verwendet. Das gesamte Bild wird in einen zweiten Speicherbereich, den sogenannten Backbuffer, aufgebaut und anschließend auf die Display-Surface kopiert (Fenster) oder geflippt (Fullscreen). Flipping ist schneller als Kopieren. Dabei werden einfach die Zeiger vertauscht: Der Backbuffer wird zur neuen Display-Surface und umgekehrt.
Tiefenpufferung Verdeckte Oberflachen sind abhängig von der Anordnung der Polygone im Raum zur Kamera: Objekte, die näher zur Kamera liegen, können Teile der Objekte, die weiter weg sind, verdecken. Um dies in den Griff zu bekommen, benutzt man die Tiefenpufferung. (Verfahren zur Tiefenpufferung werden durch DirectX zur Verfügung gestellt.) Beispiel z-Buffer: Funktioniert wie ein Array mit den gleichen Dimensionen der Bildschirmauflösung. Darin sind die Tiefeninformationen der Polygone auf Pixelbasis gespeichert. Vor dem Rendern wird der gesamte z-Buffer mit dem maximal möglichen Tiefenwert belegt. Beim Rendern eines Polygons wird pro Pixel überprüft, ob der an der entsprechenden Position im z-Buffer gespeicherte Tiefenwert größer oder kleiner als der Tiefenwert des Pixels ist. Ist der gespeicherte Tiefenwert größer als der des Pixels, wird er mit dessen Tiefenwert überschrieben.
Anwendungsgerüst Das Anwendungsgerüst ist stark modularisiert und beinhaltet erstmal eine scheinbar unverhältnismäßig große Menge an Dateien. Wenn man genauer hinschaut ...
Anwendungsgerüst ... die vorgestellten Funktionen aus Kapitel 1 ...
Anwendungsgerüst ... das stellt uns DirectX zur Verfügung ...
Anwendungsgerüst ... Header-Dateien mit Deklaration von globalen Variablen und Externals ...
Anwendungsgerüst ... und schon sieht das Ganze nicht mehr ganz so wild aus.
„Hello World“ in DirectX Das Projekt „First Contact“ ist hier unsere DirectX- Entsprechung für ein herkömmliches „Hello World“ Programm. Die eigentliche Funktionalität des Programms ● DirectX initialisieren ● Text ausgeben Die zentralen Dateien sind (hier) Space3D.cpp und der Header Space.h. In Space3D.h werden alle verwendeten Programmmodule eingebunden und die in Space3D.cpp definierten Funktionen deklariert. Durch diese Modularisierung verfügbare Funktionalität: ● DirectX-Makros und Helperfunktionen ● Textausgabe ● eine lauffähige DirectX-Anwendung ● mathematische Funktionen ● Funktionen für die Ausrichtung von Billoards ● Spielsteuerung und Sound ● Klassen für Erzeugung von und Umgang mit Texturen
Programmablauf GameShell GameRoutines void GameInitialisierungsRoutine(void) { InitRenderOptions(); // in GameRoutines InitSpieleinstellungen(); InitD3D(); Init_View_and_Projection_Matrix(); } void GameCleanUpRoutine(void) { CleanUpD3D(); } void GameMainRoutine(void) { [ ... ] Show_3D_Scenario(); [ ... ] } Space3D
Programmablauf Space3D.h #include #include #include "d3dutil.h" #include "d3dfont.h" #include "dxutil.h" #include "DXSubstitutions.h" #include "GameExternals.h" #include "D3DGlobals.h" #include "CoolMath.h" #include "tempGlobals.h" #include "TextureClasses.h" #include "GameGlobals.h" #include "VertexFormats.h" #include "BillboardFunctions.h" void InitSpieleinstellungen(void); void CleanUpD3D(void); void InitD3D(void); void Init_View_and_Projection_Matrix(void); void Show_3D_Scenario(void);
Programmablauf Space3D.cpp inline void Player_Flugdrehung(float winkel) inline void Player_Horizontaldrehung(float winkel) inline void Player_Vertikaldrehung(float winkel) 3 void Init_View_and_Projection_Matrix(void) 2 void InitD3D(void) 5 void CleanUpD3D(void) 1 void InitSpieleinstellungen(void) 4 void Show_3D_Scenario(void)
1. InitSpieleinstellungen() void InitSpieleinstellungen(void) { FILE* pfile; if((pfile = fopen("SpielEinstellungen.txt","r")) == NULL) Game_Shutdown(); fscanf(pfile,"%s", strBuffer ); fscanf(pfile,"%f", &SizeOfUniverse ); if( SizeOfUniverse < 1000.0f ) SizeOfUniverse = 1000.0f; SizeOfUniverseSq = SizeOfUniverse*SizeOfUniverse; fclose(pfile); } Funktion liest alle relevanten Grafikeinstellungen aus SpielEinstellungen.txt heraus. Inhalt SpielEinstellungen.txt: Sichtweite_3DScenario: 2000
2. InitD3D() : Pseudocode void InitD3D(void) { Kordinaten des Bildschirmmittelpunktes speichern Erzeugung des Direct3D-Objektes Aktuellen Display-Modus für eine Fensteranwendung feststellen Ein geeignetes Pixelformat für eine Fullscreen-Anwendungen suchen Präsentationsparameter festlegen z-Buffer anlegen Warten, bis der vertikale Elektronenstahl- Rücklauf des Monitors beendet ist. Surface-Flipping erfolgt synchron zum Aufbau des Monitorbilds. Grafikkarten-Objekt anlegen, über das man auf die Treiber der primären Grafikkarte zugreifen kann Verarbeiten der ausgelesenen RenderingOption und Etablieren einer Vertexverarbeitung SamplerStates für 2 Texturstufen festlegen RenderStates festlegen 2 Objekte die für die Textausgabe initialisieren zugehörige Klasse: d3dfont.cpp }
2. InitD3D() 1/9 void InitD3D(void) { Kordinaten des Bildschirmmittelpunktes speichern: screenwidth und screenheight deklariert in GameRoutines.h und definiert in GameRoutines.cpp centerX und centerY deklariert in D3DGlobals.h centerX = screenwidth/2.0f; centerY = screenheight/2.0f; Kordinaten des Bildschirmmittelpunktes speichern 1.Schritt Erzeugung des Direct3D-Objektes Erzeugung des Direct3D-Objektes mit (selbstgwähltem) Namen g_pD3D: Aktuellen Display-Modus für eine Fensteranwendung feststellen siehe D3DGlobals.h: LPDIRECT3D9 g_pD3D = NULL; Der Funktion Direct3DCreate9 wird einfach nur als Parameter die Konstante Ein geeignetes Pixelformat für eine Fullscreen- D3D_SDK_VERSION übergeben. Anwendungen suchen g_pD3D = Direct3DCreate9(D3D_SDK_VERSION); Präsentationsparameter festlegen Grafikkarten-Objekt anlegen, über das man auf die Treiber der primären Grafikkarte zugreifen kann Aktuellen Display-Modus für eine Fensteranwendung feststellen: (Vorbereitung 2.Schritt: Checken, ob Fenster oder Fullscreen) Verarbeiten der ausgelesenen RenderingOption und Etablieren einer Vertexverarbeitung Falls das Programm als Fensteranwendung gestartet werden sollte (g_bFullScreen SamplerStates für 2 Texturstufen festlegen == FALSE), dann wird der Displaymodus (Auflösung, Farbtiefe und Format) abgefragt und in der Variable d3ddm vom Typ D3DDISPLAYMODE abgespeichert. RenderStates festlegen 2 Objekte die für die Textausgabe initialisieren D3DDISPLAYMODE d3ddm; if(g_bFullScreen == FALSE) g_pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm); ...
2. InitD3D() 2/9 Falls das Programm Fullscreen gestartet werden sollte: ein geeignetes Pixelformat für eine Fullscreen-Anwendungen suchen; grober Überblick (Erklärung siehe nächste Folie) if(Farbtiefe == 16) { if(SUCCEEDED( g_pD3D->CheckDeviceType( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, D3DFMT_R5G6B5, D3DFMT_R5G6B5, FALSE))) Kordinaten des Bildschirmmittelpunktes speichern { Erzeugung des Direct3D-Objektes g_d3dfmtFullscreen = D3DFMT_R5G6B5; } Aktuellen Display-Modus für eine Fensteranwendung else if(SUCCEEDED( g_pD3D->CheckDeviceType( feststellen D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, Ein geeignetes Pixelformat für eine Fullscreen- D3DFMT_X1R5G5B5, Anwendungen suchen D3DFMT_X1R5G5B5, Präsentationsparameter festlegen FALSE))) { Grafikkarten-Objekt anlegen, über das man auf die g_d3dfmtFullscreen = D3DFMT_X1R5G5B5; Treiber der primären Grafikkarte zugreifen kann } else if(SUCCEEDED( g_pD3D->CheckDeviceType( Verarbeiten der ausgelesenen RenderingOption und Etablieren einer Vertexverarbeitung D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, SamplerStates für 2 Texturstufen festlegen D3DFMT_X1R5G5B5, D3DFMT_A1R5G5B5, RenderStates festlegen FALSE))) { 2 Objekte die für die Textausgabe initialisieren g_d3dfmtFullscreen = D3DFMT_A1R5G5B5; } } else if(Farbtiefe == 32) { ... ... }
2. InitD3D() 3/9 Falls in Fullscreen Modus gestartet wird, müssen geeignete Display- und Backbufferformate gesucht werden, die von der Grafikkarte unterstützt werden. Das macht CheckDeviceType. Der Prototyp sieht so aus: HRESULT CheckDeviceType ( UINT Adapter, welche Grafikkarte? D3DDEVTYPE DeviceType, Hardwarebeschl./Softwareemu.? D3DFORMAT DisplayFormat, Display-Format? D3DFORMAT BackBufferFormat, Backbuffer-Format? BOOL Windowed Fenster oder Fullscreen? ); Kordinaten des Bildschirmmittelpunktes speichern Mit den DirectX-Makros SUCCEEDED() und FAILED() läßt sich mit dem Erzeugung des Direct3D-Objektes Rückgabewert HRESULT überprüfen, ob ein Funktionsaufruf erfolgreich ist Aktuellen Display-Modus für eine Fensteranwendung oder nicht. feststellen (Siehe D3DGlobals.h für Deklaration von D3DFORMAT g_d3dfmtFullscreen.) Ein geeignetes Pixelformat für eine Fullscreen- if(Farbtiefe == 16) Anwendungen suchen { Präsentationsparameter festlegen Grafikkarten-Objekt anlegen, über das man auf die if(SUCCEEDED( g_pD3D->CheckDeviceType( D3DADAPTER_DEFAULT, Treiber der primären Grafikkarte zugreifen kann D3DDEVTYPE_HAL, D3DFMT_R5G6B5, Verarbeiten der ausgelesenen RenderingOption und D3DFMT_R5G6B5, Etablieren einer Vertexverarbeitung FALSE))) SamplerStates für 2 Texturstufen festlegen { g_d3dfmtFullscreen = D3DFMT_R5G6B5; RenderStates festlegen } 2 Objekte die für die Textausgabe initialisieren ... ... ... else if(Farbtiefe == 32) { ... ... } Für Displayformate (D3DFMT_R5G6B5 etc.) siehe SDK: D3DFORMAT
2. InitD3D() 4/9 2.Schritt Präsentationsparameter festlegen: D3DPRESENT_PARAMETERS d3dpp; Struktur namens d3dpp erzeugen alle Parameter unten siehe SDK: D3DPRESENT_PARAMETERS ZeroMemory( &d3dpp, sizeof(d3dpp) ); d3dpp.Windowed = !g_bFullScreen; d3dpp.BackBufferCount = 1; z-Buffer anlegen: Kordinaten des Bildschirmmittelpunktes speichern If this value is TRUE, Direct3D will manage depth buffers for the application. d3dpp.EnableAutoDepthStencil = TRUE; Erzeugung des Direct3D-Objektes d3dpp.AutoDepthStencilFormat = D3DFMT_D16; // 16-Bit z-Buffer Aktuellen Display-Modus für eine Fensteranwendung feststellen if( g_bFullScreen ) Fullscreen? { Ein geeignetes Pixelformat für eine Fullscreen- Anwendungen suchen Art der Erzeugung von Wechsel Backbuffer/Display Surface d3dpp.SwapEffect = D3DSWAPEFFECT_FLIP; Präsentationsparameter festlegen The maximum rate at which the swap chain's back buffers Grafikkarten-Objekt anlegen, über das man auf die Treiber der primären Grafikkarte zugreifen kann can be presented to the front buffer. d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE; Verarbeiten der ausgelesenen RenderingOption und Etablieren einer Vertexverarbeitung d3dpp.hDeviceWindow = main_window_handle; d3dpp.BackBufferWidth = screenwidth; SamplerStates für 2 Texturstufen festlegen d3dpp.BackBufferHeight = screenheight; RenderStates festlegen d3dpp.BackBufferFormat = g_d3dfmtFullscreen; } 2 Objekte die für die Textausgabe initialisieren else Fenster? { d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE; d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; d3dpp.BackBufferFormat = d3ddm.Format; }
2. InitD3D() 5/9 3.Schritt Grafikkarten-Objekt anlegen, über das man auf die Treiber der primären Grafikkarte zugreifen kann RenderOption wird aus RenderingAndMore.txt in GameRoutines.cpp mit der Funktion InitRenderOptions(void) ermittelt. FILE* file; if((file = fopen("protokoll.txt","w")) == NULL) Game_Shutdown(); Kordinaten des Bildschirmmittelpunktes speichern if(RenderingOption == 0) Transform_And_Lightning_with_PureDevice(0) { Erzeugung des Direct3D-Objektes // Versuche, Pure Device zu etablieren, Aktuellen Display-Modus für eine Fensteranwendung // versuche, Hardware-Vertexverarbeitung zu etablieren, feststellen // versuche, gemischte Vertexverarbeitung zu etablieren, // Software-Vertexverarbeitung etablieren Ein geeignetes Pixelformat für eine Fullscreen- } Anwendungen suchen Präsentationsparameter festlegen else if(RenderingOption == 1) Transform_And_Lightning_without(1) { Grafikkarten-Objekt anlegen, über das man auf die // Versuche, Hardware-Vertexverarbeitung zu etablieren, Treiber der primären Grafikkarte zugreifen kann // versuche, gemischte Vertexverarbeitung zu etablieren, Verarbeiten der ausgelesenen RenderingOption und // Software-Vertexverarbeitung etablieren Etablieren einer Vertexverarbeitung } SamplerStates für 2 Texturstufen festlegen else if(RenderingOption == 2) Mixed_Transform_And_Lightning(2) { RenderStates festlegen // Versuche, gemischte Vertexverarbeitung zu etablieren, 2 Objekte die für die Textausgabe initialisieren // Software-Vertexverarbeitung etablieren } else if(RenderingOption == 3) Non_Transform_And_Lightning(3) { // Software-Vertexverarbeitung etablieren } fclose(file);
2. InitD3D() 6/9 Mit CreateDevice() wird ein Objekt erzeugt, über das auf die Grafikkarte zugegriffen werden kann. Staffelung nach Verarbeitungsgeschwindigkeit: 0- Pure Device Transform_And_Lightning_with_PureDevice 1- Hardware-Vertexverarbeitung Transform_And_Lightning_without_PureDevice 2- gemischte Vertexverarbeitung Mixed_Transform_And_Lightning 3– Software-Vertexverarbeitung Non_Transform_And_Lightning Kordinaten des Bildschirmmittelpunktes speichern Für einzelne Parameter siehe SDK-Dokumetation: CreateDevice Erzeugung des Direct3D-Objektes Aktuellen Display-Modus für eine Fensteranwendung Beispiel: feststellen // versuche, Pure Device zu etablieren Ein geeignetes Pixelformat für eine Fullscreen- if(SUCCEEDED(g_pD3D->CreateDevice( Anwendungen suchen Präsentationsparameter festlegen D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, Grafikkarten-Objekt anlegen, über das man auf die main_window_handle, Treiber der primären Grafikkarte zugreifen kann D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE, &d3dpp, Verarbeiten der ausgelesenen RenderingOption und Etablieren einer Vertexverarbeitung &g_pd3dDevice))) SamplerStates für 2 Texturstufen festlegen // versuche, Hardware-Vertexverarbeitung zu etablieren RenderStates festlegen if(SUCCEEDED(g_pD3D->CreateDevice( 2 Objekte die für die Textausgabe initialisieren D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, main_window_handle, D3DCREATE_HARDWARE_VERTEXPROCESSING, &d3dpp, &g_pd3dDevice)))
2. InitD3D() 7/9 SamplerStates für 2 Texturstufen festlegen [in] The sampler stage index [in] any member of the D3DSAMPLERSTATETYPE enumerated type [in] State value to set Verwendung von bilinearer Textur-Filterung: g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); Kordinaten des Bildschirmmittelpunktes speichern g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, Erzeugung des Direct3D-Objektes D3DTEXF_LINEAR); Aktuellen Display-Modus für eine Fensteranwendung ... ebenso für SetSamplerState mit index 1 feststellen Ein geeignetes Pixelformat für eine Fullscreen- Anwendungen suchen TexturStageStates für 2 Texturstufen einstellen: Präsentationsparameter festlegen Die zu verwendenen Texturkoordinaten festlegen: Grafikkarten-Objekt anlegen, über das man auf die Treiber der primären Grafikkarte zugreifen kann [in] Stage identifier of the texture for which the state value is set. Verarbeiten der ausgelesenen RenderingOption und [in] Texture state to set. This parameter can be any member of the Etablieren einer Vertexverarbeitung D3DTEXTURESTAGESTATETYPE enumerated type. SamplerStates für 2 Texturstufen festlegen [in] State value to set. RenderStates festlegen g_pd3dDevice->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, 2 Objekte die für die Textausgabe initialisieren 0); ... ebenso für SetTextureStageState mit index 1
2. InitD3D() 8/9 Prototyp: HRESULT SetRenderState( D3DRENDERSTATETYPE State, DWORD Value ); [in] Device state variable that is being modified. This parameter can be any member of the D3DRENDERSTATETYPE enumerated type. [in] New value for the device render state to be set. The meaning of this parameter is dependent on the value specified for State. For example, if State were D3DRS_SHADEMODE, the second parameter would be one member of the D3DSHADEMODE enumerated type. Kordinaten des Bildschirmmittelpunktes speichern RenderStates festlegen: Festlegung der Steuerung der Renderzustände Erzeugung des Direct3D-Objektes Culling im Uhrzeigersinn: Aktuellen Display-Modus für eine Fensteranwendung g_pd3dDevice->SetRenderState( D3DRS_CULLMODE, feststellen D3DCULL_CW); Ein geeignetes Pixelformat für eine Fullscreen- Dithering verwenden - verbessert die grafische Qualität der Darstellung: Anwendungen suchen g_pd3dDevice->SetRenderState( D3DRS_DITHERENABLE, Präsentationsparameter festlegen TRUE); Grafikkarten-Objekt anlegen, über das man auf die speculare Lichtanteile bei der Beleuchtung berücksichtigen: Treiber der primären Grafikkarte zugreifen kann g_pd3dDevice->SetRenderState( D3DRS_SPECULARENABLE, TRUE); Verarbeiten der ausgelesenen RenderingOption und Etablieren einer Vertexverarbeitung Beleuchtung einschalten: SamplerStates für 2 Texturstufen festlegen g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, TRUE); RenderStates festlegen 2 Objekte die für die Textausgabe initialisieren z-Buffer einschalten: g_pd3dDevice->SetRenderState( D3DRS_ZENABLE, TRUE); natürlich kein RenderState... wird später gebraucht deklariert in D3DGlobals.h Aspect = (float)screenwidth/(float)screenheight;
2. InitD3D() 9/9 zwei Objekte Initialisieren, die für die Textausgabe verwendet werden können. Die Implementierung der zugehörigen Klasse befindet sich in der Datei d3dfont.cpp. Font1->InitDeviceObjects(g_pd3dDevice); Font1->RestoreDeviceObjects(); Font2->InitDeviceObjects(g_pd3dDevice); Font2->RestoreDeviceObjects(); Hier wird die Initialisierung von 2 Instanzen der Klasse CD3DFont vorbereitet. Kordinaten des Bildschirmmittelpunktes speichern Die eigentliche Initialisierung erfolgt unmittelbar nach Deklaration in D3DGlobals.h: Erzeugung des Direct3D-Objektes ... Aktuellen Display-Modus für eine Fensteranwendung Objekte für die Textausgabe feststellen CD3DFont* Font1 = new CD3DFont(_T("Arial"), 20, D3DFONT_BOLD ); Ein geeignetes Pixelformat für eine Fullscreen- CD3DFont* Font2 = new CD3DFont(_T("Arial"), 10, D3DFONT_BOLD ); Anwendungen suchen ... Präsentationsparameter festlegen Grafikkarten-Objekt anlegen, über das man auf die Treiber der primären Grafikkarte zugreifen kann Verarbeiten der ausgelesenen RenderingOption und Etablieren einer Vertexverarbeitung SamplerStates für 2 Texturstufen festlegen RenderStates festlegen ... soviel zu dieser sehr langen Initialisierung in InitD3D() 2 Objekte die für die Textausgabe initialisieren
Zur Erinnerung: Programmablauf GameShell GameRoutines void GameInitialisierungsRoutine(void) { InitRenderOptions(); // in GameRoutines InitSpieleinstellungen(); // hatten wir schon InitD3D(); // hatten wir schon Init_View_and_Projection_Matrix(); // kommt jetzt } void GameCleanUpRoutine(void) { CleanUpD3D(); } void GameMainRoutine(void) { [ ... ] Show_3D_Scenario(); [ ... ] } Space3D ... jetzt kommt Init_View_and_Projection_Matrix()
Kurzer Zwischenblick auf D3DGlobals.h In D3DGlobals.h werden alle möglichen Dinge deklariert und/oder definiert, die u.a. für die Funktion Init_View_and_Projection_Matrix() in SpaceD3D.cpp genutzt werden. ... float SizeOfUniverse = 1000.0f; // momentane Blickweite D3DXVECTOR3 PlayerPosition; D3DXVECTOR3 PlayerVerschiebungsvektor; D3DXVECTOR3 PlayerFlugrichtung (0.0f, 0.0f, 1.0f); D3DXVECTOR3 PlayerVertikale (0.0f, 1.0f, 0.0f); D3DXVECTOR3 PlayerHorizontale (1.0f, 0.0f, 0.0f); D3DXMATRIX identityMatrix; // Einheitsmatrix D3DXMATRIX matView; // Viewmatrix "Das Auge der Kamera" D3DXMATRIX matProj; // Projektionsmatrix 3D-Welt auf Bildschirm D3DXMATRIX ViewProjectionMatrix; // matView*matProj Wird für die Bestimmung der Bildschirmkoordinaten von 3D-Vektoren oder Vertices benötigt ...
Init_View_and_Projection_Matrix() Nach der Initialisierung durch die Funktion InitD3D() werden nun die Sicht- und Projektionsmatrizen erzeugt und die zugehörigen Transformationen durchgeführt. void Init_View_and_Projection_Matrix(void) { PlayerPosition = NullVektor; PlayerVerschiebungsvektor = NullVektor; PlayerFlugrichtung = D3DXVECTOR3(0.0f, 0.0f, 1.0f); PlayerVertikale = D3DXVECTOR3(0.0f, 1.0f, 0.0f); PlayerHorizontale = D3DXVECTOR3(1.0f, 0.0f, 0.0f); D3DXMatrixLookAtLH( &matView, D3DXMATRIX * pOut &PlayerPosition, D3DXVECTOR3 * pEye &PlayerFlugrichtung, D3DXVECTOR3 * pAt &PlayerVertikale); D3DXVECTOR3 * pUp g_pd3dDevice->SetTransform( D3DTS_VIEW, &matView); If the method succeeds, the return value is D3D_OK D3DXMatrixPerspectiveFovLH( &matProj, D3DXMATRIX * pOut FLOAT fovy 45*D3DX_PI/180, FLOAT Aspect Aspect, FLOAT z 1.0f, // niemals < 1.0 !!!! FLOAT zf SizeOfUniverse); g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj); If the method succeeds, the return value is D3D_OK. }
Init_View_and_Projection_Matrix() Nach der Initialisierung durch die Funktion InitD3D() werden nun die Sicht- und Projektionsmatrizen erzeugt und die zugehörigen Transformationen durchgeführt. void Init_View_and_Projection_Matrix(void) { PlayerPosition = NullVektor; PlayerVerschiebungsvektor = NullVektor; PlayerFlugrichtung = D3DXVECTOR3(0.0f, 0.0f, 1.0f); PlayerVertikale = D3DXVECTOR3(0.0f, 1.0f, 0.0f); PlayerHorizontale = D3DXVECTOR3(1.0f, 0.0f, 0.0f); D3DXMatrixLookAtLH( &matView, D3DXMATRIX * pOut &PlayerPosition, D3DXVECTOR3 * pEye &PlayerFlugrichtung, D3DXVECTOR3 * pAt &PlayerVertikale); D3DXVECTOR3 * pUp g_pd3dDevice->SetTransform( D3DTS_VIEW, &matView); If the method succeeds, the return value is D3D_OK D3DXMatrixPerspectiveFovLH( &matProj, D3DXMATRIX * pOut FLOAT fovy 45*D3DX_PI/180, FLOAT Aspect Aspect, FLOAT z 1.0f, // niemals < 1.0 !!!! FLOAT zf SizeOfUniverse); g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj); If the method succeeds, the return value is D3D_OK. }
Zur Erinnerung: Programmablauf GameShell GameRoutines void GameInitialisierungsRoutine(void) // fertig { InitRenderOptions(); // in GameRoutines InitSpieleinstellungen(); // hatten wir schon InitD3D(); // hatten wir schon Init_View_and_Projection_Matrix(); // hatten wir schon } void GameCleanUpRoutine(void) { CleanUpD3D(); } void GameMainRoutine(void) { [ ... ] Show_3D_Scenario(); // kommt jetzt [ ... ] } Space3D ... jetzt kommt Show_3D_Scenario()
Show_3D-Scenario() void Show_3D_Scenario(void) { 1.Schritt: Löschen der vorangegangenen Szene g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,0), 1.0f, 0 ); 2.Schritt: neue Szene beginnen g_pd3dDevice->BeginScene(); Font1->DrawTextScaled( -0.55f, 0.0f, 0.9f, Position 0.06f, 0.02f, Skalierung D3DCOLOR_XRGB(0,0,250), Farbe "Hello to all Game Programmers", D3DFONT_FILTERED); Textausgabe geht auch so: sprintf( strBuffer, "Framerate: %03d", (long)FrameRate ); Font1->DrawTextScaled(-0.8f, -0.8f, 0.9f, 0.03f, 0.02f, D3DCOLOR_XRGB(250,0,0), strBuffer, D3DFONT_FILTERED ); sprintf( strBuffer, "Resolution: %d, %d, %d", screenwidth, screenheight, Farbtiefe); Font1->DrawTextScaled(-0.8f, -0.7f, 0.9f, 0.03f, 0.02f, D3DCOLOR_XRGB(250,0,0), strBuffer, D3DFONT_FILTERED ); sprintf( strBuffer, "Blickweite: %0.1f", SizeOfUniverse ); Font1->DrawTextScaled(-0.8f, -0.6f, 0.9f, 0.03f, 0.02f, D3DCOLOR_XRGB(250,0,0), strBuffer, D3DFONT_FILTERED ); 3.Schritt: neue Szene beenden (Die 3D-Szene ist komplett aufgebaut und fertig für die Anzeige) g_pd3dDevice->EndScene(); 4.Schritt: neue Szene anzeigen g_pd3dDevice->Present( NULL, NULL, NULL, NULL ); }
Zur Erinnerung: Programmablauf GameShell GameRoutines void GameInitialisierungsRoutine(void) // fertig { InitRenderOptions(); // in GameRoutines InitSpieleinstellungen(); // hatten wir schon InitD3D(); // hatten wir schon Init_View_and_Projection_Matrix(); // hatten wir schon } void GameCleanUpRoutine(void) { CleanUpD3D(); // kommt jetzt } void GameMainRoutine(void) // fertig { [ ... ] Show_3D_Scenario(); // hatten wir schon [ ... ] } Space3D ... jetzt kommt CleanUpD3D()
CleanUpD3D() void CleanUpD3D(void) { Font1->DeleteDeviceObjects(); Font2->DeleteDeviceObjects(); SAFE_DELETE(Font1) SAFE_DELETE(Font2) Wird von einigen Billboard-Transformationsfunktionen verwendet SAFE_DELETE(Zufallswinkel) SAFE_RELEASE(g_pd3dDevice) SAFE_RELEASE(g_pD3D) }
Punkte, Linien und Kreise Das Anwendungsgerüst wird um die Datei 3D_ScenarioClass.h erweitert. Darin ist die Funktionalität der Klasse C3DScenario implementiert. Im Prinzip sieht 3D_ScenarioClass.h so aus:
Einbinden von 3D_ScenarioClass.h GameRoutines.h extern void Init3DScenario(void); extern void CleanUp3DScenario(void); GameRoutines.cpp void GameInitialisierungsRoutine(void) { InitRenderOptions(); InitSpieleinstellungen(); InitD3D(); Init_View_and_Projection_Matrix(); Init3DScenario(); } void GameCleanUpRoutine(void) { CleanUp3DScenario(); CleanUpD3D(); } Space3D.h #include "3D_ScenarioClass.h" Space3D.cpp In Space3D.cpp erfolgt das Rendern von Punikten, Linien und Kreisen aus der Funktion Show_3D-Scenario(). Die Funktion wird entsprechend erweitert ...
Einbinden von 3D_ScenarioClass.h void Show_3D_Scenario(void) { g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,0), 1.0f, 0 ); g_pd3dDevice->BeginScene(); Scenario->Render_Points(); Scenario->Render_Circle(); D3DCOLOR Color = D3DCOLOR_XRGB(0, 200, 0); temp1Vektor3 = D3DXVECTOR3(-2.0f, -2.0f, 5.0f); temp2Vektor3 = D3DXVECTOR3(2.0f, 2.0f, 5.0f); Scenario->Render_Line(&temp1Vektor3, &temp2Vektor3, &Color ); temp1Vektor3 = D3DXVECTOR3(-2.0f, 2.0f, 5.0f); temp2Vektor3 = D3DXVECTOR3(2.0f, -2.0f, 5.0f); Scenario->Render_Line(&temp1Vektor3, &temp2Vektor3, &Color ); Font1->DrawTextScaled(-0.55f, 0.0f, 0.9f, 0.06f, 0.02f, D3DCOLOR_XRGB(0,0,250), "Hello to all Game Programmers", D3DFONT_FILTERED); sprintf( strBuffer, "Framerate: %03d", (long)FrameRate ); Font1->DrawTextScaled(-0.8f, -0.8f, 0.9f, 0.03f, 0.02f, D3DCOLOR_XRGB(250,0,0), strBuffer, D3DFONT_FILTERED ); ... noch mehr Text g_pd3dDevice->EndScene(); g_pd3dDevice->Present( NULL, NULL, NULL, NULL ); }
Was passiert in 3D_ScenarioClass.h? Flexible Vertexformate (FVF) Je nach Anwendungszweck müssen für einem Vertex verschiedene Daten gespeichert werden. Dazu zählen: ● Position ● Normale (für die Lichtberechnung) ● Farbe (in diesem Fall wird auf Lichtberechnung verzichtet) ● ein oder mehrere Sätze von Texturkoordinaten (TextureMapping, Multitexturing) Für jeden Verwendungszweck kann ein geeignetes Vertexformat definiert werden. Dies geschieht hier in VertexFormats.h. Für die Darstellung von Linien und Punkte wird dieses Vertexformat verwendet: struct POINTVERTEX { D3DXVECTOR3 position; D3DCOLOR color; }; #define D3DFVF_POINTVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE) D3DFVF_XYZ: Die Vertices müssen noch transformiert werden D3DFVF_DIFFUSE: Die Vertices sind bereits beleuchtet
Was passiert in 3D_ScenarioClass.h? Vertexbuffer Damit eine 3D-Szene möglichst effizient transformiert und gerendert werden kann, werden die Vertexdaten in einem geschützen Speicherbereich, den sogenannten Vertexbuffer, abgelegt. g_pd3dDevice->CreateVertexBuffer( [1] AnzahlPunkte*sizeof(POINTVERTEX), [2] D3DUSAGE_WRITEONLY, [3] D3DFVF_POINTVERTEX, [4] D3DPOOL_MANAGED, [5] &PunkteVB, [6] NULL ); [3] Das Vertexformat, für den der Vertexbuffer angelegt wird [5] Adresse eines Zeigers, der auf den Vertexbuffer verweist Der Vertexbuffer ist ein geschützer Speicherbereich. Darum muss er vor jedem Zugriff mit Lock() verriegelt werden. Nach allen Schreibvorgängen durchgeführt wurden, muss er mit Unlock() wieder entriegelt werden: POINTVERTEX* pPointVertices; PunkteVB->Lock(0, 0, (VOID**)&pPointVertices, 0); ... irgendein Zugriff (siehe 3D_ScenarioClass.h) PunkteVB->Unlock();
Was passiert in 3D_ScenarioClass.h? Vertexdaten transformieren und rendern Bevor Vertxdaten gerendert werden können, müssen sie durch die Transformations- und Beleuchtungspipeline geschickt werde. Über die Methode SetFVF() wird ein vertexshader ausgewählt, mit SetStreamSource() werden die zu rendernden Vertexdaten ausgewählt. Zum Rendern der Vertexdaten eines Vertexbuffers wird DrawPrimitive() verwendet. Beispiel aus 3D_ScenarioClass.h: void Render_Points(void) { g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, FALSE); g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE); g_pd3dDevice->SetFVF(D3DFVF_POINTVERTEX); g_pd3dDevice->SetStreamSource( 0, PunkteVB, 0, sizeof(POINTVERTEX)); g_pd3dDevice->DrawPrimitive( D3DPT_POINTLIST, 0, AnzahlPunkte); g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, TRUE); g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, TRUE); }
Was passiert in 3D_ScenarioClass.h? Grafikprimitiven Es gibt im Grunde nur Punkte, Linien und Dreiecke.
Was passiert in 3D_ScenarioClass.h? Kreis rendern 1/2 Ein Kreis ist ein Vieleck und wird hier unter Verwendung eines Line Strip (siehe Render_Circle() )gerendert. C3DScenario() { ... ... ... RingSegmente = 80; // des Kreises g_pd3dDevice->CreateVertexBuffer( RingSegmente*sizeof(POINTVERTEX), D3DUSAGE_WRITEONLY , D3DFVF_POINTVERTEX, D3DPOOL_MANAGED, &GreenCircleVB, NULL ); GreenCircleVB->Lock( 0, 0, (VOID**)&pPointVertices, 0 ); float SegmentTeilwinkel = 2*D3DX_PI/(RingSegmente-1); for( i = 0; i < RingSegmente; i++ ) { pPointVertices[i].position = D3DXVECTOR3(2.0f*sinf(i*SegmentTeilwinkel), 2.0f*cosf(i*SegmentTeilwinkel),5.0f ); pPointVertices[i].color = D3DCOLOR_XRGB(0,200,0); } GreenCircleVB->Unlock(); } weiter mit Render_Circle() ...
Was passiert in 3D_ScenarioClass.h? Kreis rendern 2/2 void Render_Circle(void) { g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, FALSE ); g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE); g_pd3dDevice->SetStreamSource( 0, GreenCircleVB, 0, sizeof(POINTVERTEX)); g_pd3dDevice->SetFVF(D3DFVF_POINTVERTEX); g_pd3dDevice->DrawPrimitive( D3DPT_LINESTRIP, 0, RingSegmente-1); g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, TRUE); g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, TRUE); } Für die Anzahl der RingSegmente in DrawPrimitive() muss 1 abgezogen werden, weil es genau eine Linie weniger gibt als Eckpunkte. (Ein Kreis mit 9 Punkten hat 8 Linien, weil Endpunkt gleich Startpunkt ist – sonst wäre der Kreis nicht geschlossen.)
Das war Teil 1 zum Anwendungsgerüst. Es folgt Teil 2 mit der Implementierung der Textur- Funktionalität.
Texturen Texturen sind Bitmap-Dateien, die im Videospeicher der Grafikkarte gespeichert sind. Im Zusammenhang von Texturen spricht man nicht von Pixeln, sondern von Texeln. Nach Transformation eines 3D-Objekts werden die Dreiecke, aus denen das Objekt zusammengesetzt sind, mit derTextur überzogen. Dies geschieht gemäß der in Vertices gespeicherten Texturkoordinaten. Das Verarbeiten von Texturen ist rechenintensiv. Erschwert wird das Texture Mapping dadurch, dass Objekte frei beweglich sind und entsprechend ihrem Abstand zur Kamera vergrößert oder verkleinert erscheinen.
Texturen Als Texturkoordinaten werden gewöhnlich die Parameter tu und tv verwendet. Zum vollständigen Mappen über ein Polygon müssen Texturkoordinaten im Bereich vom 0 bis 1 verwendet werden. Das Koordinatenpaar (0,0) ist die obere linke Ecke und (1,1) die untere rechte Ecke der Bitmap.
Die neuen Dateien, Klassen und Funktionen Das Anwendungsgerüst wird um die folgenden Dateien erweitert: BildschirmSchablonenClass.h Für die Darstellung der Bildschirmschablone wird die Klasse CSchablone verwendet. GiveInput.h ... wird in Kapitel 9 besprochen: unsere DirectInput Bibliothek für die Mausabfrage. SimpleCursorClass.h Die Klasse CCursor wird für die Erzeugung und Darstellung eines einfachen Cursors benötigt. SimpleStarfield.h Die Klasse CStarfield wird für die Erzeugung und Darstellung eines Sternenfelds (im Hintergrund) genutzt. Das ist die ausgelagerte Funktionalität des Punkthintergrundes aus 'Punkte, Linien und Kreise'.
Was machen die neuen Dateien? SimpleStarfieldClass.h ist das Ergebnis einer Entrümpelung der ehemaligen Datei 3D_ScenarioClass.h von 'Punkte, Linien und Kreise'. Jetzt gibt es allerdings eine Klasse CStarfield, die die Punkte erzeugt. (Die Instanz wird in 3D_Scenario.h erzeugt) BildschirmSchabloneClass.h besitzt eine KlasseCSchablone, die für die Initialisierung, Darstellung und Zerstörung eines beleuchteten und transformierten Quads von der Größe des Bildschirms verwendet wird. SimpleCursor.h besitzt eine Klasse Ccursor, die für die Initialisierung, Darstellung und Zerstörung des Cursor- Quads verwendet wird. Dort wird eine Textur auf ein beleuchtetes Quad gerendert. Damit der Cursor immer an der aktuellen Mausposition gerendert wird, muss vor dem Rendern eine Welttransformation durchgeführt werden.
Die Klasse CTexturPool in TextureClasses.h In TextureClasses.h ist die Klasse CTexturPool zum Umgang mit Texturen implementiert. Im Prinzip sieht diese Klasse so aus: Die Fu nktionen lassen einen unterschiedlichen Umgang mit Texturen zu, ansonsten machen sie alle das Gleiche – nämlich Texturen erzeigen: CreateTextur(): Textur mit vollständiger Mipmap-Kette schwarze Bereiche sind bei eingeschaltetem Alpha Blending transparent CreateNonTransparentTextur(): Textur mit vollständiger Mipmap-Katte ohne Transparenz CreateTextur_withoutMipmaps() / CreateNonTransparentTextur_withoutMipmaps(): Textur ohne vollständige Mipmap-Kette mit / ohne Transparenz
Die Klasse CTexturPool in TextureClasses.h Die 4 implementierten Methoden in der Klasse CTexturPool rufen alle den Prototyp D3DXCreateTextureFromFileEx() mit jeweils verschiedenen Parametern auf. Siehe SDK-Dokumentation: D3DXCreateTextureFromFileEx Die Methode CreateTexture() beinhaltet z.B. folgenden Aufruf: D3DXCreateTextureFromFileEx( g_pd3dDevice, Zeiger auf Device Objekt Speicherpfad, TexturPfad D3DX_DEFAULT, Texturbreite in Pixeln D3DX_DEFAULT, Texturhoehe in Pixeln D3DX_DEFAULT, Anzahl Mipmaps 0, Verwendungszweck D3DFMT_UNKNOWN, Pixelformat D3DPOOL_MANAGED, Art der Texturverwaltung für dieTtexturerstellung: D3DX_FILTER_TRIANGLE|D3DX_FILTER_MIRROR|D3DX_FILTER_DITHER, Mipmap-Filter: D3DX_FILTER_BOX|D3DX_FILTER_MIRROR|D3DX_FILTER_DITHER, D3DCOLOR_XRGB(0,0,0), bei Alphablending transparente Farbe 0, unwichtig: NULL 0, unwichtig: NULL &pTexture Adresse des Texturzeigers );
Die Klasse CTexturPoolEx in TextureClasses.h Eine Arbeitshilfe: Mit Hilfe der Klasse CTexturPoolEx lassen sich mehrere Texturen in einem Zug erzeugen. (Siehe Woche 3) Dazu muss ein Textur-Stammname an ein Objekt der Klasse CtexturPoolEx übergeben werden. Anschließend wird die Methode InitTextures() aufgerufen. Beispiel: EineTextur1M1.bmp EineTextur1M2.bmp EineTextur1M3.bmp MeineTextur = new CTexturPoolEx; sprintf(MeineTextur->Speicherpfad, "EineTextur1"); MeineTextur->InitTextur();
Erzeugen von Texturen Wenn eine Textur erzeugt werden soll, braucht man dafür grundsätzlich 3 Schritte: ● 1. einTexturklassen Objekt erzeugen Beispiel: CursorTextur = new CTexturPool; ● 2. Speicherpfad der Textur übergeben Beispiel: sprintf(CursorTextur->Speicherpfad, "Cursor.bmp"); ● 3. Textur erzeugen Beispiel: CursorTextur->CreateTextur(); (siehe SimpleCursorClass.h und BildschirmSchablone.h)
Einbinden in das Anwendungsgerüst Die neue Funktionalität wird in folgende Dateien eingebaut: ● GameRoutine.h ● GameRoutines.cpp ● Space3D.h ● Space3D.cpp ● 3D_ScenarioClass.h
Einbinden in das Anwendungsgerüst GameRoutine.h ... ... ... //--------------------------------------------------------- // EXTERNALS //--------------------------------------------------------- extern HWND main_window_handle; extern HINSTANCE hinstance_app; //--------------------------------------------------------- // externe Funktionsprototypen //--------------------------------------------------------- extern HRESULT InitDirectInput(HINSTANCE, HWND); extern void FreeDirectInput(void); extern void ReadImmediateDataMouse(void); extern void ReadImmediateDataJoystick(void); extern void ReadImmediateDataKeyboard(void); ... extern void BuildIdentityMatrices(void); ... ... ...
Einbinden in das Anwendungsgerüst GameRoutines.cpp void GameInitialisierungsRoutine(void) { if(InitDirectInput(hinstance_app, main_window_handle) == S_FALSE) { Game_State = -1000; // nicht existierender Game-State SendMessage(main_window_handle,WM_CLOSE,0,0); return; } ... ... ... BuildIdentityMatrices(); ... ... ... } void GameCleanUpRoutine(void) { FreeDirectInput(); ... ... ... } void GameMainRoutine(void) { if(Game_State == -1) { start_time = GetTickCount(); if(Pause == FALSE) { Show_3D_Scenario(); } // Eingabegeräte (Joystick, Maus und Keyboard) abfragen: ReadImmediateDataMouse(); ... ... ... }
Einbinden in das Anwendungsgerüst Space3D.h ... ... ... //------------------------------------------------------- // Funktionsprototypen //------------------------------------------------------- ... ... ... void BuildIdentityMatrices(void); //------------------------------------------------------- // zusätzliche Includes //------------------------------------------------------- #include "SimpleStarfieldClass.h" #include "SimpleCursorClass.h" #include "BildschirmSchablonenClass.h" #include "3D_ScenarioClass.h" #include "GiveInput.h"
Einbinden in das Anwendungsgerüst Space3D.cpp void BuildIdentityMatrices(void) { D3DXMatrixIdentity(&g_ObjectKorrekturMatrix); D3DXMatrixIdentity(&identityMatrix); D3DXMatrixIdentity(&g_ScaleMatrix); D3DXMatrixIdentity(&g_VerschiebungsMatrix); D3DXMatrixIdentity(&g_VerschiebungsMatrix3); } void Show_3D_Scenario(void) { ... Scenario->New_Scene(); Methode in 3d_Scenario.h ... } Bei Programmaufruf (siehe GameRoutines.cpp) werden mit BuildIdentityMatrices() die global verwendeten Matrizen als Eineitsmatrizen initialisiert. Zur Erinnerung die Reihenfolge des Funktionsaufrufs in GameRoutines.cpp: InitRenderOptions(); InitSpieleinstellungen(); InitD3D(); Init_View_and_Projection_Matrix(); BuildIdentityMatrices(); Init3DScenario();
Einbinden in das Anwendungsgerüst 3D_ScenarioClass.h void Init3DScenario(void); void CleanUp3DScenario(void); class C3DScenario { public: CStarfield* Starfield; CCursor* Cursor; CSchablone* Schablone; C3DScenario() alle Elemente der 3D-Szene werden initialisiert { Starfield = new CStarfield; Cursor = new CCursor; Schablone = new CSchablone; } ~C3DScenario() alle Elemente der 3D-Szene werden zerstört { SAFE_DELETE(Schablone) SAFE_DELETE(Starfield) SAFE_DELETE(Cursor) } Diese Methode wird durch Show_3D_Scenario() in Space3D.cpp aufgerufen: void New_Scene(void) { Starfield->Render_Starfield(); Cursor->Render_Cursor(); Schablone->Render_Schablone(); } }; C3DScenario* Scenario = NULL; void Init3DScenario(void) { Scenario = new C3DScenario; } void CleanUp3DScenario(void) { SAFE_DELETE(Scenario) }
Was machen die neuen Dateien? SimpleStarfieldClass.h ist das Ergebnis einer Entrümpelung der ehemaligen Datei 3D_ScenarioClass.h von 'Punkte, Linien und Kreise'. Jetzt gibt es allerdings eine Klasse CStarfield, die die Punkte erzeugt. (Die Instanz wird in 3D_Scenario.h erzeugt) BildschirmSchabloneClass.h besitzt eine KlasseCSchablone, die für die Initialisierung, Darstellung und Zerstörung eines beleuchteten und transformierten Quads von der Größe des Bildschirms verwendet wird. SimpleCursor.h besitzt eine Klasse Ccursor, die für die Initialisierung, Darstellung und Zerstörung des Cursor- Quads verwendet wird. Dort wird eine Textur auf ein beleuchtetes Quad gerendert. Damit der Cursor immer an der aktuellen Mausposition gerendert wird, muss vor dem Rendern eine Welttransformation durchgeführt werden.
Sie können auch lesen