SelfDXD von Martin Pyka
SelfDXD --- DirectXGraphic --- Vertex- und Indexbuffer
Das Flexible Vertex Format (FVF)
Einleitung
Was ist das Flexible Vertex Format?
Drei Arten von Vertices
Definieren eines eigenen Formates
Formatübergabe an das Device

Einleitung

In den nun folgenden Kapiteln geht es wirklich ums Eingemachte. Sie lernen, wie Sie Vertices, Dreiecke und die daraus resultierenden 3D-Körper selber erstellen und darstellen. Nebenbei machen wir im dritten Kapitel dieses Abschnittes (Transformation und Tiefenbuffer) einen kleinen Exkurs in Sachen Animation.


Was ist das Flexible Vertex Format?

Wie Sie wissen, besteht jedes 3D-Modell aus einer bestimmten Anzahl an Punkten, genannt Vertex, die über Dreiecke miteinander verbunden sind. Jedes dieser Vertice kann neben seinen 3 Koordinaten viele weitere Eigenschaften besitzen, wie Farbe, Texturkoordinate, Normale, etc.. Doch nicht in jeder Anwendung werden Sie unbedingt alle Eigenschaften benötigen. Um Speicher und Rechenkapazität zu sparen, wurde das Flexible Vertex Format (FVF) erstellt, das es dem Entwickler erlaubt, nur die Eigenschaften eines Vertex zu verwenden, die auch wirklich gebraucht werden. Wenn Sie zum Beispiel ein 3D-Modell haben, das unabhängig vom Licht immer gleich hell erscheinen soll, dann brauchen Sie für Ihre Vertice keine Normale definieren.

Hier eine Liste aller Eigenschaften, die man für Vertice definieren kann:

Position x-, y- und z-Koordinate
RHW Farbeinfluss des Vertex auf das ganze Dreieck
Blend Weight Data 1-3 Single-Werte mit denen sich Transformationsmatrizen gewichten lassen. So können Sie den Vertices mehrere Transformationsmatrizen zuweisen. DirectXGraphics errechnet dann einen Mittelwert aus den Matrizen, bei denen er diese Blending-Werte miteinbezieht. Mehr dazu unter Transformation und Z-Buffer
Vertex Normale Die Normale des Vertex, die relevant für die Beleuchtung ist.
Vertex Punkt Grösse Die Grösse eines einzelnen Vertex
Diffuse Die Farbe des Vertex
Specular Die Farbe des Vertex, wenn er direkt angestrahlt wird, d.h. die Normale des Vertex genau in Richtung Lichtquelle zeigt. Mit Specular lässt sich eine Art Glanzeffekt erziehlen, wie bei metallischen Gegenständen.
Texturkoordinaten Set 1-8 Einem Vertex lassen sich bis zu 8 Texturen zuweisen. Dazu müssen Sie dem Vertex sogenannte Texturkoordinaten zuweisen.

Um die einzelnen Format-Elemente in Ihr eigenes Format integrieren zu können, müssen Sie, damit beschäftigen wir uns aber erst später genauer, bestimmte Konstanten verwenden, die in den D3DFVFFLAGS-Flags enthalten sind. Diese werden dem Vertexbuffer, den wir nachher kennen lernen werden, übergeben. Dabei ist zu beachten, dass bestimmte Flags nicht miteinander kombiniert werden dürfen. Dazu mehr im nächsten Abschnitt.


Drei Arten von Vertices

Obwohl das FVF, wie der Name schon sagt, flexibel ist, gibt es im wesentlichen 3 Arten von Vertex-Formaten. Dies hat folgenden Hintergrund: Direct3D wendet normalerweise auf alle Vertices die gleichen Berechnungen an. Sie werden im 3D-Raum positioniert, und beleuchtet und anschliessend die Bildschirmkoordinaten errechnet, an denen das 3D-Objekt mit seinen Vertices angezeigt werden soll. Doch was ist, wenn Sie zum Beispiel eine Menüleiste als 3D-Objekt aus Vertices definiert haben, die immer am rechten Rand angezeigt werden soll, also niemals in ihrer Position verändert werden soll? Dann wäre es im Grunde verschwendete Rechenzeit, wenn Direct3D sämtliche Transformations- und Beleuchtungsschritte ausführen würde. Aus diesem Grund kann man Direct3D mitteilen, welche Berechnungen es für welche Vertices durchführen soll, und für welche nicht. Man unterscheidet zwischen 3 Arten von Vertices:

Untransformierte und nicht beleuchtete Vertices
Solche Vertices werden mit den Flags D3DFVF_XYZ und D3DFVF_NORMAL der D3DFVFFLAGS-Flags definiert. Sie bewirken, dass Direct3D jedes Vertex erst beim Rendern über Matrizen an seine gewünschte Position transformiert und anschliessen beleuchtet.

Untransformierte und beleuchtete Vertices
Vertices dieser Art haben das D3DFVF_XYZ-Flag, nicht aber D3DFVF_NORMAL. Die Vertices gelten also als bereits beleuchtet, was zur Folge hat, dass Direct3D keine Beleuchtungsberechnungen auf das Vertex anwendet sondern die Farben bzw. die Texturen ohne Helligkeitsabstufungen, die mit der Beleuchtung zusammenhängen könnten, ausgibt.

Transformierte und beleuchtete Vertices
Transformierte und beleuchtete Vertices werden durch das Flag D3DFVF_XYZRHW definiert, was gleichzeitig D3DVF_XYZ und D3DFVF_NORMAL ausschliesst. Die Koordinaten, die Sie solchen Vertices zuweisen, sind Bildschirmkoordinaten. Die Z-Koordinate bleibt zwar als Tiefenangabe erhalten, hat aber keine optischen Auswirkungen auf die Ausgabe des Objektes. Sie dient lediglich dazu, entscheiden zu können, welche Objekte von welchen überlappt werden.


Definieren eines eigenen Formates

Wenn Sie in Delphi ein bestimmtes Vertex-Format nutzen wollen, dann haben Sie zwei Möglichkeiten zur Auswahl. Entweder nehmen Sie eines der Standardvertexformate, die Ihnen die Jedi-Header bieten, oder Sie entwerfen Ihr eigenes Format. Dafür ist es notwendig, ein Record zu erstellen. Da die bereits vorhandenen Formate auch nichts anderes als Records sind, wollen wir uns diese einmal genauer anschauen. Um diese in Ihrer Anwendung nutzen zu können, tragen Sie in der Uses-Klausel bitte direct3d ein.

Hier das Format für untransformierte und unbeleuchtete Vertices:


  TD3DVertex = packed record
    x: TD3DValue;
    y: TD3DValue;
    z: TD3DValue;
    nx: TD3DValue;
    ny: TD3DValue;
    nz: TD3DValue;
    tu: TD3DValue;
    tv: TD3DValue;
  end;

Dieses Vertexformat besitzt also neben den üblichen Koordinaten noch den Normalvektor und die Texturkoordinaten. Denkbar wäre es natürlich ausserdem, noch eine Farbe als Eigenschaft einzuführen. Dann würde die Textur in die entsprechende Farbe getaucht werden.

So sieht das Format für untransformierte aber beleuchtete Vertices aus:


  TD3DLVertex = packed record
    x: TD3DValue;
    y: TD3DValue;
    z: TD3DValue;
    dwReserved: DWORD;
    color: TD3DColor;
    specular: TD3DColor;
    tu: TD3DValue;
    tv: TD3DValue;
  end;

Wie sie feststellen, verwenden wir hier keine Normale, dafür aber einen Specular-Wert. Color ist hierbei der Diffuse-Wert, also die Eigenfarbe des Vertex. Farben sind vom Typ TD3DColor, was LongWord entspricht. Wie man solchen Variablen RGB-Farben zuweist, erkläre ich später. Einen Tipp noch: da die SDK davon abrät dwReserved zu verwenden, sollten Sie lieber Ihr eigenes Format erstellen, und diese Variable Eigenschaft heraus nehmen.

Und das ist das Format für transformierte und beleuchtete Vertices:


  TD3DTLVertex = packed record
    sx: TD3DValue;
    sy: TD3DValue;
    sz: TD3DValue;
    rhw: TD3DValue;
    color: TD3DColor;
    specular: TD3DColor;
    tu: TD3DValue;
    tv: TD3DValue;
  end;

Dieses Format ist für Sie vor allem dann interessant, wenn Sie ein reines 2D-Objekt benutzen wollen, da die x- und y-Koordinaten bereits die Bildschirmkoordianten sind.

Bei allen drei Formaten könnte man übrigens die Texturkoordinaten auch weglassen. Dann würden die Vertices bzw. die Dreiecke, die daraus entstehen, nur in Farben getaucht werden.

So ein Record in dieser Art muss im Vorfeld entweder in Ihrer Type-Klausel definiert werden oder bereits in einer anderen Unit (wie zum Beispiel direct3D) enthalten sein. Dabei müssen Sie auf eine korrekte Einhaltung der Reihenfolge achten. So müssen am Anfang immer die x-, y- und z-Koordinaten stehen, danach ggf. die Normale-Koordinaten usw.. Die korrekte Reihenfolge entnehmen Sie bitte der Tabelle im Abschnitt Was ist das Flexible Vertex Format?


Formatübergabe an das Device

Vorhin sprachen wir die D3DFVFFLAGS an. Sie werden zum einen bei der Erstellung des Vertexbuffers benötigt, weil seine Grösse logischerweise vom Umfang des Formates abhängig ist, und zum anderen wird das Device vom Format informiert, damit es weiss, welche Renderschritte es überhaupt ausführen soll. Da sie also dieses Format immer wieder an verschiedenen Stellen übergeben müssen, bietet es sich an, eine Konstante dafür zu erstellen. Hier ein paar Beispiele:


// für TD3DVertex

const
  D3DFVF_VertexFormat = ( D3DFVF_XYZ or D3DFVF_NORMAL or D3DFVF_TEX1 );

// für TD3DLVertex

const
  D3DFVF_LVertexFormat = ( D3DFVF_XYZ or D3DFVF_RESERVED0 or D3DFVF_DIFFUSE or D3DFVF_SPECULAR or D3DFVF_TEX1 );

// für TD3DTLVertex

const
  D3DFVF_TLVertexFormat = ( D3DFVF_XYZRHW or D3DFVF_DIFFUSE or D3DFVF_SPECULAR or D3DFVF_TEX1 );

Diese 3 Formatkonstanten beziehen sich also auf die 3 Vertexformate, die im Abschnitt Definieren eines eigenen Formates vorgestellt wurden.

Ich hoffe, Ihnen ist nun klar geworden, wie Sie die (leicht eingeschränkte) Flexibilität des FVF nutzen können. Wenn nicht, dann wird Ihnen das nächste Kapitel, das nun das gelernte in die Praxis umsetzt, sicherlich alles nötige verständlich machen.