DirectX 8 und DelphiLektion 6: Vollbild oder Fenstermodus, Hintergrund mit Texturvon Jürgen Rathlev |
In dieser Lektion soll unserer 3D-Szene mit der würfelförmigen Kiste ein Rundumhintergrund
hinzugefügt werden. Die dazu erforderlichen Texturen (Wolkenhimmel und Wasseroberfläche)
sind den Beispielen aus dem Microsoft DirectX-SDK entnommen. Als erstes wollen wir aber unsere 3D-Initialisierung so erweitern, dass die Darstellung wahlweise im Vollbildmodus (wie bisher) oder im Fenstermodus (neu) erfolgen kann. Die letztere Alternative bietet vor allem bei der Programmentwicklung und Fehlersuche Vorteile, da nur hier der integerierte Delphi-Debugger einwandfrei arbeitet. Im Deklarationsteil sind einige Variablen hinzuzufügen: |
type TSample3DForm = class(TForm) ... private { Private-Deklarationen } // Direct3D Interfaces (siehe Lektion 5) lpd3d : IDIRECT3D8; lpd3ddevice : IDirect3DDevice8; D3dDevCaps : TD3DCaps8; HwVertexProcess, FullScreen : boolean; ... procedure TSample3DForm.FormCreate(Sender: TObject); begin ... FullScreen:=false; // setze auf"true" für Vollbildmodus ... |
Durch Setzen der Variable FullScreen auf true oder false beim
Programmstart kann zwischen den beiden Darstellungen gewählt werden. In der Routine
zur Initialisierung der 3D-Grafik beibt der Teil für den Vollbildmodus unverändert.
Für den Fenstermodus wird die aktuelle Einstellung über GetAdapterDisplayMode
abgefragt. Außerdem ist eine Abfrage eingefügt, ob die Grafikkarte das Hardware-Vertexprocessing unterstützt. Entsprechend wird die DirectX-Schnittstelle initialisiert. |
procedure TSample3DForm.D3DInit; var ... d3ddm : TD3DDISPLAYMODE; vp : integer; begin ... with d3dpp do begin ... Windowed:=not FullScreen; if FullScreen then begin // Vollbild BackBufferWidth := 640; BackBufferHeight := 480; BackBufferFormat := D3DFind16BitMode; BackBufferCount:=1; // 1 Backbuffer end else begin // Fenster hr:=lpd3d.GetAdapterDisplayMode(D3DADAPTER_DEFAULT,d3ddm); if failed(hr) then FatalError(hr,'Fehler beim Ermitteln des Dislaymodes'); BackBufferFormat := d3ddm.Format; end; end; ... // Hardware T&L? hr:=lpd3d.GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,D3dDevCaps); if FAILED(hr) then FatalError(hr,'Fehler beim Abfragen der DevCaps'); HwVertexProcess:=D3dDevCaps.DevCaps and D3DDEVCAPS_HWTRANSFORMANDLIGHT <> 0; if HwVertexProcess then vp:=D3DCREATE_HARDWARE_VERTEXPROCESSING else vp:=D3DCREATE_SOFTWARE_VERTEXPROCESSING; //Erstellen des D3D-Device hr:=lpd3d.CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, Handle, vp, d3dpp, lpd3ddevice); ... |
Nachfolgend soll beschrieben werden, wie ein Rundumhintergrund in die Szene eingefügt
wird. Wir benutzen dazu wieder den Würfel, machen ihn aber sehr groß und legen die
Texturen auf die Innenseiten. In den Beispielen des Microsoft DirectX-SDK sind geeignete
Bitmaps für alle Seiten enthalten (skybox_...bmp). Sie bilden einen Wolkenhimmel
mit einer Wasseroberfläche. Da unsere Kiste auf dem Wasser schwimmen soll, legen wir den
Boden des Würfels in den Koordinatennullpunkt. Da die Kiste etwas eintauchen soll, muss
der Z-Buffer verwendet werden. DirectX berechnet dann automatisch die Entfernung
der Vertizes vom Beobachter und verdeckt unsichtbare Bereiche.
In diesem Beispiel soll sich im Gegensatz zur vorangegangenen Lektion nicht die Kiste
sondern der Beobachter in einem Kreis um den Nullpunkt bewegen. Als erstes definieren wir einige Konstanten am Anfang des Programms, mit denen sich später sehr einfach einige Einstellungen ändern lassen: |
const Fovy = Pi/4; // Öffnungswinkel des Sichtkegels (Field of view) ViewDist = 15; // Abstand des Betrachters vom Nullpunkt ViewHeight = 5; // Höhe des Betrachters über der Wasseroberfläche Delta = 0.3; // Winkelinkrement für Animation CubeScale = 1.5; // Skalierungsfaktor für Kiste SkyScale = 100; // Skalierungsfaktor für Hintergrundbox |
BoxDepth = 0.4; // Eintauchtife der Kiste
Die Konstante Fovy lässt sich mit der Brennweitenverstellung eines Zoomobjektivs
vergleichen. Große Werte bewirken eine Sicht wie durch ein Weitwinkelobjektiv, kleine
Werte entsprechen einem Teleobjektiv. Mit Delta kann die Geschwindigkiet der
Animation an den jeweiligen Rechner angepasst werden. Es ist ein Maß für die
Winkeländerung bei der kreisförmigen Bewegung des Beobachters. Bei langsamen Rechnern
wählt man einen etwas größeren Wert. Der Initialsierungsteil des Programms muss um einige neue Variablen für die verschiedenen Texturen erweitert werden: |
type TSample3DForm = class(TForm) ... private ... // Unsere Texturen SkyTop,SkyBottom, SkyFront,SkyBack, SkyLeft,SkyRight, CubeTexture : IDIRECT3DTEXTURE8; // Rotationswinkel für den Beobachter in Grad RotY : single ; ... procedure TSample3DForm.FormCreate(Sender: TObject); begin ... SkyTop:=nil; SkyBottom:=nil; SkyFront:=nil; SkyBack:=nil; SkyLeft:=nil; SkyRight:=nil; ... |
In der Szeneninitialisierung gibt es ebenfalls einige Änderungen. Da wir mehrere Texturen laden müssen, definieren wir uns eine kleine lokale Unterroutine. Außerdem müssen wir für das Rendern den Z-Buffer einschalten. Den Vertexbuffer für den Würfel können wir sowohl für die Kiste als auch für den Hintergrund verwenden. Hier werden beim Rendern nur unterschiedliche Weltkoordinaten mit SetTransform(D3DTS_WORLD,..) verwendet. |
// Initialisieren der Szenenobjekte procedure TSample3DForm.D3DInitScene; var hr : HRESULT; vbVertices : pByte; ProjMatrix : TD3DXMATRIX; // Textur laden function LoadTexture (Filename : string) : IDIRECT3DTEXTURE8; var hr : HRESULT; begin if assigned(lpd3ddevice) then with lpd3ddevice do begin hr:=D3DXCreateTextureFromFile(lpd3ddevice,pchar(MPath+Filename),Result); if FAILED(hr) then FatalError(hr,'Fehler beim Laden der Textur: '+Filename); end; end; begin if assigned(lpd3ddevice) then with lpd3ddevice do begin ... // Lighting abschalten, da unsere Vertices nocht keine Normalenvektoren besitzten SetRenderState(D3DRS_LIGHTING,0); // Z-Buffer beim Rendern einschalten SetRenderState(D3DRS_ZENABLE,D3DZB_TRUE); // Erstelle eine Projektionsmatrix D3DXMatrixPerspectiveFovLH(ProjMatrix, // Resultierende Matrix Fovy, // Öffnungswinkel des Sichtkegels 640/480, // Seitenverhältnis der Ansicht 1.0, // Mindeste Nähe 1000.0); // Maximal sichtbare Entfernung SetTransform(D3DTS_PROJECTION,ProjMatrix ); // Texturen laden CubeTexture:=LoadTexture ('ieap-kiste-g.bmp'); SkyTop:=LoadTexture ('skybox_top.bmp'); SkyBottom:=LoadTexture ('skybox_bottom.bmp'); SkyFront:=LoadTexture ('skybox_front.bmp'); SkyBack:=LoadTexture ('skybox_back.bmp'); SkyLeft:=LoadTexture ('skybox_left.bmp'); SkyRight:=LoadTexture ('skybox_right.bmp'); ... |
Die Routine zum Rendern der Szene muss als erstes den sich ändernden Standort des Beobachters festlegen SetTransform(D3DTS_VIEW,..). Anschließend werden nacheinander Die Oberseite und die Seitenwände des Hintergrundwürfels mit den verschiedenen Texturen gezeichnet. Der Standardwürfel wird dabei um den Faktor SkyScale vergrößert. Für das Rendern der Textur muss hier D3DCULL_CW gesetzt werden, da die Innenseiten des Würfels sichtbar sein sollen. |
// Rendern der Szene procedure TSample3DForm.D3DRender; var WorldMatrix, ViewMatrix, TempMatrix : TD3DXMATRIX; begin RotY:=RotY+Delta; // Rotation des Beobachters if assigned(lpd3ddevice) then with lpd3ddevice do begin ... // Hier erstellen wir unsere SichtMatrix // Der Beobachter bewegt sich in einer Höhe von ViewHeight // kreisförmig im Abstand "ViewDist" um die Kiste D3DXMatrixLookAtLH (ViewMatrix,D3DXVECTOR3(ViewDist*sin(Pi180*RotY), ViewHeight, ViewDist*cos(Pi180*RotY)), D3DXVECTOR3(0.0,0.0,0.0), D3DXVECTOR3(0.0,1.0,0.0)); SetTransform(D3DTS_VIEW,ViewMatrix); // Vertex Typ einstellen SetVertexShader(D3D8T_CUSTOMVERTEX); // Stream auf Vertexbuffer für Würfel setzen SetStreamSource(0,CubeVB,sizeof(TMyVertex)); // Berechne die Welt-Matrix für den Hintergrund (Seiten und oben) D3DXMatrixScaling(WorldMatrix,SkyScale,SkyScale,SkyScale); SetTransform(D3DTS_WORLD,WorldMatrix); // Hintergrund (Seiten): Textur auswählen und zeichnen SetRenderState(D3DRS_CULLMODE,D3DCULL_CW); SetTexture(0,SkyFront); // Vorderseite des Hintergrunds DrawPrimitive(D3DPT_TRIANGLELIST,0,2); SetTexture(0,SkyLeft); // linke Seite des Hintergrunds DrawPrimitive(D3DPT_TRIANGLELIST,6,2); SetTexture(0,SkyBack); // Hinterseite des Hintergrunds DrawPrimitive(D3DPT_TRIANGLELIST,12,2); SetTexture(0,SkyRight); // rechte Seite des Hintergrunds DrawPrimitive(D3DPT_TRIANGLELIST,18,2); // Hintergrund (oben): Textur auswählen und zeichnen SetTexture(0,SkyTop); // Oberseite des Hintergrunds DrawPrimitive(D3DPT_TRIANGLELIST,24,2); ... |
Der Boden wird auf die Höhe des Koordinatennullpunkts angehoben und mit gleichen Skalierung wie vor gezeichnet. |
... // Berechne die Welt-Matrix für den Hintergrund (Boden) // setze dabei y auf 0 (Wasseroberfläche) D3DXMatrixScaling(WorldMatrix,SkyScale,0,SkyScale); SetTransform(D3DTS_WORLD,WorldMatrix); // Hintergrund (unten): Textur auswählen und zeichnen SetTexture(0,SkyBottom); DrawPrimitive(D3DPT_TRIANGLELIST,30,2); ... |
Als letztes kommt noch die Kiste dran. Sie wird mit CubeScale skaliert und um 0,4 einheiten angehoben (Eintauchtiefe). Der eingeschaltete Z-buffer sorgt dafür, dass die eingetauchten Teile der Kiste vom Wasser verdeckt werden. Für die Textur muss hier wieder D3DCULL_CCW gesetzt werden, da die Außenseiten sichtbar sein sollen. |
// Textur für Kiste auswählen SetTexture(0,CubeTexture); SetRenderState(D3DRS_CULLMODE,D3DCULL_CCW); // Setze die Welt-Matrix für die Kiste etwas nach oben D3DXMatrixTranslation(WorldMatrix,0,1-BoxDepth,0); // Skaliere die Kiste D3DXMatrixScaling(TempMatrix,CubeScale,CubeScale,CubeScale); D3DXMatrixMultiply(WorldMatrix,WorldMatrix,TempMatrix); SetTransform(D3DTS_WORLD,WorldMatrix); // Zeichnen der Kiste DrawPrimitive(D3DPT_TRIANGLELIST,0,12); ... |
Es empfiehlt sich, an den verschiedenen Parametern Veränderungen vorzunehmen, um
den Einfluss auf die 3D-Szene beurteilen zu können (z.B. verschiedene Sichtwinkel,
Betrachterhöhen, Ein- und Ausschalten des Z-Buffers, etc.). Die Quelltexte der Beispiele stehen zum Download zur Verfügung. Die Zip-Datei enthält alle Lektionen. Zum Ausführen einer der Lektionen muss in den Projekt-Optionen von Delphi als Bedingung einer der Werte Lesson1, Lesson2, ... definiert werden.
|