Lektion 6 Delphi und DirectX Lektion 4

DirectX 8 und Delphi

Lektion 5: 3D-Objekte mit Texturen

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

Lektion 4 Delphi und DirectX Lektion 6