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.
|
|
|