DirectX 8 und DelphiLektion 5: 3D-Objekte mit Texturenvon Jürgen Rathlev |
In dieser Lektion wollen wir ein 3D-Objekt (Würfel) mit einer Textur, d.h. mit einer
aus einem Bitmap generierten Oberfläche versehen. Das Bitmap kann mit einer beliebigen
Grafikanwendung erzeugt werden (z.B. PhotoShop, PhotoPaint, o.ä.). Es gibt aber auch
spezielle Programme zum Generieren von Texturen. Das Ergebnis sollte als Bitmap-Datei
vorliegen. Mögliche Formate sind: Windows-Bitmap (BMP), JPEG-Bitmaps (JPG) oder
Targa-Bitmaps (TGA). Um ein solches Bitmap auf einer Oberfläche abzubilden, benötigt DirectX Angaben darüber, wird dies geschehen soll. Wir müssen daher zu jedem Vertex ein zusätzliches Koordinatenpaar (tu,tv) angeben. Es beschreibt die Position des Vertex innerhalb des Textur-Bitmaps. Die Koordinaten sind derart normiert, dass für die linke obere Ecke des Bitmaps tu = 0, tv = 0 und für die untere rechte Ecke tu = 1, tv =1 ist. Im DirectX-SDK findet man unter Stichwort ("Texel") weitere Informationen. Die Vertex-Defintionen sehen nun wie folgt aus: |
const MPath = 'Media\'; // Pfad mit Texturen CubeCount = 36; type // Unsere Struktur, in der wir die Dreiecke speichern TMyVertex = record x,y,z : single; // Position des Vertex color : dword; // Farbe des Vertex tu,tv : single; // Die Koordinaten der Textur end; TCube = array [0..CubeCount-1] of TMyVertex; const // Beschreibung des Vertextyps: Mit D3DFVF_DIFFUSE sagen wir DX, das unsere // Struktur eine Farbe hat. D3DFVF_XYZ bedeutet, dass es sich um ein untransformiertes // Vertex handelt, D3DFVF_TEX1 zeigt die Existenz einer Textur an D3D8T_CUSTOMVERTEX =D3DFVF_XYZ or D3DFVF_DIFFUSE or D3DFVF_TEX1; NormCube : TCube = ( (x :-1.0; y :-1.0; z :-1.0; color : $000000FF; tu : 0; tv : 1), // Vorn (x :-1.0; y : 1.0; z :-1.0; color : $000000FF; tu : 0; tv : 0), (x : 1.0; y : 1.0; z :-1.0; color : $000000FF; tu : 1; tv : 0), (x : 1.0; y : 1.0; z :-1.0; color : $000000FF; tu : 1; tv : 0), (x : 1.0; y :-1.0; z :-1.0; color : $000000FF; tu : 1; tv : 1), (x :-1.0; y :-1.0; z :-1.0; color : $000000FF; tu : 0; tv : 1), (x :-1.0; y :-1.0; z : 1.0; color : $00FF00FF; tu : 0; tv : 1), // Links (x :-1.0; y : 1.0; z : 1.0; color : $00FF00FF; tu : 0; tv : 0), (x :-1.0; y : 1.0; z :-1.0; color : $00FF00FF; tu : 1; tv : 0), (x :-1.0; y : 1.0; z :-1.0; color : $00FF00FF; tu : 1; tv : 0), (x :-1.0; y :-1.0; z :-1.0; color : $00FF00FF; tu : 1; tv : 1), (x :-1.0; y :-1.0; z : 1.0; color : $00FF00FF; tu : 0; tv : 1), (x : 1.0; y :-1.0; z : 1.0; color : $0000FF00; tu : 0; tv : 1), // Hinten (x : 1.0; y : 1.0; z : 1.0; color : $0000FF00; tu : 0; tv : 0), (x :-1.0; y : 1.0; z : 1.0; color : $0000FF00; tu : 1; tv : 0), (x :-1.0; y : 1.0; z : 1.0; color : $0000FF00; tu : 1; tv : 0), (x :-1.0; y :-1.0; z : 1.0; color : $0000FF00; tu : 1; tv : 1), (x : 1.0; y :-1.0; z : 1.0; color : $0000FF00; tu : 0; tv : 1), (x : 1.0; y :-1.0; z :-1.0; color : $0000FFFF; tu : 0; tv : 1), // Rechts (x : 1.0; y : 1.0; z :-1.0; color : $0000FFFF; tu : 0; tv : 0), (x : 1.0; y : 1.0; z : 1.0; color : $0000FFFF; tu : 1; tv : 0), (x : 1.0; y : 1.0; z : 1.0; color : $0000FFFF; tu : 1; tv : 0), (x : 1.0; y :-1.0; z : 1.0; color : $0000FFFF; tu : 1; tv : 1), (x : 1.0; y :-1.0; z :-1.0; color : $0000FFFF; tu : 0; tv : 1), (x :-1.0; y : 1.0; z :-1.0; color : $00FF0000; tu : 0; tv : 1), // Oben (x :-1.0; y : 1.0; z : 1.0; color : $00FF0000; tu : 0; tv : 0), (x : 1.0; y : 1.0; z : 1.0; color : $00FF0000; tu : 1; tv : 0), (x : 1.0; y : 1.0; z : 1.0; color : $00FF0000; tu : 1; tv : 0), (x : 1.0; y : 1.0; z :-1.0; color : $00FF0000; tu : 1; tv : 1), (x :-1.0; y : 1.0; z :-1.0; color : $00FF0000; tu : 0; tv : 1), (x : 1.0; y :-1.0; z :-1.0; color : $00FFFF00; tu : 0; tv : 1), // Unten (x : 1.0; y :-1.0; z : 1.0; color : $00FFFF00; tu : 0; tv : 0), (x :-1.0; y :-1.0; z : 1.0; color : $00FFFF00; tu : 1; tv : 0), (x :-1.0; y :-1.0; z : 1.0; color : $00FFFF00; tu : 1; tv : 0), (x :-1.0; y :-1.0; z :-1.0; color : $00FFFF00; tu : 1; tv : 1), (x : 1.0; y :-1.0; z :-1.0; color : $00FFFF00; tu : 0; tv : 1)); |
Die Textur-Koordinaten sind so gewählt, dass auf jeder Seite des Würfels eine
vollständige Abbildung des Textur-Bitmaps erfolgt. Beim Initialisieren der Szene sind einige Einstellungen für die Verarbeitung der Textur vorzunehmen. Nähere Angaben über die benutzten und weitere andere Parameter findet man in der DirectX-SDK-Dokumentation. Die MAGFILTER und MINFILTER legen fest, mit welchem Filter die Pixel der Textur bearbeitet werden sollen, wenn das geladene Bitmap kleiner oder größer als die zu füllenden Vertexfläche ist. Außerdem muss die Textur aus einer Bitmap-Datei geladen werden (D3DXCreateTextureFromFile). |
type TSample3DForm = class(TForm) ... private // Buffer, der die Vertizes des Würfels enthält CubeVB : IDirect3DVertexBuffer8; // Textur des Würfels CubeTexture : IDIRECT3DTEXTURE8; RotX,RotY,RotZ : single ; //Rotation des Würfels um die drei Achsen ... procedure TSample3DForm.D3DInitScene; ... begin if assigned(lpd3ddevice) then with lpd3ddevice do begin ... // Unsere Projektion wird sich niemals bewegen, also setzen wir sie fest SetTransform(D3DTS_PROJECTION,matProj ); // Mit D3DTSS_COLLORP wird festgelegt, wie die Farbe jedes einzelnen Pixels // verarbeitet wird. D3DTOP_SELECTARG1 verweist auf D3DTSS_COLORARG1 SetTextureStageState(0,D3DTSS_COLOROP, D3DTOP_SELECTARG1); // Mit D3DTSS_COLORARG1 wird festgelegt, daß die Farbe nur von der Textur // genommen wird und von nichts anderem. SetTextureStageState(0,D3DTSS_COLORARG1, D3DTA_TEXTURE); // Wir benutzen kein Alpha blending, also schalten wir es ab. Dadurch wird // der Code etwas schneller SetTextureStageState(0,D3DTSS_ALPHAOP,D3DTOP_DISABLE); // MAGFILTER ist dafür, wenn es kleiner ist als unser Objekt. // MINFILTER ist genau das umgekehrte // Standard ist D3DTEXF_LINEAR. Dieses ist zwar langsamer als D3DTEXF_POINT, // sieht aber besser aus. SetTextureStageState(0,D3DTSS_MAGFILTER, D3DTEXF_LINEAR); SetTextureStageState(0,D3DTSS_MINFILTER, D3DTEXF_LINEAR); // Ich glaube diese Funktion bräuchte ich nicht zu beschreiben, aber hier // wird die Textur geladen. hr:=D3DXCreateTextureFromFile(lpd3ddevice,pchar(MPath+'ieap-kiste-g.bmp'),CubeTexture); if(FAILED(hr)) then FatalError(0,'Fehler beim Laden der Textur'); end; end; |
Einige weitere kleine Ergänzungen und Änderungen sind dann noch in der Render-Routine nötig. Wir haben jetzt nur ein Objekt (Würfel), das um alle drei Achsen rotieren soll. Mit SetTexture wird die gewünschte Textur für die Oberflächen ausgewähl. Die weiteren Programmteile entsprechen den vorangegangenen Lektionen. |
procedure TSample3DForm.D3DRender; var WorldMatrix,TempMatrix : TD3DXMATRIX; begin RotY:=RotY+0.03; RotX:=RotX+0.04; RotZ:=RotZ+0.02; ... if SUCCEEDED(BeginScene) then begin // Hier wird unsere Textur geladen SetTexture(0,CubeTexture); // Vertex Shader sind wirklich komplex, aber es lassen sich damit gute Effekte // erzielen. Genauere Beschreibungen in der SDK, denn alles hier niederschreiben // sprengt den Rahmen eines Tutorials SetVertexShader(D3D8T_CUSTOMVERTEX); // Die D3D Renderfunktionen lesen aus Streams. Hier sagen wir DX welchen Stream // es verwenden soll SetStreamSource(0,CubeVB,sizeof(TMyVertex)); // Die Rotation um alle Achsen. D3DXMatrixRotationYawPitchRoll(TempMatrix,RotY,RotX,RotZ); // Verschiebe den Würfel etwas nach vorn D3DXMatrixTranslation(WorldMatrix,0.0,0.0,-2.0); // Berechne die Welt-Matrix für die Transformation D3DXMatrixMultiply(WorldMatrix,TempMatrix,WorldMatrix); SetTransform(D3DTS_WORLD,WorldMatrix); // Zeichnen des Würfels DrawPrimitive(D3DPT_TRIANGLELIST,0,12); EndScene; end; ... |
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.
|