SelfDXD - Editorial

SelfDXD von Martin Pyka
SelfDXD
 | Editorial
 | Was ist SelfDXD? Vorwissen Die DirectX-Header Anmerkung zu der Befehlsrefernz Danksagung

Was ist SelfDXD?

SelfDXD steht für Self DirectX für Delphi und ist für deutschsprachige Delphi-Entwickler gedacht, die möglichst schnell und effizient DirectX lernen wollen. Das Ziel des Projekts SelfDXD ist es, sämtliche Befehle und Techniken zu erklären und zu dokumentieren, die für das Verständnis von DirectX notwendig sind. Dabei liegt der Schwerpunkt ganz klar auf DirectXGraphics, also dem grafischen Teil von DirectX. Da ich nur in meiner Freizeit an SelfDXD arbeiten kann, sind die Fortschritte entsprechend klein. Das Erstellen dieser Dokumentation bringt mir keine finanziellen Vorteile oder dergleichen.

Es gibt nur sehr wenige DirectX-Tutorials, die für Delphiprogrammierer geschrieben wurden (die Zahl der deutschen Tutorials lässt sich sogar an einer Hand abzählen). Die SDK existiert nur in Englisch und obwohl sie DirectX umfassend erklärt, ist sie für den ersten Einstieg meines Erachtens nicht sehr geeignet. SelfDXD ist so konzipiert, dass jeder, der einigermassen sicher mit Delphi programmieren kann, problemlos den Einstieg in DirectX finden sollte. Ein weiteres Ziel dieser Dokumentation ist es, alle Themen so zu erklären, dass möglichst keine Fragen offen bleiben.

SelfDXD befindet sich noch in seiner Anfangsphase und wird in den nächsten Monaten noch stark anwachsen. Mit dem Know-How, das bisher in dieser Dokumentation implementiert ist, lässt sich allerdings schon eine Menge anfangen. Zu wichtigen Kapiteln gibt es zudem Samples, die Sie in Delphi kompilieren können, und die Ihnen die gelernten Themen in einer Beispielanwendung demonstrieren. Alle Beispiele finden Sie in dem Verzeichnis /demo/.

Vorwissen

Keine Angst, Sie müssen zunächst kein Experte in Sachen Delphi sein, um DirectX erlernen zu können. Doch es ist von Vorteil, wenn Sie bereits mit Zeigern / Pointern und OOP (objekt-orientiertes Programmieren) vertraut sind. Das heisst also, Sie können Klassen erstellen und mit deren Instanzen umgehen. Da wir in DirectX nicht mit der VCL arbeiten, brauchen Sie in dieser Hinsicht keine speziellen Kenntnisse. Im grossen und ganzen sollte Ihnen also einfach nur der Aufbau eines Delphiprogramms geläufig sein. Alles, was DirectX angeht, werden Sie von Grund auf lernen.

Die DirectX-Header

Alle DirectX-Befehle sind in DLL-Dateien ausgelagert. Um auf diese zugreifen zu können wurden sogenannte Header-Dateien geschrieben, die JEDI-Header. Die aktuellsten Header können Sie unter www.crazyentertainment.net downloaden. Kopieren Sie diese in das lib/-Unterverzeichnis des Delphi-Ordners. Welche Teile der Header wann in die Anwendung mit eingebunden werden müssen, wird in den einzelnen Kapiteln angekündigt.

Anmerkung zu der Befehlsreferenz

In der [image] Direct3D Delphi Referenz und in der [image] Direct3DX Delphi Referenz sind die Namen der Records an die der SDK angelehnt, um ein Nachschlagen im Software Development Kit zu erleichtern. In Delphi geht jedem Recordname ein T vorraus. Da dies, wie gesagt, in der SDK nicht der Fall ist, wurde das T in der Referenzliste einfach weggelassen.

Danksagung

Ohne die direkte und indirekte Mithilfe von vielen Programmierern, die ich über das Internet kennengelernt habe, wäre dieses Projekt nur schwer möglich gewesen. Besonderen Dank gilt dabei besonders:

Jan Michalowsky
Der mich nicht nur über zahlreiche Gespräche im Chat angetrieben hat, weiter zu schreiben, sondern bei der Übersetzung der Renderstates aktiv mitgeholfen hat.

Neo
für sein tolles Forum bei den Neobrothers, wo ich auf viele meiner speziellen Fragen, Antworten fand, die ich in SelfDXD einbauen konnte.

den vielen Leute aus dem Forum
Ich kann unmöglich alle Leute aufzählen, die ich aus diversen Foren her kenne, und Dir mir mit Rat und Tat zur Seite gestanden habe oder mein Projekt zumindest moralisch unterstützten ;-).

den Cappuccino-Herstellern
Was würde ich nur machen ohne den inspirativen Milchkaffe, mit dem ich mich täglich aufputschte, um die Rückenschmerzen, die roten Augen und meinen Hunger zu betäuben. ;-)


SelfDXD

SelfDXD
 | DirectXGraphics
 | Theoretisches Vorwissen Die ersten Schritte Vertex- und Indexbuffer Licht und Schatten Texturen Direct3D Rahmeneinstellungen Direct3D Delphi Referenz Direct3DX Delphi Referenz Programme / Quellcodes / Klassen


SelfDXD: Theoretisches Vorwissen

SelfDXD --- [image] DirectXGraphic
Theoretisches Vorwissen


So arbeitet DirectX Graphics

SelfDXD --- [image] DirectX Graphics --- [image] Theoretisches Vorwissen
 | Matrizen
 | Einleitung Was ist eine Matrix? Translation Rotation Skalieren Multiplizieren von Matrizen

Einleitung

Dieses Kapitel bietet Ihnen eine kurze Einführung in das Thema Matrizen. Alle hier erklärten Dinge sind auf das Arbeiten mit Direct3D ausgerichtet. Die Theorie der Matrizenrechnung ist damit also noch lange nicht erschöpfend behandelt.

Was ist eine Matrix?

Eine Matrix ist, um es in Programmierersprache auszudrücken, ein zweidimensionales Array, das Zahlen speichert. In DirectX wird dabei eine ganz spezielle Matrix verwendet. Sie ist 4x4 Felder gross. In Delphi ist der Datentyp TD3DMatrix allerdings kein Array sondern ein Record:

TD3DMatrix = record
{ | _11 | _12 | _13 | _14 | }
 | _21 | _22 | _23 | _24
 | _31 | _32 | _33 | _34
 | _41 | _42 | _43 | _44
alle Elemente sind vom Typ Single

Die erste Zahl jeder Variable gibt die Zeile an, die zweite Zahl die Spalte.

Der Einsatz von Marizen hat folgenden Hintergrund: im Kapitel [image] Elemente der 3D-Welt werden Sie lernen, dass 3D-Körper in erster Linie aus Vertices bestehen, die zum Beispiel die Ecken von Körpern kennzeichnen. Für das Verschieben, Rotieren und Skalieren (Vergrössern, Verkleinern) von 3D-Modellen sind bestimmte Berechnungen notwendig. Diese Berechnungen sind jedoch bei allen Punkten eines Modells gleich. Um zum Beispiel beim Verschieben nicht für jeden Vertex den gleichen Term eingeben zu müssen, kann man sich der Hilfe einer Matrix bedienen. Eine Matrix wird nämlich wie folgt auf einen Vertex angewendet:

x_neu = | (x * _11) + (y * _21) + (z * _31) + _41
y_neu = | (x * _12) + (y * _22) + (z * _32) + _42
z_neu = | (x * _13) + (y * _23) + (z * _33) + _43

Leichter kann man es sich so erklären: Wenn man sich den Aufbau des Matrix-Record's anschaut, dann beziehen sich die ersten drei Spalten von links auf die Berechnung der x-, y- und z-Koordinaten der Vertices. Dabei werden die ersten drei Zeilen einer Spalte mit der x-, y- und z-Koordinate multipliziert und alle 3 Ergebnisse mit der vierten Zeile zusammen addiert. Die vierte Spalte wird bei der Berechnung von Vertices nicht benötigt. Sie hat eine andere Funktion, die in einem späteren Kapitel behandelt wird, für den Theorieteil aber erstmal nicht von Bedeutung ist.

Diese Form der Berechnung bietet Ihnen alle Möglichkeiten Ihr Objekt verschieben, skalieren oder rotieren zu können. Hier ein Beispiel:

Verschieben um 3 Pixel nach rechts |  | die entsprechende Matrix
x_neu = | (x * 1) + (y * 0) + (z * 0) + 3 | { | _11= 1 | _12= 0 | _13= 0 | _14= 0 | }
y_neu = | (x * 0) + (y * 1) + (z * 0) + 0 |  | _21= 0 | _22= 1 | _23= 0 | _24= 0
z_neu = | (x * 0) + (y * 0) + (z * 1) + 0 |  | _31= 0 | _32= 0 | _33= 1 | _34= 0
 |  |  | _41= 3 | _42= 0 | _43= 0 | _44= 1

Wichtig ist dass die Faktoren _11, _22, _33 den Wert 1 haben, da sonst der vorherige x-Wert nicht übernommen wird. Die Variable _41 gibt schliesslich die Verschiebung auf der x-Achse an.

Eine Matrix die in der Hauptdiagonalen (von oben links nach unten rechts) nur den Wert 1 besitzt und ansonsten nur 0, nennt man Einheitsmatrix.

Einheitsmatrix
{ | 1 | 0 | 0 | 0 | }
 | 0 | 1 | 0 | 0
 | 0 | 0 | 1 | 0
 | 0 | 0 | 0 | 1

Wendet man diese Matrix auf Vertices an, kommen die gleichen Koordinaten dabei heraus.

Translation

Translation meint, ein 3D-Objekt um eine bestimmte Anzahl an Koordinaten zu verschieben. Dabei bleibt die Grösse und die Ausrichtung des Objektes die gleiche.

Transformationsmatrix
{ | 1 | 0 | 0 | 0 | }
 | 0 | 1 | 0 | 0
 | 0 | 0 | 1 | 0
 | Tx | Ty | Tz | 1

Zur Verschiebung eines 3D-Objektes, werden der Matrix einfach nur die Werte, um die das Objekt verschoben werden soll, in die Additionsfelder eingegeben.

Rotation

Ein Objekt können Sie sowohl auf der x-, y-Achse als auch auf der z-Achse rotieren lassen. Dazu müssen Sie mit den Winkeklfunktionen Sinus und Cosinus arbeiten. Grundlagen zur Rotation werden im Kapitel [image] Berechnung von Rotationen behandelt.

Rotation um die x-Achse
{ | 1 | 0 | 0 | 0 | }
 | 0 | cos(a) | sin(a) | 0
 | 0 | -sin(a) | cos(a) | 0
 | 0 | 0 | 0 | 1

Rotation um die y-Achse
{ | cos(a) | 0 | -sin(a) | 0 | }
 | 0 | 1 | 0 | 0
 | sin(a) | 0 | cos(a) | 0
 | 0 | 0 | 0 | 1

Rotation um die z-Achse
{ | cos(a) | sin(a) | 0 | 0 | }
 | -sin(a) | cos(a) | 0 | 0
 | 0 | 0 | 1 | 0
 | 0 | 0 | 0 | 1

Diese drei Tabellen zeigen, wie Matrizen aussehen, die zur Berechnung von Rotationen entlang der verschiedenen Achsen notwendig sind. a bezeichnet hierbei den Winkel im RAD-Format. Dabei sind 2*Pi=360°.

Skalieren

Eine Matrix können Sie auch dazu verwenden, 3D-Objekte in Ihrer Grösse zu verändern. Im Prinzip werden dabei alle Punkte des Objektes um einen bestimmten Faktor von ihrem Ursprung verschoben.

Skalieren
{ | Sx | 0 | 0 | 0 | }
 | 0 | Sy | 0 | 0
 | 0 | 0 | Sz | 0
 | 0 | 0 | 0 | 1

Sx, Sy und Sz sind dabei die Faktoren, um die das Objekt bzw. Ihre einzelnen Punkte skaliert werden.

Multiplizieren von Matrizen

Durch das Multiplizieren von Matrizen können Sie mehrere Veränderungen eines Objektes in einer Matrix zusammenfassen. Wenn Sie zum Beispiel ein Objekt der x-Achse entlang rotieren möchten, um es anschliessend nach oben zu verschieben und grösser zu skalieren, dann können Sie alle drei Matrizen durch das Multiplizieren zusammenfassen und müssen somit nur eine Matrix an Ihrem Objekt anwenden.

Das Multiplizieren selbst ist bei Matrizen ein wenig komplizierter. Allerdings bietet DirectX Ihnen eine Funktion, die Ihnen die Multiplikation zweier Matrizen abnimmt, so dass wir uns selbst darum nicht kümmern brauchen. Daher verzichte ich hier auf eine genaue Erklärung. Im Internet werden Sie hierzu jedoch erschöpfendes Material finden. Für uns reicht es jedoch zu wissen, dass eine Multiplikation zweier Matrizen immer bewirkt, dass die Bewegungen, die in den beiden Matrizen verankert sind, zusammengefasst werden.


Berechnung von Rotationen

SelfDXD --- [image] DirectX Graphics --- [image] Theoretisches Vorwissen
 | Berechnung von Rotationen
 | Einleitung Sinus, Cosinus und der Einheitskreis Rotieren in 2D Rotieren in 3D

Einleitung

Wenn Sie mit der Programmierung einer dreidimensionalen Anwendung beginnen, werden Sie feststellen, dass das Rotieren von Objekten ein sehr wichtiges Element in der Erstellung von Animationen sein kann. In diesem Kapitel geht es um die reine Mathematik, die hinter dem Rotieren von Punkten um eine Achse steht. Das Kapitel wendet sich an alle, die in der Schule dieses Thema, aus welchen Gründen auch immer, versäumt haben.

Dieses Kapitel erklärt die Materie in einem zweidimensionalen Koordinatensystem. Doch alle hier erwähnten Dinge lassen sich problemlos in die dritte Dimension übertragen.

Sinus, Cosinus und der Einheitskreis

Ja da war doch irgendwas, höre ich Sie jetzt sagen! In der Tat, Sinus und Cosinus haben irgendwas mit einem Einheitskreis zu tun. Ein Einheitskreis ist ein Kreis mit dem Radius 1. Das heisst die Strecke vom Mittelpunkt des Kreises zum Rand ist genau eine Längeneinheit lang. Wenn wir nun einen Punkt haben, den wir von den Koordinaten (1, 0), also dem rechtesten Punkt im Einheitskreis, aus um einen bestimmten Winkel a verschieben wollen, dann liefert uns cos(a) die x-Koordinate des Punktes auf dem Einheitskreis und sin(a) die y-Koordinate.

Wenn wir den Punkt also zum Beispiel um 90° bewegen, erhalten wir: cos(90) = 0 und sin(90) = 1. Die Koordinaten unseres Punktes wären demzufolge dann also (0, 1).

Nun kommt beim Rechnen mit Winkelfunktionen mit dem Computer noch eine Besonderheit hinzu. Der Computer erwartet als Eingabe für sin() und cos() keinen Winkel sondern ein Bogenmass, also den Umfang des Kreisabschnittes. Der Umfang des Einheitskreises beträgt 2*Pi. Pi ist eine Zahl mit unendlich vielen Stellen nach dem Komma (Pi = 3,1415926535897932384626433832795...). Für das Arbeiten am Computer reichen jedoch für eine hohe Genauigkeit die ersten 9-13 Stellen.

Um also aus dem Winkel das Bogenmass zu erhalten rechnen wir: Winkel / 180 * PI
In der unteren Grafik ist aB demzufolge das Bogenmass, bei dem die selben Koordinaten herauskommen, wie beim oberen Beispiel die Eingabe des Winkels a.

Rotieren in 2D

Um also einen Punkt, oder im Spiel ein ganzes Objekt um eine Achse rotieren zu lassen, müssen Sie lediglich kontinuirlich den Winkel verändern um den der Punkt von seiner Ausgangslage aus gedreht werden soll. Angenommen Sie haben nun einen Punkt, der r Einheiten von der Achse entfernt um diese kreisen soll. Da wir bei Berechnungen mit Sinus und Cosinus immer von einem Einheitskreis ausgehen, bei dem der Radius also 1 beträgt, muessen wir die Ergebnisse, die wir mit sin() und cos() erhalten lediglich mit r multiplizieren, um den Radius auf r zu erhöhen.

Ein Beispiel: Der Punkt soll in einem Abstand von 5 Einheiten um den Nullpunkt gedreht werden. Der gewünschte Winkel ist 40° Grad. Und so sieht die Rechnung dazu aus:

  radWinkel:= 40 / 180 * Pi;            // radWinkel = 0.698131...
  x_koordinate:= cos( radWinkel ) * 5;  // x_koordinate = 3,830222...
  y_koordinate:= sin( radWinkel ) * 5;  // y_koordinate = 3,213938...

Und damit kennen wir die neuen Koordinaten, an denen wir unseren Punkt oder unser 3D-Objekt postieren müssen.

Rotieren in 3D

Wenn Sie in einem dreidimensionalen Raum einen Punkt rotieren lassen möchten, dann gibt es im Prinzip zwei Arten von Rotationsberechnungen. Zum einen die Rotationen, bei denen sich nur zwei Koordinaten verändern (also zum Beispiel nur x und y oder x und z oder y und z) und zum anderen die Rotationen, bei denen sich alle drei Koordinaten verändern. Wenn sich nur zwei Koordinaten verändern, dann lässt sich das Problem auf 2D-Ebene lösen. Dann hilft Ihnen der obere Abschnitt weiter. Hier soll es um Rotationen gehen, die (umgangssprachlich gesagt) schräg im Raum stattfinden, in etwa so, wie es der folgende Screenshot zu verdeutlichen versucht.

Der Rahmen um die Rotationskurve soll zeigen, dass hier sowohl die Breite wie die Höhe als auch die Tiefe eine Rolle spielen. Wir müssen also die x-, y und z-Koordinaten berechnen, da sich alle drei Koordinaten je nach gewünschtem Winkel verändern.

Die Lösung für dieses Problem ist im Prinzip ganz einfach. Sie berechnen die Rotation zunächst nur auf zweidimensionaler Ebene, beziehen also zum Beispiel nur die x- und z-Koordinate mit ein. Damit zieht Ihr Objekt, dass Sie drehen lassen wollen, einen Kreis, der nur zwei Ebenen verwendet. Wenn Sie also den Kreis noch ein wenig anwinkeln wollen, wie es die obere Darstellung andeutet, muessen Sie jede Koordinate jeweils nochmals um ihre Achse drehen lassen, diesmal aber in Einbeziehung der y-Achse.


Elemente der 3D-Welt

SelfDXD --- [image] DirectX Graphics --- [image] Theoretisches Vorwissen
 | Elemente der 3D-Welt
 | Einleitung Das Koordinatensystem Vektoren Vertices und Dreiecke Primitiven und Polygone Normale Reihenfolge, in der Vertices definiert werden Wie Vertices zu Primitiven werden

Einleitung

Dieses Kapitel klärt lediglich das Grundlegendste an Basiswissen ab bezüglich der Bestandteile der dreidimensionalen Darstellung. Die spezielle Theorie zum Definieren der Vertices, der Funktionsweise der Beleuchtung oder der Art und Weise, wie Texturiert wird, wird in den jeweiligen Kapiteln erläutert, bevor es in die Praxis geht.

Das Koordinatensystem

Die meisten Anwendungen benutzen zwei Arten des kartesischen Koordinatensystems: das linkshändige oder das rechtshändige Koordinatensystem. Beiden gemeinsam ist die Tatsache, dass die x-Achse nach rechts und die y-Achse nach oben verläuft. Um sich die Richtung der z-Achse leichter merken zu können gibt es folgenden Trick: man bewegt die linke oder die rechte Hand so, dass die Finger in die x-Richtung zeigen und die Handinnenfläche nach oben. Die Richtung, in die der Daumen zeigt, ist die Richtung der z-Achse.

DirectX8.0 nutzt das linkshändige Koordinatensystem. Falls Sie also Anwendungen mit einem rechtshändigen Koordinatensystem nach DirectX übertragen wollen, ist es notwendig, dass Sie bestimmte Änderungen an Ihren 3D-Objekten vornehmen. Darauf werden wir jedoch später noch näher eingehen.

Vektoren

Vektoren sind ein wesentlicher Bestandteil in der Programmierung mit DirectXGraphics. Sie finden in der 3D-Programmierung sehr vielseitige Anwendungsmöglichkeiten. Eine vollständige Erläuterung dieser Thematik würde den Rahmen dieser Dokumentation sprengen, deshalb sei hier nur das nötigste erwähnt. Die Grafik zeigt einen Vektor im zweidimensionalen Raum. Gerichtete Vektoren werden grundsätzlich als Pfeil dargestellt.

Vektoren geben also eine Richtung an, die in verschiedenen Bereichen der 3D-Programmierung verwendet werden können. Zum Beispiel wird die Position von Vertices über Vektoren definiert. Desweiteren spielen Vektoren im Zusammenhang mit Matrizen eine wichtige Rolle. Im Kapitel [image] Transformationen und Tiefenbuffer werden wir näher darauf eingehen. Eine konkrete Verwendung finden Vektoren aber zum Beispiel auch in der Einstellung des imaginären Auges, mit dem die 3D-Szene betrachtet wird. Lage und Richtung werden auch hier wieder als Vektoren übergeben.

Vertices und Dreiecke

Ein Vertex ist ein einzelner Punkt im Raum. Er ist die Grundlage der dreidimensionalen Darstellung. Drei Vertices lassen sich als Dreieck darstellen und aus Dreiecken dreidimensionale Körper. Die Tatsache, dass man alle dreidimensionalen Modelle aus Dreiecken zusammensetzt, beruht auf einer besonders praktischen Eigenschaft von Dreiecken: egal wie man die Punkte im Raum positioniert, das Dreieck hat garantiert immer eine glatte Fläche. Rein theoretisch könnte man mit vier Vertices auch Rechtecke bilden, doch das Füllen dieser Rechtecke mit einer Textur wäre zu zeitintensiv in anbetracht der Tatsache, dass die vier Vertices nicht unbedingt alle auf einer Ebene liegen müssen. Deshalb greift man lieber auf Dreiecke zurück.

Zunächst verfügt jeder Vertex über seine Positionskoordinaten x, y und z. Doch ein Vertex kann noch über viel mehr Informationen verfügen. Sie können zum Beispiel jedem Vertex einen Farbe zuweisen. Wenn der Vertex nur als Punkt dargestellt wird erscheint der Vertex in seiner jeweiligen Farbe. Wenn der Vertex jedoch in ein Dreieck mit eingebunden ist, wird das Dreieck bis zu einem gewissen Grad, den Sie auch selbst bestimmen können, in die jeweilige Farbe eingefärbt. Das Bild zeigt wie so etwas zum Beispiel aussehen kann. Hier haben die Vertices die Farben Rot, Grün und Türkis. Alle drei Farben haben die gleiche Stärke und sind deshalb zu gleichen Teilen im Dreieck vertreten. Diese Farbgebungen kann man auch dazu benutzen Texturen, die man auf das Dreieck legt, in eine gewünschte Farbe zu tauchen. Sie können zum Beispiel damit auch bestimmte Bereiche dunkler erscheinen lassen, in dem Sie einfach den jeweiligen Ecken eine dunkle Farbe zuweisen. Darüberhinaus können Vertices Koordinaten für mehrere Texturen besitzen und sogenannte Normalvektoren.

Primitiven und Polygone

Eine 3D-Primitive ist eine Menge an Vertices, die ein Objekt bilden. Die einfachste Primitive ist ein einzelner Punkt. Primitiven können aber auch Dreiecke bzw. Dreiecksaneinanderreihungen sein.

Oftmals sind Primitiven auch Polygone. Ein Polygon ist ein geschlossenes 3D-Modell, dass sich aus mindestens 3 Vertices definiert. Hier sehen Sie einen Würfel, ein Polygon bestehend aus 8 Vertices die über Dreiecke miteinander verbunden sind.

Obwohl Ihnen in DirectXGraphics nur Dreiecke als kleinstes Polygon zur Verfügung stehen, können Sie damit 3D-Körper erzeugen, die rund bzw. gewölbt wirken. Dies hängt allein davon ab, wieviele Dreiecke Sie verwenden.

Normale

Jeder Vertex kann über eine Normale verfügen. Eine Normale ist ein Vektor, der angibt, wie ein Vertex ausgerichtet ist. Dies ist notwendig, wenn Lichtquellen mit ins Spiel kommen. Dann kann anhand der Ausrichtung der Normale zur Lichtquelle ermittelt werden, wie stark ein Vertex bzw. die damit verbundene Fläche angestrahlt wird.

Normalen müssen nicht unbedingt orthogonal zur Fläche verlaufen. Wenn Sie in eine nicht orthogonale Richtung zur Dreiecksfläche zeigen, lassen sich Wölbungen vortäuschen. Die beiden unteren Kugeln demonstrieren den Unterschied.

Die linke Kugel benutzt überhaupt keine Normal-Vektoren. DirectXGraphics rendert daher jede Fläche mit einer ganzheitlichen Schattierung. Unter geschickter Benutzung von Normal-Vektoren kann man jedoch diese Schattierung interpolieren lassen, so dass man den Eindruck erhält, die Fläche wäre gewölbt.

Reihenfolge, in der Vertices definiert werden

Bei Dreiecken wird für gewöhnlich unterschieden zwischen einer Vorder- und einer Rückseite. Die Vorderseite eines Dreiecks wird gerendert, die Rückseite nicht. Als Vorderseite gilt standardmässig die Seite, bei der die Vertices im Uhrzeigersinn angeordnet sind. Grundsätzlich können Sie aber in DirectXGraphics auch einstellen, dass Dreiecke, bei denen die Vertices gegen den Uhrzeigersinn angeordnet sind, gerendert werden sollen oder das beide Seiten in jedem Fall gerendert werden sollen. Das Nicht-Rendern einer Dreiecksseite aufgrund der Vertex-Anordnung bezeichnet man als Culling.

Wie Vertices zu Primitiven werden

Um 3D-Modelle von Hand zu erstellen, können Sie Vertices auf 6 verschieden Arten miteinander verbinden.

Punktliste (Point List)

Jeder Vertex wird als Punkt gerendert und stellt damit eine einzelne Primitive dar.

Linienliste (Line List)

Jeder Vertex wird mit dem nachfolgenden verbunden. Danach wird mit dem nächsten Vertex wieder eine neue Linie begonnen.

Linienkette (Line Strip)

Alle Vertices werden der Reihe nach miteinander verbunden.

Dreiecksliste (Triangle List)

Jeweils drei aufeinanderfolgende Vertices werden zu einem Dreieck verbunden. Mit dem nächsten Vertex wird wieder ein neues Dreieck begonnen. Diese Technik eignet sich für die meisten 3D-Modelle, wenn folgende Technik nicht funktioniert.

Dreieckskette (Triangle Strip)

Die ersten 3 Vertices werden zu einem Dreieck verbunden, jedes folgende Vertex wird mit den letzten beiden Vertices verbunden. So bilden V1, V2, V3 ein Dreieck, V2, V3, V4 ein Dreieck usw. Dies ist die effizienteste Art Dreiecke zu Rendern und damit 3D-Modelle zu erstellen. Je nach Geometrie jedoch nicht immer anwendbar.

Dreiecksfächer (Triangle Fan)

Hierbei werden V1, V2, V3 zu einem Dreieck verbunden, danach V1, V3, V4 usw.. Diese Technik ist vorallem bei kegelartigen Objekten sehr sinnvoll.


SelfDXD

SelfDXD --- [image] DirectXGraphic
Die ersten Schritte


SelfDXD

SelfDXD --- [image] DirectXGraphic --- [image] Die ersten Schritte
 | Direct3D8 initialisieren
 | Was ist Direct3D8? Direct3D8 initialisieren Direct3D8 freigeben

Was ist Direct3D8?

Direct3D8 ist das Hauptinterface Ihrer Direct3D-Anwendung. Mit diesem Interface selbst kann man grafisch gesehen eigentlich noch gar nichts machen, doch es ist trotzdem notwendig, weil Sie über dieses Interface Grafikkarten- und Bildschirmparameter abfragen können. Das ist zum Beispiel wichtig, um die Bildschirmeinstellungen Ihres Programms einstellen zu können. Direct3D8 ist das erste Objekt, dass ihre Anwendung erstellt, und das letzte, dass ihre Anwendung freigibt.

Um Direct3D8 verwenden zu können ist es notwendig, dass Sie in den uses-Bereich Ihres Programms die Datei DirectXGraphics angeben. Wenn Sie eine normale Anwendung unter Delphi erstellen, schreiben Sie DirectXGraphics zu den anderen Units hinzu:

unit Main;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, DirectXGraphics;

Direct3D8 initialisieren

Um Direct3D8 und damit DirectXGraphics nutzen zu können, müssen Sie eine Variable definieren, der Sie das Direct3D8-Interface zuweisen. Das Initialisieren erfolgt über einen einfachen Befehl. Nach dem Beispiel folgt die Erklärung.

var
  d3d8: IDirect3D8;

procedure d3d_initialisieren;
begin
  d3d8:= Direct3DCreate8( D3D_SDK_VERSION );
  if d3d8 = nil then
    exit;
end;
function Direct3DCreate8(SDKVersion : Cardinal) : IDirect3D8;

Mit der Funktion Direct3DCreate8 weisen Sie einer Variable das Direct3D8-Interface zu. D3D_SDK_VERSION ist dabei eine Konstante die sicherstellt, dass Sie immer die richtige DirectX-Version benutzen. Das ist zum Beispiel wichtig, wenn Sie eine neuere DirectX-Version installieren und auch auf die neuen Befehle zugreifen wollen. In DirectX 8.0 steht diese Konstante für die Zahl 120. In der nächsten Version kann das aber anders sein. Wenn Sie immer die aktuellste DirectX-Version verwenden wollen, benutzen Sie immer die Konstante D3D_SDK_VESION. Wenn Sie immer nur DirectX 8.0 verwenden wollen, können Sie auch einfach nur 120 eingeben.

Gewöhnen Sie sich bei solchen Initialisierungen auch gleich an, zu kontrollieren, ob diese Initiallisierung auch wirklich erfolgte. Wenn Ihr Programm auf möglichst vielen Rechnern laufen sollen, sind diese Abfragen notwendig, um schnell den Grund dafür zu finden, warum Ihr Programm nicht funktioniert.

Sie haben nun Direct3D8 initialisiert, haben grafisch gesehen aber noch ungefähr gar nichts davon. Denn das Direct3D8-Interface dient nur dazu, die Verbindung zu den Hardwarekomponenten Ihres PC's zu gewährleisten (also Grafikkarte, Monitor), um auf diese zugreifen zu können.

Direct3D8 freigeben

Da das Direct3D8-Interfacae Speicher belegt, sollten Sie nie vergessen, diesen auch wieder freizugeben, wenn das Programm beendet wird. Wider erwarten macht das Delphi nämlich nicht automatisch. So könnte eine Lösung aussehen:

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  if d3d8 <> NIL then d3d8:= NIL;
end;

Da d3d8 ja ein Interface und keine Klasse ist, gibt es auch nicht die .free-Prozedur. Ein einfaches NIL-setzen ist hier die Lösung.


SelfDXD

SelfDXD --- [image] DirectXGraphic --- [image] Die ersten Schritte
 | Das Direct3DDevice initiallisieren
 | Was ist das Direct3DDevice? Direct3D8.CreateDevice Ein Beispiel Direct3DDevice8 freigeben

Was ist das Direct3DDevice?

Das Direct3DDevice ist sozusagen die Bühne Ihres Theaterstückes. So, wie Sie als Regisseur im Theater bestimmen können, wie weit der Vorhang geöffnet, die Szene beleuchtet und die Darsteller positioniert sein sollen, so können Sie mit dem Device von der Auflösung des Bildschirms über die Beleuchtung, die Texturzuweisung bis hin zur Kameraeinstellung alles nötige einstellen. Das Device ist sozusagen Ihr Ansprechpartner in Sachen Präsentation und bietet dafür eine Fülle an Einstellungsmöglichkeiten. Fast alles, was Sie im grafischen Bereich einstellen möchten funktioniert über dieses Device.

Bevor wir jedoch dieses Device nutzen können, müssen wir es zuerst initialisieren, d.h. also mal wieder einer Variablen dieses Device zuweisen und es so einstellen, wie wir es für unsere Bedürfnisse brauchen. Dieses Kapitel beschäftigt sich also nur mit dem Initialisieren des Direct3DDevice. Es wird aber noch Bestandteil vieler weiterer Kapitel sein.

Direct3D8.CreateDevice

Falls Sie glauben sollten, dass man mit diesem simplen Befehl mal eben das Device kreiert, dann haben Sie nur zum Teil Recht. Tatsächlich ist dies der Schlüsselbefehl, mit dem die Initialisierung erfolgt. Doch dieser Funktion werden Parameter zugewiesen, die wir erstmal genauer kennenlernen müssen. Die Syntax dieses Befehls sieht folgendermassen aus:

function CreateDevice(
                      const Adapter : Cardinal;
                      const DeviceType : TD3DDevType;
                            hFocusWindow : HWND;
                            BehaviorFlags : LongWord;
                      var   pPresentationParameters : TD3DPresent_Parameters;
                      out   ppReturnedDeviceInterface : IDirect3DDevice8
                     ) : HResult;

Ja, das ist sozusagen der Hacken an der ganzen Sache. Im Prinzip ist es nur dieser kleine Befehl, doch der hat es in sich. Nachfolgend wird jeder einzelne Parameter erkläärt. Wenn Sie etwas auf Anhieb nicht ganz verstehen sollten, dann seien Sie unbesorgt. Spätestens beim Beispiel, dass weiter unten folgt, wird Ihnen vermutlich wieder alles klar werden.

Adapter: Adapter ist nichts anderes als die Grafikkarte. Die meisten PC's haben nur eine Grafikkarte, die auch zugleich die Standardgrafikkarte ist. Diese können Sie benutzen, in dem Sie einfach den Parameter D3DADPATER_DEFAULT übergeben. Für den Fall, dass sie bei mehreren Grafikkarten, die zur Auswahl stehen, nicht die Standardgrafikkarte benutzen möchten, gibt es Methoden, die wir in einem späteren Kapitel näher erläutern werden.

DeviceType: Mit diesem Parameter können Sie einstellen, ob Ihre Anwendung hardwarebeschleunigt laufen soll, oder nicht. Verwenden Sie hierzu eine Konstante aus [image] D3DDEVTYPE.

hFocusFlags: Hier übergeben Sie Ihrem Device das Handle des Fensters, in dem Ihre Anwendung dargestellt werden soll.

BehaviorFlags: Wie der Name schon andeutet, können Sie mit diesem Parameter noch bestimmte Verhaltenseigenschaften einstellen, insbesondere was die Berechnung der Vertices angeht. Hierbei müssen Sie einen oder mehrere Konstanten, die durch die [image] D3DCREATEFLAGS-Aufzählung definiert sind, verwenden.

pPresentationParameters: Über diesen Parameter wird ein Record übergeben, dass Sie im Vorfeld ausgefüllt haben müssen. Sie stellen damit zum Beispiel die Auflösung im Vollbildmodus ein, und wieviele Backbuffer sie verwenden möchten. Das Record ist vom Typ [image] D3DPRESENT_PARAMETERS.

ppReturnedDeviceInterface: Hier geben Sie die Variable an, die als Direct3DDevice8 zurückgegeben werden soll.

Ein Beispiel

Ich kann mir vorstellen, wie Sie sich jetzt fühlen. Auf der einen Seite haben Sie das Gefühl, etwas dazugelernt zu haben, aber auf der anderen Seite wissen Sie überhaupt nicht, wie Sie das gelernte Wissen anwenden sollen. Ich bin sicher, spätestens nach diesem Beispiel wird Ihnen alles klar sein.

Im nachfolgenden Quellcode werden sowohl Direct3D8 als auch das Direct3DDevice initialisiert:

var
  d3d8    : IDirect3D8;
  d3ddev8 : IDirect3DDevice8;

procedure TForm1.FormCreate( Sender : TObject );
var
  d3ddm : TD3DDISPLAYMODE;
  d3dpp : TD3DPRESENT_PARAMETERS;
  DeviceTypeFlag : Cardinal;
begin
// Erstellen von Direct3D8
  d3d8 := Direct3DCreate8( D3D_SDK_VERSION );
  if ( d3d8 = nil ) then
    Application.Terminate;

// Auslesen der aktuellen Bildschirmeinstellungen (Auflösung, Format, Refreshrate)
  if ( d3d8.GetAdapterDisplayMode( D3DADAPTER_DEFAULT, d3ddm ) <> D3D_OK ) then
    Application.Terminate;

// Alle Werte des Records d3dpp werden auf 0 bzw Nil gestellt
  ZeroMemory( @d3dpp, sizeof( d3dpp ) );

// Das zukünftige Device wird über das Record eingestellt
  d3dpp.Windowed := TRUE;
  d3dpp.SwapEffect := D3DSWAPEFFECT_DISCARD;
  d3dpp.BackBufferFormat := d3ddm.Format;
  d3dpp.EnableAutoDepthStencil := TRUE;
  d3dpp.AutoDepthStencilFormat := D3DFMT_D16;

// Das Device wird erstellt
  if ( d3d8.CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, handle,
       D3DCREATE_SOFTWARE_VERTEXPROCESSING,
       d3dpp, d3ddev8 ) <> D3D_OK ) then
    Application.Terminate;
end;

Bei dieser Initialisierungsprozedur wird ein Device erstellt, dass im Fenstermodus arbeitet und über einen Tiefenbuffer verfügt. Da im Fenstermodus die aktuelle Bildschirmauflösung übernommen wird, ist es nicht nötig diese nochmal seperat einzustellen. Lediglich das Format muss übergeben werden. Das aktuelle Bildschirmformat erhalten Sie mit der Funktion d3d8.GetAdapterDisplayMode( D3DADAPTER_DEFAULT, d3ddm ). Sie füllt ein Record vom Typ [image] D3DDISPLAYMODE mit den entsprechenden Informationen zu der derzeitigen Bildschirmauflösung.

Würden Sie Ihre Anwendung gerne im Vollbildmodus mit einer Auflösung von 800x600x16 laufen lassen, müssten Sie folgendes schreiben:

  d3dpp.Windowed := FALSE;
  d3dpp.BackBufferWidth := 800;
  d3dpp.BachBufferHeight := 600;
  d3dpp.BackBufferFormat := D3DFMT_R5G6B5;

Direct3D8Device freigeben

Auch bei dem Device sollten Sie nicht vergessen, dieses am Ende des Programms freizugeben. Wie beim Direct3D8-Interface geht das über eine kurze If-Abfrage am besten. Beachten Sie, dass Sie das Device vor dem Direct3D8-Interface freigeben müssen.

  if d3ddev8 <> nil then d3ddev8 := nil;

Obwohl Sie bereits jetzt schon viele neue Befehle und Parameter kennengelernt haben, können Sie immer noch nichts konkretes darstellen. Dazu werden Sie in den nun folgenden Kapiteln endlich was erfahren. Zumindest können Sie jetzt schonmal ein eigenes Device einrichten und haben damit schonmal Ihr wichtigstes Werkzeug, um grafisch loslegen zu können.


Der Rahmen Ihrer Anwendung

SelfDXD --- [image] DirectXGraphic --- [image] Die ersten Schritte
 | Der Rahmen Ihrer Anwendung
 | Was ist Ihr Rahmen? Direct3DDevice8.BeginScene und .EndScene Direct3DDevice8.Clear(...) Direct3DDevice8.Present(...) Ein paar Beispiele Ort der Rahmenhandlung Kleines Fazit

Was ist Ihr Rahmen?

In diesem Kapitel werden Sie zum ersten Mal etwas lernen, was Sie in Ihrer Endanwendung auch sehen werden. Dies ist zwar nur eine Farbe, in der Ihre Anwendung gefärbt wird, aber immerhin. In diesem Kapitel lernen Sie den letzten Teil des Grundgerüstes, der Bestandteil jeder DirectX-Anwendung ist.

DirectX arbeitet mit zwei Speicherplätzen für den Bildschirm, dem sogenannten Frontbuffer und dem Backbuffer. Der Frontbuffer ist der Speicherbereich, der auf dem Bildschirm sichtbar ist. Der Backbuffer ist von der selben Grösse, wie der Frontbuffer, und kann mit Ihm ausgetauscht werden, so dass der Backbuffer zum Frontbuffer wird und umgekehrt. In dem Sie Ihre 3D-Szene immer jeweils auf dem Backbuffer generieren und mit dem Frontbuffer austauschen, können Sie flimmerfreie Animationen erzeugen, da dass Vertauschen der beiden Buffer über Zeiger gesteuert wird und deshalb sehr schnell geht. In diesem Kapitel lernen Sie, wie Sie Zugriff auf den Backbuffer bekommen, diesen in eine Farbe tauchen und anschliessend mit dem Frontbuffer vertauschen.

Direct3DDevice.BeginScene und .EndScene

Alle Renderbefehle müssen zwischen folgenden beiden Befehlen liegen:

  d3ddev8.BeginScene;
  // Render-Befehle
  d3ddev8.EndScene;

d3ddev8 ist hierbei das aus den vorigen Kapiteln verwendete IDirect3DDevice8. Diese Befehlsfolge können Sie ruhig mehrmals hintereinander benutzen. Diese Befehle haben noch keinen Einfluss auf den Frontbuffer. In den meisten Anwendungen wird es allerdings nur einmal nötig sein, diese Befehlsfolge pro Frame zu verwenden.

Direct3DDevice.Clear(...)

Mit dem Clear-Befehl Ihres Devices löschen Sie Ihren Backbuffer um ihn von Grund auf neu beschreiben zu können. Dieser Befehl ist also nicht zwingend notwendig. Wenn Sie die Funktion jedoch nicht verwenden und eine Animation zeigen, dann werden Ihre Objekte, die Sie bewegen, eine Art Spur hinter sich her ziehen, weil die Bereiche, in denen das Objekt gezeigt wird, nicht gelöscht werden. Der Befehl sieht so aus:

  function Clear(const Count : LongWord;
                       pRects : PD3DRect;
                 const Flags : LongWord;
                 const Color : TD3DColor;
                 const Z : Single;
                 const Stencil : LongWord) : HResult;

Count: Gibt die Anzahl der Rechtecke an, die im Array pRects gespeichert ist. Dies hat folgenden Sinn. Sie können bestimmen, ob der ganze Bildschirm gelöscht werden soll, oder nur bestimmte Bereiche. Entscheiden Sie sich für das Erstere, dann muss dieser Parameter 0 sein und pRects NIL. Wollen Sie jedoch, dass nur bestimmte Bereiche gelöscht werden, dann bestimmen Sie mit diesem Parameter, wieviele der Rechtecke aus dem Array pRects gelöscht werden sollen.

pRects: Erstes Element eines Arrays des Typs [image] D3DRect. Die Koordinaten, die Sie in diesem Record angeben, entsprechen den Bildschirmkoordinaten. Möchten Sie alles sichtbare löschen, können Sie als Parameter auch einfach NIL übergeben. Dann muss Count 0 sein.

Flags: Eine oder mehrere Konstanten der [image] D3DClearFlags, mit denen Sie angeben, was genau Sie löschen möchten. Denn neben der sichtbaren Oberfläche gibt es noch den sogenannten Z-Buffer und den Stencil-Buffer. Die letzten beiden werden wir in einem späteren Kapitel noch ausführlicher behandeln. Beachten Sie, dass mindestens ein Flag angegeben sein muss.

Color: Ein Farbwert, mit der der zu löschende Bereich überdeckt wird. TD3DColor ist dabei vom Typ Longword. Sie übergeben also Delphi im Prinzip eine 32 Bit-Zahl, die für eine Farbe steht. Die Jedi-Header bieten Funktionen, mit denen Sie bequem die gewünschten Farbwerte übergeben können.

Z: Dieser Parameter wird dem Z-Buffer übergeben. Er bestimmt, wie weit Sie in der Tiefe noch Objekte erkennen können. Z kann zwischen 0.0 und 1.0 betragen, wobei 0.0 die nächste Distanz zum Betrachter ist und 1.0 die entfernteste.

Stencil: Dieser Wert bestimmt, wieviel Sie vom Stencil-Buffer löschen möchten. Stencil kann zwischen 0 und 2n-1 liegen, wobei n die Bittiefe des Stencil-Buffers angibt.

Direct3DDevice8.Present(...)

Nachdem Sie alle Dinge, die Sie darstellen wollen, gerendert und mit der EndScene-Funktion das Rendern abgeschlossen haben, bringen Sie mit dem .Present-Befehl den Inhalt des Backbuffers auf den Bildschirm und erhalten einen neuen Backbuffer für die nächste Renderprozedur.

  function Present(pSourceRect, pDestRect : PRect;
             const hDestWindowOverride : HWND;
                   pDirtyRegion : PRgnData) : HResult;

pSourceRect: Mit diesem Parameter bestimmen Sie in Form eines Rechtecks, welcher Teil des Backbuffer-Bildes auf den Bildschirm gebracht werden soll. Es gelten, wie auch beim Clear-Befehl die normalen Bildschirmkoordinaten. Übersteigen die angegebenen Koordinaten den Fenster- oder Bildschirmrahmen, werden die Koordinaten angepasst. Es entsteht also keine Fehlermeldung. Möchten Sie den ganzen Inhalt auf den Bildschirm bringe, dann geben Sie NIL an. Vorraussetzung, um diesen Parameter nutzen zu können, ist, dass das Device mit dem SwapEffect D3DSWAPEFFECT_COPY oder D3DSWAPEFFECT_COPY_VSYNC erstellt worden ist. Ansonsten muss der Parameter NIL sein.

pDestRect: Hier geben Sie in Form eines Rechtecks an, in welchem Teil Ihrer Anwendung das Bild, dass Sie mit pSourceRect eingegrenzt haben, dargestellt werden soll. Übersteigen die angegebenen Koordinaten den Fenster- oder Bildschirmrahmen, werden die Koordinaten angepasst. Es entsteht also keine Fehlermeldung. Sie können, wie bei pSourceRect entweder Bildschirmkoordinaten oder NIL angeben. Letzteres ist zwingend notwendig, wenn Sie einen anderen SwapEffect benutzen als D3DSWAPEFFECT_COPY oder D3DSWAPEFFECT_COPY_VSYNC.

hDestWindowOverride: Das Handle des Fensters oder der visuellen Komponente, in der das gerenderte Bild angezeigt werden soll. Geben Sie 0 an, wenn Sie das Handle aus den [image] D3DPresent_Parameters benutzen wollen. Wenn Sie aber zum Beispiel Ihre 3D-Welt innerhalb eines Panels oder Labels anzeigen lassen wollen, so können Sie auch das Handle dieser Komponenten angeben.

pDireyRegion: Dieser Parameter wird von DirectX 8.0 zur Zeit noch nicht genutzt, und sollte deshalb 0 sein.

Ein paar Beispiele

Nachfolgend möchte ich Ihnen zeigen, wie typische Rahmen(-bedingungen) in der Praxis aussehen können. Beachten Sie auch hier wieder, dass ich die Variablenvorgaben aus den vorherigen Kapiteln verwende. Fangen wir mit dem ersten an:

  d3ddev8.BeginScene;
  d3ddev8.Clear( 0, nil, D3DCLEAR_TARGET or D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0, 0);

  // Render-Anweisungen

  d3ddev8.EndScene;
  d3ddev8.Present(nil, nil, 0, nil);

So könnte zum Beispiel Ihre Renderprozedur aussehen. In diesem Fall wird der gesamte Backbufferinhalt auf die gesamte zur Verfügung stehende Fläche des Fensters bzw. Bildschirms gebracht. Da wir bisher nichts konkretes Rendern, sehen wir ein schwarze Fläche. Die Farbübergabe lässt sich am praktischsten mit der Funktion D3DCOLOR_XRGB erledigen, bei der Sie einen Rot-, Grün und Blau-Farbwert zwischen 0 und 255 angeben.

Hier ist ein Beispiel, wie Sie nur bestimmte Bereiche Ihres Backbuffers löschen:

var
  recht: Array[0..1] of TD3DRect;
  precht: Array of PD3DRect;
begin
  setlength(precht, 2);
  recht[0].x1:= 100; recht[0].y1:= 120; recht[0].x2:= 300; recht[0].y2:= 320;
  recht[1].x1:= 400; recht[1].y1:= 420; recht[1].x2:= 500; recht[1].y2:= 520;

  precht[0]:= @recht[0];   // precht[0] zeigt auf recht[0]
  precht[1]:= @recht[1];   // precht[1] zeigt auf recht[1]

  d3ddev8.Clear( 2, precht[0], D3DCLEAR_TARGET or D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0, 0);
  d3ddev8.BeginScene;

  // Render-Anweisungen

  d3ddev8.EndScene;
  d3ddev8.Present(nil, nil, 0, nil);
end;

Da der .Clear-Befehl des Direct3DDevices das erste Element eines Arrays mit einem Zeiger auf ein [image] D3DRect haben möchte, müssen wir zuerst die gewünschten Koordinaten in einem Array vom Typ D3DRect angeben, und diese dann dem jeweiligen Zeiger übergeben. Nun werden nur der Bereiche von (100/120) bis (300/320) und der Bereich von (400, 420) bis (500, 520) gelöscht. Objekte, die sich ausserhab dieser Felder bewegen, ziehen eine Spur Ihrer eigenen Farbe hinter sich her. Nebenbei zeigt dieses Code-Sample, dass der .Clear-Befehl auch vor .BeginScene verwendet werden kann.

Und so könnte ein Programm aussehen, dass nur einen bestimmten Bereich des Backbuffers auf den Bildschirm bringt:

var
  flaeche: TRect;
  pflaeche: PRect;
begin
  flaeche.left:= 100; flaeche.top:= 300; flaeche.right:= 400; flaeche.bottom:= 600;
  pflaeche:= @flaeche;

  d3ddev8.Clear( 0, nil, D3DCLEAR_TARGET or D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0, 0);
  d3ddev8.BeginScene;

  // Render-Anweisungen

  d3ddev8.EndScene;
  d3ddev8.Present(pflaeche, pflaeche, 0, nil);
end;

Das Prinzip ist hierbei im Grunde das selbe. Zuerst werden die Daten einem Record vom Typ TRect übergeben, und anschliessend der Zeiger auf dieses Record einem typisiertem Pointer (pflaeche). In diesem Beispiel liegt der Bereich, der aus dem Backbuffer geholt wird, am gleichen Ort, wie der Bereich, in dem der Inhalt dargestellt wird. Dies hat folgenden Vorteil: wenn das Fenster einer laufenden DirectX-Anwendung vergrössert oder verkleinert, wird auch automatisch ihre 3D-Szene dem neuen Fenster angepasst und dem entsprechend skaliert. Wenn Sie dies jedoch nicht wollen, können Sie mit diesem Code angeben, dass Ihre 3D-Szene immer gleich gross angezeigt werden soll, egal wie gross oder klein Ihr Fenster ist. Vergessen Sie nicht, beim initialisieren des Devices D3DSWAPEFFECT_COPY oder D3DSWAPEFFECT_COPY_VSYNC als SwapEffect anzugeben.

Ort der Rahmenhandlung

Wenn Sie bereits versucht haben, die Beispiele aus den Kapiteln direkt umzusetzen, dann werden Sie sich vielleicht auch schon gefragt haben, in welchen Prozeduren diese einzelnen Schritte am besten aufgehoben sind.

In den meisten DirectX-Anwendungen werden Sie wahrscheinlich wollen, dass das DirectX von Beginn an aktiv ist. Hier bietet es sich an, die Initialisierung in der OnCreate oder OnActivate-Prozedur vorzunehmen, womit garantiert wäre, dass dies auch nur genau einmal geschieht. Ihre eigentliche Anwendung, also das, was wir hier erstmal als Rahmen bezeichnet haben, sollten Sie in einer anderen Prozedur auslagern. Wenn Sie eine Animationen, Bewegungen etc. in Ihrer Anwendung benutzen wollen, ist es notwendig, dass kontinuirlich Frame auf Frame das Bild generiert wird. Zum eine könnten Sie dafür den Delphi-Standardtimer benutzen. Doch damit erhalten sie nicht unbedingt die optimalste Geschwindigkeit. Für den Anfang empfehle ich Ihnen die sogenannte OnIdle-Prozedur, die von Delphi automatisch genau dann aufgerufen wird, wenn keine anderen Operationen anfallen. Über einen kleinen Trick sorgen Sie dafür, dass diese Prozedur immer und immer wieder aufgerufen wird:

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    procedure OnIdle(Sender: TObject; var done: boolean);
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
  // Initialisierungsbefehle
  Application.OnIdle:= OnIdle;
end;

procedure TForm1.OnIdle(Sender: TObject; var done: boolean);
begin
  done:= false;

  // Rahmen und Renderanweisungen
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  // Close-Anweisungen
end;

Entscheidend sind folgende Dinge: sie müssen dem Event OnIdle eine Prozedur zuweisen, die aufgerufen werden soll. In diesem Fall ist das die gleichnamige Prozedur OnIdle. Normalerweise wird diese immer jeweils nur einmal aufgerufen, nachdem festgestellt wurde, dass sich die Anwendung in einer Leerlaufphase befindet. Damit OnIdle immer wieder aufgerufen wird, muss die Variable done der OnIdle-Prozedur auf false gestellt werden. Das Resultat: wir haben einen kontinuirlichen sehr zügig aufeinanderfolgenden Selbstaufruf, der für unsere Zwecke völlig ausrecht.

Falls Sie stattdessen DirectXGraphics kontrolliert und nicht kontinuirlich verwenden möchten, ist es natürlich auch möglich die entscheidenen Befehle in irgendeiner anderen Prozedur unterzubringen.

Kleines Fazit

Wenn Sie bis hier hin gekommen sind, dann haben Sie die grösste Durststrecke bis zu Ihrer Direct3D-Anwendung überwunden. Denn im Gegensatz zu den letzten Kapiteln, werden Sie in den nun folgenden Abschnitten Dinge lernen, von denen Sie am PC auch optisch etwas haben. Also, nochmal kurz durchatmen, und weiter gehts. Denn jetzt beginnt es, wirklich interessant zu werden.


Vertex- und Indexbuffer

SelfDXD --- [image] DirectXGraphic
Vertex- und Indexbuffer


SelfDXD - Das Flexible Vertex Format (FVF)

SelfDXD --- [image] DirectXGraphic --- [image] 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 ( [image] 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 [image] 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 [image] 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 [image] Was ist das Flexible Vertex Format?

Formatübergabe an das Device

Vorhin sprachen wir die [image] 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 [image] 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.


SelfDXD - Vertexbuffer und Matrizen

SelfDXD --- [image] DirectXGraphic --- [image] Vertex- und Indexbuffer
 | Der Vertexbuffer
 | Was ist der Vertexbuffer? IDirect3DDevice8.CreateVertexbuffer Vertexbuffer mit Daten füllen Vertices rendern Die letzten Einstellungen Den Vertexbuffer freigeben Das Beispielprogramm

Was ist der Vertexbuffer?

Nun, im Prinzip ist der Name ja fast selbst erklärend. Im wesentlichen ist der Vertexbuffer ein Ort, an dem die Vertices gespeichert werden und für Direct3D jederzeit zum Rendern abrufbereit sind. Das Interface des Vertexbuffer selbst enthält eigentlich nicht so viele Funktionen, die wir brauchen, um Vertices darzustellen. In diesem Kapitel lernen Sie, wie Sie einen Vertexbuffer erstellen, ihn mit Vertices füttern und anschliessend ein Objekt darstellen. In diesem Fall wollen wir zunächst ein einfaches Dreieck darstellen. Im nächsten Kapitel werden wir uns dann an einen Zylinder wagen.

IDirect3DDevice8.CreateVertexbuffer

Wie bei allen Interface-Objekten, die wir benötigen, muss auch dieses erstmal initialisiert werden. Da sich ein Vertexbuffer zum Teil über die Anzahl der Vertices und dem Format der Vertices definiert, müssen wir uns also zunächst erstmal klar machen, welche Eigenschaften unsere Vertices haben sollen, und wieviele wir davon brauchen.

Angenommen, wir wollen ein einfaches Dreieck darstellen, dann brauchen wir demzufolge auch nur 3 Vertices. Da wir zunächst nicht mit Licht arbeiten wollen, aber dafür mit Animationen brauchen wir nicht-transformierte aber beleuchtete Vertices. Erstellen wir also ein Record dieser Art:

type
  CUSTOMVERTEX = record
    x, y, z : Single;   // Positionskoordinaten
    color : TD3DColor;  // Farbe
  end;

const                   // Format von CUSTOMVERTEX
  D3DFVF_CUSTOMVERTEX = ( D3DFVF_XYZ or D3DFVF_DIFFUSE );

procedure InitGeometry;
var
  Vertices: Array[0..2] of CUSTOMVERTEX;
begin
  ...

Ausserdem erstellen wir schonmal eine Konstante, die das Format unseres Records angibt. Das ist beim Initialisieren wichtig. Des weiteren definieren wir ein Array mit 3 Elementen vom Typ CUSTOMVERTEX. Hierrein schreiben wir später die nötigen Informationen für unser Dreieck. Es reicht, wenn Sie Vertices nur innerhalb einer Funktion definieren, da die Daten ja später sowieso Bestandteil des Vertexbuffers sein werden und nicht noch ein zweites Mal in einem Array stehen müssen.

Jetzt, wo wir also wissen, was wir darstellen wollen, können wir uns an's eigentliche Initialisieren wagen. Dazu benötigen wir eine Variable vom Typ IDirect3DVertexBuffer8. Das Erstellen des Vertexbuffers funktioniert über das Direct3DDevice8-Interface, dass dafür folgende Funktion bereithält:

function CreateVertexBuffer(const Length : Cardinal;
                            const Usage, FVF : LongWord;
                            const Pool : TD3DPool;
                              out ppVertexBuffer : IDirect3DVertexBuffer8) : HResult;

Length: Hier steht die Länge des Vertexbuffers in Bytes. Die gewünschte Bytegrössen errechnet man am besten so: Sizeof( Vertexformat ) * Anzahl_der_Vertices.

Usage: Gibt an, für welche Zwecke der Vertexbuffer erstellt wird. Hier kann man bei grossem Datenaufwand die ein oder andere FPS-Zahl gewinnen. Sie können dafür einen Teil der [image] D3DUSAGEFLAGS verwenden. Man kann auch mehrere miteinander kombinieren. Desweiteren ist es auch möglich einfach 0 zu übergeben und damit einen Standardvertexbuffer zu erzeugen. Für Ihre ersten Samples ist das sicherlich die einfachste Möglichkeit. Die Wahl der Flags hängt davon ab, wie Sie die Vertices verwenden wollen. Wenn die Anzahl der Vertices veränderbar sein soll, empfiehlt es sich D3DUSAGE_DYNAMIC zu verwenden. Wenn Sie Vertexbuffer nur zum rendern und beschreiben brauchen dann empfiehlt es sich D3DUSAGE_WRITEONLY zu verwenden. All diese Flags sorgen für eine sinnvolle Verwaltung der Vertexdaten im Speicher um Ihren Verwendungszweck optimal erfüllen zu können.

FVF: Hier geben Sie das Format Ihrer Vertices an, die Sie in den Buffer schreiben. Verwenden Sie hierzu eine Kombination der [image] D3DFVFFLAGS-Flags. Wenn wir die oberen Vorgaben betrachten, würden wir hier D3DFVF_CUSTOMVERTEX übergeben.

Pool: Eine Konstante der [image] D3DPool-Aufzählungen, die angeben, in welcher Speicherklasse die Daten abgelegt werden sollen.

ppVertexBuffer: eine Variable, die auf das erstellte Direct3DVertexBuffer8-Interface zeigt.

Vertexbuffer mit Daten füllen

Wie auch im vorhergehenden Abschnitt müssen wir erstmal Daten haben, die wir dem Vertexbuffer zuweisen können. Definieren wir doch einfach mal eine Prozedur, in der wir das oben angefangene Beispiel fortsetzen.

var
  vbuffer: IDirect3DVertexBuffer8;

function InitGeometry: HRESULT;
var
  Vertices: Array[0..2] of CUSTOMVERTEX;
  pVertices : PByte;
begin
  // Wenn die Funktion vorzeitig abgebrochen wird, wird E_FAIL zurückgegeben
  Result := E_FAIL;

  // In das Vertex-Array werden die Koordinaten und Farben geschrieben
  Vertices[ 0 ].x := 1.0;
  Vertices[ 0 ].y := -1.0;
  Vertices[ 0 ].z := 0.0;
  Vertices[ 0 ].color := $FFFF0000;

  Vertices[ 1 ].x := -1.0;
  Vertices[ 1 ].y := -1.0;
  Vertices[ 1 ].z := 0.0;
  Vertices[ 1 ].color := $FF0000FF;

  Vertices[ 2 ].x := 0.0;
  Vertices[ 2 ].y := 1.0;
  Vertices[ 2 ].z := 0.0;
  Vertices[ 2 ].color := $FFFFFFFF;

  // Erstellen des Vertexbuffers
  if ( d3ddev8.CreateVertexBuffer( sizeof( CUSTOMVERTEX ) * 3, 0, D3DFVF_CUSTOMVERTEX,
    D3DPOOL_DEFAULT, vbuffer ) <> D3D_OK ) then Exit;

  // wird fortgesetzt....

Wenn die .CreateVertexBuffer-Funktion nicht D3D_OK zurück gibt, wird die Funktion an dieser Stelle abgebrochen, weil etwas schief gelaufen ist. Durch Abfragen wie diese, lassen sich ggf. leichter Fehlerquellen finden. Der typisierte Pointer pVertices wird in folgendem eine Rolle spielen.

Um dem Vertexbuffer Daten übergeben zu können, müssen wir diesen locken. Das bedeutet, wir sperren ihn für Zugriffe, die das System auf den Speicherbereich machen möchte, an dem sich der Vertexbuffer befindet, damit keine Komplikationen auftreten, wenn wir diese Speicherstelle beschreiben. Der Lock-Befehl des Vertexbuffers hat folgende Syntax.

function Lock(const OffsetToLock: Cardinal;
              const SizeToLock: Cardinal;
                var ppbData : PByte;
              const Flags : LongWord) : HResult;

OffsetLock: Die Stelle (in Bytes) an der der Vertexbuffer beschrieben bzw. ausgelesen werden soll.

SizeToLock: Die Grösse des zu sperrenden Speichers (in Bytes).

ppbData: Ein Zeiger/Pointer auf den Speicherbereich, den man mit OffsetToLock und SizeToLock "markiert" hat.

Flags: 0 oder eine Kombination aus den D3DLOCKFLAGS, mit denen man angibt, für welche Zwecke der Vertexbuffer gelockt werden soll.

Die eigentliche Übergabe der Daten ist sehr simpel. Sie müssen lediglich die Daten aus dem Array an die Stelle kopieren, auf die der Pointer zeigt. Wenn wir unser Beispielprogramm fortsetzen, dann sieht das so aus:

// Fortsetzung des Beispielprogrammes

  if ( vbuffer.Lock( 0, sizeof( Vertices ), pVertices, 0 ) <> D3D_OK ) then Exit;
  Move( Vertices, pVertices^, sizeof( Vertices ) );
  vbuffer.Unlock;

end;

Hier der Rest unserer InitGeometry-Funktion. Der Vertexbuffer wird gelockt, mit dem Move-Befehl werden alle Vertices in den Speicherbereich geschrieben, auf den der Pointer zeigt, und schliesslich wird mit der parameterfreien .Unlock-Funktion der Zugriff auf den Vertexbuffer wieder freigegeben. Die GetMem-Funktion ist für den Pointer diesmal nicht notwendig, da er ja über den Vertexbuffer bereits Speicher zugewiesen bekommt. Wenn Sie keine Lust haben, immer zwei Variablen für das Beschreiben eines simplen Vertexbuffer zu verwenden, dann führen Sie sich bitte folgendes Beispiel kurz zu Gemüte:

var
  pVertices: Array of CUSTOMVERTEX;
begin

  //Initialisieren des Vertexbuffers
  if ( d3ddev8.CreateVertexBuffer( sizeof( CUSTOMVERTEX ) * 3, 0, D3DFVF_CUSTOMVERTEX,
    D3DPOOL_DEFAULT, vbuffer ) <> D3D_OK ) then Exit;

  // Grösse des dynamischen Array's festlegen
  setlength( pVertices, 3);

  // Beim locken wird einfach nur ein Zeiger auf pVertices übergeben, dass heisst
  // pVertices ist nun der Ort im Vertexbuffer, an dem die Vertices gespeichert werden
  if ( vbuffer.Lock( 0, sizeof(customvertex) * 3, PBYTE(pVertices), 0 ) <> D3D_OK ) then Exit;

  // diese Änderungen werden direkt im Speicher des Vertexbuffers geändert
  pVertices[ 0 ].x := 1.0;
  pVertices[ 0 ].y := -1.0;
  pVertices[ 0 ].z := 0.0;
  pVertices[ 0 ].color := $FFFF0000;

  pVertices[ 1 ].x := -1.0;
  pVertices[ 1 ].y := -1.0;
  pVertices[ 1 ].z := 0.0;
  pVertices[ 1 ].color := $FF0000FF;

  pVertices[ 2 ].x := 0.0;
  pVertices[ 2 ].y := 1.0;
  pVertices[ 2 ].z := 0.0;
  pVertices[ 2 ].color := $FFFFFFFF;

  vbuffer.Unlock;
end;

Hier wird der Ort, an dem die Vertices gespeichert werden als pVertices benannt. Damit sparen wir uns eine Variable.

Vertices rendern

Und nun kommt endlich der Abschnitt, auf den Sie sicher schon lange gewartet haben, der eigentliche Rendervorgang. Jetzt gleich werden Sie zum ersten Mal ein 3D-Objekt sehen, das zwar zugegebener massen noch ziemlich zweidimensional wirken wird, weil wir ja nur ein Dreieck zeichnen, aber der Weg bis zur Dreidimensionalität ist dann nur noch ein Katzensprung.

Das Rendern der Vertices aus einem Vertexbuffer erfolgt in 3 Schritten:

  1. dem Device mitteilen, welchen Vertexbuffer er auslesen soll
  2. dem Device mitteilen, welches Vertexformat es benutzen soll
  3. die Vertices rendern

Schritt 1 wird mit folgender Funktion des Devices ausgeführt:

function SetStreamSource(const StreamNumber : Cardinal;
                         const pStreamData : IDirect3DVertexBuffer8;
                         const Stride : Cardinal) : HResult;

StreamNumber: Das Device ist in der Lage, mehrere Vertexbuffers in einem Rutsch zu rendern. Hier weisen Sie also dem Vertexbuffer, das Sie rendern wollen, eine Streamnummer zu. Dadurch, dass jedem Vertexbuffer im Device einer Nummer zugewiesen wird, können Sie später Vertexbuffer, die nicht mehr gerendert werden sollen, gezielt rausnehmen.

pStreamData: Hier geben Sie den Vertexbuffer an, den Sie rendern wollen.

Stride: Gibt den Abstand in Bytes an, der zwischen zwei Vertexdatensätzen im Speicher liegt. Benutzen Sie hier am besten Sizeof( Vertexrecord ). In dem Sie der Funktion die Grösse unserer Record-Struktur übergeben, stellen wir sicher, dass das Device jedes Vertex korrekt abarbeitet.

Als nächstes muss dem Device mitgeteilt werden, was für ein Vertexformat er auf die zu rendernden Vertexbuffer anwenden soll. Dafür benutzen wir folgende Funktion des Devices:

function SetVertexShader(const Handle : LongWord) : HResult;

Handle: Das Vertexformat, dass Sie verwenden möchten.

So, nun kommen wir zum letzten Schritt, dem eigentlichen Rendervorgang. Auch dieser erfolgt über eine einfache Funktion des IDirect3DDevice8-Interfaces:

function DrawPrimitive(const PrimitiveType : TD3DPrimitiveType;
                       const StartVertex: Cardina;
                       const PrimitiveCount : Cardinal) : HResult;

PrimitiveType: Eine Konstante der [image] D3DPRIMITIVETYPE-Aufzählungen, die angibt, wie die Dreiecke gerendert werden sollen.

StartVertex: Gibt an, bei welchem Vertex angefangen werden soll, zu rendern. Wenn Sie zum Beispiel 100 Vertices haben, aber erst mit dem 50sten Vertex anfangen wollen zu rendern, dann geben Sie hier 50 ein.

PrimitiveCount: Anzahl der Primitiven, die ab StartVertex gerendert werden soll. Die Zahl ist desweiteren abhängig von PrimitivType. Wenn Sie zum Beispiel 100 Vertices als Punkte (D3DPT_POINTLIST) darstellen wollen, dann ist jeder Vertex auch gleichzeitig eine vollständige Primitive, demnach müssen Sie also 100 angeben. Bei D3DPT_TRIANGLELIST würden immer 3 aufeinanderfolgende Vertices ein Dreieck bilden. Die maximale Anzahl an darstellbaren Primitiven wäre dann also 33, wobei der 100ste Vertex entfallen würde. Bei D3DPT_TRIANGLESTRIP wäre die maximale Anzahl 100 - 2 = 98, da ab dem 3 Vertex mit jedem Vertex auch ein neues Dreieck hinzukommt.

Diese drei Funktionen werden in den Rahmen Ihrer Anwendung eingefügt. Dies könnte dann bei unserem Beispielprogramm so aussehen:

procedure TForm1.OnIdle(Sender: TObject; var done: boolean);
begin
  done:= false;
  // den Backbuffer löschen
  d3dDev8.Clear( 0, nil, D3DCLEAR_TARGET, D3DCOLOR_XRGB( 0, 0, 0 ), 1.0, 0 );

  // Rendern der Szene
  d3dDev8.BeginScene;

  // Rendern des Vertexbuffers
  d3dDev8.SetStreamSource( 0, vBuffer, sizeof( CUSTOMVERTEX ) );
  d3dDev8.SetVertexShader( D3DFVF_CUSTOMVERTEX );
  d3dDev8.DrawPrimitive( D3DPT_TRIANGLELIST, 0, 1 );

  // Ende des Renderns
  d3ddev8.EndScene;

  // Den Backbuffer mit dem Frontbuffer vertauschen.
  d3ddev8.Present( nil, nil, 0, nil );
end;

Da wir ja nur 3 Vertices haben, können wir auch nur ein Dreieck rendern. Deswegen geben wir in der .DrawPrimitive()-Funktion als letzten Parameter 1 an. Nun fehlen nur noch ein paar kleine Details, bis wir unser Dreieck auf dem Bildschirm sehen können.

Die letzten Einstellungen

Ein winziges Detail müssen wir nur noch einstellen, dann haben wir es geschafft. Und zwar wird beim Initialisieren des Devices die Lichtberechnung für das Rendern von Vertices immer auf AN gestellt. Da wir aber Vertices benutzen, die keine Normale haben, kann das Device nicht wissen, wo es seine Vorderseite hat. Beim Rendern mit Lichtberechnung würde unser Dreieck deshalb in schwarz erscheinen. Deswegen müssen wir nur noch die Lichtberechnung ausschalten, und das geht über folgenden Befehl:

  d3ddev8.SetRenderState( D3DRS_LIGHTING, Cardinal( FALSE ) );

Mit den Renderstates werden wir uns später noch genauer befassen. Für diesen Moment reicht es, wenn Sie wissen, dass das Device eine Fülle an Renderstates beinhaltet, die mit der Syntax .SetRenderState( <was eingestellt werden soll>, <wie es eingestellt werden soll> ) aufgerufen werden. In diesem Fall soll die Lichtberechnung eingestellt werden, und zwar schalten wir sie aus. Da die Funktion nicht direkte boolische Werte erwartet, sondern eine Zahl, müssen wir den boolischen Wert über Cardinal() in einen numerischen Wert umwandeln. Sie können diesen Befehl direkt nach dem Initialisiern eingeben oder erst beim Rendern der Szene.

Ein weiteres Renderstate, das ich Ihnen jetzt schonmal zeigen möchte, legt fest, welche Renderstates gecullt werden sollen, und welche nicht. Sie erinnern sich? Culling nennt man das Nicht-Rendern von Dreiecken, weil Sie in einer bestimmten Reihenfolge liegen. Standardmässig werden in DirectXGraphics nur die Dreiecke angezeigt, dessen Vertices im Uhrzeigersinn liegen. Alle anderen werden gecullt. Sie können jedoch auch selbst einstellen, welche gecullt werden sollen, und welche nicht. Dazu sieht die .SetRenderState-Funktion wie folgt aus:

  d3ddev8.SetRenderState( D3DRS_CULLMODE, D3DCULL_CCW );

Mit diesen Parametern werden alle Dreiecke gecullt, die gegen den Uhrzeigersinn angeordnet sind (counterclockwise). D3DCULL_CW würde bewirken, dass alle Dreiecke nicht angezeigt werden würden, deren Vertices im Uhrzeigersinn (clockwise) angeordnet sind. Desweiteren können Sie noch D3DCULL_NONE angeben. Ist diese Einstellung aktiv, sehen Sie das Dreieck von beiden Seiten.

Den Vertexbuffer freigeben

Achten Sie darauf, dass Sie alle Vertexbuffer, die Sie erstellen, beim Beenden des Programmes auch wieder freigeben. Die Close-Prozedur wächst also um ein drittes Element an, betrachtet man das, was wir bisher erarbeitet haben:

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  if vbuffer <> NIL then vbuffer := NIL;
  if d3ddev8 <> NIL then d3ddev8 := NIL;
  if d3d8 <> NIL then d3d8:= NIL;
end;

Das Beispielprogramm

So, jetzt ist es an der Zeit, das Gelernte ein wenig auszuprobieren. Unter directxgraphics/demos/vertices/ finden Sie ein kleines Programm, dass alles bisher gelernte beinhaltet (Achtung: nur in der Offline-Version enthalten). Alle Beispielcodes, die Sie hier finden, werden Sie auch in dieser Demo finden (als Beweis dafür, dass das auch wirklich funktioniert, was ich hier geschrieben habe). Damit Sie sofort verstehen, wie das Programm strukturiert ist, hier eine kurze Übersicht über die enthaltenen Prozeduren und Funktionen:

InitGeometry
Erstellt das Dreieck und schreibt die Vertices in den Vertexbuffer

FormCreate
Initialisiert IDirect3D8 und IDirect3DDevice8. Danach wird InitGeometry aufgerufen. Anschliessend wird das Licht ausgeschaltet und die OnIdle-Prozedur in Gang gesetzt.

OnIdle
Das Dreieck wird immer wieder neu gerendert.

FormClose
Wenn die Anwendung beendet wird, werden alle wichtigen Objekte wieder auf NIL gesetzt.

Nehmen Sie sich Zeit und probieren Sie ruhig aus, die einzelnen Einstellungen beim Initialisieren zu verändern. Bei dieser Anwendung ist es eigentlich überflüssig, den Rendervorgang in die OnIdle-Prozedur zu schreiben, da es genügen würde, wenn das Programm das Dreieck nur einmal rendern würde. Daher kann der Rendervorgang auch in der FormPaint-Prozedur stehen.

Im nächsten Kapitel werden wir uns mit Transformationen beschäftigen. Wir erstellen einen Zylinder und werden diesen im 3D-Raum rotieren lassen. Sie werden überrascht sein, wie einfach das geht!


SelfDXD - Transformation und Tiefenbuffer

SelfDXD --- [image] DirectXGraphic --- [image] Vertex- und Indexbuffer
 | Transformation und Tiefenbuffer
 | Einleitung Die Vertexdaten Transformationsmöglichkeiten Perspektive und Blickwinkel Translation von Vertices Rotation von Vertices Skalierung von Vertices Schatten von Vertices Multiplizieren von Matrizen Einstellen des Z-Buffers Das Beispielprogramm Weitere Transformationsmethoden Weltmatrizen-Blending Ortogonale Perspektive

Einleitung

Ich denke mal, die Freude über Ihre erste Direct3D-Anwendung wird ein wenig getrübt sein durch die Tatsache, dass an der 3D-Anwendung gar nicht soviel dreidimensionales dran ist. Mal ehrlich, das hätten wir auch wesentlich einfacher haben können, oder? Naja, für simple 2D-Anwendungen ist DirectXGraphics ja auch nicht gedacht. Die vielen Einstellungen, Parameter und Funktionen, die wir in den letzten Kapiteln benutzt haben, dienen dem Grundsatz, sich alle Möglichkeiten in der dritten Dimension offenhalten zu können. Und mit ein paar dieser Möglichkeiten werden wir uns in diesem Kapitel beschäftigen.

In diesem Kapitel lernen Sie, wie man Körper dreidimensional erscheinen lässt. Darüberhinaus werden wir uns mit den Grundelementen der Animationstechnik befassen. Ausserdem werden wir den Tiefenbuffer (auch Z-Buffer genannt) kennenlernen, mit dem wir sicherstellen, dass Objekte im Hintegrund nicht versehentlich Objekte im Vordergrund überlappen. Auch dieses Kapitel ist mal wieder verknüpft mit einem Beispielprogramm, das wir von Abschnitt zu Abschnitt zusammenbauen werden. Diesmal stellen wir zwei Zylinder dar, von denen einer rotiert.

Den linken Zylinder werden wir zudem auch noch ein wenig stauchen. Beiden Zylindern gemeinsam ist die Tatsache, dass ihre Vertices im selben Vertexbuffer enthalten sind. Damit lernen Sie ein ganz wesentliches Element des Programmierens in 3D kennen. Wenn Sie mehrmals das gleiche 3D-Objekt darstellen wollen, benötigen Sie immer nur einen Vertexbuffer. Die Koordinaten werden durch Matrizen lediglich transformiert. Desweiteren werden wir den beiden Zylindern einen Schatten geben. Ein kleiner Kameraflug um diese beiden Zylinder peppt die Sache noch ein wenig auf.

Die Vertexdaten

Bevor wir uns mit den verschiedenen Arten der Transformation befassen, will ich Ihnen nur kurz den Algorithmus für die Erstellung des Zylinders zeigen:

// Diese Funktion dient uns lediglich dazu, die Eingabe der Daten zu erleichtern

function GetCustomVertex(_x, _y, _z: single; _color: TD3DColor): CUSTOMVERTEX;
begin
  with Result do
   begin
     x := _x;
     y := _y;
     z := _z;
     color := _color;
   end;
end;

procedure InitGeometry;
var
  Vertices: Array[0..99] of CUSTOMVERTEX;
  cx: Integer;
  theta: Single;
  pVertices : PByte;
begin

  // Erstellen der Vertex-Koordinaten für einen Zylinder
  for cx := 0 to 49 do
  begin
    theta := ( 2 * D3DX_PI * cx ) /49;
    Vertices[ 2 * cx ] := GetCustomVertex(sin( theta ), -1.0, cos( theta ), $FF00FF00);
    Vertices[ 2 * cx + 1 ] := GetCustomVertex(sin( theta ),  1.0, cos( theta ), $FFFF0000);
  end;

  // Erstellen des Vertexbuffers
  if ( d3ddev8.CreateVertexBuffer( sizeof( CUSTOMVERTEX ) * 100, 0, D3DFVF_CUSTOMVERTEX,
    D3DPOOL_DEFAULT, vbuffer ) <> D3D_OK ) then Exit;

  if ( vbuffer.Lock( 0, sizeof( Vertices ), pVertices, 0 ) <> D3D_OK ) then Exit;
  Move( Vertices, pVertices^, sizeof( Vertices ) );
  vbuffer.Unlock;

end;

Wir greifen hierbei das Vertexformat aus dem Kapitel [image] Der Vertexbuffer auf. Diesmal erstellen wir 100 Vertices, damit der Zylinder auch schön rund wirkt. Die Vertices werden jeweils um den Nullpunkt herum angeordnet und zwar jeweils immer ein Vertex mit der Höhe -1 und ein Vertex mit der Höhe +1. Unser Zylinder hat also insgesamt 50 Ecken.

Der Aufbau des Zylinders wird recht deutlich an dem unteren Bild, bei dem man nur die Vorderseite des Zylinders sieht. Damit das Drahtgittermodell deutlicher wird, habe ich die Vertex-Zahl für diesen Screenshot ein wenig verringert.

Transformationsmöglichkeiten

Die Art und Weise, wie wir ein 3D-Objekt sehen ist abhängig von vier Dingen:

  1. die Lage des Objektes
  2. die Texturen, die das Objekt besitzt
  3. die Lage und der Blickwinkel des Betrachters, kurz: die Kameraeinstellungen
  4. die Linse, die wir zum Sehen benutzen

All diese Dinge können wir beeinflussen, und zwar mit Matrizen. Wenn Sie nicht wissen, was Matrizen sind, dann sollten Sie an dieser Stelle einen Blick in das Kapitel [image] Matrizen werfen. In dem wir also in die Matrizenfelder die entsprechenden Zahlen eingeben, und diese Matrizen dann dem Device übergeben, können wir ein 3D-Objekt perspektivisch korrekt, so, wie wir es wünschen, betrachten. Die Übergabe der Matrizen für die vier entscheidenden Faktoren erfolgt mit der .SetTransform-Funktion des Devices:

function SetTransform(const State : TD3DTransformStateType;
                      const pMatrix : TD3DMatrix) : HResult;

State: Hier geben Sie an, was Sie transformieren wollen. Verwenden Sie hierzu eine Konstante der [image] D3DTransformStateType-Aufzählungen. Sie können unter anderem zwischen der Kamera-, Textur-, und Welttransformierung wählen.

pMatrix: Die Matrix, die die Transformierungszahlen enthält. Sie ist vom Typ [image] D3DMatrix oder D3DXMatrix.

Um vorhandene Matrizeneinstellungen erweitern zu können, können Sie sich diese ausgeben lassen. Der Befehl dafür sieht ganz ähnlich aus, mit dem Unterschied, dass die Matrix, die als Parameter übergeben wird, mit den entsprechenden Daten gefüllt wird.

function GetTransform(const State : TD3DTransformStateType;
                      out pMatrix : TD3DMatrix) : HResult;

Die nachfolgenden Abschnitte beschäftigen sich damit, wie man die richtigen Zahlen der Matrix übergibt.

Perspektive und Blickwinkel

Nehmen wir uns zuerst die View-, und die Projection-Matrix vor. Letzteres beinhaltet sozusagen die Einstellungen der Linse, durch die wir gucken. Eine der möglichen Einstellungen ist der Sichtwinkel, also der Umfang dessen, was wir sehen können. Betrachten Sie dazu folgende beiden Skizzen:

Zwei Kameras betrachten einen Zylinder aus der gleichen Entfernung, jedoch mit einem wesentlichen Unterschied. Die linke Kamera hat einen wesentlich kleineren Sichtwinkel, als die rechte. Demzufolge erscheint der Zylinder mit der linken Kamera näher, als mit der rechten, weil er grösser dargestellt wird, obwohl in beiden Fällen der Zylinder den gleichen Abstand zur Kamera hat. Das nachfolgende Bild macht dieses Phänomen an einem Flugzeug deutlich.

Da der Sichtwinkel bei der rechten Kamera grösser ist, ist auch die Fläche grösser, die auf den Bildschirm untergebracht werden muss. Demzufolge erscheinen die Dinge kleiner. Der Normalwinkel für eine realitätsnahe Darstellung liegt bei PI / 4. Das ist in etwa der Sichtwinkel, den wir beim Blick auf den Bildschirm haben.

In der Projektionsmatrix lassen sich noch weitere Dinge einstellen, die nachfolgend erklärt werden. Zum einfachen Erstellen der Projektionsmatrix gibt es zwei Befehle, die Ihnen bei der Eingabe helfen. Fügen Sie dazu in der USES-Klausel Ihrer Anwendung d3dx8 ein. Diese Unit beinhaltet viele nütliche Funktionen, die DirectXGraphics unterstützt.

function D3DXMatrixPerspectiveLH( out MOut: TD3DXMatrix;
                                      w: Single;
                                      h: Single;
                                      zn: Single;
                                      zf: Single;
                                );

MOut: Eine Variable vom Typ [image] D3DXMatrix, die mit den entsprechenden Zahlen gefüllt wird.

w: Die Breite des sichtbaren Bereiches bei zn.

h: Die Höhe des sichtbaren Bereiches bei zn.

zn: Ab welcher Tiefe von der Kamera ausgehend Vertices bzw. Dreiecke sichtbar sein sollen.

zf: Ab welcher Tiefe von der Kamera ausgehend Vertices bzw. Dreiecke nicht mehr sichtbar sein sollen.

In dieser Funktion wird also der Sichtwinkel mit den Variablen w und h eingestellt. Anders ist das bei der zweiten Funktion, die Sie statt D3DXMatrixPerspectiveLH() verwenden können:

function D3DXMatrixPerspectivePovLH( out MOut: TD3DXMatrix;
                                         fovy: Single;
                                         Aspect: Single;
                                         zn: Single;
                                         zf: Single;
                                   );

MOut: Eine Variable vom Typ [image] D3DXMatrix, die mit den entsprechenden Zahlen gefüllt wird.

fovy: der oben angesprochene Sichtwinkel, als radialer Wert.

Aspect: Verhältnis zwischen Höhe und Breite. 1.0 ist gleiches Verhältnis zwischen Höhe und Breite. Sie sehen aber auch nur dann ein gleiches Verhältnis, wenn auch Ihre Anwendung die gleiche Höhe und Breite hat. Da dies im Vollbildmodus zum Beispiel nicht der Fall ist, können Sie hier zum Beispiel auch Anwendung.Breite/Anwendung.Höhe angeben, um sicherzustellen, dass das Verhältnis immer gleich bleibt.

zn: Ab welcher Tiefe von der Kamera ausgehend Vertices bzw. Dreiecke sichtbar sein sollen.

zf: Ab welcher Tiefe von der Kamera ausgehend Vertices bzw. Dreiecke nicht mehr sichtbar sein sollen.

Hier wird der Sichtwinkel wie anfangs besprochen als Winkelmass angegeben. Beide Befehle beziehen sich auf ein linkshändiges Koordinatensystem (zu erkennen an -LH am Ende des Befehls). Natürlich gibt es auch die äquivalenten Befehle für ein rechtshändiges Koordinatensystem.

Damit hätten wir also das Auge, mit dem wir in unserer 3D-Welt gucken, richtig eingestellt. Nun müssen wir nur noch die Lage und Ausrichtung dieses Auges einstellen. Das geschieht mit folgendem Befehl:

function D3DXMatrixLookAtLH(out mOut: TD3DXMatrix;
                            var Eye, At, Up : TD3DXVector3) : PD3DXMatrix;

MOut: Eine Variable vom Typ [image] D3DXMatrix, die mit den entsprechenden Zahlen gefüllt wird.

Eye: Ein Vektor vom Typ [image] D3DXVECTOR3 oder D3DVECTOR, der die Position des Auges angibt.

At: Ein Vektor vom Typ D3DXVECTOR3, der den Punkt angibt, auf den geguckt wird. Dieser Vektor ist ein absoluter Vektor, also nicht abhängig von Eye. Geben Sie 0/0/0 an, dann wird immer auf den Nullpunkt geguckt, egal wo Sie sich grade befinden. Dies ist vor allem dann sehr hilfreich, wenn Sie eine Kamerabewegung erzeugen wollen, bei der ein fester Punkt immer im Zentrum des Blickfeldes sein soll.

Up: Ein Vektor vom Typ D3DXVector3, der angibt, wo eigentlich 'oben' ist. 0/1/0 ist wohl die gewöhnlichste Einstellung.

Auch zu diesem Befehl gibt es das entsprechende Gegenstück für Rechtshändige Koordinatensystem. Die Funktion lautet D3DXMatrixLookAtRH. In unserem Beispielprogramm wollen wir zunächst aus einer gewissen Höhe und Distanz zum Zylinder auf denselbigen gucken, und zwar mit einer möglichst realistischen Einstellung. Damit dieser Theorieteil nicht zu unübersichtlich wird, finden Sie den Code an [image] dieser Stelle. In unserer Anwendung erscheint der Zylinder zum ersten Mal dreidimensional:

Der grafische Fehler im Bild kommt daher, dass wir noch keinen Z-Buffer verwenden. Zur Zeit rendert DirectX den Zylinder beginnend mit der vordersten Seite im Uhrzeigersinn. Dabei wird auch die hintere Fläche komplett gerendert, da DirectXGraphics noch nicht erkennt, dass die hintere Fläche von der vorderen überlagert wird. Wenn Sie es nicht erwarten können, einen normalen Zylinder zu sehen, dann lesen Sie im Abschnitt [image] Einstellen des Z-Buffers weiter. Ansonsten beschäftigen wir uns in den nächsten 4 Abschnitten mit dem Transformieren von 3D-Körpern.

Translation von Vertices

Translation meint hier im Grunde nichts anderes, als das Bewegen von Vertices im Raum. Im Kapitel [image] Matrizen wird gezeigt, welche Felder der Matrix hier betroffen sind. Obwohl also das Beschreiben der drei Felder sehr einfach ist, gibt es auch hierfür eine Funktion, die das für uns übernimmt. Diese sieht wie folgt aus (MOut wird zukünftigt nicht mehr erklärt. Es handelt sich immer um die Matrix, die mit Zahlen gefüllt wird):

function D3DXMatrixTranslation( out MOut: TD3DXMatrix,
                                    x, y, z: Single): PD3DXMatrix;

x,y und z: Die Koordinaten, um die die Vertices bewegt werden sollen.

In diesem [image] Beispielcode finden Sie diese Funktion integriert.

Rotation von Vertices

Die Rotation, also das Drehen der Vertices, geschieht immer um den Nullpunkt. Für alle drei Achsen gibt es folgende Funktionen:

function D3DXMatrixRotationX(var mOut : TD3DXMatrix;
                             const angle : Single) : PD3DXMatrix;

function D3DXMatrixRotationY(var mOut : TD3DXMatrix;
                             const angle : Single) : PD3DXMatrix;

function D3DXMatrixRotationZ(var mOut : TD3DXMatrix;
                             const angle : Single) : PD3DXMatrix;

Angle ist hierbei das Winkelmass, um das die Vertices gedreht werden sollen. Wenn Sie statt dessen die Vertices um eine Achse drehen wollen, die in irgendeiner Weise diagonal im Raum liegt, dann benutzen Sie dafür folgenden Befehl:

function D3DXMatrixRotationAxis( out mOut: TD3DXMatrix;
                                     VAxis: TD3DVector;
                                     angle: Single): PD3DXMatrix;

VAxis ist hierbei ein Vektor vom Typ [image] D3DVECTOR und angle der Winkel um den gedreht wird. Dabei werden die Vertices im Uhrzeigersinn gedreht, betrachtet man den Vektor von der Spitze zum Ursprung.

In diesem [image] Beispielcode finden Sie eine dieser Funktion integriert.

Skalierung von Vertices

Das Skalieren, also das Vergrössern bzw. Verkleinern, eine Objektes geschieht natürlich auch nur um den Nullpunkt. Dabei wird der Abstand von jedem Vertice zum Nullpunkt mit einem Faktor multipliziert. In dem Kapitel [image] Matrizen wird gezeigt, welche Felder der Matrix hier betroffen sind. Die Funktion, die das Eingeben der Faktoren vereinfacht, sieht so aus:

function D3DXMatrixScaling( out MOut: TD3DXMatrix;
                                x, y, z: Single): PD3DXMatrix;

X, Y und Z sind dabei die Faktoren für den x-, y- und z-Abstand des Vertex zum Nullpunkt.

In diesem [image] Beispielcode finden Sie diese Funktion integriert.

Schatten von Vertices

Sie können mit Matrizen auch einen Schatten von Ihrem Modell erzeugen. Dabei werden alle Vertices auf eine Ebene projeziert, in Abhängigkeit von der Lichtquelle. Mit folgender Funktion machen Sie aus einem 3D-Objekt einen Schatten von sich selbst.

function D3DXMatrixShadow( out MOut: TD3DXMatrix;
                               VLight: TD3DXVECTOR4;
                               Plane: TD3DXPLANE): PD3DXMatrix;

VLight: Ist ein 4-dimensionaler Vektor vom Typ [image] D3DXVECTOR4. X, Y und Z geben die Position der Lichtquelle an. W ist ein Wert zwischen 0.0 und 1.0. 0.0 bedeutet, dass an jeder Stelle im Raum das Licht in die gleiche Richtung verläuft. Das ist bei sehr weit entfernte Objekten, wie der Sonne zum Beispiel, der Fall. 1.0 steht für ein Punktlicht. Je weiter also die Ebene von dem Objekt entfernt ist, desto grösser erscheint der Schatten.

Plane: Gibt die Lage und Ausrichtung der Ebene an, auf die der Schatten projeziert werden soll. Plane ist vom Typ [image] D3DXPLANE.

Für das Erstellen einer Ebene (Plane) gibt es auch hier wieder zwei Funktionen, die uns dabei helfen. Je nach dem, was Ihnen grade günstiger erscheint, können Sie zwischen folgenden Funktionen wählen.

function D3DXPlaneFromPoints( out POut: TD3DXPLANE;
                                  v1, v2, v3: TD3DVECTOR);

v1, v2, v3 sind dabei Vektoren vom Typ [image] D3DVECTOR, die 3 Punkte der Ebene markieren. Da 3 Punkte im Raum bekanntermassen immer eine grade Fläche bilden, ist es dadurch möglich, die Lage und Ausrichtung der Ebene zu bestimmen. Die andere mögliche Funktion sieht so aus:

function D3DXPlaneFromPointNormal( out POut: TD3DXPLANE;
                                       VPoint: TD3DVECTOR;
                                       vNormal: TD3DVECTOR);

VPoint ist dabei ein Punkt auf der Ebene und vNormal ein Vektor der orthogonal zur Ebene verläuft. Beide sind vom Typ [image] D3DVECTOR. Der Punkt bestimmt sozusagen die ungefähre Lage der Ebene, und der Vektor bestimmt, wie schräg die Ebene ausgerichtet ist.

Bei der Schatten-Transformation werden die Vertices also so transformiert, dass Sie auf der Ebene einen Schatten des ursprünglichen Modells bilden. Um also ein 3D-Objekt mit Schatten zu zeigen, müssen Sie das Objekt zweimal rendern. Einmal in seiner normalen Position, und einmal mit der Schattenmatrix. Desweiteren müssen Sie dafür sorgen, dass das Schattenobjekt eine einheitliche dunkle Farbe erhält, sonst würde es so aussehen, als ob das Objekt einfach nur gegen eine Wand geklatscht ist und zusammengepresst worden ist. In diesem [image] Beispielcode finden Sie die oben vorgestellten Funktion integriert.

Multiplizieren von Matrizen

Jetzt haben Sie alle grundlegenden Transformationsmethoden kennengelernt. Doch was ist, wenn Sie zum Beispiel mehrere Transformationen miteinander kombinieren wollen? Wenn Sie zum Beispiel wollen, dass sich das Objekt um seine eigene Achse dreht, aber 3 X-Einheiten entfernt von der Ausgangslage? Dann kommt das Multiplizieren von Matrizen mit ins Spiel. Durch multiplizieren zweier Matrizen verbinden Sie die beiden Transformationen aus den Matrizen in einer. Die Funktion dafür sieht so aus:

function D3DXMatrixMultiply( out MOut: TD3DXMATRIX;
                                 M1: TD3DXMATRIX;
                                 M2: TD3DXMATRIX);

M1, M2 sind dabei die beiden Matrizen, die miteinander multipliziert werden sollen.

Im Gegensatz zum Multiplizieren von Zahlen, ist es ein Unterschied, in welcher Reihenfolge Sie die Matrizen angeben. Angenommen wir haben die Matrix matMove, die die Vertices einfach nur verschiebt, und eine Matrix matRotationY, die Vertices um die Y-Achse rotieren lässt. Wenn wir nun schreiben würden,

D3DXMatrixMultiply( matResult, matMove, matRotate);
werden die Vertices zu erst verschoben und dann rotiert. Das bedeutet, das Objekt würde in dem Abstand, den wir mit matMove erzielt haben um den Nullpunkt rotieren. Wenn wir statt dessen schreiben würden

D3DXMatrixMultiply( matResult, matRotate, matMove);
rotieren wir die Vertices zu erst und verschieben sie erst danach. Das Resultat: das Objekt dreht sich um sich selbst aber befindet sich an der Stelle, um die wir es mit matMove bewegt haben. Ich hoffe, damit ist Ihnen deutlich geworden, welche Möglichkeiten aber auch Fehlerquellen im Multiplizieren von Matrizen stecken.

Einstellen des Z-Buffers

Der Z-Buffer ist ein zusätzlicher Speicher, der zu jedem Pixel, dass auf dem Bildschirm gerendert wird, eine Tiefeninformation speichert, im Prinzip also die Distanz von der Kamera zur jeweiligen Stelle des Objektes. Wenn nun ein zweites Objekt gerendert wird, dass unter anderem den gleichen Pixel auf dem Bildschirm beinhalten würde, dann wird geprüft, ob der neue Pixel näher an der Kamera liegt, als der bereits gemalte Pixel. Durch diese Abfragetechnik kann sichergestellt werden, dass Objekte, die sich im Hintergrund befinden nicht Objekte überlagern, die sich im Vordergrund befinden.

An dieser Stelle wird nur kurz beschrieben, wie Sie den Z-Buffer aktivieren. In einem späteren Kapitel werden wir uns näher mit Tiefenbuffern beschäftigen, und wie man die Performance dieser für seine Zwecke optimieren kann.

Damit Sie nach all den vielen Informationen auch endlich Resultate sehen können, hier die notwendigen Schritte, um einen Z-Buffer zu verwenden.

1. Z-Buffer initialisieren:
Wenn Sie Ihr Device initialisieren, übergeben Sie dem unter anderem ein Record namens [image] D3DPRESENT_PARAMETERS. In diesem Record können Sie einstellen, ob Sie einen Tiefenbuffer verwenden möchten, und wenn ja, welches Format er haben soll. Das könnte dann zum Beispiel so aussehen:

var
  d3dpp : TD3DPRESENT_PARAMETERS;
begin
  ...
  d3dpp.EnableAutoDepthStencil := TRUE;
  d3dpp.AutoDepthStencilFormat := D3DFMT_D16;
Damit initialisieren wir einen 16-Bit-Tiefenbuffer. Für die meisten Zwecke reicht dieser aus.

2. Z-Buffer aktivieren
Sie müssen dem Device mitteilen, dass es beim Rendern den Z-Buffer auch benutzen soll. Das geht über die Renderstates:

  d3ddev8.SetRenderState( D3DRS_ZENABLE, CARDINAL( TRUE ) );

3. Z-Buffer regelmässig löschen
Wie auch unser Renderfenster, muss auch der Z-Buffer mit jedem neuen Frame gereinigt werden, dass heisst also, von den alten Tiefenzahlen befreit werden, damit wir das nächste Bild korrekt darstellen können. Dazu bauen wir das entsprechende Flag in die .Clear-Funktion des Devices ein:

d3dDev8.Clear( 0, nil, D3DCLEAR_TARGET or D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB( 50, 50, 50 ), 1.0, 0 );

Jetzt ist alles nötige eingestellt, damit es beim Rendern zu keinen optischen Fehlern kommt.

Das Beispielprogramm

Jetzt ist es mal wieder an der Zeit, sich den mitgelieferten Beispielcode genauer anzuschauen. Sie finden Ihn im directxgraphics/demos/Matrizen und Z-Buffer/-Verzeichnis (Achtung: nur in der Offline-Version). Mit den in diesem Kapitel beschriebenen Transformationsmatrizen und guten Modellen für den Vertexbuffer lässt sich schon eine Menge machen. Der Zylinder ist eher ein mageres Beispiel dafür, was alles durch reine Algorithmik möglich ist.

Zum ersten Mal werden Sie auch eine Animation, also Bewegung im Bild, sehen. Eine Bewegung bzw. Rotation kommt allein dadurch Zustande, dass man von Frame zu Frame die Zahlen für die Matrizen ein wenig ändert. Hierbei helfen wir uns, in dem wir GetTickCount in die Funktionen miteinbinden. GetTickCount liefert uns die aktuelle Zeit in Form einer grossen Zahl zurück. Da sich diese Zahl jede Tausendstelsekunde erhöht, verlangsamen wir das Tempo, in dem wir GetTickCount durch eine 3- oder 4-stellige Zahl teilen. Das langsame Anwachsen dieser Zahl ist geeignet für einen Animationsablauf. Desweiteren bietet sich beim Übernehmen der aktuellen Zeit ein weiterer Vorteil: es wird sichergestellt, dass auf jedem Rechner, egal wie schnell dieser ist, die Bewegung gleich schnell abläuft. Die Anzahl der Frames pro Sekunde hat also auf die Geschwindigkeit der Animation keinen Einfluss. Es ist allein die Zeit.

Hier die einzelnen Codeabschnitte aus den behandelten Kapiteln

Perspektive und Blickwinkel

procedure SetupViewAndProjection;
var
  matView, matProj: TD3DXMatrix;
  _v1, _v2, _v3 : TD3DXVector3;
begin
  // View.
  _v1 := D3DXVector3( 0.0, 2.0, -5.0);
  _v2 := D3DXVector3( 0.0, 0.0,  0.0);
  _v3 := D3DXVector3( 0.0, 1.0,  0.0);
  D3DXMatrixLookAtLH(matView, _v1, _v2, _v3);
  D3DDEV8.SetTransform(D3DTS_VIEW, matView);

  // Projection.
  D3DXMatrixPerspectiveFovLH(matProj, D3DX_PI / 4, form1.width/form1.height, 1.0, 100.0);
  D3DDEV8.SetTransform(D3DTS_PROJECTION, matProj);
end;

Translation, Rotation, Skalierung

procedure SetupZylinder1;
var
  matWorld, matScale, matMove: TD3DXMatrix;
begin
  // Einstellen der Weltmatrix für die Darstellung des ersten Zylinders

  // Der Zylinder wird auf 60% verkleinert
  D3DXMatrixScaling( matScale, 0.6, 0.6, 0.6 );

  // Der Zylinder rotiert um die X-Achse
  D3DXMatrixRotationX(matWorld, getTickCount / 500);

  // Der Zylinder wird um eine Welteinheit nach rechts verschoben
  D3DXMatrixTranslation( matMove, 1, 0, 0);

  // Alle drei Matrizen werden miteinander multipliziert
  D3DXMatrixMultiply( matWorld, matScale, matWorld );
  D3DXMatrixMultiply( matWorld, matWorld, matMove );

  D3DDEV8.SetTransform(D3DTS_WORLD, matWorld);
end;

Schatten

procedure Schatten;
var
  matshadow, matWorld: TD3DXMatrix;
  plane: TD3DXPlane;
  vlicht: TD3DXVector4;
  v1, v2, v3: TD3DXVector3;
begin
  v1:= D3DXVector3( -1, -1, 0);
  v2:= D3DXVector3( 0, -1, 0);
  v3:= D3DXVector3( -1, -1, -1);
  D3DXPlaneFromPoints( plane, v1, v2, v3);
  vlicht:= D3DXVector4(4, 4, 4, 1);
  D3DXMatrixShadow( matShadow, vlicht, plane);

  d3ddev8.GetTransform( D3DTS_WORLD, matWorld);
  D3DXMatrixMultiply( matWorld, matWorld, matShadow);
  d3ddev8.SetTransform( D3DTS_WORLD, matWorld);
end;

Nehmen Sie sich für das Ausprobieren des Programms ruhig Zeit, und versuchen Sie, die Bewegungen der Körper und der Kamera zu verändern. Wie Sie feststellen werden, lassen sich mit vergleichsweise wenig Veränderungen im Quellcode neue Animationen erzeugen.

Entscheidend hierbei ist die OnIdle-Prozedur, die mit den Prozeduren SetupZylinder1 und SetupZylinder2 die Matrizen für den zu rendernden Zylinder einstellt und mit der Prozedur Schatten eine Matrix für den Schatten des Zylinders erstellt.

Weitere Transformationsmethoden

Neben den oben genannten Transformationsmöglichkeiten gibt es noch ein paar weitere, die ich Ihnen hier kurz vorstellen möchte.

Determinante

function D3DXMatrixfDeterminant( m: TD3DXMatrix): Single;
Gibt die Determinante der Matrix m zurück.

Einheitsmatrix

function D3DXMatrixIdentity(out mOut : TD3DXMatrix) : TD3DXMatrix;
Macht MOut zur Einheitsmatrix.

Kontrolle auf Einheitsmatrix

function D3DXMatrixIsIdentity(const m : TD3DXMatrix) : BOOL;
Kontrolliert, ob m eine Einheitsmatrix ist und liefert WAHR oder FALSCH zurück

Reflektion

function D3DXMatrixReflect( out MOut: TD3DXMatrix; Plane: TD3DXPlane): TD3DXMatrix;
Macht aus MOut eine Spiegelmatrix, durch die Vertices an Plane vom Typ [image] D3DXPLANE gespiegelt werden.

Vertauschen

function D3DXMatrixTranspose( out MOut: TD3DXMatrix; M: TD3DXMatrix): TD3DXMatrix;
Vertauscht die Inhalte der Matrizen MOut und M.

Weltmatrizen-Blending

In den letzten Kapiteln haben Sie gelernt, wie Sie dem Device eine sogenannte Welt-Matrix übergeben. Das Device wendet dabei diese Matrix auf alle Vertices zu 100% an. Was soll man auch anderes erwarten? höre ich Sie jetzt fragen. Ganz einfach. Sie können dem Device noch mehr Weltmatrizen übergeben, und dann selbst bestimmen, zu welchen Anteilen diese Matrizen auf die Vertices angewendet werden. Im Prinzip werden also diese Matrizen miteinander vermischt, was man auch als Überblenden bezeichnen kann.

Um diese Technik nutzen zu können, müssen Sie Ihrem Vertexformat zunächst etwas hinzufügen. Und zwar eine der folgenden Flags aus den [image] D3DFVFFLAGS.

  D3DFVF_XYZB1
  D3DFVF_XYZB2
  D3DFVF_XYZB3
  D3DFVF_XYZB4
  D3DFVF_XYZB5

Wenn Sie die eigentliche Weltmatrix mit einer zweiten Vermischen wollen, dann verwenden Sie das Flag D3DFVF_XYZB1, bei 3 Matrizen, also zwei zusätlichen Matrizen, die ineinandergeblendet werden sollen, verwenden Sie D3DFVF_XYZB2 usw. . Die Blendfaktoren werden direkt nach den Positionskoordinaten angegeben. Das sieht dann bei D3DFVF_XYZB2 so aus:

const
  D3DFVF_CUSTOMVERTEX = ( D3DFVF_XYZB1 or D3DFVF_DIFFUSE );

type
  CUSTOMVERTEX = record
    x, y, z: single;
    b1, b2: single;
    color: TD3DColor;
  end;

b1 und b2 liegen dabei zwischen 0.0 und 1.0, wobei 1.0 bedeutet, dass die normale Weltmatrix zu 100% übernommen wird, und 0.0, dass die jeweilige zweite oder dritte Matrix zu 100% übernommen wird. Diese Matrizen übergeben Sie dem Device, in dem Sie in der SetTransform-Funktion D3DWORLD1-D3DWORLD3 aus den [image] D3DTRANSFORMTYPE verwenden. Der letzte Schritt besteht darin, das Matrizenblending über diesen Renderstate einzuschalten:

d3ddev.setrenderstate(D3DRS_VERTEXBLEND, D3DVFB_2WEIGHTS);

D3DVBF_2WEIGHTS ist dabei eine Konstante aus den [image] D3DVERTEXBLENDFLAGS. Nun werden 3 Matrizen mit den unter b1 und b2 angegebenen Gewichtungen auf die Vertices angewendet.

Orthogonale Perspektive

Zum Schluss möchte ich Ihnen noch etwas zur Projektionsmatrix zeigen. Und zwar haben wir im Kapitel [image] Perspektive und Blickwinkel Matrizen kennengelernt, die uns eine räumliche Vorstellung von Körpern vermitteln können. Was ist aber, wenn wir das gar nicht wollen, sprich, dass Dinge die weiter Hinten, genauso gross dargestellt werden, wie Dinge, die sich weiter vorne befinden. Das ist zum Beispiel bei isometrischen Spielen, wie Age of Empires oder Command&Conquer 3 der Fall. Hier blicken wir von schräg oben auf das Spielfeld und glauben, in die Tiefe zu gucken, je mehr wir uns dem oberen Bildschirmrand nähern. Tatsächlich werden die Felder aber nicht kleiner. Sie bleiben gleich gross. Die untere Montage verdeutlicht den Unterschied an der beiligenden Demo.

Das linke Bild enthält eine orthogonale Projektionsmatrix. Sie erkennen das zum Beispiel daran dass der Durchmesser des rechten Zylinders in die Tiefe hin nicht kleiner wird. Anders ist das im rechten Screenshot. Hier erhalten wir einen realistischen Eindruck räumlicher Tiefe.

Statt der Funktion D3DXMatrixPerspectiveLH() oder D3DXMatrixPerspectiveFovLH() können Sie folgende beiden Funktionen verwenden, um diesen Effekt erzielen zu können.

function D3DXMatrixOrthoLH( out MOut: TD3DXMatrix;
                                w: Single;
                                h: Single;
                                zn: Single;
                                zf: Single): TD3DXMatrix;

w und h geben an, wieviele Welteinheiten in der Vertikalen und Horizontalen dargestellt werden sollen. zn (z-near) und zf (z-far) geben an, ab welcher Tiefe Objekte sichtbar sein sollen, und ab welcher Tiefe nicht mehr.

Mit der zweiten Funktion lassen sich im Prinzip die gleichen Ergebnisse erzielen, wie mit der ersten Funktion. Sie setzt lediglich andere Parameter vorraus.

function D3DXMatrixOrthoOffCenterLH( out MOut: TD3DXMatrix;
                                         l: Single;
                                         r: Single;
                                         b: Single;
                                         t: Single;
                                         zn: Single;
                                         zf: Single): TD3DXMatrix;

l und r ist der Bereich auf der x-Achse der angezeigt werden soll. Wenn also der Nullpunkt im Zentrum ihres Bildes sein soll, und Sie alles abbilden möchten, was sich 2 Welteinheiten von diesem Punkt entfernt befindet, so geben Sie hier an -2 und 2.

b und t markieren die sichtbaren Grenzen in der Vertikalen.

zn und zf markieren die sichtbaren Grenzen in der Tiefe.


SelfDXD - Der Indexbuffer

SelfDXD --- [image] DirectXGraphic --- [image] Vertex- und Indexbuffer
 | Der Indexbuffer
 | Was ist der Indexbuffer? IDirect3DDevice8.CreateIndexBuffer() Indexbuffer mit Daten füllen Indizierte Vertices darstellen

Was ist der Indexbuffer?

Wenn Sie schonmal versucht haben ein 3D-Modell mit nur einem Vertexbuffer zu erstellen, werden Sie sicherlich schnell festgestellt haben, dass das gar nicht so einfach ist. Intuitiv versucht man ja zunächst, alle Dreiecke über eines der [image] D3DPRIMITIVTYPE-Flags zu erstellen. Dabei erscheint D3DPT_TRIANGLESTRIP am günstigsten, weil man mit einer minimalen Anzahl an Vertices eine maximale Anzahl an Dreiecken rendern kann. Doch schon bei den einfachsten Aufgaben wird es hier schwierig. Ein Beispiel dafür sehen Sie hier:

Angenommen, Sie möchten eine Hügellandschaft erstellen. Dann würden Sie wahrscheinlich mit einem Raster dieser Art arbeiten. Die Höhe liesse sich dann bequem für jeden Vertex einstellen. Aus jedem Viereck würden wir dann zwei Dreiecke machen. Doch in welcher Reihenfolge schreibt man die Vertices in den Vertexbuffer, um eine Fläche erscheinen zu lassen? Als Trianglstrip werden wir das nicht hinbekommen. Es gibt keine Möglichkeit, jeden Vertex nur einmal in den Vertexbuffer zu schreiben und damit die ganze Fläche zu rendern. Bleibt also nur die Möglichkeit, die Vertices als TriangleList zu rendern, doch das hat den entscheidenden Nachteil, dass wir mehrmals die gleichen Vertices dem Vertexbuffer übergeben müssen. Betrachten wir exemplarisch mal ein Viereck aus dem Raster.

Wenn wir dieses Viereck als TriangleList rendern wollen, dann müssen wir eine Vertexreihenfolge wie 1/4/2/1/3/4 im Buffer verwenden. Die Vertices 1 und 4 werden dabei also zweimal übergeben, obwohl sich ihre Koordinaten ja nicht verändern. Das heisst also, die Vertices 1 und 4 werden zweimal von Grund auf neu gerendert, obwohl ein einmaliges Rendern völlig ausreichen würde. Desweiteren wird die Liste der Vertices im Vertexbuffer länger. Bei 4 Vertices hätten wir 6 Elemente im Buffer. Bei unserem Raster wird der Unterschied schon extremer. Obwohl wir nur 25 Vertices haben, müssten wir 96 Vertices dem Vertexbuffer übergeben (16 Vierecke * 6 Vertices). Das ist ein Zuwachs von über 380%. Sie können sich vorstellen, dass man bei grösseren Konstruktionen schnell an die Grenzen der Rechenleistung kommt, um noch akzeptable FPS zu erhalten.

Bei solchen Problemen kann hier ein Indexbuffer Abhilfe schaffen. Dabei übergeben wir dem Vertexbuffer lediglich alle 25 Vertices, die wir für die Fläche brauchen und dem Indexbuffer die Reihenfolge, in der diese gerendert werden sollen. Dadurch enthält der Vertexbuffer nur die notwendige Anzahl an zu berechnenden Vertices. Eine doppelte Berechnung ist hier ausgeschlossen. Der Indexbuffer speichert also nur Integer-Werte. Von der Konstruktion her ähnelt er aber trotzdem sehr dem Vertexbuffer, wie Sie in den folgenden Abschnitten feststellen werden.

Auch dieses Kapitel beinhaltet wieder eine Demo (Achtung: nur in der Offline-Version enthalten). Diesmal wird eine Rasterfläche, wie Sie oben vorgestellt wurde, gezeigt. Zwei Parabeln geben der Fläche dabei eine Biegung.

IDirect3DDevice8.CreateIndexBuffer()

Der Indexbuffer funktioniert so ähnlich, wie der Vertexbuffer. Auch hier wird wieder ein Speicherbereich für die Indexzahlen reserviert. Diese Zahlen werdem dem Buffer als Array des Typs WORD übergeben. Bevor wir den Indexbuffer erstellen, müssen wir zunächst das Array mit den Indizes anlegen. Und bevor wir dieses Array anlegen können, müssen wir natürlich zu erst die Fläche haben, die wir später anzeigen wollen. Deshalb hier zunächst der Code für das Raster:

function InitGeometry: HRESULT;
var
  vertices: Array[0..99] of CUSTOMVERTEX;
  pvertices: PByte;
  indizes: Array[0..((81*6) - 1)] of WORD;
  pindizes: PByte;
  z: Word;
  x,y: byte;
begin
  result:= E_FAIL;

// Erstellen des 10x10 grossen Feldes
  for y:= 0 to 9 do
    for x:= 0 to 9 do                       // die y-Koordinate gibt die Höhe an und
                                            // ist eine Mischung aus zwei Parabel-Kurven
      vertices[y*10 + x]:= GetCustomVertex( x-5, -(x-5)*(x-5)*0.1 + (y-4)*(y-4)*0.07, y-5, D3DColor_RGBA(x*10, 255 - y*10, (x+y)*10, 0));

  d3ddev8.CreateVertexBuffer( 100 * sizeof(CUSTOMVERTEX), 0, D3DFVF_CUSTOMVERTEX, D3DPool_Default, vbuffer);

  vbuffer.Lock(0, sizeof(vertices), pvertices, 0);
    move( vertices, pvertices^, sizeof(vertices) );
  vbuffer.unlock;

// wird fortgesetzt....

Wir erstellen also ein 10x10 grosses Raster. Damit der Nullpunkt im Zentrum des Rasters liegt, ist die x-Koordinate immer x-5 und die z-Koordinaten y-5. Die y-Koordinate beinhaltet einen Term, der aus zwei Parabeln besteht, nämlich
-(x-5)2*0.1 und (y-4)2*0.07.
Dadurch erhalten wir diese Biegungen in der Fläche. Wenn Sie statt dessen einfach nur 0 übergeben, werden Sie eine glatte Ebene später sehen.

Als nächstes wird das Array mit den Indizes angelegt. Dabei spielt es natürlich eine Rolle, in welcher Reihenfolge wir die oberen Vertices erstellt haben. Nach dem oberen Algorithmus haben wir die Vertices in folgender Reihenfolge erstellt:

Demzufolge lautet die Vertexreihenfolge für das erste Viereck 0/11/1/0/10/11. Damit die Eingabe der Vertexindizes ein wenig einfacher wird, kann man dafür folgenden Algorithmus benutzen:

// Fortsetzung.....

  x:= 0;
  y:= 0;
  z:= 0;

  for y:= 0 to 8 do
    for x:= 0 to 8 do
    begin
      indizes[z]:= y*10 + x;
      inc(z);
      indizes[z]:= y*10 + x + 11;
      inc(z);
      indizes[z]:= y*10 + x + 1;
      inc(z);
      indizes[z]:= y*10 + x;
      inc(z);
      indizes[z]:= (y+1)*10 + x;
      inc(z);
      indizes[z]:= y*10 + x + 11;
      inc(z);
    end;

// wird fortgesetzt....

Für jedes der 9x9 Vierecke in dem Raster werden die 3 Ecken der 2 Dreiecke angegeben. Daher muss dass Array auch 81*6 Felder gross sein. Nun können wir uns endlich um die eigentliche Initialisierung kümmern, die mit folgender Funktion ausgeführt wird:

function CreateIndexBuffer(const Length : Cardinal;
                                 Usage : LongWord;
                           const Format : TD3DFormat;
                                 Pool : TD3DPool;
                           out   ppIndexBuffer : IDirect3DIndexBuffer8) : HResult;

Length: Grösse des Indexbuffers in Bytes. Hierfür können Sie am besten Sizeof( Indexarray ) verwenden.

Usage: 0 oder eine Kombination der [image] D3DUSAGEFLAGS-Flags, mit denen Sie angeben, für welche Zwecke Sie den Buffer verwenden. Dadurch kann DirectXGraphics den Buffer im Speicher optimal positionieren, womit die Performance ein wenig steigen kann.

Format: Gibt an, wieviele Bits für jeden Index verwendet werden soll. Sie haben die Wahl zwischen D3DFMT_INDEX16 und D3DFMT_INDEX32 aus den [image] D3DFORMAT-Flags. Bei 16-Bit können Sie 65536 (216) und bei 32-Bit 4294967296 (232) Indizes verwenden.

Pool: Eine Konstante aus der [image] D3DPOOL-Aufzählung, die angibt, in welcher Speicherklasse der Indexbuffer angelegt werden soll.

ppIndexBuffer: eine Variable, die auf den Indexbuffer zeigt.

In unserem Beispiel würde die Funktion so aussehen:

d3ddev8.CreateIndexBuffer( sizeof(indizes),
                           0,
                           D3DFMT_INDEX16,
                           D3DPOOL_DEFAULT,
                           ibuffer);

Indexbuffer mit Daten füllen

Wie beim Vertexbuffer funktioniert das Übergeben der Daten auch wieder in Verbindung mit dem Locken des Speichers.

function Lock(const OffsetToLock : LongWord;
                    SizeToLock : LongWord;
                var ppbData : PByte;
              const Flags : LongWord) : HResult;

OffsetToLock: Die Stelle in Bytes, an der der Indexbuffer beschrieben bzw. ausgelesen wird.

SizeToLock: Die Grösse des zu sperrenden Bereiches in Byte.

ppbData: Ein Zeiger auf den Speicherbereich, in dem die Indexdaten stehen.

Flags: 0 oder eine Kombination der [image] D3DLOCKFLAGS mit denen man angibt, für welche Zwecke man den Indexbuffer lockt.

Die Übergabe der Daten dürfte Ihnen noch aus dem Kapitel [image] Der Vertexbuffer bekannt sein, deshalb hier der restliche Beispielcode:

  ibuffer.Lock( 0, sizeof(indizes), pindizes, 0);
    move( indizes, pindizes^, sizeof(indizes));
  ibuffer.UnLock;

Indizierte Vertices darstellen

Das Rendern von Vertices, die über Indizes aufgerufen werden ist im Prinzip genauso einfach, wie der Rendervorgang, den Sie bisher kennen. Lediglich zwei Dinge sind anders. Denn neben dem Vertexbuffer, den wir dem Device bekannt machen müssen, müssen wir natürlich auch den Indexbuffer dem Device übergeben. Das geschieht mit folgender Funktion.

function SetIndices(const pIndexData : IDirect3DIndexBuffer8;
                    const BaseVertexIndex : Cardinal) : HResult;

pIndexData: Der Zeiger auf den Indexbuffer.

BaseVertexIndex: Dieser Wert wird mit jedem Indize addiert, womit also Vertices, die ganz am Anfang im Vertexbuffer liegen, ausgeschlossen werden können.

Bisher haben Sie zum Rendern die .DrawPrimitiv-Funktion verwendet. Wenn Sie indizierte Vertices darstellen wollen, müssen Sie eine andere Funktion benutzen. Diese lautet wie folgt:

function DrawIndexedPrimitive(const _Type : TD3DPrimitiveType;
                              const minIndex : Cardinal;
                                    NumVertices : Cardinal;
                                    startIndex : Cardinal;
                                    primCount : Cardinal) : HResult;

_Type: Gibt an, welche Art von Primitiven Sie darstellen wollen. Verwenden Sie hierzu eine der [image] D3DPRIMITIVETYPE-Konstanten.

minIndex: Kleinster Vertexindex, der für diesen Rendervorgang benutzt werden soll. Im Prinzip das gleiche, wie BaseVertexIndex.

NumVertices: Anzahl der Vertices, die gerendert werden sollen, beginnend mit BaseVertexIndex + minIndex.

startIndex: Gibt die Stelle im Indexbuffer an, ab der die Indizes gelesen werden sollen.

PrimCount: Anzahl der Primitiven, die gerendert werden sollen. Also bei D3DPT_TRIANGLELIST Vertexanzahl / 3, bei D3DPT_LINELIST Vertexanzahl / 2, etc.

In unserem Beispielcode sieht das dann folgendermassen aus:

  d3ddev8.setstreamsource( 0, vbuffer, sizeof(CUSTOMVERTEX));
  d3ddev8.SetVertexShader(D3DFVF_CUSTOMVERTEX);
  d3ddev8.SetIndices(ibuffer, 0);
  d3ddev8.DrawIndexedPrimitive( D3DPT_TRIANGLELIST, 0, 100, 0, 486 div 3);

Es werden also alle Vertices und alle Indizes in den Rendervorgang mit einbezogen. Da wir D3DPT_TRIANGLELIST verwenden, müssen wir die Anzahl der Indizes durch 3 teilen.

Damit haben Sie ein weiteres wichtiges Element in der Erzeugung und Darstellung dreidimensionaler Objekte kennengelernt. Das Beispielprogramm finden Sie unter demos/indexbuffer/. Es enthält alle hier gezeigten Codes. Auch hier kann ich Ihnen wieder nur raten, sich die Beispiele genauer anzuschauen und durch verändern der Zahlen oder Formeln herauszufinden, welche Möglichkeiten sich bieten. Übungshalber würde es sich hier auch anbieten, Matrizen auf die Objekte anzuwenden.


SelfDXD - Licht und Schatten

SelfDXD --- [image] DirectXGraphic
Licht und Schatten


SelfDXD - Was ist Licht?

SelfDXD --- [image] DirectXGraphic --- [image] Licht und Schatten
 | Was ist Licht?
 | Einleitung Zwei Arten von Licht Lichtquellen Materialien Lichtberechnung

Einleitung

Nachdem wir jetzt 3D-Modelle selbst erstellen können, wollen wir uns mit diesem Kapitel ein wenig der Präsentation der Szene widmen, genauergesagt alles ins rechte Licht rücken. Bisher haben wir nur mit bereits beleuchteten Vertices gearbeitet, also Vertices, die keine Normale besitzen. Das hatte zur Folge, das alle Seiten eines 3D-Modells (Sie erinnern sich an die beiden Zylinder und an die gebogene Fläche) in genau den Farben angezeigt wurden, die wir definiert haben.

Beim Beleuchten einer Szene ist das ein wenig anders. Die Farben, die wir für die einzelnen Vertices angeben, gelten natürlich immer noch, aber darüberhinaus werden weitere Faktoren berücksichtigt, die letztendlich die Farbe des Pixels auf dem Bildschirm beeinflussen. Nun spielt die Normale eine Rolle, die angibt, wie ein Vertex zur Lichtquelle ausgerichtet ist. Hinzu kommen sogenannte Materialeigenschaften, mit denen man unter anderem bestimmen kann, wie stark ein 3D-Objekt Licht überhaupt reflektiert. Durch rein mathematische Berechnungen, um die sich zum Glück DirectXGraphics kümmert, können wir 3D-Objekte realistischer erscheinen lassen, in dem Licht- und Schattenbereiche die Form des Objektes akzentuieren.

Zwei Arten von Licht

In DirectXGraphics wird zwischen zwei Arten von Licht unterschieden. Dem ambienten Licht und dem direkten Licht. Letzteres definiert den Lichtanteil einer Fläche anhand der Distanz, der Ausrichtung und der Materialeigenschaften der Vertices. Direktes Licht besitzt eine Farbe, einen Intensitätsgrad und eine Richtung. Wenn die Richtung des Lichtes orthogonal zur Fläche ist, wird die Fläche natürlich am stärksten und hellsten angestrahlt. Je mehr sich die Fläche von der Lichtquelle abwendet, desto dunkler erscheint sie, bis sie schliesslich zur Schattenseite zugewendet ist. Das folgende Bild verdeutlicht den Effekt von direktem Licht.

Alles, was also nicht direkt zur Lichtquelle zugewendet ist, wird schwarz dargestellt. Wenn wir uns aber in unserer Umgebung umschauen, stellen wir fest, dass wir nicht alles im Pechschwarz sehen, nur weil es vielleicht nicht der Sonne zugewendet ist (zum Beispiel die Unterseite ihrer Hand). Jeder Bestandteil Ihres Zimmers, in dem Sie jetzt vielleicht grade sitzen, absorbiert Licht und reflektiert Licht. Das reflektierte Licht kann andere Objekte beleuchten, weshalb Sie Schattenseiten oftmals nur ein wenig dunkler sehen, als dem Licht zugewendete Seiten. Die Erfassung der tatsächlichen realistischen Lichtverhältnisse ist also ein komplexer mathematischer Prozess, der allenfalls für Raytracing-Programm verwendet wird. Um jedoch einen selben Effekt in Echtzeit berechnen zu können, verwendet DirectXGraphics das sogenannte ambiente Licht. Dieses Licht ist eine Grundhelligkeit, die überall vorherrscht. Das heisst also, selbst Flächen, die dem direkten Licht komplett abgewendet sind, können noch deutlich erkennbar dargestellt werden. So könnte unsere obere Kugel in Kombination von direktem und ambientem Licht so aussehen:

Bei diesem Bild ist durch das direkte Licht wieder erkennbar, aus welcher Richtung die Kugel angestrahlt wird. Da die Schattenseite jedoch mit einem ambienten Licht angestrahlt wird, lässt sich auch diese betrachten. Die Form des 3D-Körpers lässt sich also vollständig erfassen.

Lichtquellen

DirectXGraphics verwendet 3 Arten von Lichtquellen, die als direktes Licht auf das Objekt wirken können. Hier eine Grafik mit den englischen Bezeichnungen dafür:

Directional Light
Im Grunde sind hierbei alle Lichtstrahlen parallel zu einander. Es eignet sich besonders für sehr weit entfernte Objekte, wie der Sonne, da es hier kaum auffallen würde, wenn man das Directional Light durch das Point Light ersetzen würde.

Point Light
Dieses Lichquelle ist für alle unmittelbaren sich in der Nähe befindlichen Objekte relevant, also Kerzen, Strassenlaternen, Fackeln etc.

Spotlight
Spotlights kennen wir im wirklichen Leben aus dem Theater oder Zirkus. Aber auch Scheinwerfer und Taschenlampen funktionieren nach dem Spotlight-Prinzip. Dabei ist das Licht in eine Richtung gebündelt. Würde das Licht durch Rauch fallen, würde ein Art Kegelform entstehen.

Materialien

Neben den Eigenschaften des Lichtes können Sie auch noch dem Objekt, das beleuchtet wird, bestimmte Eigenschaften zuweisen. Dabei geht es darum, wie stark das Objekt das Licht absorbiert und welche Farben es reflektiert. Es gibt folgende Eigenschaften:

Diffuse: Gibt an, in wie weit der Rot-, Grün-, und Blauanteil des direkten Lichtes reflektiert werden soll. Angenommen, Sie lassen nur rotes Licht auf ein Objekt fallen, das aber nur Grünes reflektiert, so würde Ihr Objekt schwarz erscheinen. Wenn Sie angeben, dass alle Farben reflektiert werden sollen, erscheint Ihr Objekt in den Farben, in denen es angestrahlt wird.

Ambient: Gibt den Rot-, Grün, und Blauanteil an, den das Objekt vom ambienten Licht reflektiert.

Specular: Sie können Ihren Objekten eine Art Glanz geben. Dabei bestimmen Sie mit dem Specular-Wert, welche Farbe dieses Glanzlicht haben soll und wie stark es in der Reflektion auftreten soll. Der Specular-Wert kann zum Beispiel für Objekte eingesetzt werden, die metallisch oder poliert wirken sollen. Die untere Grafik verdeutlicht diesen Effekt.

Emissive: Desweiteren können Sie ihrem Objekt den Eindruck verleihen, es würde selbst leuchten. Mit dem Emissive-Wert bestimmen Sie die Farbe die Ihr Objekt in jedem Fall abgibt.

Lichtberechnung

DirectXGraphics berechnet nicht, wie Sie vielleicht denken könnten, den Lichtanteil für jeden einzelnen Pixel, sondern lediglich für die Ecken eines Dreiecks. Wie Sie wissen, besitzen Vertices eine Normale, die ihre Ausrichtung bestimmen. Durch die Ausrichtung der Normale und der Position, Entfernung und Art der Lichtquelle kann ermittelt werden, wie stark ein Vertex beleuchtet wird. Wenn nun die gesamte Fläche eines Dreiecks beleuchtet werden soll, wird der Lichtanteil jedes einzelnen Pixels der Dreiecksfläche nicht anhand seiner Ausrichtung und der Lichquelle errechnet, sondern aus den Lichtinformationen der drei Vertices, die das Dreieck bilden. Die folgende Grafik verdeutlicht es nochmal:

Eine quadratische Fläche wird in der Mitte mit einem Spotlight angestrahlt. Im linken Beispiel besteht diese Fläche nur aus 9 Vertices. Lediglich der mittlere Vertex wird vom Spotlight getroffen, die anderen 8 Vertices nicht. Obwohl der Radius des Spotlight wesentlich kleiner ist, als die quadratische Fläche, werden weite Teile der Fläche heller dargestellt, weil die Vertices soweit auseinanderliegen. (Farbabstufungen gibt es dabei aber nicht! Das liegt am Gif-Format!)

Im rechten Beispiel ist die Zahl der Vertices deutlich höher. Es werden mehrere Vertices vom Spotlight getroffen. Dabei besitzen diese Vertices unterschiedliche Lichtanteile. Aufgrund der vielen nah beieinanderliegenden Vertices ist die Form des Spotlights gut zu erkennen.

Diese Berechnungstechnik ist natürlich wesentlich schneller als eine Pixel-für-Pixel Berechnung. Jedoch müssen Sie, wie das obere Beispiel zeigt, immer darauf achten, wieviele Vertices Sie für ein 3D-Objekt einsetzen, wenn Sie ein Spotlight oder ein Point Light verwenden. Würden Sie Directional Light verwenden, hätten Sie die quadratische Fläche auch nur mit 4 Vertices bilden können, da Lichteinfall und -intensität an jeder Stelle im Raum gleich sind.


SelfDXD - Licht eintellen

SelfDXD --- [image] DirectXGraphic --- [image] Licht und Schatten
 | Licht einstellen
 | Einleitung Einstellen der Lichteigenschaften Einstellen der Materialeigenschaften Anschalten des Lichtes Materialquellen Das Beispielprogramm

Einleitung

Das Ein- bzw. Anstellen des Lichtes ist eigentlich sehr einfach. Sie müssen wirklich nur die gewünschten Parameter setzen. Die 3D-Objekte werden dann beim rendern automatisch korrekt beleuchtet, ohne, dass Sie sich um bestimmte mathematische Formeln kümmern müssen. Das Licht wird in 3 Schritten eingestellt:

  1. Einstellen der Lichteigenschaften
  2. Einstellen der Materialeigenschaften
  3. Anschalten des Lichtes
Hierbei verwendet man nur wenige einfache Befehle und Records. Sie werden feststellen, dass das Einstellen des Lichtes keineswegs kompliziert ist.

Einstellen der Lichteigenschaften

In DirectXGraphics können Sie mehrere Lichtquellen einstellen. Die genaue Anzahl ist von ihrer Grafikkarte abhängig und lässt sich über die [image] D3DCAPS8 ermitteln. Das Einstellen des Lichtes erfolgt mit folgender Funktion des Direct3DDevices:

function SetLight(const Index : LongWord;
                  var   pLight : TD3DLight8) : HResult;

Index: Jeder Lichtquelle, die Sie definieren, weisen Sie eine Indexzahl zu. Möchten Sie ein bestimmtes Licht ändern oder herausnehmen, können Sie dieses über den Index ansprechen.

pLight: Ein Record des Typs [image] D3DLIGHT8, in dem alle notwendigen Farben und Parameter definiert sind.

Zu jeder Set-Funktion gibt es das entsprechende Gegenstück, die Get-Funktion:

function GetLight(const Index : LongWord;
                  out   pLight : TD3DLight8) : HResult;

GetLight liefert für den angegebenen Index ein Record des Typs D3DLIGHT8 mit den entsprechenden Eigenschaften zurück.

Als kleines Beispiel will ich Ihnen eine Prozedur zeigen, die uns das Eingeben der Parameter für ein Spotlight erleichtert.

procedure SetSpotLight( _dr, _dg, _db,
                        _ar, _ag, _ab,
                        _sr, _sg, _sb,
                        _at0, _at1, _at2: Single;
                        _pos, _Dir: TD3DXVector3;
                        _theta, _phi, _falloff,
                        _range: single;
                        _index: cardinal);
var
  light : TD3DLIGHT8;
begin
  ZeroMemory( @light, Sizeof( TD3DLIGHT8 ) );

  with light do
  begin
    _Type := D3DLIGHT_SPOT;
    Diffuse.r    := _dr;
    Diffuse.g    := _dg;
    Diffuse.b    := _db;
    Ambient.r    := _ar;
    Ambient.g    := _ag;
    Ambient.b    := _ab;
    Specular.r   := _sr;
    Specular.g   := _sg;
    Specular.b   := _sb;

    attenuation0 := _at0;
    attenuation1 := _at1;
    attenuation2 := _at2;

    position     := _pos;
    direction    := _dir;
    theta        := _theta;
    phi          := _phi;
    falloff      := _falloff;
    range        := _range;
  end;

  d3ddev8.SetLight( _index, light );
end;

Einmal eingebunden in Ihrer Anwendung ist das Definieren von Spotlights sehr einfach, weil Sie lediglich eine Prozedur mit allen Parametern aufrufen, die nötig sind. Das spart Zeit und Platz. Der Übung halber habe ich die Prozeduren für ein Point Light und ein Directional Light weggelassen. Aber mit dieser Hilfe dürfte das für Sie jetzt kein Problem sein.

Ambientes Licht setzen Sie mit einem RenderState des Direct3DDevices:

  d3ddev8.setrenderstate( D3DRS_AMBIENT, $006B6B6B );

Hierbei geben Sie das ambiente Licht als 32-Bit-Wert an. Schwarz ist dabei gleichbedeutend mit keinem ambienten Licht.

Einstellen der Materialeigenschaften

Materialeigenschaften eines Objektes werden nach dem gleichen Prinzip in DirectXGraphics gehandhabt, wie Lichter, bis auf den Unterschied, dass Sie jeweils immer nur ein Material zur gleichen Zeit verwenden. Daher fällt die Indexierung der Materialien raus. Das Setzen und Auslesen von Materialien geschieht mit folgenden Funktionen des Direct3DDevices:

function SetMaterial(var pMaterial : TD3DMaterial8) : HResult;

function GetMaterial(out pMaterial : TD3DMaterial8) : HResult;

pMaterial: Ist in beiden Funktionen ein Record des Typs [image] D3DMATERIAL8, das alle Materialeigenschaften beinhaltet.

Auch hier bietet es sich an, eine Funktion zu schreiben, die uns das umständliche Eingeben und Setzen des Materials erleichtert. Diese Funktion könnte zum Beispiel so aussehen:

procedure SetMaterial( _ar, _ag, _ab, _aa,
                       _dr, _dg, _db, _da,
                       _sr, _sg, _sb, _sa,
                       _er, _eg, _eb, _ea,
                       _power: Single);
var
  mtrl : TD3DMATERIAL8;
begin
  ZeroMemory( @mtrl, sizeof( TD3DMATERIAL8 ) );

  with mtrl do
  begin
    Ambient.r := _ar;
    Ambient.g := _ag;
    Ambient.b := _ab;
    Ambient.a := _aa;
    Diffuse.r := _dr;
    Diffuse.g := _dg;
    Diffuse.b := _db;
    Diffuse.a := _da;
    Specular.r := _sr;
    Specular.g := _sg;
    Specular.b := _sb;
    Specular.a := _sa;
    Emissive.r := _er;
    Emissive.g := _eg;
    Emissive.b := _eb;
    Emissive.a := _ea;
    Power := _power;
  end;

  d3ddev8.SetMaterial( mtrl );
end;

Anschalten des Lichtes

Sie können auf mehreren Ebenen kontrollieren, ob Sie Licht in Ihrer Szene haben möchten oder nicht. Zum einen können Sie entscheiden, ob die Lichtberechnung überhaupt aktiviert sein soll. Dies stellen Sie über einen Renderstate an, der wie folgt aussieht:

  d3ddev8.SetRenderState( D3DRS_LIGHTING, Cardinal( TRUE ) );

Mit true oder false in der Cardinal-Funktion entscheiden Sie, ob DirectXGraphics überhaupt irgendwelche Lichtalgorithmen verwenden soll. Geben Sie false an, werden alle Vertices wie beleuchtete Vertices (also ohne Normale) gehandhabt und in ihren Grundfarben dargestellt.

Desweiteren können Sie für jedes einzelne Licht, dass Sie indexiert haben, entscheiden, ob dieses aktiviert sein soll, oder nicht. Die Funktion des Direct3DDevices sieht dafür folgendermassen aus:

function LightEnable(const Index : LongWord;
                     const Enable : BOOL) : HResult;

Index: Die Indexnummer, dessen Licht Sie an- oder ausschalten wollen.

Enable: true schaltet das Licht an, false aus.

Materialquellen

Das Verwenden der Ambient-Farbe des Materials kann einen entscheidenden Nachteil haben. Schattenseiten werden nur in der Ambientfarbe des Materials dargestellt, aber nicht in den eigentlichen Vertexfarben. Die Vertexfarben werden im Schatten praktisch völlig ignoriert. Dieses Problem können Sie lösen, in dem Sie DirectXGraphics mitteilen, dass Sie statt des Material eine andere Quelle für die Ambientfarbe verwenden wollen. Das gilt übrigens nicht nur für Ambient sondern auch für Diffuse, Emissive, Specular:

  d3ddev8.SetRenderState( D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_COLOR1);
  d3ddev8.SetRenderState( D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_COLOR1);
  d3ddev8.SetRenderState( D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_MATERIAL);
  d3ddev8.SetRenderState( D3DRS_SPECULARMATERIALSOURCE, D3DMCS_COLOR2);

D3DMCS_MATERIAL, D3DMCS_COLOR1 und D3DMCS_COLOR2 sind Bestandteile der [image] D3DMATERIALCOLORSOURCE-Aufzählungen.

Der Zweck oder Nutzen dieser Einstellungen lässt sich schwer erklären. Durch Ausprobieren können Sie am schnellst feststellen, welche Effekte sich damit erzielen lassen. Dazu kann Ihnen aber auch das folgende Beispielprogramm helfen.

Das Beispielprogramm

Diesmal fällt das Beispielprogramm ein wenig anders aus, als sonst. Es ist zwar auch eine selbstlaufende Demo, aber es ist noch viel mehr als das. Da es recht schwierig ist, auf Anhieb die Zusammenhänge zwischen Vertexfarben, Materialfarben und Lichtfarbe zu verstehen und die vielen Lichtparameter sehr grossen Spielraum lassen, habe ich für Sie ein Programm geschrieben, bei dem Sie zur Laufzeit alle relevanten Dinge verändern und direkt sehen können, welche Auswirkungen das auf die Szene hat (Achtung: nur in der Offline-Version enthalten.

Ich habe das Programm auf den Namen Lichtkasten getauft. Alle Material- und Lichtparameter (bis auf Position und Direction) sind veränderbar. Darüberhinaus können Sie auch noch einige relevanten Renderstates konfigurieren. Nehmen Sie sich Zeit für das Programm und verändern Sie die Vorgaben nach Lust und Laune. Sie werden feststellen, dass nicht jede Lichtart mit jeder Konfiguration realistisch aussieht.

Dieses Programm verwendet einen skalierten Viewport, um die 3D-Szene nur oben links darzustellen. Genauere Informationen zum Thema Viewports finden Sie im Abschnitt [image] Viewports.


SelfDXD - Texturen

SelfDXD --- [image] DirectXGraphic
Texturen


SelfDXD - Informationen zu Texturen

SelfDXD --- [image] DirectXGraphic --- [image] Texturen
 | Informationen zu Texturen
 | Einleitung Was ist eine Textur? Texturkoordinaten Mipmap Texturadressierung

Einleitung

In diesem Kapitel lernen Sie, wie Sie 3D-Objekten ein plastischeres Aussehen verleihen, und zwar mittels Texturen. Sie werden lernen, wie Sie Bildinhalte laden und auf 3D-Objekte anwenden. Letzteres ist hierbei eindeutig der Schwerpunkt. Es gibt eine Vielzahl an Techniken und Methoden, wie Sie Texturen im Zusammenhang mit 3D-Objekten nutzen können. Dabei werden Sie einige Effekte lernen, die Sie wahrscheinlich schon aus kommerziellen 3D-Spielen kennen. Schlagwörter, wie Anisotropisches Filtering, Bump Mapping, Environment Cube Map, Wrapping, Lightmap, Mipmapping werden für Sie nach diesem Kapitel praktische und effektvoll Rendertechniken sein, mit denen Sie Ihre Anwendungen um einen Quantensprung bereichern können.

Was ist eine Textur?

Eine Textur ist in erster Linie ein aus Farbinformationen bestehendes Grafikformat, Bitmap genannt. Texturen werden dazu verwendet, 3D-Objekten eine realistische oder zumindest effektvollere Oberfläche zu verleihen. Gute Holz oder Steintexturen lassen einen Holzstamm oder einen Stein viel glaubwürdiger erscheinen, als eine einfache braune oder graue Farbe.

Doch mit Texturen lässt sich noch viel mehr anfangen. Sie können als sogenannte Lightmaps dienen, um beleuchtete und nicht beleuchtete Regionen zu kennzeichnen. Diese Technik kann in vielen Fällen wertvolle Rechenzeit sparen. Beim Bump Mapping enthält eine zweite Textur eine Art Tiefeninformation, durch die die sichtbare Textur den Eindruck einer unebenen Fläche erwecken kann. Texturen können also auch dazu eingesetzt werden, andere Texturen in irgendeiner Weise visuell zu beeinflussen.

In den folgenden Abschnitten wird gelegentlich der Begriff Texel fallen. Ein Texel ist ein Textur-Pixel, nicht zu verwechseln mit einem Bildschirmpixel. Durch die Transformation kann es vorkommen, dass mehrere Pixel den Inhalt eines Texels darstellen (wenn man zum Beispiel sehr nah an ein Objekt herangeht).

Texturkoordinaten

Eine Textur wird an einem 3D-Objekt anhand sogenannter Texturkoordinaten angewendet, die in den Vertices enthalten sind. Die tatsächliche Grösse einer Textur spielt dabei keine Rolle. Die Masse werden in Prozent übergeben und reichen demnach von 0.0, was den horizontalen oder vertikalen Anfang einer Textur markiert, bis 1.0, dem horizontalen oder vertikalen Ende einer Textur. Während die Variablen x, y und z für räumliche Distanzen gebraucht werden, benutzt man u, v und w um Abmessungen an der Textur zu bestimmen. U gilt für die Breite, V für die Höhe einer Textur und w für die Tiefe bei Volumentexturen. Die folgende Bildreihe verdeutlicht es nochmal.

Das erste Bild zeigt eine Textur (Popstar Madonna) und die Texturkoordinaten für die vier Ecken. Das zweite Bild zeigt drei Vertices, die ein Dreieck bilden. Jeder dieser Vertices besitzt dabei eine u- und v-Texturkoordinaten. Im dritten Bild wird die Textur auf die Primitive angewendet.

Mipmap

Eine Mipmap besteht aus verketteten Bitmaps innerhalb einer Textur, deren Bildinhalte von Bitmap zu Bitmap kleiner dargestellt werden. Eine Mipmap ist eine Optimierungsmethode, um Rechenzeit zu sparen. Da bei weit entfernten 3D-Objekten längst nicht alle Details einer Textur so gut erkennbar sind, wie bei nahen Objekten, wird, je nach Entfernung eine der jeweiligen Bitmaps aus der Mipmap gezeigt. Betrachten Sie dazu die folgenden Bilder:

Hier sehen Sie so eine Mipmap, bestehend aus 3 Miplevel. Das erste Bild würde man verwenden, wenn man wirklich weit von dem Objekt entfernt ist. Man erkennt grademal eine Frau, die mit dem Zeigefinger ihre Lippe berührt. Der weisse Text ist aber überhaupt nicht lesbar. Im zweiten Bild kann man schon den Text lesen und erkennen, dass diese Frau an einem Schachbrett sitzt. Das letzte Bild würde angezeigt werden, wenn wir uns sehr nah am 3D-Objekt befinden. Der Text ist klar erkennbar und hebt sich durch seine scharfen Kanten gut vom Hintergrund ab.

Die Anzahl der Level einer Mipmap ist variabel. Jedoch ist jedes kleinere Bild ein Viertel (also halbe Breite und Höhe) vom nächst grösseren.

Texturadressierung

Die Anzeige der Textur zwischen den Koordinaten 0.0 und 1.0 ist klar. Je nachdem, welche Koordinaten angegeben werden, wird der entsprechende Bildauschnitt gezeigt. Doch was ist, wenn Sie Koordinaten ausserhalb dieses Bereiches, also <0.0 oder >1.0 angeben? Dann können Sie mal wieder entscheiden, was passieren soll. Es gibt 4 verschiedene Arten, eine Texturfläche ausserhalb der normalen Bereiche zu gestalten. Diese 4 Effekte bezeichnet man als Texturadressierungsmodi. Nachfolgend sind alle Modi in ihren englischen Bezeichnungen aufgeführt.

Wrap Texture Address Mode
In diesem Modus wird die Textur mit jedem ganzzahligen Integer-Wert wiederholt. Dies ist das gleiche Prinzip wie das Kacheln eines kleinen Bildes auf dem Desktop.

Mirror Texture Address Mode
Die Texel werden in der umgekehrten Reihenfolge ausgegeben. Es entsteht dabei ein Spiegeleffekt.

Clamb Texture Address Mode
Jeweils die erste bzw. letzte Pixelzeile oder -spalte wird ausserhalb von 0.0 und 1.0 gedehnt. In den diagonalen Richtungen wird die jeweilige Eckpixelfarbe benutzt.

Border Texture Address Mode
Ausserhalb der Texturgrenzen wird eine freiwählbare Farbe verwendet. Mit dieser Technik lässt sich zum Beispiel ein Rahmen um die Textur erzeugen. Der Rahmen muss also nicht Bestandteil der Textur sein, womit man Platz und Rechenpower spart.


SelfDXD - Texturen laden und anwenden

SelfDXD --- [image] DirectXGraphic --- [image] Texturen
 | Texturen laden und anwenden
 | Einleitung D3DXCreateTextureFromFile( ) D3DXCreateTextureFromFileEx( ) D3DXCreateTexture( ) Änderungen im Vertexformat Textur anwenden Textureinstellugen

Einleitung

Eine Textur ist in DirectXGraphics ein Interface vom Typ IDirect3DTexture8. Für das Erstellen einer Instanz dieses Interfaces gibt es mehrere Funktionen. Unter anderem haben Sie die Möglichkeit direkt mit einer Bilddatei eine Textur zu erstellen oder eine leere Textur zu erzeugen. Dieses Kapitel stellt Ihnen die wichtigsten Funktionen vor.

Zum Schluss wird gezeigt, wie welche Änderungen Sie im Vertexformat vornehmen müssen,und wie Sie die Textur grundsätzlich auf 3D-Objekte anwenden. Die verschiedenen Blending- und Filtertechniken werden in den nachfolgenden Kapiteln genauer beschrieben.

D3DXCreateTextureFromFile( )

Die wohl einfachste und gängigste Funktion. Mit dieser Funktion erstellen Sie eine Standardtextur, die in ihrer Grösse und ihrem Format abhängig von der zu ladenden Bilddatei ist. Die Syntax dieser Funktion sieht wie folgt aus:

function D3DXCreateTextureFromFile( const Device  : IDirect3DDevice8;
                                    const pSrcFile : PChar;
                                    out   ppTexture : IDirect3DTexture8 );

Device: Das Device, dem die Textur zugewiesen werden soll.

pSrcFile: Der Pfad und der Name der Bilddatei, die Sie laden möchten.

ppTexture: Eine Variable des Typs IDirect3DTexture8, die auf die erstellte Textur zeigt.

Diese Funktion ist zwar sehr einfach zu verwenden hat aber folgende Nachteile. Die Grösse der Textur entspricht immer der Grösse der Bilddatei, Sie können keine transparenten Farben verwenden und keine Mipmaps. Daher eignet sich diese Funktion nur für einfachere Zwecke. Wollen Sie eine Performanceoptimierte Textur erzeugen, mit der Sie mehr Features nutzen können, dann sollten Sie lieber den nachfolgenden fortgeschrittenen Befehl verwenden.

D3DXCreateTextureFromFileEx( )

Dies ist sozusagen die Expertenfunktion, wenn es um das Erstellen einer Textur aus einer Bilddatei heraus geht. Ihnen stehen weit aus mehr Parameter zur Verfügung, um das Texturinterface Ihren Wünschen anpassen zu können.

function D3DXCreateTextureFromFileEx( const Device    : IDirect3DDevice8;
                                      const pSrcFile  : PChar;
                                      const Width     : Cardinal;
                                      const Height    : Cardinal;
                                      const MipLevels : Cardinal;
                                      const Usage     : Cardinal;
                                      const Format    : Cardinal;
                                      const Pool      : Cardinal;
                                      const Filter    : Cardinal;
                                      const MipFilter : Cardinal;
                                      const ColorKey  : TD3DColor;
                                      const pSrcInfo  : PD3DXImageInfo;
                                      const pPalette  : pPaletteEntry;
                                      out   ppTexture : Idirect3DTexture8);

Device: Das Device, dem die Textur zugewiesen werden soll.

pSrcFile: Der Pfad und der Name der Bilddatei, die Sie laden möchten.

Width: Breite der Textur in Pixel. Wenn dieser Parameter 0 oder D3DX_DEFAULT (&FFFFFFFF) ist, wird die Breite der Bilddatei übernommen.

Height: Höhe der Textur in Pixel. Wenn dieser Parameter 0 oder D3DX_DEFAULT ist, wird die Höhe der Bilddatei übernommen.

MipLevels: Die Anzahl der Mipmaps, die benötigt wird. Wenn dieser Paramter 0 oder D3DX_DEFAULT ist, wird die Textur nur in der angegebenen Auflösung erstellt.

Usage: 0 oder D3DUSAGE_RENDERTARGET aus den [image] D3DUSAGEFLAGS. Wenn Sie letzteres angeben, kann eine Szene auch in eine Textur gerendert werden. Dies lässt sich zum Beispiel für Spiegeleffekte nutzen. Über diese Technik erfahren Sie mehr im Kapitel [image] Textur bearbeiten.

Format: Eine Konstante der [image] D3DFORMAT-Aufzählungen, die das Pixelformat der Textur beschreiben. Geben sie D3DFMT_UNKNOWN an, wird das Format der Bilddatei übernommen.

Pool: Eine Konstante aus der [image] D3DPOOL, mit der Sie bestimmen, in welchem Speicherbereich die Textur plaziert wird.

Filter: Eine oder mehrere Flags aus der [image] D3DX_FILTER-Aufzaehlung, mit der Sie bestimmen, welche Filter Sie zum Auslesen der Textur verwenden möchten. Mit Filter können Sie einstellen, auf welche Weise der zu setzende Texturpixel aus der Bilddatei errechnet werden soll. Dadurch lässt sich die Bildqualität hinsichtlich Schärfe und Detail regeln.

MipFilter: Eine oder mehrere Flags aus der [image] D3DX_FILTER-Aufzählung, mit der Sie bestimmen, welche Filter Sie zur Anzeige der Textur verwenden möchten. Mit Filter können Sie einstellen, auf welche Weise der zu setzende Pixel aus den Texturpixeln errechnet werden soll. Dadurch lässt sich die Bildqualität hinsichtlich Schärfe und Detail regeln.

ColorKey: Ein 32-Bit-Farbwert (egal, welche Format die Bilddatei hat), der angibt, welche Farbe als transparentes Schwarz ausgegeben werden soll. 0 deaktiviert die Schlüsselfarbe. Der Alpha-Wert gibt dabei an, wie transparent die Schlüsselfarbe sein soll. FF ist dabei vollkommen nicht-transparent. Für nichttransparentes Schwarz geben Sie also an &FF000000

pSrcInfo: Ein Zeiger auf ein Record des Typs [image] D3DXIMAGEINFO, das mit Informationen über das Bild gefüllt wird, oder nil, wenn Sie ein solches Record nicht brauchen.

pPalette: Ein Zeiger auf ein Record des Typs [image] TPALETTEENTRY, mit dem Sie die Farbanteile der Textur auslesen können, oder nil, wenn Sie ein solches Record nicht brauchen.

ppTexture: Ein Zeiger auf die Textur, die mit dieser Funktion erstellt wird.

In der Praxis könnte diese Funktion zum Beispiel so aussehen:

var
  textur: IDirect3DTextur8;
begin
  D3DXCreateTextureFromFileEx( d3ddev8, 'bild.bmp', 300, 300,
                               0, 0, D3DFMT_UNKNOWN, D3DPOOL_DEFAULT,
                               D3DX_FILTER_LINEAR, D3DX_FILTER_NONE, $FF000000,
                               nil, nil, textur );

Hierbei wird eine Textur geladen, die 300x300 Texel gross ist. Für den Fall, dass das Bild kleiner ist als 300x300, wird durch das Lineare Filtering sichergestellt, dass man nicht jeden einzelnen Pixel des Bildes sieht, sondern jeder Texel eine individuelle Farbe besitzt, die von seiner Position abhängt.

D3DXCreateTexture( )

Mit dieser Funktion legen Sie eine leere Textur an. Diese Funktion ist praktisch, wenn Sie zur Laufzeit eine eigene Textur erzeugen wollen und dabei auf Bildinhalte aus Dateien verzichten können.

function D3DXCreateTexture( const Device    : IDirect3DDevice8;
                            const Width     : Cardinal;
                            const Height    : Cardinal;
                            const MipLevels : Cardinal;
                            const Usage     : Cardinal;
                            const Format    : Cardinal;
                            const Pool      : Cardinal;
                            out   ppTexture : Idirect3DTexture8);

Device: Das Device, dem die Textur zugewiesen werden soll.

Width: Breite der Textur in Pixel. Wenn dieser Parameter 0 oder D3DX_DEFAULT (&FFFFFFFF) ist, wird die Breite der Bilddatei übernommen.

Height: Höhe der Textur in Pixel. Wenn dieser Parameter 0 oder D3DX_DEFAULT ist, wird die Höhe der Bilddatei übernommen.

MipLevels: Die Anzahl der Mipmaps, die benötigt wird. Wenn dieser Paramter 0 oder D3DX_DEFAULT ist, wird eine komplette Mipmap-Kette erstellt.

Usage: 0 oder D3DUSAGE_RENDERTARGET aus den [image] D3DUSAGEFLAGS. Wenn Sie letzteres angeben, kann eine Szene auch in eine Textur gerendert werden. Dies lässt sich zum Beispiel für Spiegeleffekte nutzen. Über diese Technik erfahren Sie mehr im Kapitel [image] Textur bearbeiten genaueres.

Format: Eine Konstante der [image] D3DFORMAT-Aufzählungen, die das Pixelformat der Textur beschreiben. Geben sie D3DFMT_UNKNOWN an, wird das Format der Bilddatei übernommen.

Pool: Eine Konstante aus der [image] D3DPOOL, mit der Sie bestimmen, in welchem Speicherbereich die Textur plaziert wird.

ppTexture: Ein Zeiger auf die Textur, die mit dieser Funktion erstellt wird.

In der Praxis könnte diese Funktion zum Beispiel so aussehen:

var
  textur : IDirect3DTexture8;
begin
  D3DXCreateTexture( d3ddev8,
                     256, 256, 0,
                     D3DUSAGE_RENDERTARGET,
                     D3DFMT_R5G6B5,
                     D3DPOOL_DEFAULT,
                     textur );

In diesem Fall wird also eine Textur mit den Grössen 256x256 erstellt. Sie wird uns in dem Kapitel [image] Textur bearbeiten als Rendertarget dienen.

Änderungen im Vertexformat

Bevor wir Texturen überhaupt verwenden können, müssen wir natürlich ein entsprechendes Vertexformat definieren, in dem wir Texturkoordinaten verwenden können. Da wir uns zunächst erstmal nur mit der einfachen Darstellung von Texturen beschäftigen, verwenden wir fürs erste nur ein Texturkoordinatenset.

type

  CUSTOMVERTEX = record
    x, y, z : single;
    nx, ny, nz: single;
    color: TD3DColor;
    tu, tv : Single;     // Die Texturkoordinaten
  end;

const
  D3DFVF_CUSTOMVERTEX = ( D3DFVF_XYZ or D3DFVF_NORMAL or D3DFVF_DIFFUSE or D3DFVF_TEX1);

Statt D3DFVF_TEX1 hätten wir auch D3DFVF_TEX2 oder D3DFVF_TEX8 angeben können. Diese Flags stehen in keiner Reihenfolge. Sie geben lediglich an, dass ein Texturenset verwendet werden soll. Lediglich die Anzahl dieser Flags bestimmt also, wieviele Texturkoordinatensets Sie verwenden, nicht aber welche der Flags Sie nun angeben. Angenommen, Sie wollen zwei Texturkoordinatensets verwenden, dann könnten Sie auch angeben D3DFVF_TEX3 und D3DFVF_TEX6.

Wenn Sie nun also Ihr 3D-Objekt erstellen, müssen Sie darauf achten, dass Sie nun auch die Texturkoordinaten angeben.

Textur anwenden

Das eigentliche Anwenden einer Textur ist sehr einfach. Setzen Sie die folgende Funktion des Direct3DDevices einfach vor dem Rendern des 3D-Objektes ein:

function SetTexture(const Stage : LongWord;
                          pTexture : IDirect3DBaseTexture8) : HResult;

Stage: Indexzahl, die für die gewählte Textur verwendet wird. Da Sie ja mehrere Texturen auf ein Objekt anwenden können (zum Beispiel beim [image] Texture Blending) wird über den Stage-Parameter jede Textur indiziert. Da Sie bis zu 8 Texturen verwenden können, sind Zahlen von 0 bis 7 erlaubt.

pTexture: Dieser Parameter kann auch vom Typ IDirect3DTexture8 sein. IDirect3DBaseTexture8 ist nur ein erweitertes Interface, dass die Verwendung von Cube oder Volumentexturen erlaubt. Möchten Sie unter einem bestimmten Stage keine Textur verwenden, genügt die Angabe von NIL.

Textureinstellungen

Alle Einstellungen, die eine Textur betreffen, werden mit einer einzigen Funktion ausgefuehrt. Die Parameter bestimmten, was Sie genau einstellen wollen, und wie Sie es einstellen wollen. Durch mehrmaliges Aufrufen dieser Funktion können Sie die Darstellung der Texturen in vielerlei Hinsicht verändern. Die Funktion des Devices lautet wie folgt.

function SetTextureStageState(const Stage : LongWord;
                              const _Type : TD3DTextureStageStateType;
                              const Value : LongWord) : HResult;

Stage: Indexzahl, der Textur, an deren Darstellung Sie etwas einstellen wollen.

_Type: Eine Konstante aus der [image] D3DTextureStageStateType-Aufzählung, mit der Sie angeben, was genau Sie mit Value einstellen wollen.

Value: Die Eingabe dieses Wertes hängt logischerweise davon ab, was Sie genau einstellen wollen, also was Sie als _Type angegeben haben. Mehr Informationen dazu finden Sie in den D3DTextureStageStateType-Aufzählungen.

Da es schwer ist, allein durch die Definition der Konstanten zu verstehen, wie Sie eingesetzt werden, und welchen Effekt Sie haben, finden Sie in den nächsten Kapiteln genauere Erläuterungen zu den speziellen Techniken der Texturierung.


SelfDXD - Texturaddressierung und -koordinaten

SelfDXD --- [image] DirectXGraphic --- [image] Texturen
 | Texturaddressierung und -koordinaten
 | Texture Adressing Modes Texturkoordinaten setzen Texturkoordinaten transformieren

Texture Adressing Modes

Welche Adressing Modes es gibt und was diese bewirken, haben Sie bereits im Kapitel [image] Informationen zu Texturen gelernt. Daher beschreibe ich hier nur kurz, wie man diese Adressing Modes in Delphi setzt.

Sie können jeweils für die X- und für die Y-Achse bestimmen, welche Texturadressierung verwendet werden sollen, wobei X und Y bei Texturkoordinaten immer U und V genannt werden.

  d3ddev8.SetTextureStageState(0, D3DTSS_ADDRESSU, D3DTADDRESS_MIRROR);
  d3ddev8.SetTextureStageState(0, D3DTSS_ADDRESSV, D3DTADDRESS_BORDER);
  d3ddev8.SetTextureStageState(0, D3DTSS_BORDERCOLOR, D3DCOLOR_RGBA(204, 108, 57));

In diesem Beispiel werden also alle Pixel auf der U-Achse (= X-Achse) ausserhalb des Bereiches [0, 1] gespiegelt. Auf der V-Achse dagegen wird eine Rahmenfarbe angezeigt, die Sie mit dem Flag D3DTSS_BORDERCOLOR wählen können. D3DTADDRESS_MIRROR usw. sind Konstanten aus der [image] D3DTEXTUREADDRESS-Aufzählung

Texturkoordinaten setzen

Haben Sie schonmal darüber nachgedacht, wie eigentlich Spiegelungen in 3D-Anwendungen erzielt werden? Alle virtuellen Lichtstrahlen zu berechnen, um dann entscheiden zu können, welcher Gegenstand aus der Umgebung reflektiert wird, und welcher nicht, wäre viel zu aufwendig für heutige Echtzeit-3D-Spiele. Der Trick ist, die Umgebung des Spiegels aus Sicht des Spiegels in eine Textur zu rendern, und diese dann über den Spiegel zu ziehen. Wie man eine Szene in eine Textur rendert, erfahren Sie im Kapitel [image] Textur bearbeiten. Wie man jedoch eine augenscheinlich physikalisch korrekte Spiegelung hinbekommt, lesen Sie hier.

Es gibt vier verschiedene Möglichkeiten Texturkoordinaten zu definieren. Eine davon nutzen wir bereits, nämlich die standardmässige. Das heisst, die Textur wird anhand der Texturkoordinaten, die jeder Vertex enthält angezeigt. Diese Darstellungsmöglichkeit vermittelt uns einen Eindruck von der Beschaffenheit eines 3D-Objektes. Die Textur gehört halt zum Objekt. Anders verhält es sich, wenn wir die Texturkoordinaten von anderen Dingen abhängig machen.

 D3DDEV8.SetTextureStageState(0,
   D3DTSS_TEXCOORDINDEX,
   D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR );

D3DTSS_TEXCOORDINDEX ist das Flag, mit dem Sie angeben, dass Sie die Texturkoordinaten neu setzen möchten. Mit D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR erreichen Sie diesen Spiegeleffekt.

Sie haben folgende vier Konstanten zur Auswahl:

D3DTSS_TCI_PASSTHRU:
Die Texturkoordinaten der Vertices werden verwendet. Dies ist die gängige Methode bei der es den Anschein hat, als ob das Objekt aus den Farben der Textur beschaffen ist.

D3DTSS_TCI_CAMERASPACENORMAL:
Die Textur wird immer orthogonal zur Kamera ausgerichtet. Die Texturkoordinaten sind die Orte, an denen sich die Normalen der Vertices auf der Textur befinden.

D3DTSS_TCI_CAMERASPACEPOSITION:
Die Textur wird immer orthogonal zur Kamera ausgerichtet. Sie sehen die Textur nur da, wo sich auch das Objekt befindet.

D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR:
Berechnet einen Reflektionsvektor aus den Vertexkoordinaten, der Normale und der Kameraposition.

Eines der vielen Samples aus der SDK zeigt die Anwendung des Cameraspace-Reflection-Vektors:

Texturen, bei denen man den Eindruck hat, sie werden von dem 3D-Objekt nur reflektiert, bezeichnet man auch als Environmentmap, als Umgebungstextur.

Texturkoordinaten transformieren

Kurz zur sachlichen Unterscheidung: im Kapitel [image] Texturkoordinaten setzen geht es darum, wie die Texturkoordinaten der Vertices ermittelt werden. In diesem Kapitel beschäftigen wir uns damit, wie wir die ermittelten Texturkoordinaten transformieren können. Diese Transformierungen finden innnerhalb der üblichen 4x4 Matrix statt, wobei in diesem Fall allerdings nur die ersten beiden Spalten von wirklicher Relevanz sind, da wir uns bisher ja nur mit zweidimensionalen Texturen befassen.

Texturkoordinaten transformieren
{ | 1 | 0 | 0 | 0 | }
 | 0 | 1 | 0 | 0
 | 0 | 0 | 1 | 0
 | 0 | 0 | 0 | 1

Was für 3D-Objekte gilt, ist für Texturen nicht anders. Durch Verändern der Parameter können Sie verschiedene Effekte erzielen, zum Beispiel einfache Verschiebungen (die untersten beiden Werte) oder Verzerrungen. Die Vielzahl der Möglichkeiten ist unbegrenzt. Sie alle hier aufzulisten ist daher leider nicht möglich.

Sie können für jedes einzelne Texturstage entscheiden, welche Transformationsmatrix Sie verwenden möchten. Dazu verwenden Sie die bekannte .SetTransform-Funktion:

  d3ddev8.SetTransform(D3DTS_TEXTURE0, matrix);

Darüber hinaus müssen Sie noch explizit einschalten, dass Sie Texturtransformationen vornehmen möchten. Dies erreichen Sie mit folgenden Parametern:

D3DDEV8.SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2 );

D3DTTFF_COUNT2 ist eine Konstante aus den [image] D3DTEXTURETRANSFORMFLAGS, mit der Sie angeben, dass Sie eine zweidimensionale Textur verwenden. Standardmässig ist D3DTTFF_DISABLE aktiv, womit also keine Transformierung vorgenommen wird.


SelfDXD - Texture Filtering

SelfDXD --- [image] DirectXGraphic --- [image] Texturen
 | Texture Filtering
 | Was ist Texture Filtering? Point Filtering Linear Filtering Anisotropic Filtering Mipmaps Filter einstellen

Was ist Texture Filtering?

Texture Filtering bezeichnet den Vorgang, bei dem die Darstellung einer Textur im dreidimensionalen Raum auf die zweidimensionale Bildschirmfläche übertragen wird. Nach einem bestimmten Filtersystem werden die Bildschirmpixel aus den Texturpixeln errechnet. Die verschiedenen Filter werden wir in diesem Kapitel genauer kennenlernen.

Wenn Sie sich schon mal mit der Bildbearbeitung (in 2D) beschäftigt haben, werden Sie die Problematik, um die es hier geht, bereits kennen. Angenommen, wir haben ein 256x256 grosses Bild. Wird dieses Bild auf einer Fläche dargestellt, die auch 256x256 Pixel gross ist, sehen wir das Bild originalgetreu im normalen Zustand. Wenn wir dieses Bild aber auf einer 300x300 Pixel grossen Fläche darstellen würden, müssten sich mehrere Bildschirmpixel auf die gleichen Texturpixel beziehen. Doch nach welchen Regeln werden die Bildschirmpixel aus den Texturpixeln errechnet? Problematisch wird es auch, wenn wir das Bild auf einer wesentlich kleineren Fläche darstellen würden. Dann können unter Umständen einige Texturpixel gar nicht berücksichtigt werden. Das Bild könnte verzerrt wirken.

Bei der Transformierung von 3D nach 2D passiert genau das gleiche: eine Textur wird in unterschiedlichen Grössenverhältnissen dargestellt, je nachdem, wie nah oder weit der Betrachter vom Polygon entfernt ist. Zur Darstellung der Textur gibt es verschiedene Filter, die sich sowohl auf die Bildqualität als auch auf die Geschwindigkeit in der Anwendung auswirken können.

Um die verschiedenen Technik deutlicher aufzeigen zu können, gehen wir anhand eines Beispieles vor. Die Textur, die Sie auf der linken Seite sehen, werden wir nun den verschiedenen Filtertechniken unterziehen und sehen, was passiert.

Point Filtering

Dies ist die älteste und schnellste Methode, Texel in Bildschirmpunkte zu verwandeln. Dabei wird errechnet, zu welchem Texel der Bildschirmpunkt am nächsten liegt. Die Farbe des Texels wird dann ohne zusätzliche Berechnungen direkt übernommen. Das Resultat sieht in der 3D-Awendung dann so aus:

Durch die direkte Zuweisung der Texel kommt es zu einer leicht verzerrten Darstellung des Bildes. So ist zum Beispiel die obere waagerechte Linie dicker als die untere. In der diagonalen grünen Linie sind nicht alle Kästchen gleich dick. Diese Fehler entstehen, weil die berechneten Koordinaten auf ganzzahlige Werte gerundet werden.

Diese Filtertechnik ist sinnvoll, wenn Sie ganz bewusst eine Textur zeigen möchten, bei der jeder einzelne Pixel des Bildes erkennbar ist. Der Performancegewinn bei diesem Filter ist sehr gering, da sich die meisten Grafikkarten auf den folgenden Filter spezialisiert haben.

Linear Filtering

Beim linearen Filter berechnet DirectX Mittelwerte aus den Texel. Liegt zum Beispiel ein Bildschirmpixel genau zwischen zwei Texel, werden die Farben der beiden Texel zu einer Durchschnittsfarbe gemischt, und diese dann angezeigt. Unsere Textur sieht dann so aus:

Nur damit keine Missverständnisse entstehen: der Rahmen des Bildes ist nur der Hintergrund der 3D-Anwendung, die ich dafür geschrieben habe. Er gehört nicht mit zur Textur. Bei der Darstellung der Textur fällt auf, dass diese leicht verschwommen wirkt. Das ist sozusagen der Preis den man dafür zahlt, dass die Verhältnisse der einzelnen Linien nun wirklich stimmen. Sowohl die waagerechten Linien wirken gleich dickt, als auch die Diagonale, bei der man die einzelnen Pixel nur noch erahnen kann.

Anisotropic Filterin

Das Anisotropic Filtering ist sozusagen der lineare Filter für die Tiefe. Solange Sie orthogonal auf eine Textur schauen, erhalten Sie mit dem linearen Filter die besten Ergebnisse. Wenn aber eine Textur auch in die tiefe des Raumes verläuft, kann es zu Verzerrungen kommen, da Texel, die sich in verschiedenen Distanzen vom Betrachter befinden aus Gründen der Perspektive nur unzureichend in die Berechnung mit aufgenommen werden. Während beim linearen Filter die Berechnungsfläche kreisförmig ist, haben wir beim anisotropischen Filter eher eine elliptische Berechungsfläche. Da meine Grafikkarte diesen Effekt nicht unterstützt musste ich notgedrungen auf ein Bild von der Internetseite TecChannel zurückgreifen.

Hier sehen wir einen direkten Vergleich zwischen dem Point Filter und dem Anisotropic Filter. Der entscheidende Unterschied ist: beim anisotropischen Filter werden selbst die Kästchen, die sich ganz weit im Hintergrund befinden, noch glaubwürdig angezeigt, während beim Point Filter alles in einem ungeordneten Pixelsalat endet, bevor die letzte Mipmap das Schachbrett ganz verschwommen anzeigt.

Mipmaps

Wenn Sie noch nicht wissen, was Mipmaps sind, dann sollten Sie zuerst den entsprechenden [image] Abschnitt im Theoriekapitel lesen.

Mipmaps werden von DirectXGraphics automatisch generiert. Das heisst, Sie müssen nicht extra seperate Texturen anlegen. Sie stellen DirectXGraphics lediglich die hochauflösendste Textur zur Verfügung. Das Programm erstellt dann von selbst die gewünschste Anzahl an Mipmaps. Dabei ist zu beachten, dass jede kleinere Mipmap genau die halbe Breite und Höhe der vorrausgehenden Auflösung hat. Sie sollten also von vornherein die Texturgrössen so wählen, dass Sie möglichst oft durch zwei teilbar sind. Am idealsten ist es, Grössen, wie 256x256 oder 128x128 zu nehmen. Aber auch Auflösungen, wie 256x64, sind denkbar. Wie Sie Mipmaps erstellen, erfahren Sie im Kapitel [image] Texturen laden und anwenden.

Und so rechnet DirectXGrahics: wenn Sie bei einer Texturgrösse von 256x256 3 Miplevel erstellen, dann ist das erste Level 256x256, das zweite 128x128 und das dritte 64x64. Sie können nur so viele Mipmaps erstellen, bis die Texturgrösse 1x1 beträgt (in unserem Beispiel wären das also 9 Mipmaps). Ein Mipmaplevel kommt dann immer zum Einsatz, wenn die Auflösung der jeweiligen Mipmap in etwa gleich ist mit der Grösse der Textur auf dem Bildschirm. Bei unserer Beispieltextur kann das zum Beispiel so aussehen.

Hier sehen Sie gleich zwei Mipmaps im Bild. Der linke Teil der Textur ist noch soweit im Vordergrund, dass die hochauflösende Textur verwendet wird. Ab einem bestimmten Punkt wird aber eine Grenze überschritten, bei der die zweite kleinere Textur zum Einsazt kommt. Daher wirkt das Bild weiter links ein wenig verschwommen.

Auch für Mipmaps können Sie einstellen, was für Filter Sie verwenden möchten. Beim Point Filtering zum Beispiel sehen Sie diese scharfen Kanten, wie im Screenshot. Wählen Sie statt dessen Linear Filtering, dann werden auch hier wieder Mittelwerte bestimmt, diesmal zwischen den beiden Mipmaps die dem Pixel am nächsten liegen. Je nach Grafikkarte ist dann der Übergang nicht mehr erkennbar bzw. bei älteren Grafikkarten von leichtem Bildrauschen geprägt.

Sie können mit ein paar Faktoren noch die Übergangsabstände zwischen zwei Mipmaps beeinflussen. Zum einen erreichen Sie dies mit dem Texturestagestate D3DTSS_MIPMAPLODBIAS.

function F2DW(f: Single): DWORD;
begin
  Result:= PDWord(@f)^;
end;

begin
  d3ddev8.SetTextureStageState( 0, D3DTSS_MIPMAPLODBIAS, f2dw(2.1));

Mit diesem Parameter stellen Sie ein, wie früh oder spät eine gering-auflösende Mipmap zum Einsatz kommt. Je grösser der Wert ist, desto schneller kommt eine Textur mit geringer Auflösung zum Einsatz. Je geringer (also kleiner als 0) der Wert ist, desto hochauflösender ist die Textur auch bei grösseren Distanzen. Da Float-Werte eigentlich nicht in der .SetTextureStageState-Funktion verwendet werden dürfen, muss man sich einer kleinen Funktion bedienen, die auf diesen Floatingwert zeigt.

Zum Schluss noch einen anderen Parameter: D3DTSS_MAXMIPLEVEL

  d3ddev8.SetTextureStageState( 0, D3DTSS_MAXMIPLEVEL, 2 );

Mit diesem Parameter bestimmen Sie, ab welchem Miplevel die Textur angezeigt werden soll. Ist der Wert 0, so wird die Textur auch in ihrer höchsten Auflösung verwendet. Ist der Wert 1, so wird als bestauflösende Textur das erste Miplevel benutzt, usw. . Mit diesem Parameter können Sie also auch, nachdem Sie eine komplette Mipmapkette erstellt haben, noch entscheiden, wie hochauflösend die Textur aus der Nähe sein soll.

Filter einstellen

Die Texturfilter wählen Sie über die .SetTextureStageState-Funktion des Devices aus. Dabei wird unterschieden zwischen einem Filter, der angewendet wird, wenn die Textur auf einer grösseren Pixelfläche dargestellt werden soll, und einem Filter der bei einer kleineren Pixelfläche, als die der Textur, angewendet werden soll. Darüberhinaus können Sie noch einen Filter für den Einsatz von Mipmaps angeben. Der jeweilige Funktionsaufruf dafür sieht folgendermassen aus:

  d3ddev8.SetTextureStageState( 0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
  d3ddev8.SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
  d3ddev8.SetTextureStageState( 0, D3DTSS_MIPFILTER, D3DTEXF_POINT );

D3DTSS_MAGFILTER, D3DTSS_MINFILTER und D3DTSS_MIPFILTER sind dabei Konstanten aus den [image] D3DTEXTURESTAGESTATETYPE-Flags. MAG steht für Maginification (Vergrösserung) und Min für Minification (Verkleinerung). Mit einem [image] D3DTEXTUREFILTERTYPE-Flag können sie dabei als dritten Paramter den genauen Filter auswählen.

Der aufwendigste Filter muss nicht immer der beste sein. Damit Sie besser erkennen können, welche Filter in welcher Kombination am besten zueinander passen, liegt der Dokumentation ein Texturfilterkasten bei. In diesem Programm können Sie zur Laufzeit die Filter und Mipmaps beliebig einstellen und direkt sehen, was passiert.

Das Programm lädt die bereits gezeigte Textur. Diese bewegt sich ständig vom Betrachter weg und zum Betrachter hin. Gleichzeitig rotiert sie um sich selbst. Damit können Sie die Übergänge zwischen zwei Mipmaps besser beobachten.


SelfDXD - Texture Blending

SelfDXD --- [image] DirectXGraphic --- [image] Texturen
 | Texture Blending
 | Einleitung Die Arbeitsweise Lightmaps Alphablending Bumpmaps Beleuchtete Polygone Multipass Texture Blending

Einleitung

Texture Blending, das ist einer dieser Effekte, die in Spielen viel her machen können. Texture Blending kommt immer dann zum Einsatz, wenn mehr als eine Textur auf einem Polygon angewendet werden sollen. Die Rede ist ganz bewusst von anwenden und nicht von darstellen, denn nicht immer müssen alle Texturen, die auf einem Polygon angewendet werden auch dargestellt werden. Bei Lightmaps oder Bumpmaps zum Beispiel werden die Informationen einer Textur nur dazu genutzt, um eine andere Textur zu manipulieren.

Da die Möglichkeiten des Texture Blendings extrem gross sind, werden wir uns in diesem Kapitel lediglich mit der grundlegenden Arbeitsweise beschäftigen und anhand von Beispielen einige interessante Effekte vorführen. Mit dem Grundlagenwissen, dass Sie in diesem Kapitel erwerben, werden Sie in der Lage sein, mit Texturen experimentieren zu können und erstaunliche Effekte zu erzielen, wie sie Sie aus vielen kommerziellen Titeln bereits gewohnt sind. Nebenbei werden Sie, so hoffe ich, feststellen, dass alles wesentlich einfacher ist, als ursprünglich vermutet.

Die Arbeitsweise

Beim Texture Blending spielen die bereits erwähnten Texturestages eine Rolle. In jedem Stage können Sie zwei Farbargumente definieren und angeben, wie diese miteinander zu einer Farbe verrechnet werden sollen. Die Farbargumente können sich auch auf vorhergehende Texturestages beziehen, so dass sie mehrere Stages miteinander verknüpfen können. Durch diese Technik lässt sich ein Blendingeffekt erzielen. Die Arbeitsweise verdeutlicht nochmal folgende Grafik.

Argument1 und Argument2 in jedem Stage geben an, woher die Farben genommen werden sollen. Mögliche Quellen sind zum Beispiel die Diffusewerte der Vertices, die Textur des jeweiligen Stages oder, wie in Stage 2, das Resultat des vorhergehenden Stages. Eine Liste aller Argumente bieten Ihnen die [image] Textureargument-Flags. Mit einem Operator bestimmen Sie, wie beide Farbargumente miteinander verrechnet werden sollen. Im oberen Beispiel wird im ersten Stage eine Durchschnittsfarbe gebildet. Diese wird als ein Farbargument im zweiten Stage weiterverwendet. Im zweiten Stage werden beide Farbargumente miteinander addiert. Das Ergebnis der Berechnungen im zweiten Stage kann dann in einem dritten Stage weiter verwendet werden usw..

Die Farbargumente werden miteinander verrechnet, indem auf die Farbwerte jedes Texels beider Argumente die entsprechende Operation ausgeführt wird. Wenn also im zweiten Stage eines der Texel die RGB-Werte (1.0, 0.5, 0.0) hat, was Orange entspricht, und im zweite Argument die RGB-Werte (0.4, 0.4, 0.4), dann erhalten wir durch den Operator Addition als Rückgabewerte (1.0, 0.9, 0.4), diesen hellen Gelbton, wie er im Diagramm zu sehen ist. Theoretische Werte über 255 werden auf diese Obergrenze wieder herabgesetzt.

Für jeden Stage ergeben sich also prinzipiell drei Einstellungen, die Sie verändern können:

// Blending-Einstellungen für das erste Stage
  d3ddev8.SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_DIFFUSE );   // Argument 1
  d3ddev8.SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_TEXTURE );   // Argument 2
  d3ddev8.SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG2 ); // Operation

// Blending-Einstellungen für das zweite Stage
  d3ddev8.SetTextureStageState( 1, D3DTSS_COLORARG1, D3DTA_CURRENT );
  d3ddev8.SetTextureStageState( 1, D3DTSS_COLORARG2, D3DTA_TEXTURE );
  d3ddev8.SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_MODULATE );

D3DTSS_COLORARG1, D3DTSS_COLORARG2 und D3DTSS_COLOROP sind Konstanten aus den [image] Texturestagestate-Flags. Als Argumente stehen Ihnen die Konstanten der [image] Textureargument-Flags zur Verfügung. Alle Farboperationen finden Sie in den [image] D3DTEXTUREOP-Flags. D3DTA_CURRENT ist dabei die Verknüpfung, mit der Sie das Resultat aus dem vorhergehenden Stage in das aktuelle Stage mit einbinden. Würde im zweiten Stage (also das Stage mit der Nummer 1) als erstes Farbargument D3DTA_DIFFUSE enthalten, würde man in der Anwendung auch nur die zweite Textur in den Farben der Vertices sehen.

Das ist im Prinzip alles, was sich hinter dem Texture Blending befindet. Mit diesem einfachen Operatoren- und Argumentensystem lassen sich alle Blendingeffekte erzielen. Im folgenden seien ein paar dieser Effekte vorgestellt.

Lightmaps

Wenn Sie in einer Anwendung Point- oder Spotlights verwenden, deren Position sich nicht verändert, empfiehlt es sich, die Beleuchtung einer Textur nicht über die Lichtquelle berechnen zu lassen, sondern eine Lightmap zu verwenden. Eine Lightmap ist eine Textur, dessen Bildinformationen dazu genutzt werden, um die Helligkeit der darzustellenden Textur zu bestimmen. Das folgende Schaubild verdeutlicht das Prinzip:

Je heller der Texel einer Lightmap ist, desto stärker erscheint der Texel der anzuzeigenden Textur in seinen Farben. Ist der Texel der Lightmap dunkler, wird auch der Texel der sichtbaren Textur dunkler dargestellt. Wenn tex1 ein Textur-Interface mit der anzuzeigenden Textur ist, und tex2 das Interface mit der Lightmap, dann könnte der Code für diesen Effekt so aussehen:

  d3ddev8.SetTexture( 0, tex1 );
  d3ddev8.SetTexture( 1, tex2 );

  d3ddev8.SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
  d3ddev8.SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1 );

  d3ddev8.SetTextureStageState( 1, D3DTSS_COLORARG1, D3DTA_CURRENT );
  d3ddev8.SetTextureStageState( 1, D3DTSS_COLORARG2, D3DTA_TEXTURE );
  d3ddev8.SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_MODULATE );

Hier wird also durch das Multiplizieren (D3DTOP_MODULATE) beider Texturen im zweiten Stage eine Textur unter Einbeziehung einer Lightmap dargestellt.

Alphablending

Beim Alphablending sieht man mehrere Texturen gleichzeitig. Sie werden sozusagen ineinander geblendet. Wie stark eine Textur in eine andere eingeblendet wird, können Sie selbst bestimmen.

Im wesentlichen werden beim Alphablending bestimmte Anteile aus beiden Texturen miteinander addiert. In DirectXGraphics haben Sie mehrere Möglichkeiten um diesen Effekt zu erzielen:

  d3ddev8.SetTexture( 0, tex1 );
  d3ddev8.SetTexture( 1, tex2 );

  d3ddev8.SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
  d3ddev8.SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1 );

  d3ddev8.SetRenderState( D3DRS_TEXTUREFACTOR, D3DCOLOR_RGBA( 0, 0, 0, 127) );
  d3ddev8.SetTextureStageState( 1, D3DTSS_COLORARG1, D3DTA_CURRENT );
  d3ddev8.SetTextureStageState( 1, D3DTSS_COLORARG2, D3DTA_TEXTURE );
  d3ddev8.SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_BLENDFACTORALPHA );

Dies ist wohl die schnellste und einfachste Methode. Sie legen in den Renderstates einfach einen Alphawerten fest, der das Verhältnis zwischen der ersten und zweiten Textur festlegt. Bei 255 wird die erste Textur (tex1) zu 100% dargestellt. Bei 0 die zweite. Die Rot-, Grün-, und Blauwerte spielen hierbei keine Rolle. Entscheidend ist, dass Sie als Farboperation D3DTOP_BLENDFACTORALPHA verwenden.

Der Nachteil dieses Verfahrens ist allerdings, dass die zweite Textur überall zu gleichen Teilen auf die erste Textur geblendet wird. Mit dem folgenden Code benutzen Sie die Alphawerte der Vertices, um die Texturen zu überblenden.

  d3ddev8.SetTexture( 0, tex1 );
  d3ddev8.SetTexture( 1, tex2 );

  d3ddev8.SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
  d3ddev8.SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1 );

  d3ddev8.SetTextureStageState( 1, D3DTSS_COLORARG1, D3DTA_CURRENT );
  d3ddev8.SetTextureStageState( 1, D3DTSS_COLORARG2, D3DTA_TEXTURE );
  d3ddev8.SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_BLENDDIFFUSEALPHA  );

Diese Einstellung erlaubt es Ihnen also, bestimmte Bereiche Ihrer Polygone zu unterschiedlichen Verhältnissen mit Ihren Texturen darzustellen. Davor müssen Sie natürlich die entsprechenden Alphawerte Ihren Vertices übergeben haben.

Wenn Sie wirklich texelgenau entscheiden wollen, welcher Texel wie transparent sein soll, dann können Sie auch die Alphawerte einer Textur für das Alphablending verwenden.

  d3ddev8.SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_BLENDTEXTUREALPHA  );

In diesem Beispiel werden die Alphawerte der Textur aus dem aktuellen Stage übernommen. Mit dem folgenden Befehl werden die Alphawerte aus der Textur übernommen, die mit D3DTA_CURRENT übernommen wird.

  d3ddev8.SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_BLENDCURRENTALPHA );

Bumpmaps

Bumpmaps werden dazu verwendet, rauhe, unebene Oberflächen in Abhängigkeit von den Lichtverhältnissen und dem Betrachtungswinkel realistischer erscheinen zu lassen. So brauchen zum Beispiel in einer rissigen Steinwand die Schatten nicht in die Textur eingearbeitet werden. Sie können zur Laufzeit in Abhängigkeit zum Licht berechnet werden, in dem man eine sogenannte Bumpmap, eine Graustufentextur, verwendet, in der Höhen und Tiefen der Steinwand abgebildet sind. Der folgende Screenshot aus der SDK verdeutlicht es nochmal:

Bei der linken Weltkugel wurde die Textur einfach über das Modell gezogen und angezeigt. Dabei wirkt die Kugel spiegelglatt. Im rechten Erdball kam zusätlich noch eine Bumpmap zum Einsatz. Beachten Sie, wie das Spekularlicht reflektiert wird. Man hat den Eindruck eines unebenen (verschrumpelten) Erdballs.

Für den Einsatz von Bumpmaps können bis zu 3 Texturen zum Einsatz kommen:

  1. die anzuzeigende Textur
  2. die Bumpmap
  3. eine Environmentmap (optional)

Damit benötigen Sie mindestens zwei Texturestages. Die Einstellungen des ersten Texturestages stehen Ihnen weitgehend frei. Hier ein Beispiel:

  d3ddev8.SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
  d3ddev8.SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
  d3ddev8.SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);

Im nächsten Stage folgt dann die Bumpmapmap. Dabei haben Sie zwei Farboperatoren zur Auswahl, mit denen Sie entscheiden, wie die Bumpmap angewendet werden soll.

  d3ddev8.SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
  d3ddev8.SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
  d3ddev8.SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_BUMPENVMAP);

Mit D3DTOP_BUMPENVMAP reflektieren Sie eine Environmentmap die im nächsten Stage folgt. Licht fliesst hier also nicht mit in die Berechnung ein.

  d3ddev8.SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
  d3ddev8.SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
  d3ddev8.SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_BUMPENVMAPLUMINANCE);

Mit D3DTOP_BUMPENVMAPLUMINANCE werden sowohl die Environmentmap als auch Licht gemäss der Struktur der Bumpmap korrekt wiedergegeben. Wenn Sie nun im zweiten Stage die Environmentmap weglassen, haben Sie nur die Lichtberechnung, wie im oberen Screenshot.

Auch für die Bumpmap gibt es eine Transformationsmatrix, allerdings ist diese nur 2x2 Felder gross. Sie können diese Matrix direkt über die Texturestagestate-Flags ansprechen, wie im folgenden Beispiel:

function F2DW(f: Single): DWORD;
begin
  Result:= PDWord(@f)^;
end;

begin
  d3ddev8.SetTextureStageState(1, D3DTSS_BUMPENVMAT00, F2DW(1.0));
  d3ddev8.SetTextureStageState(1, D3DTSS_BUMPENVMAT01, F2DW(0.0));
  d3ddev8.SetTextureStageState(1, D3DTSS_BUMPENVMAT10, F2DW(0.0));
  d3ddev8.SetTextureStageState(1, D3DTSS_BUMPENVMAT11, F2DW(1.0));

Standardmässig sind alle Bumpmap-Matrizenwerte auf 0.0 gestellt, weshalb die Matrix am Anfang eingestellt werden muss.

Darüber hinaus können Sie noch einstellen, wie stark das Licht bzw. die Environmentmap vom 3D-Körper wiedergegeben wird. Dazu stehen Ihnen folgende zwei Parameter zur Verfügung:

function F2DW(f: Single): DWORD;
begin
  Result:= PDWord(@f)^;
end;

begin
  d3ddev8.SetTextureStageState(1, D3DTSS_BUMPENVLSCALE,  F2DW(4.0));
  d3ddev8.SetTextureStageState(1, D3DTSS_BUMPENVLOFFSET, F2DW(0.0));

Mit D3DTSS_BUMPENVLSCALE geben Sie an, wie stark das Licht bzw. die Environmentmap reflektiert werden soll und mit D3DTSS_BUMPENVLOFFSET wie stark das Licht bzw. die Environment der normalen Textur grundsätzlich beigemischt werden soll.

Beleuchtete Polygone

Beleuchtung? Hatten wir das nicht schonmal? Ja, im Kapitel [image] Licht und Schatten haben wir gelernt, wie wir unsere Polygone mit realistischen Licht und Schattenverhältnissen ausstatten. Wenn Sie jedoch die Texturestagestates dazu verwenden, mehrere Texturen miteinander zu kombinieren, gibt es ein Problem: das Polygon wird plötzlich nicht mehr beleuchtet (ausgenommen Texturen mit Bumpmaps). Der Grund dafür liegt darin, dass die Beleuchtung der Polygone zu den Bestandteilen der Texturargumente gehört. Es handelt sich hierbei um das Argument D3DTA_DIFFUSE. Wir müssen es also seperat einbauen. Dies könnte dann so aussehen:

  d3ddev8.SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
  d3ddev8.SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1 );

  d3ddev8.SetTextureStageState( 1, D3DTSS_COLORARG1, D3DTA_CURRENT );
  d3ddev8.SetTextureStageState( 1, D3DTSS_COLORARG2, D3DTA_TEXTURE );
  d3ddev8.SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_BLENDDIFFUSEALPHA  );

  d3ddev8.SetTextureStageState( 2, D3DTSS_COLORARG1, D3DTA_CURRENT );
  d3ddev8.SetTextureStageState( 2, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
  d3ddev8.SetTextureStageState( 2, D3DTSS_COLOROP, D3DTOP_MODULATE );

Beachten Sie hierbei, dass ältere Grafikkarten nicht mehr als zwei Texturestages unterstützen und diese Befehle deshalb wirkungslos sein könnten. Unterstützt Ihre Grafikkarte dieses Feature, so werden in diesem Beispiel zwei Texturen überblendet (abhängig von den Alphawerten der Diffusefarbe der Vertices) und diese dann mit den Diffuse-Werten der Vertices multipliziert, wodurch sich der Lichteinfall auf die Helligkeit der Vertices auswirkt.

Multipass Texture Blending

Zum Schluss möchte ich Ihnen noch eine ganz andere Möglichkeit des Blendings vorstellen. Das so genannte Multipass Texturing. Hierbei werden die Blend-Effekte nicht über die Stages erreicht sondern über Einstellungen, die Sie innerhalb der Renderstates festlegen. Durch mehrfaches Rendern des selben Objektes mit einer anderen Textur können Sie die gleichen Effekte erzielen. Dabei legen Sie bei jedem Rendervorgang lediglich fest, wie die neu hinzukommenden Pixel mit den bereits vorhandenen Pixeln addiert werden sollen. Hierbei spricht man zum einen vom Sourceblendfactor, wobei Source das zu rendernde Objekt meint, und vom Destinationblendfactor, bei dem mit Destination die bereits vorhandene Szene im Buffer gemeint ist. Aus den beiden Pixeln wird nach folgendem Term der neue Pixel gebildet:

NeuerPixel = DestPixel + DestBlendFactor + SrcPixel x SrcBlendFactor

Nochmal zum Verständnis: bei der bisher besprochenen Texturblendingmethode verteilen wir alle Texturen auf unterschiedliche Stages und rendern das Objekt einmal. DirectXGraphics bezieht in diesem Fall alle Texturen in diesen einen Rendervorgang mit ein. Beim Multipass Texturing rendern wir das Objekt zunächst mit nur einer Textur. Danach setzen wir in den ersten Stage eine neue Textur und rendern das Objekt nochmal. Mit SrcBlendFaktor und DestBlendFactor stellen wir aber diesmal ein, wie der bereits vorhandene Pixel mit dem neuen Addiert werden soll. Die Grafik verdeutlicht es nochmal:

Der Vorteil dieser Technik ist: Sie können beliebig oft Objekte hintereinander mit Multipass Rendern. Im Gegensatz zum Texturestageblending sind Sie dabei nicht auf die Fähigkeiten der Grafikkarte angewiesen. Der Nachteil dieser Technik: es ist um einiges zeitaufwendiger, da die Vertices in jedem Durchgang neu gerendert werden müssen.

Im Prinzip können Sie alle Blendingoperationen, wie wir Sie oben besprochen haben auch mit der Multipasstechnik machen. Dazu sind folgende Schritte notwendig:

1. Das Objekt mit der ersten Textur rendern

  d3ddev8.SetTexture( 0, tex1 );
  d3ddev8.SetStreamSource( 0, vb, sizeof( TVERTEX ) );
  d3ddev8.SetVertexShader( D3DFVF_TVERTEX );
  d3ddev8.DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2 );

2. Eine neue Textur in den ersten Stage stellen

  d3ddev8.SetTexture( 0, tex2 );

3. Source- und Destination-Blendfaktoren einstellen

  d3ddev8.SetRenderState(D3DRS_SRCBLEND,  D3DBLEND_INVSRCCOLOR);
  d3ddev8.SetRenderState(D3DRS_DESTBLEND, D3DBLEND_SRCCOLOR);
  d3ddev8.SetRenderState(D3DRS_ALPHABLENDENABLE, 1);

4. Das Objekt nochmal rendern

  d3ddev8.DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2 );
  d3ddev8.SetRenderState(D3DRS_ALPHABLENDENABLE, 0);

Die Schritte 2, 3 und 4 können Sie beliebig oft wiederholen.

Zwei Dinge müssen wir noch erklären. Zum einen müssen Sie, wenn Sie Multipass nutzen wollen, dies explizit einstellen. Dies geschieht mit dem Renderstate D3DRS_ALPHABLENDENABLE. Wenn Sie Multipass nicht mehr nutzen, sollten Sie es immer abstellen, da es sonst in der Render-Pipeline wertvolle Zeit verschlingt. Das zweite sind die eigentlichen Blendingoperatoren. Mit D3DRS_SRCBLEND legen Sie den Faktor fest, der für das zu rendernde Objekt benutzt werden soll und mit D3DRS_DESTBLEND den Faktor für die bereits gerenderte Szene im Buffer. Die Faktoren, die sie als zweiten Parameter angeben, sind Konstanten aus der [image] D3DBLEND-Aufzählung

Für normales Alphablending verwenden Sie folgende Einstellungen zwischen den beiden Rendervorgängen:

  d3ddev8.SetRenderState(D3DRS_SRCBLEND,  D3DBLEND_INVSRCCOLOR);
  d3ddev8.SetRenderState(D3DRS_DESTBLEND, D3DBLEND_SRCCOLOR);

Hiebei werden beide Texturen zu gleichen Teilen vermischt.

Diese Rendertechnik erlaubt Ihnen ganz neue grafische Effekte. So können Sie zum Beispiel im zweiten Rendervorgang ein ganz anderes Objekt darstellen. In der Praxis könnte man zum Beispiel zum einen einen Raum rendern, in den man hineinschaut, und danach eine leicht blau getönte Glasscheibe, durch die man den Raum sieht. Der untere Screenshot zeigt zwar keinen Raum aber dafür die Scheibe.

Multipass ist damit also das typische Werkzeug um zum Beispiel transparente Effekte von Glas, Feuer oder Rauch darzustellen. Doch mit dieser Technik können Sie noch viel mehr anfangen. Um die Vielfalt der Möglichkeiten besser begreifen zu können, liegt dieser Dokumentation im Verzeichnis /directxgraphics/demo/ des SelfDXD-Verzeichnisses ein Kastenprogramm bei, den Multipass Texturing Kasten (Achtung: nur in der Offline-Version).

In diesem Programm können Sie zur Laufzeit alle Blending-Einstellungen in Ruhe ausprobieren. Der Screenshot zeigt nur eine der vielen ungewöhnlichen Möglichkeiten, zwei Texturen miteinander zu kombinieren. Ersetzen Sie auch ruhig mal die Grafikdateien, um mit anderen Farben und Bildern die Effekte zu testen.


SelfDXD - Textur bearbeiten

SelfDXD --- [image] DirectXGraphic --- [image] Texturen
 | Textur bearbeiten
 | Einleitung Texturen im Speicher Vorraussetzungen Textur locken Texturpixel bearbeiten und unlocken Anwendungsbeispiele für die Texturbearbeitung Textur als Rendertarget benutzen

Einleitung

In diesem Kapitel geht es um eine Technik, die vor allem dann gerne eingesetzt wird, wenn der verfügbare Speicherplatz der Anwendung limitiert ist. Es gibt zum Beispiel Programmiercontests, bei denen eine 3D-Anwendung 64kb nicht überschreiten darf. Um dann noch detaillierte Texturen zu erstellen und als Datei auszulagern, reicht der Platz nicht mehr aus. Der Trick liegt darin, die Texturen zur Laufzeit zu erstellen, und über algorithmische Methoden die Grafik zu erstellen.

Aber auch für andere Zwecke kann es nützlich sein, Texturen zur Laufzeit zu bearbeiten. So benötigen Sie vielleicht die Farbinformationen einer Textur um damit eine Heightmap zu generieren. Oder vielleicht möchten Sie in Ihrem 3D-Egoshooter in der Schaltzentrale eines Raumschiffes ein paar Monitore einbauen, auf denen Aufnahmen der Überwachungskameras aus anderen Teilen des Levels laufen. Oder Sie brauchen mal eben eine neue Lightmap, weil sich der Ort der Lichtquelle verändert hat.

Nicht zuletzt bieten die Techniken, die Sie hier lernen neue Experimenitiermöglichkeiten um interessante grafische Effekte zu erzielen. Wie in den meisten anderen Kapiteln gilt auch hier die Erkenntnis: die Technik ist einfach, die Möglichkeiten unbegrenzt!

Texturen im Speicher

Bevor wir uns der Praxis zuwenden, müssen wir uns noch kurz mit der Art und Weise beschäftigen, wie Bildinformationen von Texturen im Speicher abgelegt werden und wie man auf diese zugreift.

Bisher sprachen wir nur von der Auflösung einer Textur, womit wir die Breite, Höhe und die Farbtiefe meinten. Wenn wir die Breite oder Höhe eines Bitmaps im Bildeditor ermittelten, spielte es keine Rolle, in welcher Farbauflösung dieses Bitmap vorliegt. Wen wir in DirectXGraphics aber auf Texturen zugreifen wollen, ist das von grosser Relevanz. Alle Bildinformationen einer Textur werden im Speicher hintereinander abgelegt - Pixel für Pixel. Das heisst, hat eine Textur 16 Bit, müssen wir immer 2 Bytes pro Pixel einlesen. Bei 32 Bit fassen wir immer 4 Bytes zu einem Pixel zusammen.

Und damit kommen wir auch schon zur neuen Vokabel, nämlich dem Pitch. Der Pitch gibt die Breite einer Textur an - jedoch in Bytes. Betrachten wir hierzu folgende Grafik:

Angenommen wir haben eine 16-Bit-Textur, die 3 Pixel breit ist. Dann würden wir als Pitch-Wert 6 erhalten. Denn wie wir wissen, besteht ein Byte aus 8 Bits. Bei 16 Bits pro Pixel haben wir also zwei Bytes pro Pixel. Da die Textur 3 Pixel hat, ergibt sich also 2*3=6.

Um Texturen bearbeiten zu können, stehen uns nur extrem wenige Informationen zur Verfügung. Genauer gesagt zwei: der Pitch-Wert und ein Zeiger auf den ersten Pixel der Textur. Wenn Sie sich jetzt fragen, wie man nur mit diesen beiden Werten eine Textur bearbeiten soll, dann lesen Sie ruhig weiter. Es geht!

Vorraussetzungen

Um eine Textur zur Laufzeit bearbeiten zu können, muss diese unter bestimmten Vorraussetzungen erstellt worden sein. Betrachten wir hierzu folgendes Beispiel:

  D3DXCreateTexture( d3ddev8,
                     256,
                     256,
                     0,
                     0,                // nicht D3DUSAGE_RENDERTARGET
                     D3DFMT_A4R4G4B4,
                     D3DPOOL_MANAGED,  // oder D3DPOOL_SYSTEMMEM
                     textur );

Zwei Dinge müssen Sie beachten, um später die Textur bearbeiten zu können:

  1. als Usage-Wert (5ter Parameter) dürfen Sie nicht D3DUSAGE_RENDERTARGET verwenden
  2. die Textur muss mit dem Pool-Paramter D3DPOOL_MANAGED oder D3DPOOL_SYSTEMMEM definiert sein. D3DPOOL_MANAGED ist zwingend erforderlich, wenn Sie die Textur verändern wollen.
Desweiteren sollten Sie darauf achten, in welchem Format Sie Ihre Textur erstellen. Im oberen Beispiel habe ich mich für ein 16-Bit Format entschieden, bei dem je 4 Bits jedem Farb-Kanal (inklusive Alpha-Kanal) zugeteilt sind.

Textur locken

Um auf die einzelnen Bildinformationen zugreifen zu können, müssen wir, wie bereits beim Vertex- und Indexbuffer bekannt, den Speicher für unsere Zugriffe erst einmal reservieren bzw. locken. Dies geschieht mit folgendem Befehl aus dem Textur-Interface:

function LockRect(const Level : Cardinal;
                    out pLockedRect : TD3DLocked_Rect;
                  const pRect : PRect;
                  const Flags : LongWord) : HResult;

Level: Das Mipmaplevel, dass Sie locken wollen.

pLockRect: Ein Record des Typs [image] D3DLOCKED_RECT, das mit den nötigen Informationen über den gelockten Bereich gefüllt wird.

pRect: Ein Zeiger auf ein Record vom Typ TRect, das den Bereich der Textur angibt (in Pixel), der gelockt werden soll. Wahlweise kann auch nil übergeben werden, wenn Sie die gesamte Textur locken wollen.

Flags: Eine oder mehrere Konstanten aus den [image] D3DLOCKFLAGS, ausser D3DLOCK_NOOVERWRITE.

Beachten Sie beim locken, das das Record pRect nicht grösser definiert ist, als die Textur da es sonst zu Fehlern kommen kann. Das folgende Beispiel zeigt den Befehl in der Praxis:

var
  d3dlr : TD3DLocked_Rect;
  hr : HRESULT;
begin
  hr := textur.LockRect(0, d3dlr, nil, 0);
  if hr<>D3D_OK then
  begin
    exit;
  end;

In diesem Beispiel werden alle Bildinformationen des ersten Miplevel der Textur gelockt. Wir haben sowohl Lese- als auch Schreibrechte auf den Speicherbereich, so dass wir nun damit beginnen können, die Texturfarben zu bearbeiten.

Texturpixel bearbeiten und unlocken

Falls Sie jetzt einen schwer zu verstehenden Abschnitt erwarten, dann muss ich Sie leider enttäuschen. Das eigentliche Bearbeiten einer Textur ist im Prinzip ganz einfach. Viel schwieriger, und das liegt im Können des Programmierers, ist es, etwas sinnvolles mit dieser Technik anzustellen. Doch schauen wir uns die Technik selbst erstmal an.

Das durch die LockRect-Funktion erhaltene Record TD3DLocked_Rect beinhaltet unter anderem das Attribut pBits. Es ist der Zeiger auf den Anfang des gelockten Bereiches (im oberen Beispiel sogar der Anfang der Textur). Damit haben wir im Prinzip auch schon Zugriff auf den ersten Pixel der Textur. Diesem Pixel können Sie bereits ohne weiteres einen neuen Farbwert zuweisen. Dabei ist allerdings entscheidend, in welchem Format Sie die Textur erstellt haben. Handelt es sich beispielsweise um eine 16-Bit Funktion, so können Sie an der Stelle, auf die der Zeiger zeigt, einen Integer-Wert vom Typ Word auslesen bzw. schreiben. Bei einer 32-Bit-Textur wäre die Integer-Zahl vom Typ LongWord. Um den nächsten Pixelwert auslesen bzw. beschreiben zu können inkrementieren Sie einfach den Zeiger.

Falls Sie noch nicht sicher sind, ob Sie die Beschreibung verstanden haben, gibt Ihnen vielleicht folgendes Beispiel mehr Sicherheit:

var
  d3dlr : TD3DLocked_Rect;
  hr : HRESULT;
  cursor : pWord;
begin
  hr := textur.LockRect(0, d3dlr, NIL, 0);
  if hr<>D3D_OK then
  begin
    exit;
  end;

  // Damit die 16-Bitwerte bequemer zuweisbar sind
  cursor := d3dlr.pBits;

  cursor^:= $0fa3;  // ersten Pixel beschreiben
  inc(cursor);      // zu den nächsten 16 Bits
  cursor^:= $358d;  // zweiten Pixel beschreiben

Was passiert hier? Zunächst wandeln wir den untypisierten Zeiger in einen typisierten Word-Zeiger um. Dies ermöglicht uns die 16-Bit Farbwerte einfacher auszulesen bzw. zu schreiben. Danach wird auch schon der erste Wert beschrieben. Ich habe hier statt einer nichtssagenden Dezimalzahl eine Hexadezimalzahl benutzt. Hierbei beziehe ich mich auf die Textur, die im Abschnitt [image] Vorraussetzungen erstellt wurde. In diesem Abschnitt haben wir eine 16-Bit Textur eingerichtet, bei der jedem Farbkanal 4-Bits zugewiesen wurden. Die Kanäle Alpha, Rot, Grün und Blau sind in den Stellen des Hexadezimalwertes wiedererkennbar. Die Stärke des jeweiligen Kanals reicht dabei von 0 bis f. Um auf den nächsten Pixel zugreifen zu können, wird mit der Funktion inc der Zeiger einfach inkrementiert.

Im Prinzip liegt darin das ganze Geheimnis um eine Textur zur Laufzeit verändern zu können. Es ist lediglich eine Frage des richtigen Algorithmus, was für Bilder oder Muster sich mit dieser Technik erstellen lassen.

Wichtig beim Zugriff auf die Texturpixel ist, dass sich der Zeiger nur innerhalb des gelockten Bereiches bewegt. Überschreiten Sie die Grenze kommt es in aller Regel zu einer Fehlermeldung. Daher ist es wichtig herauszubekommen, wie gross eine Textur ist. Dazu gibt es zwei Möglichkeiten. Entweder haben Sie beim Erstellen der Textur (zum Beispiel mit dem Befehl [image] D3DXCreateTextureFromFileEx) ein Record vom Typ [image] D3DXIMAGEINFO erhalten, in dem die Höhe und Breite der Textur angegeben ist, oder aber Sie ermitteln die Grösse erst nach dem Locken. Dafür bietet sich der Pitch-Wert an.

Wir erinnern uns: Pitch gibt die Breite einer Textur in Bytes zurück. Bei 256 16-Bit Pixeln wären das also 512 Bytes. Damit erhalten wir also schonmal einen Rückschluss auf die Breite der Textur. Da man aus Performancegründen grundsätzlich quadratische Texturen verwenden sollte, können wir mit dem Pitch-Wert sowohl Breite als auch Höhe ermitteln. Um in der Praxis zum Beispiel eine Textur komplett in Weiss zu färben, könnte die Funktion dafür folgendermassen aussehen:

var
 d3dlr : TD3DLocked_Rect;
 cursor : pWord;
 x : integer;
 laenge : LongWord;
 hr : HRESULT;
begin
  hr := textur.LockRect(0, d3dlr, NIL, 0);
  if hr=D3D_OK then begin
    laenge := (d3dlr.Pitch shr 1);
    cursor  := d3dlr.pBits;
    for x := 1 to (laenge*laenge) do begin
      cursor^:= $ffff;
      inc(cursor);
    end;
    textur.UnlockRect(0);
  end;
end;

In diesem Fall haben wir also eine quadratische Textur, so dass Höhe gleich Breite ist. Mit shr verschieben wir alle Bits um eine Stelle nach rechts, was im Prinzip der Division durch 2 gleichkommt.

Das Beispiel enthält auch gleich schon die Funktion zum Unlocken. Diese sieht in der Definition so aus:

function UnlockRect(const Level : Cardinal) : HResult;

Level: Das Mipmap-Level, dass Sie wieder unlocken wollen.

Anwendungsbeispiele für die Texturbearbeitung

Ich kann mir gut vorstellen, dass Sie bisher noch skeptisch gegenüber der Texturbearbeitung sind. Daher möchte ich Ihnen gerne ein Beispiel zeigen, wo diese Technik eingesetzt werden kann.

In der Einleitung habe ich kurz die 64k Demos angesprochen. Dabei handelt es sich um selbstlaufende Programme, die möglichst viele audiovisuelle Effekte bieten. Die Programmgrösse ist hierbei auf 64kb begrenzt. Diese Aufgabe ist eine grosse Herausforderung für alle Programmierer, die nach den effizientesten und am vielseitigsten einsetzbaren Algorithmen suchen, um zum Beispiel Bilder (in Texturen) zu erstellen. Ein einfaches Beispiel sind Zaun-artige Muster.

Hierbei werden durch mehrmalige Zugriffe auf die Textur sowohl der Schatten des Zaunes als auch der Zaun selbst durch einen Algorithmus aufgetragen. Der Algorithmus dafür könnte zum Beispiel so aussehen:

procedure Paint_Texture( _textur : IDirect3DTexture8; _breite, _abstand, _sx, _sy : integer; _color : word; _overwrt : Boolean );
var
  d3dlr : TD3DLocked_Rect;
  cursor : pWord;
  x,y  : integer;
  YInc : LongWord;
  summe : integer;
  hr : HRESULT;
begin
  summe := _breite + _abstand;
  hr := _textur.LockRect(0, d3dlr, nil, 0);
  if hr=D3D_OK then begin
    YInc := (d3dlr.Pitch shr 1);

    cursor  := d3dlr.pBits;

    for x := 1 to YInc do begin
      for y := 1 to YInc do begin
       if ((y+x-_sx) mod summe) <= _breite then begin
         cursor^:= _color;
       end else begin
         if _overwrt then begin
           cursor^:= $f000;
         end;
       end;
        if ((y+Yinc-x-_sy) mod summe) <= _breite then begin
          cursor^:= _color;
        end;
      inc(cursor);
      end;
    end;

  end else begin
    hHalt(PChar(errtostr(hr)), Application);
  end;
  _textur.UnlockRect(0);
end;

Das Bild auf dem Screenshot lässt sich durch den zweimaligen Aufruf der Prozedur erreichen. Für den Screenshot wurden dafür folgende Parameter übergeben:

  Paint_Texture( tex1, 4, 16, 4, 2, $0333, true );
  Paint_Texture( tex1, 4, 16, 0, 0, $0446, false );

Textur als Rendertarget

Eine weitere Möglichkeit, Texturen zur Laufzeit zur verändern, ist, sie als Rendertarget zu benutzen. Das bedeutet, alles, was gerendert werden soll, wird nicht direkt auf dem Bildschirm ausgegeben, sondern erstmal nur innerhalb der Textur. Wenn Sie dann die Textur rendern, können Sie interessante bzw. nützliche Effekte erreichen. Auf diese Weise kann man zum Beispiel einen Spiegel simulieren oder eine Überwachungsmonitor. Aber auch andere visuelle Effekte, lassen sich damit erreichen. Wollen Sie zum Beispiel ein Objekt gleich mehrmals aus der gleichen Perspektive darstellen, können Sie dieses Objekt zunächst in eine Textur rendern um diese dann beliebig oft anzuzeigen.

Um eine Szene in eine Textur zu rendern müssen Sie zunächst eine Textur erstellen, und diese so einrichten, dass Sie als Rendertarget benutzt werden kann. Dazu ist es notwendig als vierten Parameter D3DUSAGE_RENDERTARGET zu verwenden. Die Create-Prozedur könnte also zum Beispiel so aussehen.

  D3DXCreateTexture(d3ddev8,
                    256,
                    256,
                    0,
                    D3DUSAGE_RENDERTARGET,   // <-- wichtig
                    D3DFMT_A1R5G5B5,
                    D3DPOOL_DEFAULT,
                    textur);

Um das Renderziel zu ändern, bietet das Direct3DDevice-Interface folgende Funktion:

function SetRenderTarget(pRenderTarget, pNewZStencil : IDirect3DSurface8) : HResult;

pRenderTarget: Ein IDirect3DSurface8-Interace, das als neues Renderziel benutzt werden soll. Übergeben Sie nil, wird das aktuelle Surface verwendet.

pNewZStencil: Ein IDirect3DSurface8-Interace, das als neuer Tiefenbuffer benutzt werden soll. Übergeben Sie nil, wird das aktuelle Surface verwendet.

Wie Sie feststellen, wird ein Surface, und keine Textur verlangt. Wir können also den Texturzeiger nicht direkt dieser Funktion übergeben. Ein Surface repräsentiert den eigentlichen Speicherbereich der Bildinformationen. Dabei wird für jedes Mipmaplevel der Textur ein eigenes Surface angelegt. Daher müssen wir erst mal auswählen, welches Surface wir überhaupt benutzen wollen. Dies geschieht mit folgender Funktion des Textur-Interfaces:

function GetSurfaceLevel(const Level : Cardinal; out ppSurfaceLevel : IDirect3DSurface8) : HResult;

Level: Das Mipmaplevel, dessen Surface wir haben wollen.

ppSurfaceLevel: Ein Zeiger vom Typ IDirect3DSurface8.

Mit dieser Funktion erhalten wir also einen Zeiger auf das Surface, das wir als Rendertarget benutzen wollen. Im Prinzip ist das alles, was wir wissen müssen, um eine Textur als Rendertarget verwenden zu können. Ein Beispielcode könnte folgendermassen aussehen:

var
  rendertarget: IDirect3DSurface8;
  texsurf: IDirect3DSurface8;
begin
  d3ddev8.GetRenderTarget( rendertarget );
  tex1.GetSurfaceLevel( 0, texsurf );
  d3ddev8.SetRenderTarget( texsurf, nil );
  d3ddev8.Clear( 0, nil, D3DCLEAR_TARGET, D3DCOLOR_XRGB( 0, 0, 0), 1.0, 0 );
     // Hier kommen alle Renderoperationen rein,
     // die auf der Textur abgebildet werden sollen
  d3ddev8.SetRenderTarget( rendertarget, nil );
  d3ddev8.Clear( 0, nil, D3DCLEAR_TARGET, D3DCOLOR_XRGB( 0, 0, 0), 1.0, 0 );
  d3ddev8.SetTexture(0, tex1);
     // Renderoperationen, bei denen tex1
     // verwendet werden soll

.GetRenderTarget gibt, wie Sie sich sicher denken können einen Zeiger auf das aktuelle Rendertarget zurück. Diesen Zeiger brauchen wir, um später die Szene auf dem Desktop wieder ausgeben zu können. Wie üblich, bietet es sich auch beim Rendern in eine Textur an, zunächst einmal mit der .Clear-Funktion alle Bildinformationen zu löschen, damit die 3D-Objekte in der Animation keine Spuren hinter sich her ziehen.


SelfDXD - Direct3D Rahmeneinstellungen

SelfDXD --- [image] DirectXGraphic
Direct3D Rahmeneinstellungen


SelfDXD - Viewports

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Rahmeneinstellungen
 | Viewports
 | Was ist ein Viewport? Einstellen eines neuen Viewports Eventuelle Probleme

Was ist ein Viewport?

Der Viewport ist der Bereich auf dem Bildschirm, in dem die 3D-Szene gerendert wird. Im Vollbildmodus ist das also der ganze Bildschirminhalt und im Fenstermodus standardmässig das ganze Fenster. Doch Sie können die Grösse des Viewports im Fenstermodus auch selbst einstellen. Die Szene wird dann entsprechend den Grössenverhältnissen skaliert.

Wenn Sie dem Direct3DDevice das Handle ihres Fenster übergeben, dann greift DirectXGraphics auch auf das ganze Fenster zu. Mit Viewports können Sie lediglich bestimmen in welchem Teil des Fensters der Rendervorgang stattfinden soll. Viewports sind praktisch, wenn Sie neben der 3D-Szene das Fenster beispielsweise auch noch für andere visuelle Komponenten, wie Buttons etc. nutzen wollen. Desweiteren können Sie vor dem Aufruf der .Present-Funktion die Viewport-Einstellungen ändern und somit an verschiedenen Stellen des Fensters mehrere unterschiedliche Szenen zeigen. Das ist zum Beispiel nützlich, wenn Sie sich einen 3D-Modeller programmieren und ein 3D Objekt von mehreren Seiten gleichzeitig betrachten wollen.

Einstellen eines neuen Viewports

Standardmässig nutzt DirectXGraphics die ganze Fläche des gehandelten Fensters. Mit dieser Funktion des Direct3DDevices können Sie jedoch den Viewport neu einstellen.

function SetViewport(var pViewport : TD3DViewport8) : HResult;

pViewport: Ein Record vom Typ [image] D3DVIEWPORT8.

Der Einsatz des Viewports in der Praxis könnte zum Beispiel so aussehen:

var
  d3ddev8: TDirect3DDevice8;

function GetViewport( _x, _y, _width, _height: LongWord; _minz, _maxz: Single): TD3DViewPort8;
begin
  with result do
  begin
    x      := _x;
    y      := _y;
    width  := _width;
    height := _height;
    minz   := _minz;
    maxz   := _maxz;
  end;
end;

procedure IrgendeineProzedur;
var
  d3dvp8: TD3DViewport8;
begin
  // vorrausgehender Quelltext

  d3dvp8 := GetViewPort( 8, 16, 315, 240, 0.0, 1.0  );
  d3ddev8.SetViewport( d3dvp8 );

  // folgender Quelltext
end;

Auslesen können Sie den aktuellen Viewport mit der entsprechenden Get-Funktion des Devices.

function GetViewport(out pViewport : TD3DViewport8) : HResult;

Eventuelle Probleme

Wenn Ihr Viewport kleiner ist als die eigentliche Fenster- bzw. Komponentenfläche, dann kann es gelegentlich vorkommen, dass Bereiche ausserhalb des Viewports verzerrte Bildfetzen enthalten. Dies kann passieren, wenn der Speicherbereich, in dem sich der Backbuffer befindet, noch alte Bildreste aus anderen Anwendungen enthält. Ein sauberes Fenster können Sie zum Beispiel mit folgendem Trick erhalten:

procedure IrgendeineProzedur;
var
  d3dvp8: TD3DViewport8;
begin
  // vorrausgehender Quelltext

  d3ddev8.Clear( 0, nil, D3DCLEAR_TARGET, D3DCOLOR_XRGB( 192, 192, 192 ), 1.0, 1 );
  d3dvp8 := GetViewPort( 8, 16, 315, 240, 0.0, 1.0  );
  d3ddev8.SetViewport( d3dvp8 );

  // folgender Quelltext
end;

Der Code lehnt an das vorrausgehende Beispiel an. Da vor der .Clear-Funktion noch kein neuer Viewport gesetzt wurde wird die gesamte Fläche mit dem Windows-Standardgrau gelöscht. Danach wird der neue Viewport eingestellt.


SelfDXD - Features-Abfragen mit D3DCaps8

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Rahmeneinstellungen
 | Features-Abfragen mit D3DCaps8
 | Was ist D3DCaps8? D3DCaps einlesen D3DCaps verwenden

Was ist D3DCaps8?

Nicht jedes Feature, das DirectXGraphics bereithält, wird auch von jeder Grafikkarte unterstützt. Weiss eine Grafikkarte mit einer bestimmten Operation nicht umzugehen, so kann es nicht selten zum totalen Absturz des Programmes oder sogar des ganzen Systems kommen. Damit Ihr Anwendung auf möglichst vielen Rechnern einwandfrei läuft, sollten Sie deshalb bei allen wichtigen Features kontrollieren, ob diese auch von der aktuellen Grafikkarte unterstützt werden.

Für diese Aufgabe hält DirectXGraphics ein Record namens D3DCaps8 bereit. In diesem Record stehen alle wichtigen Informationen über die Features der benutzten Grafikkarte drin. Sie können dieses Record von DirectXGraphics automatisch generieren lassen und dann die entsprechenden Eigenschaften kompfortabel abfragen. D3DCaps8 steht übrigens für Direct3D-Capabilities of DirectX8.

D3DCaps8 einlesen

Es gibt zwei Arten das D3DCaps8-Record einzulesen. Beachten hierbei, das die DirectXGraphics-Features auch von der gewählten Auflösung und Farbtiefe abhängen können. Die erste Funktion ist eine Funktion des Direct3DDevice8-Interfaces.

function GetDeviceCaps(out pCaps : TD3DCaps8) : HResult;

pCaps: Ein Record vom Typ TD3DCaps8, das automatisch mit den entsprechenden Werten gefüllt wird.

Die zweite mögliche Funktion hat den gleichen Namen, gehört aber zum Direct3D8-Interface:

function GetDeviceCaps(const Adapter : Cardinal;
                       const DeviceType : TD3DDevType;
                       out   pCaps : TD3DCaps8) : HResult;

Adapter: Ordinale Zahl, die die Grafikkarte angibt, für den fall, dass Sie mehr als eine Grafikkarte benutzen. Die Standardgrafikarte wird mit der Konstante D3DADAPTER_DEFAULT benutzt.

DeviceTyp: Gibt an, ob die Features für den hardwarebeschleunigten Modus oder einen Softwaremodus gelten sollen. Verwenden Sie hierzu eine der Konstanten aus der [image] D3DDEVTYPE-Aufzählung

pCaps: Ein Record vom Typ TD3DCaps8, das automatisch mit den entsprechenden Werten gefüllt wird.

D3DCaps8 verwenden

Wenn Sie einmal das Record mit den entsprechenden Einstellungen gefüllt haben, können Sie es zu jedem Zeitpunkt in Ihrer Anwendung einsetzen. Eine Liste aller Attribute finden Sie in der Hilfe der SDK. Wenn Sie die komplette SDK besitzen, dann können Sie sich auch in Ruhe die Features Ihrer Grafikkarte anschauen. Dafür hält die SDK ein Programm namens D3DCapsViewer bereit, dass Ihnen in einer Übersicht Auskunft über jede abrufbare Eigenschaft Ihrer Graifkkarte liefert.

Hier ein kleines Beispiel dafür, wie die D3DCaps8 eingesetzt werden können:

var
  D3DCaps8 : TD3DCaps8;
begin
  d3ddev8.GetDeviceCaps( D3DCaps8 );
  if D3DCaps8.MaxTextureBlendStages > 2 then begin
    // Alphablending mit mehreren Texturen und Licht
    // über Texturestages
  end else begin
    // Alphablending mit mehreren Texturen und Licht
    // über Multipass Blending
  end;

Da einige Grafikkarten nicht mehr als zwei Stages unterstützen, lohnt es sich hier zum Beispiel diese Abfrage einzubauen. Wenn die Grafikkarte tatsächlich nicht mehr als zwei Stages unterstützt, werden alle Textur- und Alphablendingoperationen über das Multipass Blending gemacht. Das ist dann zwar langsamer, als die Verwendung der Texturestages, aber wenigstens läuft Ihr Programm dann überhaupt.


Direct3D Delphi Referenz

SelfDXD --- [image] DirectXGraphic
Direct3D Delphi Referenz


Records

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz
 | Records
 | D3DCOLORVALUE D3DDISPLAYMODE D3DLIGHT8 D3DLINEPATTERN D3DLOCKED_RECT D3DMATERIAL8 D3DMATRIX und D3DXMATRIX D3DPRESENT_PARAMETERS D3DRECT D3DVECTOR und D3DXVECTOR3 D3DVIEWPORT8 TPALETTEENTRY


SelfDXD - D3DCOLORVALUE

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Records
D3DCOLORVALUE
TD3DColorValue = packed record
  r : Single;
  g : Single;
  b : Single;
  a : Single;
end;

r, g, b, a
Geben den rot, grün, blau und alpha-Anteil der Farbe an. Die Werte liegen in der Regel zwischen 0 und 1 wobei 0 für schwarz steht.


D3DDISPLAYMODE

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Records
D3DDISPLAYMODE

TD3DDisplayMode = packed record
  Width       : Cardinal;
  Height      : Cardinal;
  RefreshRate : Cardinal;
  Format      : TD3DFormat;
end;

Width / Height
Gibt die Bildschirmauflösung, also die Anzahl der horizontalen und vertikalen Pixel an.

RefreshRate
Die Bildwiederholrate. Ist der Wert 0, wird die Standardeinstellung der Grafikkarte benutzt.

Format
Eine Konstante der D3DFormat-Aufzählungen. Hiermit wird die Farbtiefe angegeben.


SelfDXD - D3DLIGHT8

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Records
D3DLIGHT8
TD3DLight8 = packed record
  _Type        : LongWord;
  Diffuse      : TD3DColorValue;
  Specular     : TD3DColorValue;
  Ambient      : TD3DColorValue;
  Position     : TD3DVector;
  Direction    : TD3DVector;
  Range        : Single;
  Falloff      : Single;
  Attenuation0 : Single;
  Attenuation1 : Single;
  Attenuation2 : Single;
  Theta        : Single;
  Phi          : Single;
end;

_Type
Die Lichtart (Directional Light, Spot Light, Point Light). Verwenden Sie hierzu eine Konstanten der [image] D3DLIGHTTYPE-Aufzählungen.

Diffuse
Die Farbe des Lichtes. Diese Eigenschaft ist ein [image] D3DCOLORVALUE-Record.

Specular
Die Farbe des Lichtes, wenn auf dem 3D-Objekt ein Glanzeffekt durch das Licht entsteht. Diese Eigenschaft ist ein D3DCOLORVALUE-Record.

Ambient
Die Farbe des Lichtes, das Diffuse-Licht schwächer wird. Diese Eigenschaft ist ein D3DCOLORVALUE-Record.

Position
Die Position der Lichtquelle. Dieser Parameter wird nur von Spot Light und Point Light genutzt und ist ein [image] D3DVECTOR-Record.

Direction
Die Richtung des Lichtes. Dieser Parameter wird nur von Directional Light und Spot Light genutzt. Direction ist vom Typ D3DVECTOR

Range
Die maximale Distanz nach der das Licht keine Wirkung hat. Selbst starkes deutliches sichtbares Licht wird nach dieser Strecke konsequent abgebrochen. Dieser Parameter wird nur von Point und Spot Light genutzt.

Falloff
Das Licht eines Spotlight kann vom inneren Radius zum äusseren Radius abfallen. Dieser Wert bestimmt, wie stark die Lichthelligkeit sinken soll. 0.0 würde bedeuten, dass die Lichthelligkeit zum äusseren Rand hin überhaupt nicht abnimmt.

Attenuation0, Attenuation1, Attenuation2
Diese 3 Werte beschreiben, wie das Licht an Kraft verlieren soll. Sie werden in DirectXGraphics zu folgender Berechnung benötigt:

                              1
A = -------------------------------------------------------
    Attenuation0  +  D x Attenuation1  +  D2 x Attenuation2
A ist hierbei die zu berechnende Abschwächung des Lichtes und D die Distanz zur Lichtquelle. Diese Parameter werden nur von Spot und Point Lights verwendet.

Theta
Ein (radialer) Winkel, der den inneren Kegel eines Spot Lights beschreibt. Der Wert muss zwischen 0 und Phi liegen.

Phi
Ein (radialer) Winkel, der den äusseren Kegel eines Spot Light beschreibt. Der Wert muss zwischen 0 und Pi liegen.


SelfDXD - D3DLINEPATTERN

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Records
D3DLINEPATTERN
TD3DLinePattern = packed record
  wRepeatFactor : Word;
  wLinePattern  : Word;
end;

wRepeatFactor
Anzahl, wie oft die Einsen und Nullenfolge, die in wLinePattern definiert ist, wiederholt werden soll. Dies erlaubt der Anwendung LinePattern zu vergrössern, bzw. zu verkleinern.

wLinePattern
Linienmuster, bei dem jedes einzelne Bit ausgelesen wird, und der Wert der Zahl irrelevant ist. Mit dieser Bitreihenfolge erhalten Sie zum Beispiel eine gepunktete Linie: 1100110011001100


SelfDXD - D3DLOCKED_RECT

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Records
D3DLOCKED_RECT

TD3DLocked_Rect = packed record
  Pitch : Integer;
  pBits : Pointer;
end;

Pitch
Länge der gelockten Textur in Bytes.

pBits
Zeiger auf den Anfang des gelockten Bereiches.


SelfDXD - D3DMATERIAL8

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Records
D3DMATERIAL8
TD3DMaterial8 = packed record
  Diffuse  : TD3DColorValue;
  Ambient  : TD3DColorValue;
  Specular : TD3DColorValue;
  Emissive : TD3DColorValue;
  Power    : Single;
end;

Diffuse, Ambient
Diese Eigenschaften beschreiben, welche Farben des direkten (Diffuse) und des ambienten Lichtes wiedergegeben werden sollen. Diese Attribute sind vom Typ [image] D3DCOLORVALUE.

Specular
Dies ist der Farbwert der als Glanzwert wiedergegeben wird. Diese Eigenschaft ist vom Typ D3DCOLORVALUE.

Emissive
Mit diesem Farbwert können Sie Ihrem 3D-Objekt eine eigene Leuchtkraft verleihen. Dieser Farbwert wird in jedemfall angegeben. Direktes und ambientes Licht werden lediglich hinzuaddiert.

Power
Beschreibt, wie stark das Glanzlicht sein soll. 0 ist hierbei der stärkste Wert. Je höher der Wert von Power ist, desto schwächer entsteht das Glanzlicht.


SelfDXD - D3DMATRIX und D3DXMATRIX

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Records
D3DMATRIX und D3DXMATRIX
TD3DMatrix = packed record
  _11, _12, _13, _14 : Single;
  _21, _22, _23, _24 : Single;
  _31, _32, _33, _34 : Single;
  _41, _42, _43, _44 : Single;
end;
Dieses Record beschreibt eine Matrix.


D3DPRESENT_PARAMETERS

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Records
D3DPRESENT_PARAMETERS

TD3DPresent_Parameters = packed record
  BackBufferWidth                 : Cardinal;
  BackBufferHeight                : Cardinal;
  BackBufferFormat                : TD3DFormat;
  BackBufferCount                 : Cardinal;

  MultiSampleType                 : TD3DMultiSample_Type;

  SwapEffect                      : TD3DSwapEffect;
  hDeviceWindow                   : HWND;
  Windowed                        : BOOL;
  EnableAutoDepthStencil          : BOOL;
  AutoDepthStencilFormat          : TD3DFormat;
  Flags                           : LongWord;

  FullScreen_RefreshRateInHz      : Cardinal;
  FullScreen_PresentationInterval : Cardinal;
end;

BackBufferWidth/BackBufferHeight
Diese beiden Parameter sind nur wichtig, wenn Ihre Anwendung im Vollbildmodus laufen soll. In Width und Height geben Sie dann einfach die gewünschte Auflösung in Breite und Höhe ein.

BackBufferFormat
Hiermit bestimmen Sie die Auflösung, also wieviel Bit-Informationen für jeden Pixel auf dem Bildschirm gespeichert werden sollen. 'BackBufferFormat' ist vom Typ TD3DFORMAT, was nichts anderes ist als LongWord. DirectX unterstützt 40 Formate, die alle durch eine Zahl BackBufferFormat übergeben werden können. Um sich nicht alle Zahlencodes für jede Auflösung merken zu müssen, gibt es Konstante, die man dafür verwenden kann. Unter [image] D3DFormat finden Sie eine Übersicht Über alle Konstanten.

BackBufferCount
Mit BackBufferCount bestimmen Sie, wieviele Backbuffer DirectX verwalten soll. In der Regel reicht einer. Sie können bis zu 3 Backbuffer gleichzeitig verwenden. Die Übergabe des Wertes 0 wird übrigens von DirectX als 1 gewertet. Einen Backbuffer benötigen Sie also zwingend.

MultiSampleType
Multisampling ist ein Antialiasing-Verfahren. Antialiasing bedeutet, diagonal verlaufende Ränder eines 3D-Körpers zum Beispiel farblich so zu verändern, dass der typische Treppeneffekt nicht sichtbar ist. Selbst bei hohen Auflösungen kann man zum Teil noch jeden einzelnen Pixel erkennen, wenn er sich farblich stark vom Hintergrund abhebt. Bei diagonalen Linien erkennt man das vorallem daran, dass diese Linie scheinbar ganz viele Ecken am Rand hat. Antialiasing verwischt diese Ecken, so dass eine Linie bzw. ein Polygon viel glaubwürdiger zur Szene passt. Multisampling ist dabei nur eins von 3 möglichen Verfahren. Bei dieser Technik wird jedem Pixel eine bestimmte Anzahl an Ebenen zugewiesen. Beim Rendern eines Bildes werden dann jeder Ebene die Farben des Randes des jeweiligen Objektes zugewiesen. Aus diesen verschiedenen Farbe wird dann die Durchschnittsfarbe errechnet, die letztendlich angezeigt wird. Dadurch wird die Bildqualität verbessert. In MultiSampleType geben Sie also im Grunde die Anzahl der Ebenen an. Diese kann zwischen 1 und 16 liegen. Sie können dafür auch die [image] D3DMultiSample_Type-Aufzählungen verwenden. Multisampling lässt sich jedoch nur verwenden, wenn SwapEffect auf TD3DSWAPEFFECT_DISCARD gestellt ist.

SwapEffect
Mit SwapEffect geben Sie an, in welcher Weise Back- mit Frontbuffer vertauscht werden sollen. Verwenden Sie dazu eine [image] D3DSwapEffect-Konstante.

hDeviceWindows
Hier müssen Sie lediglich das Handle der Form übergeben, in der ihre DirectXGraphics-Anwendung dargestellt werden soll.

Windowed
Mit einem booleschen Wert betimmen Sie, ob Ihre Anwendung im Fenster laufen soll, oder im Vollbild. True steht für Fenstermodus, False logischerweise für Vollbildmodus. Ist Fenstermodus aktiviert, werden die Grösseneinstellungen Ihrer Form übernommen.

EnableAutoDepthStencil
Erstellt einen sogenannten Z-Buffer. Dieser wird in 3D-Anwendungen benötigt, damit Objekte die sich im Vordergrund befinden nicht versehentlich von Objekten überlappt werden, die sich eigentlich im Hintergrund befinden. Mit true schalten Sie den Tiefenbuffer ein, mit false aus. 2D-Anwendungen benötigen zum Beispiel oftmals keinen Z-Buffer.

AutoDepthStencilFormat
Hier geben Sie das Format dieses Z-Buffers an. Dafür stehen Ihnen die D3DFormat-Konstanten zur Verfügung.

Flags
Dies ist ein Parameter, der in der derzeitigen Version nicht genutzt wird, doch schonmal eingerichtet wurde und zeigt, dass in zukünfitgen Versionen neue Einstellungsmöglichkeiten und Funktionen hinzukommen werden. Bei DirectX 8.0 muss dieser Parameter allerdings 0 sein.

FullScreen_RefreshRateInHz
Die Rate in der die Grafikkarte den Screen erneuern soll. Verwenden Sie hier die durch die Funktion Direct3D8.EnumAdapterModes ermittelte Refreshrate oder eine [image] D3DPresent_Rate_Flags-Konstante. Im Fenstermodus muss diese Variable 0 sein.

FullScreen_PresentationInterval
Gibt an, wie oft das Bild, in Abhängigkeit zur Bildwiederholfrequenz, von DirectX aktuallisiert werden soll. Benutzen Sie dafür eine [image] D3DPresent_Interval_Flags-Konstante.


D3DRECT

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Records
D3DRECT

TD3DRect = packed record
  x1 : LongInt;
  y1 : LongInt;
  x2 : LongInt;
  y2 : LongInt;
end;

x1 und y1
Die Koordinate der oberen linken Ecke des Rechtecks.

x2 und y2
Die Koordinate der unteren rechten Ecke des Rechtecks.


SelfDXD - D3DVECTOR und D3DXVECTOR3

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Records
D3DVECTOR und D3DXVECTOR3
TD3DVector = packed record
  x : Single;
  y : Single;
  z : Single;
end;

x, y und z
Variablen, die den Vektor beschreiben.


SelfDXD D3DVIEWPORT8

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Records
D3DVIEWPORT8
TD3DViewport8 = packed record
  X      : LongWord;
  Y      : LongWord;
  Width  : LongWord;
  Height : LongWord;
  MinZ   : Single;
  MaxZ   : Single
end;

X, Y
Die Pixelkoordinaten der linken oberen Ecke des Viewports.

Width, Height
Die Breite und Höhe des Viewports in Pixelkoordinaten.

MinZ, MaxZ
Diese beiden Werte geben an, in welchen räumlichen Tiefen sich die Szene innerhalb des Viewports befindet. Dies ist wichtig, wenn mehrere Viewports verwendet werden, die sich überlappen. Für gewöhnlich reicht es hier aber wenn Sie 0.0 und 1.0 angeben.


SelfDXD - TPALETTEENTRY

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Records
TPALETTEENTRY
TPALETTEENTRY = packed record
  peRed: Byte;
  peGreen: Byte;
  peBlue: Byte;
  peFlags: Byte;
end;

peRed, peGreen, peBlue, peFlags
Der Rot-, Grün-, Blau- und Alpha-Anteil der Palette.


Aufzählungen

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz
 | Aufzählungen
 | D3DBLEND D3DBLENDOP D3DCLEARFLAGS D3DCLIPPLANEn D3DCMPFUNC D3DCREATEFLAGS D3DCULL D3DDEBUGMONITORTOKENS D3DDEVTYPE D3DFILLMODE D3DFOGMODE D3DFORMAT D3DFVFFLAGS D3DLIGHTTYPE D3DLOCKFLAGS D3DMATERIALCOLORSOURCE D3DMULTISAMPLE_TYPE D3DPATCHEDGESTYLE D3DPOOL D3DPRESENT_INTERVAL_FLAGS D3DPRESENT_RATE_FLAGS D3DPRIMITIVETYPE D3DRENDERSTATETYPE D3DRESOURCETYPE D3DSHADEMODE D3DSTENCILOP D3DSWAPEFFECT D3DTEXTUREADDRESS Texture Argument Flags D3DTEXTUREFILTERTYPE D3DTEXTUREOP D3DTEXTURESTAGESTATETYPE D3DTEXTURETRANSFORMFLAGS D3DTRANSFORMSTATETYPE D3DUSAGEFLAGS D3DVERTEXBLENDFLAGS D3DZBUFFERTYPE


SelfDXD - D3DBLEND

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Aufzählungen
D3DBLEND
D3DBLEND_ZERO               = 1;
D3DBLEND_ONE                = 2;
D3DBLEND_SRCCOLOR           = 3;
D3DBLEND_INVSRCCOLOR        = 4;
D3DBLEND_SRCALPHA           = 5;
D3DBLEND_INVSRCALPHA        = 6;
D3DBLEND_DESTALPHA          = 7;
D3DBLEND_INVDESTALPHA       = 8;
D3DBLEND_DESTCOLOR          = 9;
D3DBLEND_INVDESTCOLOR       = 10;
D3DBLEND_SRCALPHASAT        = 11;
D3DBLEND_BOTHSRCALPHA       = 12;
D3DBLEND_BOTHINVSRCALPHA    = 13;
D3DBLEND_FORCE_DWORD        = $7fffffff;

D3DBLEND_ZERO
Blendfaktor ist (0, 0, 0, 0).

D3DBLEND_ONE
Blendfaktor ist (1, 1, 1, 1).

D3DBLEND_SRCCOLOR
Blendfaktor ist (RS, GS, BS, AS).

D3DBLEND_INVSRCCOLOR
Blendfaktor ist (1-RS, 1-GS, 1-BS, 1-AS).

D3DBLEND_SRCALPHA
Blendfaktor ist (AS, AS, AS, AS).

D3DBLEND_INVSRCALPHA
Blendfaktor ist (1-AS, 1-AS, 1-AS, 1-AS).

D3DBLEND_DESTALPHA
Blendfaktor ist (AD, AD, AD, AD).

D3DBLEND_INVDESTALPHA
Blendfaktor ist (1-AD, 1-AD, 1-AD, 1-AD).

D3DBLEND_DESTCOLOR
Blendfaktor ist (RD, GD, BD, AD).

D3DBLEND_INVDESTCOLOR
Blendfaktor ist (1-RD, 1-GD, 1-BD, 1-AD).

D3DBLEND_SRCALPHASAT
Blendfaktor ist (f, f, f, 1>); f = min(AS, 1-AD)

D3DBLEND_BOTHSRCALPHA
Ein Relikt aus DirectX 5.0 und früheren Versionen. Stattdessen können Sie auch für den Source- und Destinationblendfactor D3DBLEND_SRCALPHA und D3DBLEND_INVSRCALPHA verwenden.

D3DBLEND_BOTHINVSRCALPHA
Diese Konstante kann nur für D3DRS_SRCBLEND verwendet werden. Sourceblendfaktor ist (1-AS, 1-AS, 1-AS, 1-AS) und der Destinatonblendfaktor ist (AS, AS, AS, AS). D3DRS_DESTBLEND wird automatisch überschrieben.

D3DBLEND_FORCE_DWORD
Zwingt den Compiler, die Konstanten als 32-Bit-Werte zu behandeln.


SelfDXD - D3DBLENDOP

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Aufzählungen
D3DBLENDOP
D3DBLENDOP_ADD              = 1
D3DBLENDOP_SUBTRACT         = 2
D3DBLENDOP_REVSUBTRACT      = 3
D3DBLENDOP_MIN              = 4
D3DBLENDOP_MAX              = 5
D3DBLENDOP_FORCE_DWORD      = $7fffffff

D3DBLENDOP_ADD
Das Zielbild wird mit dem Quellbild addiert.
Result = Source + Destination

D3DBLENDOP_SUBTRACT
Das Zielbild wird vom Quellbild subtrahiert.
Result = Source - Destination

D3DBLENDOP_REVSUBTRACT
Das Quellbild wird vom Zielbild subtrahiert.
Result = Destination - Source

D3DBLENDOP_MIN
Es wird der kleinste Wert aus Quell- und Zielbild übernommen.
Result = MIN( Source, Destination )

D3DBLENDOP_MAX
Es wird der grösste Wert aus Quell- und Zielbild übernommen.
Result = MAX( Source, Destination )

D3DBLENDOP_FORCE_DWORD
Zwingt den Compiler, die Konstanten als 32-Bit-Werte zu behandeln.


D3DCLEARFLAGS

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Aufzählungen
D3DCLEARFLAGS
D3DCLEAR_TARGET  = 1
D3DCLEAR_ZBUFFER = 2
D3DCLEAR_STENCIL = 4

D3DCLEAR_TARGET
Löscht den Bildschirmbereich, wo Ihre DirectX-Anwendung läuft.

D3DCLEAR_ZBUFFER
Löscht den Z-Buffer. Dieser Parameter ist notwendig, wenn Sie den Z-Buffer verwenden und möchten, dass Ihre DirectX-Anwendung korrekt zwischen hinteren und vorderen Objekten unterscheiden kann.

D3DCLEAR_STENCIL
Löscht den Stencil Buffer.


SelfDXD - D3DCLIPPLANEn

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Aufzählungen
D3DCLIPPLANEn
D3DCLIPPLANE0 = 1;   // (1 << 0)
D3DCLIPPLANE1 = 2;   // (1 << 1)
D3DCLIPPLANE2 = 4;   // (1 << 2)
D3DCLIPPLANE3 = 8;   // (1 << 3)
D3DCLIPPLANE4 = 16;  // (1 << 4)
D3DCLIPPLANE5 = 32;  // (1 << 5)

Durch OR-Verknüpfungen können Sie mit Hilfe dieser Konstanten eigene Clippingplanes erstellen.


SelfDXD - D3DCMPFUNC

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Aufzählungen
D3DCMPFUNC
D3DCMP_NEVER                = 1
D3DCMP_LESS                 = 2
D3DCMP_EQUAL                = 3
D3DCMP_LESSEQUAL            = 4
D3DCMP_GREATER              = 5
D3DCMP_NOTEQUAL             = 6
D3DCMP_GREATEREQUAL         = 7
D3DCMP_ALWAYS               = 8
D3DCMP_FORCE_DWORD          = $7fffffff

D3DCMP_NEVER
Der Test schlägt immer fehl.

D3DCMP_LESS
Der neue Pixel wird akzeptiert, wenn sein Wert kleiner ist, als der Wert des derzeitigen Pixels.

D3DCMP_EQUAL
Der neue Pixel wird akzeptiert, wenn sein Wert gleich ist mit dem Wert des derzeitigen Pixels.

D3DCMP_LESSEQUAL
Der neue Pixel wird akzeptiert, wenn sein Wert kleiner oder gleich ist mit dem Wert des derzeitigen Pixel.

D3DCMP_GREATER
Der neue Pixel wird akzeptiert, wenn sein Wert grösser ist mit dem Wert des derzeitigen Pixel.

D3DCMP_NOTEQUAL
Der neue Pixel wird akzeptiert, wenn sein Wert nicht gleich ist mit dem Wert des derzeitigen Pixel.

D3DCMP_GREATEREQUAL
Der neue Pixel wird akzeptiert, wenn sein Wert grösser oder gleich ist mit dem Wert des derzeitigen Pixel.

D3DCMP_ALWAYS
Der Pixel wird immer akzeptiert.

D3DCMP_FORCE_DWORD
Zwingt den Compiler, die Konstanten als 32-Bit-Werte zu behandeln.


D3DCREATEFLAGS

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Aufzählungen
D3DCREATEFLAGS
D3DCREATE_FPU_PRESERVE              = 2
D3DCREATE_MULTITHREADED             = 4
D3DCREATE_PUREDEVICE                = 16 (&H10)
D3DCREATE_SOFTWARE_VERTEXPROCESSING = 32 (&H20)
D3DCREATE_HARDWARE_VERTEXPROCESSING = 64 (&H40)
D3DCREATE_MIXED_VERTEXPROCESSING    = 128 (&H80)

D3DCREATE_FPU_PRESERVE
Keine Ahnung

D3DCREATE_MULTITHREADED

D3DCREATE_PUREDEVICE
Gibt an, dass Raster-, Transformations-, Licht- und Schattenberechnungen von der Hardware übernommen werden. Dieses Flag sollte nur verwendet werden, wenn auch D3DCREATE_HARDWARE_VERTEXPROCESSING angegeben wird.

D3DCREATE_SOFTWARE_VERTEXPROCESSING
Da viele ältere Grafikkartenmodelle noch nicht über die sogenannte T&L-Funktion verfügen, kann man mit diesem Parameter angeben, dass die Software das sogennante Vertexprocessing übernehmen soll.

D3DCREATE_HARDWARE_VERTEXPROCESSING
Die Hardware übernimmt das Verarbeiten der Vertices. Beachten Sie, dass Ihre Grafikkarte, diese Funktion auch unterstützt.

D3DCREATE_MIXED_VERTEXPROCESSING
Berechnet die Koordinaten der Vertices sowohl über die Hardwarebeschleunigung, als auch über die reine Software. Beachten Sie, dass Ihre Grafikkarte auch Hardwareprocessing unterstützt.

Anmerkungen:
Beachten Sie, dass D3DCREATE_SOFTWARE_VERTEXPROCESSING, D3DCREATE_HARDWARE_VERTEXPROCESSING und D3DCREATE_MIXED_VERTEXPROCESSING sich gegenseitig ausschliessen. Eines dieser Flags ist jedoch zwingend notwendig.


SelfDXD - D3DCULL

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Aufzählungen
D3DCULL
D3DCULL_NONE                = 1
D3DCULL_CW                  = 2
D3DCULL_CCW                 = 3
D3DCULL_FORCE_DWORD         = $7fffffff

D3DCULL_NONE
Kein Dreieck wird gecullt. Es werden alle angezeigt.

D3DCULL_CW
Alle Dreiecke, deren Vertices im Uhrzeigersinn angeordnet sind, werden gecullt.

D3DCULL_CCW
Alle Dreiecke, deren Vertices gegen den Uhrzeigersinn angeordnet sind, werden gecullt.

D3DCULL_FORCE_DWORD
Zwingt den Compiler, die Konstanten als 32-Bit-Werte zu behandeln.


SelfDXD - D3DDEBUGMONITORTOKENS

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Aufzählungen
D3DDEBUGMONITORTOKENS
D3DDMT_ENABLE          = 0 monitor
D3DDMT_DISABLE         = 1 monitor
D3DDMT_FORCE_DWORD     = $7fffffff

D3DDMT_ENABLE
Schaltet den Debug-Monitor ein.

D3DDMT_DISABLE
Schaltet den Debug-Monitor aus.

D3DDMT_FORCE_DWORD
Zwingt den Compiler, die Konstanten als 32-Bit-Werte zu behandeln.


SelfDXD - D3DDEVTYPE

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Aufzählungen
D3DEVTYPE
D3DDEVTYPE_HAL      = 1
D3DDEVTYPE_REF       = 2
D3DDEVTYPE_SW        = 3

D3DDEVTYPE_HAL
Die Grafikkarte übernimmt einen Grossteil der zu berechnenden grafischen Aufgaben. Damit schalten Sie also die sogenannte Hardwarebeschleunigung an.

D3DDEVTYPE_REF
Reines Softwarerendering. Allerdings empfiehlt Microsoft nicht, diesen Devicetype in einer Endanwendung zu benutzen. D3DDEVTYPE_REF ist lediglich für Programmierer gedacht, die ihre Anwendung sehen wollen, obwohl zur Zeit, aus welchen Gründen auch immer, grade keine Hardwarebeschleunigung zur Verfügung steht. Der Parameter hat zur Folge, dass wirklich alle 3D-Features, die sie einbinden (Licht, Nebel, Multitexturing usw.) vom Hauptprozessor berechnet werden, was zur Folge hat, dass die Anwendung extrem langsam läuft.

D3DDEVTYPE_SW
Damit aktivieren Sie den normalen Softwaremodus, in dem Sie allerdings nicht alle 3D-Features benutzen können.


SelfDXD - D3DFILLMODE

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Aufzählungen
D3DFILLMODE
D3DFILL_POINT               = 1
D3DFILL_WIREFRAME           = 2
D3DFILL_SOLID               = 3
D3DFILL_FORCE_DWORD         = $7fffffff

D3DFILL_POINT
Stellt alle Vertices, egal wie Sie gerendert werden (ob als PointList oder TriangeStrip) nur als Punkte dar.

D3DFILL_WIREFRAME
Alle 3D-Objekte werden als Drahtgittermodell dargestellt.

D3DFILL_SOLID
Stellt Dreiecke mit Inhalt dar. Dies ist der Standardwert.

D3DFILL_FORCE_DWORD
Zwingt den Compiler, die Konstanten als 32-Bit-Werte zu behandeln.


SelfDXD - D3DFOGMODE

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Aufzählungen
D3DFOGMODE
D3DFOG_NONE                 = 0
D3DFOG_EXP                  = 1
D3DFOG_EXP2                 = 2
D3DFOG_LINEAR               = 3
D3DFOG_FORCE_DWORD          = $7fffffff

D3DFOG_NONE
Kein Nebeleffekt.

D3DFOG_EXP
Die Intensität des Nebels steigt exponentiell nach folgender Formel:
f = 1 / ed x density

D3DFOG_EXP2
Die Intensität des Nebels steigt exponentiell mit quadratischer Distanz nach folgender Formel:
f = 1 / e(d x density)2

D3DFOG_LINEAR
Die Intensität des Nebels steigt linear zwischen Start und Endpunkt nach folgender Formel:
f = (end - d) / (end - start)

D3DFOG_FORCE_DWORD
Zwingt den Compiler, die Konstanten als 32-Bit-Werte zu behandeln.


D3DFORMAT

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Aufzählungen
D3DFORMAT
    D3DFMT_UNKNOWN       =   0
    D3DFMT_R8G8B8        =  20 (&H14)
    D3DFMT_A8R8G8B8      =  21 (&H15)
    D3DFMT_X8R8G8B8      =  22 (&H16)
    D3DFMT_R5G6B5        =  23 (&H17)
    D3DFMT_X1R5G5B5      =  24 (&H18)
    D3DFMT_A1R5G5B5      =  25 (&H19)
    D3DFMT_A4R4G4B4      =  26 (&H1A)
    D3DFMT_R3G3B2        =  27 (&H1B)
    D3DFMT_A8            =  28 (&H1C)
    D3DFMT_A8R3G3B2      =  29 (&H1D)
    D3DFMT_X4R4G4B4      =  30 (&H1E)
    D3DFMT_A8P8          =  40 (&H28)
    D3DFMT_P8            =  41 (&H29)
    D3DFMT_L8            =  50 (&H32)
    D3DFMT_A8L8          =  51 (&H33)
    D3DFMT_A4L4          =  52 (&H34)
    D3DFMT_V8U8          =  60 (&H3C)
    D3DFMT_L6V5U5        =  61 (&H3D)
    D3DFMT_X8L8V8U8      =  62 (&H3E)
    D3DFMT_Q8W8V8U8      =  63 (&H3F)
    D3DFMT_V16U16        =  64 (&H40)
    D3DFMT_W11V11U10     =  65 (&H41)
    D3DFMT_UYVY          = 1498831189 (&H59565955)
    D3DFMT_YUY2          =  844715353 (&H32595559)
    D3DFMT_DXT1          =  827611204 (&H31545844)
    D3DFMT_DXT2          =  844388420 (&H32545844)
    D3DFMT_DXT3          =  861165636 (&H33545844)
    D3DFMT_DXT4          =  877942852 (&H34545844)
    D3DFMT_DXT5          =  894720068 (&H35545844)
    D3DFMT_D16_LOCKABLE  =  70 (&H46)
    D3DFMT_D32           =  71 (&H47)
    D3DFMT_D15S1         =  73 (&H49)
    D3DFMT_D24S8         =  75 (&H4B)
    D3DFMT_D16           =  80 (&H50)
    D3DFMT_D24X8         =  77 (&H4D)
    D3DFMT_D24X4S4      =  79 (&H4F)
    D3DFMT_VERTEXDATA    = 100 (&H64)
    D3DFMT_INDEX16       = 101 (&H65)
    D3DFMT_INDEX32       = 102 (&H66)

D3DFMT_UNKNOWN
Surface Format ist unbekannt.

D3DFMT_R8G8B8
24-bit RGB Pixel-Format.

D3DFMT_A8R8G8B8
32-bit ARGB Pixel-Format mit Alpha.

D3DFMT_X8R8G8B8
32-bit RGB Pixel-Format; 8 Bits sind für jede Farbe reserviert.

D3DFMT_R5G6B5
16-bit RGB Pixel-Format.

D3DFMT_X1R5G5B5
16-bit Pixel-Format; 5 Bits sind für jede Farbe reserviert.

D3DFMT_A1R5G5B5
16-bit Pixel-Format; 5 Bits sind für jede Farbe reserviert und ein Bit für Alpha (Transparentes Texel).

D3DFMT_A4R4G4B4
16-bit ARGB Pixel-Format.

D3DFMT_R3G3B2
8-bit RGB Textur-Format.

D3DFMT_A8
8-bit nur Alpha.

D3DFMT_A8R3G3B2
16-bit ARGB Textur-Format.

D3DFMT_X4R4G4B4
16-bit RGB Pixel-Format; 4 Bits sind für jede Farbe reserviert.

D3DFMT_A8P8
16-Bit, 8-Bit für Farben und 8-Bits für einen Alpha-Wert.

D3DFMT_P8
8-Bit Farbwerte.

D3DFMT_L8
8-bit Beleuchtungswert.

D3DFMT_A8L8
16-bit Alpha Beleuchtung.

D3DFMT_A4L4
8-bit Alpha Beleuchtung.

D3DFMT_V8U8
16-bit Bump-Map Format.

D3DFMT_L6V5U5
16-bit Bump-Map Format mit Beleuchtung

D3DFMT_X8L8V8U8
32-bit Bump-Map Format mit Beleuchtung, wobei 8 Bits für jedes Element reserviert sind.

D3DFMT_Q8W8V8U8
32-bit Bump-Map Format.

D3DFMT_V16U16
32-bit Bump-Map Format.

D3DFMT_W11V11U10
32-bit Bump-Map Format.

D3DFMT_UYVY
UYVY Format (PC98 untergeordnet).

D3DFMT_YUY2
YUY2 Format (PC98 untergeordnet).

D3DFMT_DXT1
DXT1 Textur-Format.

D3DFMT_DXT2
DXT2 Textur-Format.

D3DFMT_DXT3
DXT3 Textur-Format.

D3DFMT_DXT4
DXT4 Textur-Format.

D3DFMT_DXT5
DXT5 Textur-Format.

D3DFMT_D16_LOCKABLE
16-bit Z-Buffer Bit-Tiefe. Ein Buffer dieser Art kann gelockt werden

D3DFMT_D32
32-bit z-buffer bit depth.

D3DFMT_D15S1
16-bit Z-Buffer Bit-Tiefe, wobei 15 Bits für jeden Tiefenkanal reserviert sind und ein Bit für den Stencil-Kanal.

D3DFMT_D24S8
32-bit Z-Buffer Bit-Tiefe, wobei 24 Bits für jeden Tiefenkanal reserviert werden und 8 Bits für den Stencil-Kanal.

D3DFMT_D16
16-bit Z-Buffer Bit-Tiefe

D3DFMT_D24X8
32-bit Z-Buffer Bit-Tiefe, wobei 24 Bits für jeden Tiefenkanal reserviert sind.

D3DFMT_D24X4S4
32-bit Z-Buffer Bit-Tiefe, wobei 24 Bits für jeden Tiefenkanal reserviert sind und 4 Bits für den Stencil-Kanal.

D3DFMT_VERTEXDATA
Beschreibt einen Vertexbuffer.

D3DFMT_INDEX16
16-bit Indexbuffer Bittiefe.

D3DFMT_INDEX32
32-bit Indexbuffer Bittiefe.


SelfDXD - D3DFVFFLAGS

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Aufzählungen
D3DFVFFLAGS
D3DFVF_XYZ              = $002;
D3DFVF_XYZRHW           = $004;
D3DFVF_XYZB1            = $006;
D3DFVF_XYZB2            = $008;
D3DFVF_XYZB3            = $00a;
D3DFVF_XYZB4            = $00c;
D3DFVF_XYZB5            = $00e;

D3DFVF_NORMAL           = $010;
D3DFVF_PSIZE            = $020;
D3DFVF_DIFFUSE          = $040;
D3DFVF_SPECULAR         = $080;
D3DFVF_TEX1             = $000;
D3DFVF_TEX2             = $100;
D3DFVF_TEX3             = $200;
D3DFVF_TEX4             = $300;
D3DFVF_TEX5             = $400;
D3DFVF_TEX6             = $500;
D3DFVF_TEX7             = $600;
D3DFVF_TEX8             = $700;
D3DFVF_POSITION_MASK    = $00E;
D3DFVF_RESERVED0        = $001;
D3DFVF_RESERVED2        = $E000;
D3DFVF_TEXCOUNT_MASK    = $f00;
D3DFVF_TEXCOUNT_SHIFT   = 8;

D3DFVF_XYZ
Beinhaltet die X, Y und Z Koordinaten eines Vertex (nicht transformiert). Dieses Flag kann nicht zusammen mit D3DFVF_XYZRHW und D3DFVF_XYZ1-5 genutzt werden.

D3DFVF_XYZRHW
Beinhaltet die X, Y und Z Koordinaten eines Vertex. Dabei entsprechen X und Y den Bildschirmkoordinaten. Dieses Flag kann nicht zusammen mit D3DFVF_XYZ und D3DFVF_XYZ1-5 genutzt werden. (transformiert)

D3DFVF_XYZB1 - D3DFVF_XYZB5
Anzahl der Matrizen, die ineinandergeblendet werden sollen. Benutzen Sie nur eine einzige Weltmatrix, brauchen Sie keine Konstanten angeben. Bei zwei Matrizen müssen Sie D3DFVF_XYZB1 angeben usw. Diese Flags schliessen D3DFVF_XYZ und D3DFVF_XYZRHW aus. Die Single-Werte geben die Gewichtungen der zu überblendenden Matrizen an.

D3DFVF_NORMAL
Ein Vektor der die Ausrichtung des Vertex angibt. Dieses Flag kann nicht zusammen mit D3DFVF_XYZRHW genutzt werden. (nicht transformiert)

D3DFVF_PSIZE
Gibt die Punktgrösse an. Bei nichttransformierten Vertices wird in Raum-Einheiten gerechnet, bei transformierten Vertices bezieht sich die Grösse auf die Bildschirmkoordinaten.

D3DFVF_DIFFUSE
Gibt die Eigenfarbe des Vertex an.

D3DFVF_PSIZE
Gibt die Glanzfarbe des Vertex an.

D3DFVF_TEX1 - D3DFVF_TEX8
Anzahl der Texturkoordinatensets.

D3DFVF_POSITION_MASK
Maske für Bitpositionen.

D3DFVF_RESERVED0 und D3DFVF_RESERVED2
Maske für reservierte Bits im Flexible Vertex Format. Sollte eher nicht genutzt werden.

D3DFVF_TEXCOUNT_MASK
Maske für Texturflagbits.

D3DFVF_TEXCOUNT_SHIFT
Die Anzahl der Bits bei der bei einer Textur in die nächste Pixelzeile gesprungen werden soll.


SelfDXD - D3DLIGHTTYPE

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Aufzählungen
D3DLIGHTTYPE

D3DLIGHT_POINT          = 1;
D3DLIGHT_SPOT           = 2;
D3DLIGHT_DIRECTIONAL    = 3;

D3DLIGHT_POINT
Das Licht hat eine Punktquelle. Der Ursprung ist als Position im Raum definierbar. Das Licht breitet sich in alle Richtungen aus.

D3DLIGHT_SPOT
Das Licht hat eine Punktquelle und breitet sich gebündelt in eine Richtung aus. Dabei besitzt das Spotlight einen inneren und einen äusseren Radius die unterschiedlich hell sein können.

D3DLIGHT_DIRECTIONAL
Vergleichbar mit Lichtstrahlen von einem sehr weit entfernten Point Light. Die Lichtstrahlen verlaufen jedoch überall parallel zu einander und die Lichtintensität lässt nicht nach.


SelfDXD - D3DLOCKFLAGS

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Aufzählungen
D3DLOCKFLAGS
D3DLOCK_READONLY         = $00000010;
D3DLOCK_DISCARD          = $00002000;
D3DLOCK_NOOVERWRITE      = $00001000;
D3DLOCK_NOSYSLOCK        = $00000800;
D3DLOCK_NO_DIRTY_UPDATE  = $00008000;

D3DLOCK_READONLY
Der Vertex- oder Indexbuffer wird nicht beschrieben. Der reine Lesezugriff wird damit ein wenig optimiert. Dieses Flag kann nicht zusammen mit D3DLOCK_DISCARD verwendet werden. Darüberhinaus ist das Flag nicht anwendbar, wenn der Vertexbuffer mit D3DUSAGE_WRITEONLY erstellt worden ist.

D3DLOCK_DISCARD
Damit wird ein reiner Schreibvorgang angekündigt. Die neuen Daten werden über den Zeiger in einen neuen Speicherbereich geschrieben. Das Flag ist nur anwendbar, wenn der Buffer oder die Textur dynamisch erstellt wurden.

D3DLOCK_NOOVERWRITE
Stellt sicher, dass bereits vorhandene Daten nicht überschrieben werden, sondern lediglich neue Daten angehängt werden. Für diese Art von Zugriff bewirkt das Flag eine Optimierung des Vorganges.

D3DLOCK_NOSYSLOCK
Dieses Flag ist sinnvoll, wenn zwischen Locken und Unlocken etwas mehr Zeit vergeht. Im Normalfall würde es zu kleinen Aussetzern (zum Beispiel in der Bewegung der Maus) kommen, während der Buffer gelockt ist, da die Grafikkarte den Zugang zum Displayspeicher vorrübergehend sperrt.

D3DLOCK_NO_DIRTY_UPDATE
Normalerweise wird beim Zugriff auf eine Ressourcendatei eine sogenannte Dirty-Region angelegt. Dieses Flag verhindert den Zugriff auf Dirty-Regions. Sie sollten das Flag benutzen, wenn Sie Informationen über die Speicherregionen verfügen, die verändert werden. Dieses Flag ist nicht für Vertex- und Indexbuffer verfügbar.


SelfDXD - D3DMATERIALCOLORSOURCE

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Aufzählungen
D3DMATERIALCOLORSOURCE
D3DMCS_MATERIAL         = 0;
D3DMCS_COLOR1           = 1;
D3DMCS_COLOR2           = 2;

D3DMCS_MATERIAL
Benutzt die Farbe des derzeitigen Materials.

D3DMCS_COLOR1
Benutzt die Diffuse-Farbe der Vertices.

D3DMCS_COLOR2
Benutzt die Specular-Farbe der Vertices.


SelfDXD - D3DMultisample_Type

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Aufzählungen
MULTISAMPLE_TYPE
D3DMULTISAMPLE_NONE       =  0
D3DMULTISAMPLE_2_SAMPLES  =  2
D3DMULTISAMPLE_3_SAMPLES  =  3
D3DMULTISAMPLE_4_SAMPLES  =  4
D3DMULTISAMPLE_5_SAMPLES  =  5
D3DMULTISAMPLE_6_SAMPLES  =  6
D3DMULTISAMPLE_7_SAMPLES  =  7
D3DMULTISAMPLE_8_SAMPLES  =  8
D3DMULTISAMPLE_9_SAMPLES  =  9
D3DMULTISAMPLE_10_SAMPLES = 10
D3DMULTISAMPLE_11_SAMPLES = 11
D3DMULTISAMPLE_12_SAMPLES = 12
D3DMULTISAMPLE_13_SAMPLES = 13
D3DMULTISAMPLE_14_SAMPLES = 14
D3DMULTISAMPLE_15_SAMPLES = 15
D3DMULTISAMPLE_16_SAMPLES = 16

D3DMULTISAMPLE_NONE
Kein Multsamplinglevel.

D3DMULTISAMPLE_2_SAMPLES bis D3DMULTISAMPLE_16_SAMPLES
Das jeweilige Multsamplinglevel.


SelfDXD - D3DPATCHEDGESTYLE

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Aufzählungen
D3DPATCHEDGESTYLE
D3DPATCHEDGE_DISCRETE    = 0
D3DPATCHEDGE_CONTINUOUS  = 1
D3DPATCHEDGE_FORCE_DWORD = $7fffffff

D3DPATCHEDGE_DISCRETE
Im Discrete-Modus werden alle Float-Werte zu Integers gerundet.

D3DPATCHEDGE_CONTINUOUS
Floatwerte werden als solche verwendet. Sie ermöglichen weiche Übergänge und verhindern damit hervortretende Kanten.

D3DPATCHEDGE_FORCE_DWORD
Zwingt den Compiler, die Konstanten als 32-Bit-Werte zu behandeln.


SelfDXD - D3DPOOL

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Aufzählungen
D3DPOOL

D3DPOOL_DEFAULT                 = 0;
D3DPOOL_MANAGED                 = 1;
D3DPOOL_SYSTEMMEM               = 2;

D3DPOOL_DEFAULT
Die Daten werden im Video- bzw. AGP-Speicher belegt und zwar so, dass das Device darauf am besten Zugreifen kann. Texturen können nicht gelockt werden, wenn sie mit D3DPOOL_DEFAULT erstellt werden.

D3DPOOL_MANAGED
Managed-Daten werden im Systemspeicher abgelegt und beim Zugriff vom Device in den Device-Speicher kopiert. Texturen, die mit D3DPOOL_MANAGED erstellt wurden, können gelockt werden.

D3DPOOL_SYSTEMMEM
Die Daten werden im Systemspeicher abgelegt. Der Zugriff des Device auf diese Daten ist nicht optimiert. Hier können also zum Beispiel Texturen abgespeichert werden, die zu reinen Bearbeitungs- und Kopierzwecken verwendet werden.


SelfDXD - D3DPRESENT_INTERVAL_FLAGS

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Aufzählungen
D3DPRESENT_INTERVAL_FLAGS
D3DPRESENT_INTERVAL_DEFAULT    =            0
D3DPRESENT_INTERVAL_ONE        =            1
D3DPRESENT_INTERVAL_TWO        =            2
D3DPRESENT_INTERVAL_THREE      =            4
D3DPRESENT_INTERVAL_FOUR       =            8
D3DPRESENT_INTERVAL_IMMEDIATE  =  -2147483648 (&H80000000)

Gibt an, in welchen Abständen abhängig von der vertikalen Bildwiederholfrequenz das Bild neu aufgebaut werden soll.

D3DPRESENT_INTERVAL_DEFAULT
Die vom Grafikkartentreiber unterstützte Standardrefreshrate.

D3DPRESENT_INTERVAL_ONE
Die Refreshrate wird mit der Bildwiederholrate abgeglichen

D3DPRESENT_INTERVAL_TWO
Bei jedem zweiten Bildneuaufbau wird das Bild aktuallisiert

D3DPRESENT_INTERVAL_THREE
Bei jedem dritten Bildneuaufbau wird das Bild aktuallisiert

D3DPRESENT_INTERVAL_FOUR
Bei jedem vierten Bildneuaufbau wird das Bild aktuallisiert

D3DPRESENT_INTERVAL_IMMEDIATE
Unabhängig von der Bildwiederholrate wird das Bild so oft wie möglich aktuallisiert. Diese Einstellung ermöglicht eine maximale Framezahl.


SelfDXD - D3DPRESENT_RATE_FLAG

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Aufzählungen
D3DPRESENT_RATE_FLAGS
D3DPRESENT_RATE_DEFAULT   =          0
D3DPRESENT_RATE_UNLIMITED = 2147483647 (&H7FFFFFFF)

D3DPRESENT_RATE_DEFAULT
Direct3D wählt automatisch eine Refreshrate oder übernimmt im Fenstermodus die aktuelle.

D3DPRESENT_RATE_UNLIMITED
Die Refreshrate ist so hoch, wie es die Grafikkarte ermöglicht.


SelfDXD - D3DPRIMITIVETYPE

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Aufzählungen
D3DPRIMITIVETYPE
D3DPT_POINTLIST             = 1;
D3DPT_LINELIST              = 2;
D3DPT_LINESTRIP             = 3;
D3DPT_TRIANGLELIST          = 4;
D3DPT_TRIANGLESTRIP         = 5;
D3DPT_TRIANGLEFAN           = 6;

D3DPT_POINTLIST
Rendert die Vertices als einzelne Punkte

D3DPT_LINELIST
Rendert immer jeweils zwei Vertices als Linie. Die Renderanweisung schlägt fehl, wenn weniger als zwei Vertices vorhanden sind.

D3DPT_LINESTRIP
Verbindet einen Vertex nach dem anderen durch eine Linie. Die Renderanweisung schlägt fehl, wenn weniger als zwei Vertices vorhanden sind.

D3DPT_TRIANGLELIST
Rendert jeweils immer drei Vertices als einzelnes Dreieck.

D3DPT_TRIANGLESTRIP
Rendert die ersten drei Vertices zu einem Dreieck und fügt mit jedem folgenden Vertex ein Dreieck hinzu.

D3DPT_TRIANGLEFAN
In jedem Dreieck ist der erste Vertex ein Eckpunkt. So bilden der erste, zweite und dritte Vertex ein Dreieck, dann der erste, dritte und vierte usw.


SelfDXD - D3DRENDERSTATETYPE

SelfDXD von Martin Pyka und Jan Michalowsky
SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Aufzählungen
D3DRENDERSTATETYPE
D3DRS_ZENABLE                   =   7
D3DRS_FILLMODE                  =   8
D3DRS_SHADEMODE                 =   9
D3DRS_LINEPATTERN               =  10
D3DRS_ZWRITEENABLE              =  14
D3DRS_ALPHATESTENABLE           =  15
D3DRS_LASTPIXEL                 =  16
D3DRS_SRCBLEND                  =  19
D3DRS_DESTBLEND                 =  20
D3DRS_CULLMODE                  =  22
D3DRS_ZFUNC                     =  23
D3DRS_ALPHAREF                  =  24
D3DRS_ALPHAFUNC                 =  25
D3DRS_DITHERENABLE              =  26
D3DRS_ALPHABLENDENABLE          =  27
D3DRS_FOGENABLE                 =  28
D3DRS_SPECULARENABLE            =  29
D3DRS_ZVISIBLE                  =  30
D3DRS_FOGCOLOR                  =  34
D3DRS_FOGTABLEMODE              =  35
D3DRS_FOGSTART                  =  36
D3DRS_FOGEND                    =  37
D3DRS_FOGDENSITY                =  38
D3DRS_EDGEANTIALIAS             =  40
D3DRS_ZBIAS                     =  47
D3DRS_RANGEFOGENABLE            =  48
D3DRS_STENCILENABLE             =  52
D3DRS_STENCILFAIL               =  53
D3DRS_STENCILZFAIL              =  54
D3DRS_STENCILPASS               =  55
D3DRS_STENCILFUNC               =  56
D3DRS_STENCILREF                =  57
D3DRS_STENCILMASK               =  58
D3DRS_STENCILWRITEMASK          =  59
D3DRS_TEXTUREFACTOR             =  60
D3DRS_WRAP0                     = 128
D3DRS_WRAP1                     = 129
D3DRS_WRAP2                     = 130
D3DRS_WRAP3                     = 131
D3DRS_WRAP4                     = 132
D3DRS_WRAP5                     = 133
D3DRS_WRAP6                     = 134
D3DRS_WRAP7                     = 135
D3DRS_CLIPPING                  = 136
D3DRS_LIGHTING                  = 137
D3DRS_AMBIENT                   = 139
D3DRS_FOGVERTEXMODE             = 140
D3DRS_COLORVERTEX               = 141
D3DRS_LOCALVIEWER               = 142
D3DRS_NORMALIZENORMALS          = 143
D3DRS_DIFFUSEMATERIALSOURCE     = 145
D3DRS_SPECULARMATERIALSOURCE    = 146
D3DRS_AMBIENTMATERIALSOURCE     = 147
D3DRS_EMISSIVEMATERIALSOURCE    = 148
D3DRS_VERTEXBLEND               = 151
D3DRS_CLIPPLANEENABLE           = 152
D3DRS_SOFTWAREVERTEXPROCESSING  = 153
D3DRS_POINTSIZE                 = 154
D3DRS_POINTSIZE_MIN             = 155
D3DRS_POINTSPRITEENABLE         = 156
D3DRS_POINTSCALEENABLE          = 157
D3DRS_POINTSCALE_A              = 158
D3DRS_POINTSCALE_B              = 159
D3DRS_POINTSCALE_C              = 160
D3DRS_MULTISAMPLEANTIALIAS      = 161
D3DRS_MULTISAMPLEMASK           = 162
D3DRS_PATCHEDGESTYLE            = 163
D3DRS_PATCHSEGMENTS             = 164
D3DRS_DEBUGMONITORTOKEN         = 165
D3DRS_POINTSIZE_MAX             = 166
D3DRS_INDEXEDVERTEXBLENDENABLE  = 167
D3DRS_COLORWRITEENABLE          = 168
D3DRS_TWEENFACTOR               = 170
D3DRS_BLENDOP                   = 171
D3DRS_POSITIONORDER             = 172,
D3DRS_NORMALORDER               = 173,
D3DRS_FORCE_DWORD               = 0x7fffffff

D3DRS_ZENABLE
D3DRS_ZENABLE beschreibt den Zustand des Z (Tiefen-) Buffers. Dieser Typ gehört zum [image] D3DZBUFFERTYPE Aufzählungstyp. Wenn das Flag den Zustand D3DZB_TRUE hat so wird der Z Buffer verwendet. Sollte D3DZB_USEW gesetzt sein so wird der W Buffer aktiviert. Um Z-buffering zu vermeiden muss D3DZB_FALSE gesetzt werden. Der vorgegebene Wert für D3DRS_ZENABLE ist D3DZB_TRUE wenn ein Tiefenbuffer im Zusammenhang mit EnableAutoDepthStencil erzeugt wurde. Andernfalls ist der Default Wert D3DZB_FALSE.

D3DRS_FILLMODE
Die Flags gehören zum [image] D3DFILLMODE Aufzählungstyp. Der vorgegebene Wert ist D3DFILL_SOLID

D3DRS_SHADEMODE
Die Flags gehören zum [image] D3DSHADEMODE Aufzählungstyp. Der vorgegebene Wert ist D3DSHADE_GOURAUD

D3DRS_LINEPATTERN
Die Flags gehören zum [image] D3DLINEPATTERN Record. Der vorgegebene Werte ist 0 für wRepeatPattern,und 0 für wLinePattern

D3DRS_ZWRITEENABLE
Diese Flag gibt an ob die Anwendung in den Tiefenbuffer schreiben soll. Der Standardwert ist TRUE. Dieses Flag erlaubt somit der Anwendung das System daran zu hindern den Tiefenbuffer zu erneuern und mit neuen Werten zu füllen. Wenn dieses Flag FALSE gesetzt ist wird der Tiefenbuffer gemäß D3DRS_ZFUNC behandelt.

D3DRS_ALPHATESTENABLE
TRUE aktiviert den Alphatest. Der Standardwert ist FALSE. Der Alphatest akzeptiert oder verwirft Pixel in Abhängigkeit ihres Alphawertes. Die ankommenden Alphawerte werden mit dem Referenzwert verglichen. Zum Vergleichen wird das [image] D3DRS_ALPHAFUNC Flag benutzt. Wenn diese Flag TRUE gesetzt wird, wird Alphablending nur ausgeführt wenn der Vergleich erfolgreich war.

D3DRS_LASTPIXEL
FALSE um das Zeichnen des letzten Pixels eines Dreiecks oder einer Line zu ermöglichen. Der Standardwert ist TRUE.

D3DRS_SRCBLEND
Die Flags gehören zum [image] D3DBLEND Aufzählungstyp. Der Standardwert ist D3DBLEND_ONE.

D3DRS_DESTBLEND
Die Flags gehören zum D3DBLEND Aufzählungstyp.Der Standardwert ist D3DBLEND_ZERO.

D3DRS_CULLMODE
Dieses Flag gibt an, welche Seite der Dreiecke nicht gerendert werden soll. Der Wert gehört zum [image] D3DCULL Aufzählungstyp. Der Standardwert ist D3DCULL_CCW.

D3DRS_ZFUNC
Die Flags gehören zum [image] D3DCMPFUNC Aufzählungstyp. Der Standardwert ist D3DLESSEQUAL. Diese Flags ermöglichen einer Anwendung das Aktzeptieren oder Verwerfen von Pixeln in Abhänigkeit des Abstandes zur Kamera. Der Tiefenwert des Pixels wird mit dem Tiefenwert des Tiefenbuffers verglichen. Wenn die Tiefe des Pixels geringer ist als der Tiefenbuffer-Wert dann wird das Pixel gezeichnet. Software-Rasterizer und viele Hardwarebeschleuniger arbeiten schneller, wenn der Tiefentest fehlschlägt, da es keiner Filter und keine Abstimmung der Textur bedarf. Wenn das Pixel nicht gezeichnet wird, dann wird die Textur nicht gerendert.

D3DRS_ALPHAREF
Der Wert gibt den Alphawert an, welcher zum Vergleich benötigt wird, wenn [image] D3DRS_ALPHATESTENABLE aktiviert ist. Der Wert beinhaltet einen 8-Bit Wert der sich in einem Low Wert eines Word befindet. Der Wert kann einen Bereich von $00000000 bis $000000FF haben.

D3DRS_ALPHAFUNC
Die Flags gehören zum D3DCMPFUNC Aufzählungstyp. Der Standardwert ist D3DCMP_ALWAYS. Diese Flag ermöglicht einer Anwendung das Akzeptieren oder Verwerfen von Pixeln in Abhängigkeit ihres Alphawertes.

D3DRS_DITHERENABLE
TRUE aktiviert Dithering. Standardwert ist FALSE.

D3DRS_ALPHABLENDENABLE
TRUE aktiviert die Alpha-Blended Transparenz. Standardwert ist FALSE.

D3DRS_FOGENABLE
TRUE aktiviert Fog Blending. Standardwert ist FALSE.

D3DRS_SPECULARENABLE
TRUE aktiviert Specular Highlights. Standardwert ist FALSE.

D3DRS_ZVISIBLE
Wird zur Zeit noch nicht unterstützt.

D3DRS_FOGCOLOR
Der Wert hierfür muss vom Typ TD3DCOLOR (LONGWORD) sein. Der Standardwert ist 0.

D3DRS_FOGTABLEMODE
Der Wert gibt die Art des Pixelnebel an. Dazu muss der zweite Parameter vom [image] D3DFOGMODE Aufzählungstyp sein.
Der Standardwert ist D3DFOG_NONE.

D3DRS_FOGSTART
Die Tiefe, ab der der Nebeleffekt beginnen soll. Standard ist 0.0. Für Vertexnebel werden Welteinheiten übergeben, für Pixelnebel Anteilwerte (also zwischen 0.0 und 1.0) übergeben.

D3DRS_FOGEND
Die Tiefe, ab der der Nebeleffekt enden soll. Standard ist 1.0. Für Vertexnebel werden Welteinheiten übergeben, für Pixelnebel Anteilwerte (also zwischen 0.0 und 1.0) übergeben.

D3DRS_FOGDENSITY
Nebelintensität für Pixel- und Vertexnebel, der für die exponentialen Nebelmodi (D3DFOG_EXP und D3DFOG_EXP2) genutzt wird. Es sind nur Werte zwischen 0.0 und 1.0 gültig.Standardmässig ist 1.0 eingetragen.

D3DRS_EDGEANTIALIAS
TRUE, um die Linien, die das Objekt umgeben zu antialiasen. Der Standardwert ist FALSE. Wenn TRUE aktiviert ist, sollte die Anwendung nur die äussersten Linien eines Polygons rendern. Antialiasing wird durchgeführt, in dem Durchschnittswerte der benachbarten Pixel errechnet werden. Obwohl das nicht die beste Methode ist, kann Sie trotzdem sehr effizient sein. Es gibt immer mehr Hardware, die diese Technik unterstützen.

D3DRS_ZBIAS
Integerwert zwischen 0 und 16. Dieser Wert wird benutzt, um Polygone, die sich räumlich auf der gleichen Position befinden trotzdem zu unterscheiden. Wenn Sie zum Beispiel eine Mauer rendern und danach einen Schatten, kann es passieren, dass der Schatten nicht richtig dargestellt wird. Wenn Sie aber der Mauer und dem Schatten einen ZBIAS-Wert zu teilen, können Sie entscheiden, was in den Vordergrund, und was in den Hintergrund gerückt werden soll. Je höher die Zahl desto mehr im Vordergrund befindet sich das Objekt.

D3DRS_RANGEFOGENABLE
TRUE, um reichweitenbasierten Vertexnebel zu aktivieren. Der Standardwert ist FALSE, dann wird tiefenbasierter Nebel verwendet. Beim tiefenbasierten Nebel wird aus der Distanz vom Objekt zum Betrachter der Nebel errechnet, und nicht aus der Tiefe (also der Z-Koordinate). Die Reichweite (Range) ist eigentlich der korrekte Faktor um den Nebelfaktor zu bestimmen. Allerdings ist diese Technik sehr rechenintensiv. Daher benutzen die meisten die Tiefenangabe, da diese sofort zur Verfügung steht, und nicht erst berechnet werden muss. Tiefennebel hat allerdings den unerwünschten Effekt, dass sich beim Drehen der Kamera die Tiefe des Objektes verändert und diese deshalb aus dem Nebel hervorzutreten scheinen.

Da zur Zeit noch keine Hardware Per-Pixel tiefenbasierten Nebel unterstützt, wird die Reichweite zur Zeit nur für Vertexnebel angeboten.

D3DRS_STENCILENABLE
TRUE, um stenciling zu aktivieren oder FALSE um es zu deaktivieren. Der Standardwert ist FALSE.

D3DRS_STENCILFAIL
Schablonenoperation, die ausgeführt wird, wenn der Stenciltest fehlschlägt. Der zweite Parameter ist eine Konstante aus der [image] D3DSTENCILOP-Aufzählung. Der Standardwert ist D3DSTENCILOP_KEEP.

D3DRS_STENCILZFAIL
Schablonenoperation, die ausgeführt wird, wenn der Stenciltest und der Tiefentest (z-test) fehlschlägt. Der zweite Parameter kann eine Konstante aus der D3DSTENCILOP Aufzählung sein. Der Standardwert ist D3DSTENCILOP_KEEP.

D3DRS_STENCILPASS
Schablonenoperation, die ausgeführt wird, wenn sowohl der Stencil, als auch der Tiefentest erfolgreich sind. Der zweite Parameter ist eine Konstante aus der D3DSTENCILOP Aufzählung. Der Standardwert ist D3DSTENCILOP_KEEP.

D3DRS_STENCILFUNC
Vergleichsfunktion für den Stenciltest. Dies kann eine Konstante aus der D3DCMPFUNC Aufzählung sein. Der Standardwert ist D3DCMP_ALWAYS.
Die Vergleichsfunktion wird dazu verwendet den referenzierten Wert mit den Stencilbufferwerten zu vergleichen. Diese Vergleiche beziehen sich nur auf die Werte, die in der Schablonenmaske definiert sind (die Schablone können Sie im Renderstate [image] D3DRS_STENCILMASK setzen).

D3DRS_STENCILREF
Integerwert für den Referenzwert des Stenciltest. Der Standardwert ist 0.

D3DRS_STENCILMASK
Eine Maske die sich auf den Referenzwert und jeden Stencilbuffereintrag bezieht, um die entscheidenden Bits beim Stenciltest selektieren zu können. Der Standardwert ist 0xFFFFFFFF.

D3DRS_STENCILWRITEMASK
Schreibmaske, die sich auf die Werte bezieht, die in den Stencilbuffer geschrieben werden. Der Standardwert ist 0xFFFFFFFF.

D3DRS_TEXTUREFACTOR
Eine Farbe, die für das mehrfach Texturblending mit dem D3DTA_TFACTOR oder dem D3DTOP_BLENDFACTORALPHA Argument eingesetzt wird. Der Standardwert ist 0xFFFFFFFF.

D3DRS_WRAP0 through D3DRS_WRAP7
Textur-wrapping Verhalten für mehrere Texturkoordinantesets. Mögliche Werte für diesen Renderstate sind Kombinationen aus den Flags: D3DWRAPCOORD_0 (oder D3DWRAP_U), D3DWRAPCOORD_1 (oder D3DWRAP_V), D3DWRAPCOORD_2 (oder D3DWRAP_W), und D3DWRAPCOORD_3. Diese Flags bewirken, dass die Textur in die erste, zweite dritte und vierte Dimension gewrappt werden. Der Standardwert ist 0 (Wrapping ist für alle Richtungen ausgeschaltet).

D3DRS_CLIPPING
TRUE, um Clipping von DirectXGraphics einzuschalten, FALSE um es zu deaktivieren. Der Standardwert ist TRUE.

D3DRS_LIGHTING
TRUE, um DirectXGraphics Licht einzuschalten, FALSE um es auszuschalten. Der Standardwert ist TRUE. Nur Vertices, die eine Normale besitzen, werden korrekt beleuchtet. Vertices, die keine Normale haben, verwenden den Lichtwert 0 in allen Lichtberechnungen.

D3DRS_AMBIENT
Setzt das ambiente Licht. Der zweite Parameter ist ein Wert vom Typ D3DCOLOR, was LongWord entspricht.

D3DRS_FOGVERTEXMODE
Nebelformel, die für Vertexnebel verwendet werden soll. Verwenden Sie hierzu eine Konstante aus den [image] D3DFOGMODE Aufzählungen. Der Standardwert ist D3DFOG_NONE.

D3DRS_COLORVERTEX
TRUE, um die Vertexfarben zu aktivieren, oder FALSE um Sie zu deaktivieren. Der Standardwert ist TRUE. Sind die Vertexfarben aktiviert, werden diese in die Beleuchtungsoperation mit eingebunden.
Mehr Informationen dazu gibt es bei den folgenden Renderstates:

D3DRS_DIFFUSEMATERIALSOURCE
D3DRS_SPECULARMATERIALSOURCE
D3DRS_AMBIENTMATERIALSOURCE
D3DRS_EMISSIVEMATERIALSOURCE

D3DRS_LOCALVIEWER
TRUE, um Kamerabezogene Glanzlichter (Specular Highlights) zu aktivieren, oder FALSE, um orthogonale Glanzlichter zu benuzten.

D3DRS_NORMALIZENORMALS
TRUE, um alle Vertices automatisch zu normalisieren, oder FALSE, um diese Funktion auszuschalten. Der Standardwert ist FALSE. Wenn diese Funktion aktiviert ist, werden alle Normalen nach dem Transformieren in den Kameraraum normalisiert, was sehr rechenintensiv sein kann.

D3DRS_DIFFUSEMATERIALSOURCE
Diffusefarbquelle für Lichtberechnungen. Gültige Werte sind Konstanten aus der [image] D3DMATERIALCOLORSOURCE Aufzählung. Der Standardwert ist D3DMCS_COLOR1. Der Wert dieses Renderstates wird nur genutzt, wenn [image] D3DRS_COLORVERTEX auf TRUE gestellt ist.

D3DRS_SPECULARMATERIALSOURCE
Glanzlichtfarbquelle für Lichtberechnungen. Gültige Werte sind Konstanten aus der D3DMATERIALCOLORSOURCE-Aufzählung. Der Standardwert ist D3DMCS_COLOR2.

D3DRS_AMBIENTMATERIALSOURCE
Ambientfarbquelle für Lichtberechnungen. Gültige Werte sind Konstanten aus der D3DMATERIALCOLORSOURCE-Aufzählung. Der Standardwert ist D3DMCS_COLOR2.

D3DRS_EMISSIVEMATERIALSOURCE
Emissivfarbquelle für Lichtberechnungen. Gültige Werte sind Konstanten aus der D3DMATERIALCOLORSOURCE-Aufzählung. Der Standardwert ist D3DMCS_MATERIAL.

D3DRS_VERTEXBLEND
Anzahl der Matirzen, die zum geometrischen Blending genutzt werden sollen. Verwenden Sie als zweiten Parameter eine Konstante aus den [image] D3DVERTEXBLENDFLAGS. Der Standardwert ist D3DVBF_DISABLE.

D3DRS_CLIPPLANEENABLE
Aktiviert oder deaktiviert benutzerdefinierte Clippingebenen. Erlaubt sind alle Werte vom Typ LongWord, bei dem jedes Bit dieses Wertes sich auf eine vom Benutzer definierte Ebene bezieht, die damit aktiviert oder deaktiviert wird. So bezieht sich das erste Bit (Bit 0) auf die Clipping-Ebene mit dem Index 0. Das nächste Bit bezieht sich auf die Clipping Ebene 1 usw.
Die [image] D3DCLIPPLANEn Makros helfen dabei die Clippingebenen auf eine etwas bequemere Art und Weise zu definieren.

D3DRS_SOFTWAREVERTEXPROCESSING
TRUE oder FALSE, um zwischen Hard- oder Software-Vertexprocessing zu wählen. Für ein D3DDEVTYPE_SW Device ist dieser Wert unveränderbar TRUE. Bei D3DDEVTYPE_REF kann dieser Wert beliebig verändert werden. Wird ein Device mit D3DDEVTYPE_HAL genutzt, muss die Hardware Hardware-Vertexprocessing auch unterstützen, damit Sie dieses Feature nutzen können. Ansonsten ist dieser Wert auf TRUE festgestellt. Ist der Wert veränderbar, ist er zunächst auf FALSE. Verändern Sie diesen Wert zur Laufzeit, werden alle derzeitigen Streams, Indizes und Vertexshader auf ihren Standardwert nil oder 0 gestellt.

D3DRS_POINTSIZE
Ein Wert vom Typ Single, der die Grösse der Vertices angibt, wenn eine Punktgrösse im Vertexformat nicht definiert ist. Dieser Wert gilt in Bildschirmeinheiten, wenn [image] D3DRS_POINTSCALEENABLE auf FALSE gesetzt ist. Ansonsten gilt der Wert in Welteinheiten. Der Standardwert ist 1.0. Es sind alle Werte grösser 0 erlaubt.

D3DRS_POINTSIZE_MIN
Ein Single-Wert der die minimale Grösse einer Punktprimitive angibt. Punktprimitiven werden auf diesen Wert verkleinert. Ist dieser Wert kleiner als 1.0 werden Punkte nicht gerendert, wenn sie sich nicht genau in der Pixelmitte befinden und Antialiasing ausgeschaltet ist, oder nur mit geringer Intensität gerendert, wenn Antialiasing aktiviert ist. Der Standardwert ist 0.0. Sie können alle Werte angeben, die grösser oder gleich 0.0 sind.

D3DRS_POINTSPRITEENABLE
Ein boolischer Wert. Wenn Sie TRUE angeben, erhält der Punkt Texturkoordinaten, so dass die gesamte Textur in jedem Punkt dargestellt wird. Bei FALSE wird nur die jeweilige Farbe des Punktes auf die gesamte Fläche übertragen.

D3DRS_POINTSCALEENABLE
Boolische Variabele, die sich auf die Grössenberechnung der Punktprimitiven auswirkt. Geben Sie TRUE an, wird die Grösse der Punkte von der Entfernung der Kamera zum Punkt abhängig gemacht. Geben Sie FALSE an, wird die definierte Punktgrösse als Bildschirmpixelgrösse interpretiert. Der Standardwert ist FALSE.

D3DRS_POINTSCALE_A
Singlewert mit dem Sie die Grösse der Punktprimitive abhängig von der Distanz beeinflussen können. Der Standardwert ist 1.0. Erlaubt sind alle Werte die grösser oder gleich 0.0 sind.

D3DRS_POINTSCALE_B
Singlewert mit dem Sie die Grösse der Punktprimitive abhängig von der Distanz beeinflussen können. Der Standardwert ist 0.0. Erlaubt sind alle Werte die grösser oder gleich 0.0 sind.

D3DRS_POINTSCALE_C
Singlewert mit dem Sie die Grösse der Punktprimitive abhängig von der Distanz beeinflussen können. Der Standardwert ist 0.0. Erlaubt sind alle Werte die grösser oder gleich 0.0 sind.

D3DRS_MULTISAMPLEANTIALIAS
Boolischer Wert, mit dem Sie entscheiden, wie einzelne Samples in einem Multisample-Rendertarget-Buffer berechnet werden sollen. Setzen Sie den zweiten Parameter auf TRUE, so werden mehrere Samples berechnet, so wird Fullscene-Antialiasing erreicht, in dem an verschiedenen Samplepositionen gerendert wird. Geben Sie FALSE an, werden mehrere Samples mit dem selben Samplewert in die Pixelmitte geschrieben. Dies erlaubt Nicht-Antialising im Multisample-Buffer. Dieser Renderstate hat keine Auswirkungen auf einen Singlesample-Buffer. Der Standardwert ist TRUE.

D3DRS_MULTISAMPLEMASK
Jedes Bit in dieser Maske, beginnend an LSB, kontrolliert die Veränderung eines Samples in einem Multisample-Rendertarget. Der Standardwert ist 0xffffffff.

D3DRS_PATCHEDGESTYLE
Hiermit bestimmen Sie, ob Patch-Kanten mit Float-Variablen gerundet werden sollen oder mit Integerwerten. Verwenden Sie hierzu eine Konstante aus der [image] D3DPATCHEDGESTYLE Aufzählung. Der Standardwert ist D3DPATCHEDGE_DISCRETE.

D3DRS_PATCHSEGMENTS
Setzt die Anzahl der Segmente in Form eines Float-Wertes für jede Kante. Der Standardwert ist 1.0.

D3DRS_DEBUGMONITORTOKEN
Schaltet den Debugmonitor ein oder aus. Verwenden Sie hierfür eine der Konstanten aus der [image] DEBUGMONITORTOKENS-Aufzählung. Der Standardwert ist D3DDMT_ENABLE.

D3DRS_POINTSIZE_MAX
Ein Single-Wert der die maximale Grösse einer Punktprimitive angibt. Punktprimitiven werden auf diesen Wert vergrössert. Der Wert muss grösser oder gleich sein als [image] D3DRS_POINTSIZE_MIN. Der Standardwert ist 64.0.

D3DRS_INDEXEDVERTEXBLENDENABLE
Boolischer Wert, mit dem Sie Indexvertexblending aktivieren oder deaktivieren können. Der Standardwert ist FALSE.

D3DRS_COLORWRITEENABLE
Bei diesem Wert interessieren nur die ersten 4 Bits, die darüber entscheiden, ob der jeweilige Farbkanal zum rendern verwendet werden soll oder nicht. Der Clear-Befehl bleibt davon unberrührt. Der Standardwert ist 0x0000000f.
Mögliche Werte für diesen Renderstate sind D3DCOLORWRITEENABLE_ALPHA, D3DCOLORWRITEENABLE_BLUE, D3DCOLORWRITEENABLE_GREEN, und D3DCOLORWRITEENABLE_RED. Sie können beliebig miteinander kombiniert werden.

D3DRS_TWEENFACTOR
Singlewert mit dem Sie den Tweenfaktor bestimmen. Der Standardwert ist 0.0.

D3DRS_BLENDOP
Ein Wert mit dem Sie eine Rechenoperation bestimmen, die ausgeführt werden soll, wenn [image] D3DRS_ALPHABLENDENABLE auf TRUE gesetzt wurde. Für den Wert können Sie eine Konstante aus der [image] D3DBLENDOP Aufzählung verwenden. Der Standardwert ist D3DBLENDOP_ADD.

D3DRS_POSITIONORDER
Ein Wert mit dem Sie die Interpolationsreihenfolge der Position für N-Patches bestimmen. Mögliche Werte sind D3DORDER_LINEAR oder D3DORDER_CUBIC. Der Standardwert ist D3DORDER_CUBIC.

D3DRS_NORMALORDER
Ein Wert mit dem Sie die Interpolationsreihenfolge der Normale für N-Patches bestimmen. Mögliche Werte sind D3DORDER_LINEAR oder D3DORDER_QUADRATIC. Der Standardwert ist D3DORDER_LINEAR.

D3DRS_FORCE_DWORD
Zwingt den Compiler dazu, die Konstanten als 32-Bit-Werte zu behandeln..

Anmerkungen
Bei einigen Renderstates sind als zweiten Parameter auch Single-Werte erlaubt. Da die Funktionsdefinition allerdings nur LongWort-Integerwerte zulässt, müssen Sie sich eines kleinen Tricks behelfen:

  d3ddev8.SetRenderState( D3DRS_POINTSIZE, PDWord(@wert)^ );


SelfDXD - D3DRESOURCETYPE

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Aufzählungen
D3DRESOURCETYPE
D3DRTYPE_SURFACE                = 1;
D3DRTYPE_VOLUME                 = 2;
D3DRTYPE_TEXTURE                = 3;
D3DRTYPE_VOLUMETEXTURE          = 4;
D3DRTYPE_CUBETEXTURE            = 5;
D3DRTYPE_VERTEXBUFFER           = 6;
D3DRTYPE_INDEXBUFFER            = 7;

D3DRTYPE_SURFACE
Surface Resource.

D3DRTYPE_VOLUME
Volume Resource.

D3DRTYPE_TEXTURE
Texture Resource.

D3DRTYPE_VOLUMETEXTURE
Volumentextur Resource.

D3DRTYPE_CUBETEXTURE
Würfeltextur resource.

D3DRTYPE_VERTEXBUFFER
Vertexbuffer Resource.

D3DRTYPE_INDEXBUFFER
Indexbuffer Resource.


SelfDXD - D3DSHADEMODE

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Aufzählungen
D3DSHADEMODE
D3DSHADE_FLAT               = 1
D3DSHADE_GOURAUD            = 2
D3DSHADE_PHONG              = 3
D3DSHADE_FORCE_DWORD        = $7fffffff

D3DSHADE_FLAT
Flat Shading Mode. Die Farbe des ersten Vertex eines Dreiecks wird dazu verwendet die Farben der ganzen Oberfläche des Dreiecks zu bestimmen. Die Farben der drei Vertices werden also nicht interpoliert.

D3DSHADE_GOURAUD
Gouraud Shading Mode. Die Farben aller drei Vertices eines Dreiecks werden linear interpoliert. Dadurch entstehen weiche Farbverläufe.

D3DSHADE_PHONG
Wird derzeit noch nicht unterstützt.

D3DSHADE_FORCE_DWORD
Zwingt den Compiler, die Konstanten als 32-Bit-Werte zu behandeln.


SelfDXD - D3DSTENCILOP

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Aufzählungen
D3DSTENCILOP
D3DSTENCILOP_KEEP           = 1
D3DSTENCILOP_ZERO           = 2
D3DSTENCILOP_REPLACE        = 3
D3DSTENCILOP_INCRSAT        = 4
D3DSTENCILOP_DECRSAT        = 5
D3DSTENCILOP_INVERT         = 6
D3DSTENCILOP_INCR           = 7
D3DSTENCILOP_DECR           = 8
D3DSTENCILOP_FORCE_DWORD    = $7fffffff

D3DSTENCILOP_KEEP
Der Eintrag im Stencil-Buffer wird nicht aktuallisiert.

D3DSTENCILOP_ZERO
Setzt den Stencil-Buffer-Eintrag auf 0.

D3DSTENCILOP_REPLACE
Ersetzt den Eintrag im Stencil-Buffer durch einen Referenzwert.

D3DSTENCILOP_INCRSAT
Inkrementiert den Stencil-Buffer-Eintrag.

D3DSTENCILOP_DECRSAT
Dekrementiert den Stencil-Buffer-Eintrag.

D3DSTENCILOP_INVERT
Invertiert die Bits im Stencil-Buffer-Eintrag.

D3DSTENCILOP_INCR
Inkrementiert den Stencil-Buffer-Eintrag. Ist der neue Wert grösser, als der Höchstwert, wird bei 0 wieder begonnen.

D3DSTENCILOP_DECR
Dekrementiert den Stencil-Buffer-Eintrag. Ist der neue Wert kleiner als 0, wird beim Höchstwert wieder begonnen.

D3DSTENCILOP_FORCE_DWORD
Zwingt den Compiler, die Konstanten als 32-Bit-Werte zu behandeln.

Anmerkung
Stencil-Buffer-Einträge sind Integerwerte zwischen 0 bis 2n-1, wobei n die Bittiefe ist.


SelfDXD - D3DSwapEffect

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Aufzählungen
D3DSWAPEFFECT
D3DSWAPEFFECT_DISCARD    = 1
D3DSWAPEFFECT_FLIP       = 2
D3DSWAPEFFECT_COPY       = 3
D3DSWAPEFFECT_COPY_VSYNC = 4

D3DSWAPEFFECT_DISCARD
Hier werden Back- und Front-Buffer ausgetauscht. Dabei werden die Daten des neuen Backbuffers gelöscht. Sie erhalten also im Grunde wieder ein leeres Blatt, dass Sie beschreiben können. Dieser SwapEffect ist auch der einzige, bei dem Sie Multisampling-Antialiasing, also einen anderen Parameter als D3DMULTISAMPLE_NONE verwenden können.

D3DSWAPEFFECT_FLIP
Im Prinzip das gleiche wie D3DSWAPEFFECT_DISCARD, nur mit dem Unterschied, dass der neue Backbuffer noch die Daten enthält, die er als Frontbuffer hatte. Damit können Sie also das bereits angezeigte Bild erweitern bzw verändern.

D3DSWAPEFFECT_COPY
Durch diesen Parameter wird der Backbuffer auf den Frontbuffer draufkopiert. Desweiteren bleibt der Backbuffer erhalten und kann erweitert oder verändert werden. Hierfür benötigen Sie nur einen Backbuffer.

D3DSWAPEFFECT_COPY_VSYNC
Im Prinzip das gleiche wie D3DSWAPEFFECT_COPY. Hier wird das aktuallisieren des Bildes jedoch in Abhängigkeit von der vertikalen Bildwiederholrate des Monitors gesetzt. Der Vorteil: der Streifeneffekt wird weitgehend unterbunden, die normalerweise beim draufkopieren entstehen können. Der Nachteil: die FPS (Frames per Second) wird nie Höher als die Hertz-Zahl des Monitors sein.


SelfDXD - D3DTEXTUREADDRESS

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Aufzählungen
D3DTEXTUREADDRESS
D3DTADDRESS_WRAP            = 1;
D3DTADDRESS_MIRROR          = 2;
D3DTADDRESS_CLAMP           = 3;
D3DTADDRESS_BORDER          = 4;
D3DTADDRESS_MIRRORONCE      = 5;
D3DTADDRESS_FORCE_DWORD     = $7fffffff;

D3DTADDRESS_WRAP
Kachelt die Textur jedesmal auf's neue. Das heisst, werden Texturkoordinaten ausserhalb des Bereiches 0.0 und 1,0 angegeben, wird die Textur genau in der Form wieder gegeben, wie innerhalb des Bereiches. Keine Spiegelung oder ähnliches.

D3DTADDRESS_MIRROR
Die Textur wird mit Überschreitung eines Integerwertes gespiegelt. Von 0.0 bis 1.0 wird Sie also normal dargestellt, von 1.0 bis 2.0 gespiegelt, von 2.0 bis 3.0 normal, und so weiter.

D3DTADDRESS_CLAMP
Ausserhalb des Bereiches 0.0 bis 1.0 werden die Texturkoordinaten in den Farben dargestellt, wie Sie an den Grenzen 0.0 und 1.0 vorhanden sind.

D3DTADDRESS_BORDER
Ausserhalb der Grenzen (0.0 - 1.0) werden mit einer Farbe dargestellt, die man mit [image] D3DTSS_BORDERCOLOR definieren kann.

D3DTADDRESS_MIRRORONCE
Im Bereich 0.0 bis 1.0 wird die Textur normal dargestellt. Im negativen Bereich (bis -1, -1) werden die Texturkoordinaten um den Punkt 0.0 gespiegelt. Alle anderen Texturkoordinaten werden wie in D3DTADDRESS_CLAMP behandelt.

D3DTADDRESS_FORCE_DWORD
Zwingt den Compiler die Konstanten als 32-Bit-Werte aufzufassen.


SelfDXD - Texture Argument Flags

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Aufzählungen
Textur Argument Flags
D3DTA_SELECTMASK        = $0000000f;
D3DTA_DIFFUSE           = $00000000;
D3DTA_CURRENT           = $00000001;
D3DTA_TEXTURE           = $00000002;
D3DTA_TFACTOR           = $00000003;
D3DTA_SPECULAR          = $00000004;
D3DTA_TEMP              = $00000005;
D3DTA_COMPLEMENT        = $00000010;
D3DTA_ALPHAREPLICATE    = $00000020;

Jedes Texturestage eines Devices kann zwei Texturargumente haben, die sich auf die Farb- und Alpha-Werte der Textur auswirken. Es können immer jeweils ein Argumentenflag und ein Modifizierflag miteinander kombiniert werden. Zwei Argumentenflags kann man nicht gleichzeitig verwenden.

Argumenten Flags

D3DTA_SELECTMASK
Maskenwert für alle Argumente (nur beim Einsatz des Stencilbuffers relevant).

D3DTA_DIFFUSE
Das Texturargument ist die Diffusefarbe, die durch die Vertices beim Gouraud Shading gebildet wird. Wenn die Vertices keine Diffusefarbe besitzen, ist der Standardwert 0xFFFFFFFF. Es ist nur ein Lesezugriff auf das Argument erlaubt.

D3DTA_CURRENT
Das Texturargument ist das Resultat aus den vorhergehenden Blendingoperationen des letzten Stages. Im ersten Stage (Stage 0) ist diese Konstante gleichbedeutend mit D3DTA_DIFFUSE. Wurde im vorhergehenden Stage eine Bumpmap verwendet, wird das vorletzte Stage benutzt..

D3DTA_TEXTURE
Das Texturargument ist die Texturfarbe für diesen Stage.

D3DTA_TFACTOR
Das Texturargument ist der Texturfaktor, der in [image] D3DRS_TEXTUREFAKTOR festgelegt wurde.

D3DTA_SPECULAR
Das Texturargument ist die Specularfarbe, die durch die Vertices beim Gouraud Shading gebildet wird. Wenn die Vertices keine Specularfarbe besitzen, ist der Standardwert 0xFFFFFFFF.

D3DTA_TEMP
Das Texturargument ist ein temporärer Farbspeicher mit Lese- und Schreib-Zugriff. Standardmässig hat der Speicher die Werte (0.0, 0.0, 0.0, 0.0).

Modifizier Flags

D3DTA_COMPLEMENT
Invertiert das Argument, so dass aus einem Farbwert x der Wert (1-x) wird.

D3DTA_ALPHAREPLICATE
Ersetzt alle Farbwerte durch den Alphawert.


SelfDXD - D3DTEXTUREFILTERTYPE

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Aufzählungen
D3DTEXTUREFILTERTYPE
D3DTEXF_NONE            = 0;
D3DTEXF_POINT           = 1;
D3DTEXF_LINEAR          = 2;
D3DTEXF_ANISOTROPIC     = 3;
D3DTEXF_FLATCUBIC       = 4;
D3DTEXF_GAUSSIANCUBIC   = 5;
D3DTEXF_FORCE_DWORD     = $7fffffff;

D3DTEXF_NONE
Mipmapping ist ausgeschaltet.

D3DTEXF_POINT
Dieser Filter kann als Vergrösserungs oder Verkleinerungsfilter eingesetzt werden. Dabei wird jeder Zielpixel aus dem Pixel der Bildquelle bestimmt, der dem Zielpixel am nächsten ist. Wenn dieser Filter als Mipmap-Filter verwendert wird, wird die Farbe der Textur genommen, zu der der Pixel am nächsten ist.

D3DTEXF_LINEAR
Auch bilineare Interpolation genannt, zur Vergrösserung oder Verkleinerung. Jeder Zielpixel wird aus den vier Pixeln der Bildquelle bestimmt, die dem Zielpixel am nächsten sind. Dabei findet eine Gewichtung der Pixel statt. Das Resultat ist eine weichere, leicht verschwommene Oberfläche. Beim Mipmaps findet eine Gewichtung der Farben statt, abhängig davon, wie weit die Mipmaps von dem Pixel entfernt sind. Übergänge zwischen zwei Mipmaps werden damit verwischt.

D3DTEXF_ANISOTROPIC
Dieser Filter kann sowohl zur Vergrösserung als auch zur Verkleinerung eingesetzt werden. Dieser Filter verhindert Verzerrungen in Texturen auf Polygonen, deren Eckkoordinaten unterschiedlich weit in den Raum hineinragen.

D3DTEXF_FLATCUBIC
Flat-Cubic-Filtering wird als Vergrösserungfilter eingesetzt.

D3DTEXF_GAUSSIANCUBIC
Gaussian-Cubic-Filtering ist ein Vergrösserungsfilter.

D3DTEXF_FORCE_DWORD
Zwingt den Compiler, die Konstanten als 32-Bit-Werte zu behandeln.


SelfDXD - D3DTEXTUREOP

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Aufzählungen
D3DTEXTUREOP
// Control
D3DTOP_DISABLE                   = 1;
D3DTOP_SELECTARG1                = 2;
D3DTOP_SELECTARG2                = 3;

// Modulate
D3DTOP_MODULATE                  = 4;
D3DTOP_MODULATE2X                = 5;
D3DTOP_MODULATE4X                = 6;

// Add
D3DTOP_ADD                       = 7;
D3DTOP_ADDSIGNED                 = 8;
D3DTOP_ADDSIGNED2X               = 9;
D3DTOP_SUBTRACT                  = 10
D3DTOP_ADDSMOOTH                 = 11

// Linear alpha blend: Arg1*(Alpha) + Arg2*(1-Alpha)
D3DTOP_BLENDDIFFUSEALPHA         = 12;
D3DTOP_BLENDTEXTUREALPHA         = 13;
D3DTOP_BLENDFACTORALPHA          = 14;

// Linear alpha blend with pre-multiplied arg1 input: Arg1 + Arg2*(1-Alpha)
D3DTOP_BLENDCURRENTALPHA         = 16;
D3DTOP_BLENDTEXTUREALPHAPM       = 15;

// Specular mapping
D3DTOP_PREMODULATE               = 17;
D3DTOP_MODULATEALPHA_ADDCOLOR    = 18;
D3DTOP_MODULATECOLOR_ADDALPHA    = 19;
D3DTOP_MODULATEINVALPHA_ADDCOLOR = 20;
D3DTOP_MODULATEINVCOLOR_ADDALPHA = 21;

// Bump mapping
D3DTOP_BUMPENVMAP                = 22;
D3DTOP_BUMPENVMAPLUMINANCE       = 23;
D3DTOP_DOTPRODUCT3               = 24;

// Triadic ops
D3DTOP_MULTIPLYADD               = 25;
D3DTOP_LERP                      = 26;

D3DTOP_FORCE_DWORD               = $7fffffff;

Auswahl

D3DTOP_DISABLE
Schaltet die Ausgabe von dieser Textur des Index und aller höheren Indizes aus. Um also das Texturieren ganz auszuschalten, genügt es, wenn Sie diesen Parameter für den ersten Stage verwenden. Alphaoperationen können nicht ausgeschaltet werden, wenn Farboperationen eingeschaltet sind.

D3DTOP_SELECTARG1
Benutzt das erste Farb- oder Alpha-Argument des Texturstages als Ausgabe.
SRGBA = Arg1

D3DTOP_SELECTARG2
Benutzt das zweite Farb- oder Alpha-Argument des Texturstages als Ausgabe.
SRGBA = Arg2

Modulation

D3DTOP_MODULATE
Multipliziert die Farbkomponenten beider Argumente.
SRGBA = Arg1 x Arg2

D3DTOP_MODULATE2X
Multipliziert die Farbkomponenten beider Argumente und verschiebt diese um 1 Bit nach links. Tatsächlich wird das Produkt also nochmal mit 2 multipliziert (= helleres Bild).
SRGBA = Arg1 x Arg2 x 2

D3DTOP_MODULATE4X
Multipliziert die Farbkomponenten beider Argumente und verschiebt diese um 2 Bit nach links. Tatsächlich wird das Produkt also nochmal mit 4 multipliziert (= helleres Bild).
SRGBA = Arg1 x Arg2 x 4

Addition und Substraktion

D3DTOP_ADD
Addiert beide Argumente miteinander.
SRGBA = Arg1 + Arg2

D3DTOP_ADDSIGNED
Addiert beide Argumente miteinander und mit -0.5. Damit können Werte zwischen -0.5 und +0.5 erreicht werden.
SRGBA = Arg1 + Arg2 - 0.5

D3DTOP_ADDSIGNED2X
Addiert beide Argumente miteinander und mit -0.5 und multipliziert das ganze mit 2.
SRGBA = (Arg1 + Arg2 - 0.5) x 2

D3DTOP_SUBTRACT
Subtrahiert die Farbwerte des zweiten Arguments von den Werten des ersten Arguments.
SRGBA = Arg1 - Arg2

D3DTOP_ADDSMOOTH
Addiert beide Argumente miteinander und subtrahiert dann das Produkt davon.
SRGBA = Arg1 + Arg2 - Arg1 x Arg2

Linears Alphablending

D3DTOP_BLENDDIFFUSEALPHA
Vermischt beide Argumente anhand des Alphawertes der Vertices.

D3DTOP_BLENDTEXTUREALPHA
Vermischt beide Argumente anhand des Textur-Alphawertes. Je nachdem, in welchem Format Sie die Textur erstellen, haben Sie mehrere Bits für den Alpha-Wert eines Pixels zur Verfügung. Durch das Verändern dieses Wertes können Sie für jeden einzelnen Pixel der Textur entscheiden, wie transparent dieser sein soll.

D3DTOP_BLENDFACTORALPHA
Vermischt Texturen anhand eines Faktors, den Sie mit [image] D3DRS_TEXTUREFACTOR bestimmen können.

D3DTOP_BLENDCURRENTALPHA
Der Alpha-Wert des vorrausgehenden Texturstages wird zum überblenden verwendet.

Allen vier Flags gemeinsam ist die Berechnungsmethode des Farbwertes:
SRGBA = Arg1 x Alpha + Arg2 x (1 - Alpha)

D3DTOP_BLENDTEXTUREALPHAPM

Spiegel-, Glanz-Mapping

D3DTOP_PREMODULATE
Moduliert diesen Texturstage mit dem nächsten.

D3DTOP_MODULATEALPHA_ADDCOLOR
Moduliert die Farbe des ersten Argumentes unter Benutzung des Alphawertes des ersten Argumentes. Anschliessend wird das Resultat zum ersten Argument addiert. Diese Operation ist nur für die Farboperationen ( [image] D3DTSS_COLOROP) verfügbar.
SRGBA = Arg1RGB + Arg1A x Arg2RGB

D3DTOP_MODULATECOLOR_ADDALPHA
Moduliert beide Argumente und addiert den Alphawert des ersten Argumentes hinzu. Diese Operation ist nur für die Farboperationen (D3DTSS_COLOROP) verfügbar.
SRGBA = Arg1RGB x Arg2RGB + Arg1A

D3DTOP_MODULATEINVALPHA_ADDCOLOR
Im Gegensatz zu D3DTOP_MODULATECOLOR_ADDALPHA wird hier die Umkehrung des Alphawertes des ersten Argumentes verwendet. Diese Operation ist nur für die Farboperationen (D3DTSS_COLOROP) verfügbar.
SRGBA = (1 - Arg1A) x Arg2RGB + Arg1RGB

D3DTOP_MODULATEINVCOLOR_ADDALPHA
Ähnlich wie D3DTOP_MODULATECOLOR_ADDALPHA. Hier wird allerdings die Umkehrung des ersten Argumentes verwendet. Diese Operation ist nur für die Farboperationen (D3DTSS_COLOROP) verfügbar.
SRGBA = (1 - Arg1RGB) x Arg2RGB + Arg1A

Bump Mapping

D3DTOP_BUMPENVMAP
Unter Verwendung der Environmentmap im nächsten Stage werden die Höhen und Tiefen Bumpmap-typisch berechnet. Beleuchtungsoperationen sind hier nicht aktiv. Diese Operation ist nur für die Farboperationen (D3DTSS_COLOROP) verfügbar.

D3DTOP_BUMPENVMAPLUMINANCE
Unter Verwendung der Environmentmap im nächsten Stage werden die Höhen und Tiefen Bumpmap-typisch berechnet. Beleuchtungsoperationen sind hier auch aktiv. Diese Operation ist nur für die Farboperationen (D3DTSS_COLOROP) verfügbar.

D3DTOP_DOTPRODUCT3
Moduliert alle Farbkomponenten beider Argumente. Die Summe dieser Modulationen wird jedem Farbkanal, inklusive des Alphakanals, hinzugefügt. Diese Operation ist für Farb- und Alphaoperationen verfügbar.
SRGBA = (Arg1R x Arg2R + Arg1G x Arg2G + Arg1B x Arg2B)

Dreieckiges Texturblending

D3DTOP_MULTIPLYADD
Das zweite und dritte Argument werden miteinander multipliziert und zum ersten addiert.
SRGBA = Arg1 + Arg2 x Arg3

D3DTOP_LERP
Lineare Interpolation zwischen dem zweiten und dritten Argument in einem Verhältnis, das durch das erste Argument angegeben wird.
SRGBA = Arg1 x Arg2 + (1 - Arg1) x Arg3

Sonstiges

D3DTOP_FORCE_DWORD
Zwingt den Compiler dazu, die Werte der Konstanten als 32-Bit-Zahlen zu verwenden. Für die Darstellung hat diese Konstante keinen Einfluss.


SelfDXD - D3DTextureStageStateType

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Aufzählungen
D3DTEXTURESTAGESTATETYPE
D3DTSS_COLOROP               =  1;
D3DTSS_COLORARG1             =  2;
D3DTSS_COLORARG2             =  3;
D3DTSS_ALPHAOP               =  4;
D3DTSS_ALPHAARG1             =  5;
D3DTSS_ALPHAARG2             =  6;
D3DTSS_BUMPENVMAT00          =  7;
D3DTSS_BUMPENVMAT01          =  8;
D3DTSS_BUMPENVMAT10          =  9;
D3DTSS_BUMPENVMAT11          = 10;
D3DTSS_TEXCOORDINDEX         = 11;
D3DTSS_ADDRESSU              = 13;
D3DTSS_ADDRESSV              = 14;
D3DTSS_BORDERCOLOR           = 15;
D3DTSS_MAGFILTER             = 16;
D3DTSS_MINFILTER             = 17;
D3DTSS_MIPFILTER             = 18;
D3DTSS_MIPMAPLODBIAS         = 19;
D3DTSS_MAXMIPLEVEL           = 20;
D3DTSS_MAXANISOTROPY         = 21;
D3DTSS_BUMPENVLSCALE         = 22;
D3DTSS_BUMPENVLOFFSET        = 23;
D3DTSS_TEXTURETRANSFORMFLAGS = 24;
D3DTSS_ADDRESSW              = 25;
D3DTSS_COLORARG0             = 26;
D3DTSS_ALPHAARG0             = 27;
D3DTSS_RESULTARG             = 28;
D3DTSS_FORCE_DWORD           = $7fffffff;

D3DTSS_COLOROP
Mit D3DTSS_COLOROP bestimmen Sie, wie die Farben beider Argumente miteinander kombiniert werden sollen. Verwenden Sie für Value eine Konstante der [image] D3DTEXTUREOP-Aufzählung. Standardmässig ist für das erste Stage (0) D3DTOP_MODULATE und für die restlichen D3DTOP_DISABLE eingestellt.

D3DTSS_COLORARG1
Das erste Farbargument, das zur Berechnung verwendet werden soll. Verwenden Sie für Value eine Konstante der [image] Texture Argument Flags.

D3DTSS_COLORARG2
Das zweite Farbargument, das zur Berechnung verwendet werden soll. Verwenden Sie für Value eine Konstante der Texture Argument Flags.

D3DTSS_ALPHAOP
Um Transparenzeffekte erzielen zu können, bestimmen Sie mit diesem Parameter die Berechnungsart. Verwenden Sie für Value eine Konstante der D3DTEXTUREOP-Aufzählung. Standardmässig ist für das erste Stage (0) D3DTOP_MODULATE und für die restlichen D3DTOP_DISABLE eingestellt.

D3DTSS_ALPHAARG1
Das erste Farbargument, das zur Berechnung eines Alphablendingeffektes verwendet werden soll. Verwenden Sie für Value eine Konstante der Texture Argument Flags.

D3DTSS_ALPHAARG2
Das zweite Farbargument, das zur Berechnung eines Alphablendingeffektes verwendet werden soll. Verwenden Sie für Value eine Konstante der Texture Argument Flags.

D3DTSS_BUMPENVMAT00
Value ist vom Typ Single für den [0][0] Koeffizienten in einer Bump-Mapping Matrix. Der Standardwert ist 0.0.

D3DTSS_BUMPENVMAT01
Value ist vom Typ Single für den [0][1] Koeffizienten in einer Bump-Mapping Matrix. Der Standardwert ist 0.0.

D3DTSS_BUMPENVMAT10
Value ist vom Typ Single für den [1][0] Koeffizienten in einer Bump-Mapping Matrix. Der Standardwert ist 0.0.

D3DTSS_BUMPENVMAT11
Value ist vom Typ Single für den [1][1] Koeffizienten in einer Bump-Mapping Matrix. Der Standardwert ist 0.0.

D3DTSS_TEXCOORDINDEX
Zum einen geben Sie mit diesem Parameter an, welche Texturkoordinaten für dieses Texturestage verwendet werden soll. Standardmässig hat jedes Stage seine eigenen Texturkoordinaten (also das erste Stage verwendet das erste Texturkoordinatenset und das dritte Stage das dritte Texturkoordinatenset). Mit Value können Sie jedoch ein anderes Texturkoordinatenset wählen.
Zusätzlich können Sie mit einer OR-Verknüpfung noch eines der folgenden Flags übernehmen, mit denen Sie bestimmen können, wie die Texturkoordinaten für die Vertices berechnet werden sollen.

Sie haben folgende vier Konstanten zur Auswahl:

D3DTSS_TCI_PASSTHRU:
Die Texturkoordinaten der Vertices werden verwendet. Dies ist die gängige Methode bei der es den Anschein hat, als ob das Objekt aus den Farben der Textur beschaffen ist.

D3DTSS_TCI_CAMERASPACENORMAL:
Die Textur wird immer orthogonal zur Kamera ausgerichtet. Die Texturkoordinaten sind die Orte, an denen sich die Normalen der Vertices auf der Textur befinden.

D3DTSS_TCI_CAMERASPACEPOSITION:
Die Textur wird immer orthogonal zur Kamera ausgerichtet. Sie sehen die Textur nur da, wo sich auch das Objekt befindet.

D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR:
Berechnet einen Reflektionsvektor aus den Vertexkoordinaten, der Normale und der Kameraposition.

D3DTSS_ADDRESSU
Bestimmt den Texture Addressing Mode für die u-Achse. Verwenden Sie hierzu eine Konstante der [image] D3DTEXTUREADDRESS-Aufzählung. Standardmässig ist D3DADDRESS_WRAP aktiviert.

D3DTSS_ADDRESSV
Bestimmt den Texture Addressing Mode für die v-Achse. Verwenden Sie hierzu eine Konstante der D3DTEXTUREADDRESS-Aufzählung. Standardmässig ist D3DADDRESS_WRAP aktiviert.

D3DTSS_BORDERCOLOR
Farbwert, der ausserhalb der Texturgrenzen 0.0 und 1.0 verwendet werden soll. Der Farbwert ist vom Typ D3DCOLOR, was Longword entspricht.

D3DTSS_MAGFILTER
Value gibt an, welcher Filter verwendet werden soll, wenn die Textur vergrössert wird. Verwenden Sie hierzu eine Konstante aus der [image] D3DTEXTUREFILTERTYPE.

D3DTSS_MINFILTER
Value gibt an, welcher Filter verwendet werden soll, wenn die Textur verkleinert wird. Verwenden Sie hierzu eine Konstante aus der D3DTEXTUREFILTERTYPE-Aufzählung.

D3DTSS_MIPFILTER
Value gibt an, welcher Filter bei einer Textur benutzt werden soll, bei der mehrere Mipllevels existieren. Verwenden Sie hierzu eine Konstante aus der D3DTEXTUREFILTERTYPE-Aufzählung.

D3DTSS_MIPMAPLODBIAS
Value gibt an, welcher Filter bei einer Textur benutzt werden soll, bei der mehrere Mipllevels existieren. Verwenden Sie hierzu eine Konstante aus der D3DTEXTUREFILTERTYPE-Aufzählung.

D3DTSS_MAXMIPLEVEL
Mit Value bestimmen Sie, wieviele MIPLEVELS Sie verwenden möchte. Geben Sie 0 an, werden alle Miplevels verwendet. Mit dieser Einstellung können Sie regeln, wie detailliert die Texturen maximal sein sollen.

D3DTSS_MAXANISOTROPY
Höchstes Anisotropie-Level. Standardmässig ist das 1.

D3DTSS_BUMPENVLSCALE
Ein Single-Wert, der den Beleuchtungsgrad der Bumpmap bestimmt. Standardwert ist 0.0.

D3DTSS_BUMPENVLOFFSET
Ein Single-Wert, der dem Beleuchtungsgrad hinzuaddiert wird. Standardwert ist 0.0.

D3DTSS_TEXTURETRANSFORMFLAGS
Verwenden Sie für Value eine Konstante der [image] D3DTEXTURETRANSFORMFLAGS. Hiermit können Sie die Texturkoordinaten transformieren.

D3DTSS_ADDRESSW
Bestimmt den Texture Addressing Mode für die w-Achse. Verwenden Sie hierzu eine Konstante der D3DTEXTUREADDRESS-Aufzählung. Standardmässig ist D3DADDRESS_WRAP aktiviert.

D3DTSS_COLORARG0
Ein Single-Wert, der dem Beleuchtungsgrad hinzuaddiert wird. Standardwert ist 0.0.

D3DTSS_ALPHAARG0
Ein Single-Wert, der dem Beleuchtungsgrad hinzuaddiert wird. Standardwert ist 0.0.

D3DTSS_RESULTARG0
Ein Single-Wert, der dem Beleuchtungsgrad hinzuaddiert wird. Standardwert ist 0.0.

D3DTSS_FORCE_DWORD
Diese Variable wird noch nicht verwendet.


SelfDXD - CONST_D3DTEXTURETRANSFORMFLAGS

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Aufzählungen
D3DTEXTURETRANSFORMFLAGS
D3DTTFF_DISABLE         = 0;
D3DTTFF_COUNT1          = 1;
D3DTTFF_COUNT2          = 2;
D3DTTFF_COUNT3          = 3;
D3DTTFF_COUNT4          = 4;
D3DTTFF_PROJECTED       = 256;
D3DTTFF_FORCE_DWORD     = $7fffffff;

D3DTTFF_DISABLE
Texturkoordinaten werden direkt zum Rasterizer weitergeleitet.

D3DTTFF_COUNT1
Der Rasterizer sollte 1D-Texturkoordinaten erwarten.

D3DTTFF_COUNT2
Der Rasterizer sollte 2D-Texturkoordinaten erwarten.

D3DTTFF_COUNT3
Der Rasterizer sollte 3D-Texturkoordinaten erwarten.

D3DTTFF_COUNT4
Der Rasterizer sollte 4D-Texturkoordinaten erwarten.

D3DTTFF_PROJECTED
Die Texturkoordinaten werden durch das letzte Element geteilt, bevor sie zum Rasterizer weitergeleitet werden. Wenn zum Beispiel dieses Flag zusammen mit D3DTTFF_COUNT3 verwendet wird, werden die erste und zweite Texturkoordinate durch die dritte geteilt.

D3DTTFF_FORCE_DWORD
Zwingt den Compiler dazu, die Konstanten als 32-Bit-Werte zu behandeln.


SelfDXD - CONST_D3DTRANSFORMSTATETYPE

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Aufzählungen
D3DTRANSFORMSTATETYPE
D3DTS_VIEW          = 2;
D3DTS_PROJECTION    = 3;
D3DTS_WORLD         = 0 + 256;
D3DTS_WORLD1        = 1 + 256;
D3DTS_WORLD2        = 2 + 256;
D3DTS_WORLD3        = 3 + 256;

D3DTS_TEXTURE0      = 16;
D3DTS_TEXTURE1      = 17;
D3DTS_TEXTURE2      = 18;
D3DTS_TEXTURE3      = 19;
D3DTS_TEXTURE4      = 20;
D3DTS_TEXTURE5      = 21;
D3DTS_TEXTURE6      = 22;
D3DTS_TEXTURE7      = 23;

D3DTS_VIEW
Transformiert die Kamera in ihrer Position, ihrem Blickwinkel und ihrer Ausrichtung (also, wo im Bild 'oben' ist). Standardmässig ist die Einheitsmatrix eingestellt.

D3DTS_PROJECTION
Transformiert die Darstellungsweise bezüglich der Tiefe, dem Verhältnis Höhe/Breite und dem sichtbaren Bereich in der Tiefe. Standardmässig ist die Einheitsmatrix eingestellt.

D3DTS_WORLD
Transformiert die zu rendernden Vertices. Standarmässig ist die Einheitsmatrix eingestellt.

D3DTS_WORLD1 bis D3DTS_WORLD3
Transformiert die zu rendernden Vertices, nachdem sie mit der Welt-Matrix aus D3DTS_WORLD transformiert worden sind. Die Transformierungen mit den Weltmatrizen 1-3 können hierbei unterschiedlich gewichtet werden. Die Gewichtungen sind im Vertexformat angegeben. So können mehrere Matrizen überblendet werden.

D3DTS_TEXTURE0 bis D3DTS_TEXTURE7
Transformiert die jeweilige Textur.


SelfDXD - CONST_D3DUsageFlags

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Aufzählungen
D3DUSAGEFLAGS

D3DUSAGE_RENDERTARGET       = &00000001;
D3DUSAGE_DEPTHSTENCIL       = &00000002;

// für Vertex- und Indexbuffer
D3DUSAGE_WRITEONLY          = &00000008;
D3DUSAGE_SOFTWAREPROCESSING = &00000010;
D3DUSAGE_DONOTCLIP          = &00000020;
D3DUSAGE_POINTS             = &00000040;
D3DUSAGE_RTPATCHES          = &00000080;
D3DUSAGE_NPATCHES           = &00000100;
D3DUSAGE_DYNAMIC            = &00000200;

D3DUSAGE_RENDERTARGET
Gibt an, dass das erstellte Surface als Renderziel verwendet werden soll.

D3DUSAGE_DEPTHSTENCIL
Gibt an, dass das erstellte Surface als Tiefenbuffer verwendet werden soll.

D3DUSAGE_WRITEONLY
Der Buffer wird nur zum beschreiben und rendern verwendet, nicht aber, um Daten aus dem Buffer auszulesen. Diese Einsellung erlaubt es dem Treiber, den besten Speicherort für schnelles Schreiben und Rendern zu wählen. Das Lesen des Vertexbuffers mit dieser Einstellung schlägt fehl.

D3DUSAGE_SOFTWAREPROCESSING
Wenn Ihr Device mit HARDWAREPROCESSING arbeitet, also die Darstellung der Vertices hardwarebeschleunigt, dann können Sie mit dieser Konstante die Hardwarebeschleunigung für den gewählten Buffer ausschalten.

D3DUSAGE_DONOTCLIP
Stellt fest, dass der Inhalt des Buffer niemals Clipping benötigt. Beachten Sie, dass vor dem Rendern Clipping in den Renderstates auf False gestellt werden muss.

D3DUSAGE_POINTS
Der Buffer wird nur zum Darstellen von Punkten verwendet.

D3DUSAGE_RTPATCHES
Gibt an, dass der Buffer für Bezierkurven verwendet wird. Diese Konstante spielt eine Rolle, wenn aus einem Mesh der Vertexbuffer gelesen wird.

D3DUSAGE_NPATCHES
Gibt an, dass die Koordinaten angeben, in welche Richtung die Bezierkurve ausgerichtet werden soll.

D3DUSAGE_DYNAMIC
Die Grösse des Speichers ist dynamisch. Des weiteren entscheidet der Grafikkartentreiber, in welchem Speicher die Vertexdaten abgelegt werden sollen. Generell kann man sagen, statische Vertexbuffer werden im Videospeicher abgelegt und dynamische Vertexbuffer im AGP Speicher. Der Vertexbuffer ist automatisch statisch, wenn Sie dieses Flag nicht angeben. Die Verwendung dieses Flags erzwingt, dass der Vertexbuffer mit den Flags D3DLOCK_DISCARD und D3DLOCK_NOOVERWRITE gelockt wird. Andersherum gilt die selbe Beziehung. D3DLOCK_DISCARD und D3DLOCK_NOOVERWRITE sind nur verfügbar, wenn D3DUSAGE_DYNAMIC angegeben ist.


SelfDXD - CONST_D3DVERTEXBLENDFLAGS

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Aufzählungen
D3DVERTEXBLENDFLAGS
D3DVBF_DISABLE  = 0;
D3DVBF_1WEIGHTS = 1;
D3DVBF_2WEIGHTS = 2;
D3DVBF_3WEIGHTS = 3;
D3DVBF_TWEENING = 255;
D3DVBF_0WEIGHTS = 256;

D3DVBF_DISABLE
Schaltet Matrizenblending aus. Es wird nur die Matrix, die über D3DTS_WORLD definiert ist, verwendet.

D3DVBF_1WEIGHTS
Schaltet das Matrizenblending zwischen 2 Matrizen ein, nämlich die, die als D3DTS_WORLD und D3DTS_WORLD1 definiert worden sind.

D3DVBF_2WEIGHTS
Schaltet das Matrizenblending der Matrizen ein, die als D3DTS_WORLD, D3DTS_WORLD1 und D3DTS_WORLD2 definiert worden sind.

D3DVBF_3WEIGHTS
Schaltet das Matrizenblending der Matrizen ein, die als D3DTS_WORLD, D3DTS_WORLD1, D3DTS_WORLD2 und D3DTS_WORLD3 definiert sind.

D3DVBF_TWEENING
Benutzt den Wert, der in den Renderstates als D3DRS_TWEENFACTOR definiert ist, als Blendfaktor.

D3DVBF_0WEIGHTS
Benutzt die Matrix, die als D3DTS_WORLD definiert ist mit einem Gewicht von 1.0.


SelfDXD - D3DZBUFFERTYPE

SelfDXD --- [image] DirectXGraphic --- [image] Direct3D Delphi Referenz --- [image] Aufzählungen
D3DZBUFFERTYPE
D3DZB_FALSE                 = 0
D3DZB_TRUE                  = 1
D3DZB_USEW                  = 2
D3DZB_FORCE_DWORD           = $7fffffff

D3DZB_FALSE
Schaltet den Tiefenbuffer aus.

D3DZB_TRUE
Schaltet den Tiefenbuffer ein.

D3DZB_USEW
Aktiviert W-Buffering.

D3DZB_FORCE_DWORD
Zwingt den Compiler dazu, die Werte als 32-Bit-Werte zu behandeln.


Direct3DX Delphi Referenz

SelfDXD --- [image] DirectXGraphic
Direct3DX Delphi Referenz


Records

SelfDXD --- [image] DirectXGraphic --- [image] Direct3DX Delphi Referenz
Records


SelfDXD - D3DXIMAGEINFO

SelfDXD --- [image] DirectXGraphic --- [image] Direct3DX Delphi Referenz --- [image] Records
D3DXIMAGEINFO
TD3DXImageInfo = packed record
  Width     : Cardinal;
  Height    : Cardinal;
  Depth     : Cardinal;
  MipLevels : Cardinal;
  Format    : TD3DFormat;
end;
DirectX 8.1

TD3DXImageInfo = packed record
  Width     : Cardinal;
  Height    : Cardinal;
  Depth     : Cardinal;
  MipLevels : Cardinal;
  Format    : TD3DFormat;
  ResourceType    : TD3DResourceType;
  ImageFileFormat: TD3DXImageFileFormat;
end;

Width
Breite des zu ladenden Bildes in Pixel.

Height
Höhe des zu ladenden Bildes in Pixel.

Depth
Tiefe des zu ladenden Bildes in Pixel.

MipLevels
Anzahl der Miplevels im Bild

Format
Format des Bildes. Eine Konstante aus den [image] D3DFORMAT-Aufzählungen

DirectX 8.1 spezifisches Attribute

Resourcetype
Gibt die Art des Bildes an. Verwenden Sie hierzu eine Konstante aus den [image] D3DResourceType-Aufzählungen

ImageFileFormat
Gibt das Format der Bilddatei an, also ob es sich hierbei um eine BMP-, PNG- usw. Endung handelt. Verwenden Sie hierzu eine der Konstanten aus den [image] D3DXIMAGEFILEFORMAT-Aufzählungen.


SelfDXD - D3DMATRIX und D3DXMATRIX

SelfDXD --- [image] DirectXGraphic --- [image] Direct3DX Delphi Referenz --- [image] Records
D3DMATRIX und D3DXMATRIX
TD3DMatrix = packed record
  _11, _12, _13, _14 : Single;
  _21, _22, _23, _24 : Single;
  _31, _32, _33, _34 : Single;
  _41, _42, _43, _44 : Single;
end;
Dieses Record beschreibt eine Matrix.


SelfDXD - D3DXPLANE

SelfDXD --- [image] DirectXGraphic --- [image] Direct3DX Delphi Referenz --- [image] Records
D3DXPLANE
TD3DXPlane = packed record
  a, b, c, d : Single;
end;

a, b, c und d
Der a, b, c und d-Koeffizient.


SelfDXD - D3DVECTOR und D3DXVECTOR3

SelfDXD --- [image] DirectXGraphic --- [image] Direct3DX Delphi Referenz --- [image] Records
D3DVECTOR und D3DXVECTOR3
TD3DVector = packed record
  x : Single;
  y : Single;
  z : Single;
end;

x, y und z
Variablen, die den Vektor beschreiben.


SelfDXD - D3DXVECTOR4

SelfDXD --- [image] DirectXGraphic --- [image] Direct3DX Delphi Referenz --- [image] Records
D3DXVECTOR4
TD3DVector = packed record
  x : Single;
  y : Single;
  z : Single;
  w : Single;
end;

x, y, z und w
Variablen, die den Vektor beschreiben.


SelfDXD - Aufzählungen

SelfDXD --- [image] DirectXGraphic --- [image] Direct3DX Delphi Referenz
Aufzählungen


SelfDXD - D3DXIMAGE_FILEFORMAT

SelfDXD --- [image] DirectXGraphic --- [image] Direct3DX Delphi Referenz --- [image] Aufzählungen
D3DXIMAGE_FILEFORMAT

  D3DXIFF_BMP         = 0;
  D3DXIFF_JPG         = 1;
  D3DXIFF_TGA         = 2;
  D3DXIFF_PNG         = 3;
  D3DXIFF_DDS         = 4;
  D3DXIFF_PPM         = 5;
  D3DXIFF_DIB         = 6;

Jedes dieser Flags gibt, an seiner jeweiligen Endung erkennbar, an, um welches Format es sich bei dem Bild handelt.


SelfDXD - D3DX_FILTER

SelfDXD --- [image] DirectXGraphic --- [image] Direct3DX Delphi Referenz --- [image] Aufzählungen
D3DX_FILTER

D3DX_FILTER_NONE      = (1 shl 0);
D3DX_FILTER_POINT     = (2 shl 0);
D3DX_FILTER_LINEAR    = (3 shl 0);
D3DX_FILTER_TRIANGLE  = (4 shl 0);
D3DX_FILTER_BOX       = (5 shl 0);

D3DX_FILTER_MIRROR_U  = (1 shl 16);
D3DX_FILTER_MIRROR_V  = (2 shl 16);
D3DX_FILTER_MIRROR_W  = (4 shl 16);
D3DX_FILTER_MIRROR    = (7 shl 16);
D3DX_FILTER_DITHER    = (8 shl 16);

Von den folgenden Flags darf jeweils nur eins gewählt werden.

D3DX_FILTER_NONE
Die Textur wird weder skaliert, noch mit einem Filter verwendet. Pixel ausserhalb der Bildgrenze werden als transparentes schwarz dargestellt.

D3DX_FILTER_POINT
Jeder Zielpixel wird aus dem Pixel der Bildquelle bestimmt, der dem Zielpixel am nächsten ist.

D3DX_FILTER_LINEAR
Jeder Zielpixel wird aus den Pixeln der Bildquelle bestimmt, die dem Zielpixel am nächsten sind. Dabei findet eine Gewichtung der Pixel statt. Das Resultat ist eine weichere, leicht verschwommene Oberfläche. Der Vorteil dieser Technik: man sieht nicht mehr jeden einzelnen Pixel der Textur.

D3DX_FILTER_TRIANGLE
Aktiviert das Triangle-Filtering. Bei diesem Verfahren wird der zu setzende Pixel nach den Methoden des Linearen Filterings berechnet. Zusätzlich werden aber auch noch die beiden Mipmaps mit eingebunden, die dem Pixel am nächsten sind. Auf diese erzielt man grafisch gesehen das optimalste Ergebnis. Es ist aber auch gleichzeitig die langsamste Methode.

D3DX_FILTER_BOX
Jeder Pixel wird berechnet in dem ein Durchschnitt aus den 4 umliegenden Pixeln des Quellbildes ermittelt wird. Diese Methode ist am effektivsten, wenn die Zielgrösse nur halb so gross ist wie die Bildquelle, was bei Mipmaps zum Beispiel der Fall ist.

Die folgenden Flags können zusätzlich in beliebiger Kombination gewählt werden.

D3DX_FILTER_MIRROR_U
Gibt an, dass Pixel ausserhalb der Texturgrenzen auf der U-Achse gespiegelt und nicht gewrappt werden.

D3DX_FILTER_MIRROR_V
Gibt an, dass Pixel ausserhalb der Texturgrenzen auf der V-Achse gespiegelt und nicht gewrappt werden.

D3DX_FILTER_MIRROR_W
Gibt an, dass Pixel ausserhalb der Texturgrenzen auf der W-Achse gespiegelt und nicht gewrappt werden.

D3DX_FILTER_MIRROR
Dieses Flag ist das gleiche, als wenn Sie D3DX_FILTER_MIRROR_U, D3DX_FILTER_MIRROR_V und D3DX_FILTER_MIRROR_W angeben würden.

D3DX_FILTER_DITHER
Verzerrt das resultierende Bild.


SelfDXD - Programme / Quellcodes / Klassen

SelfDXD --- [image] DirectXGraphic
 | Programme / Quellcodes / Klassen
 | Einleitung
Programme |
 | Vertices Matrizen und Z-Buffer Indexbuffer Lichtkasten Texturfilterkasten
Quellcodes |
 | Textur: Gittermuster zur Laufzeit erstellen Licht: Direktes Licht über Funktionen einstellen Material: Funktion zum Einstellen des Materials
Klassen |
 | tDXHeightmap tDXSkybox

Einleitung

In diesem Kapitel finden Sie nochmal alle Programme und Demos, die in den verschiedenen Kapiteln von SelfDXD/DirectXGraphics das Verständnis des Themas erleichtern sollen. Darüberhinaus stehen Ihnen ein paar kleine Quellcodes und ganze Klassen zur Verfügung, die Ihnen das Erstellen von bestimmten Objekten extrem erleichtern. Dabei kann es sein, dass einige Units Funktionen verwenden, die bisher noch nicht besprochen wurden. Ich bitte um Nachsicht, da nicht alles thematisch gesehen in den bisher vorhandenen Kapiteln unterzubringen war.

Alle Quelltexte dürfen völlig frei verwendet und weitergegeben werden. Sie finden die Programme und Klassen im DirectXGraphics/Demo-Verzeichnis von SelfDXD (Achtung: nur in der Offline-Version enthalten). Die Weiterentwicklung des Codes ist ausdrücklich erlaubt. Ich wünsche Ihnen viel Spass mit dem Material, und hoffe, dass Sie das ein oder andere in Ihren Anwendungen gebrauchen können.


SelfDXD - Vertices

SelfDXD --- [image] DirectXGraphic --- [image] Programme / Quellcodes / Klassen
 | Vertices
 | Hier wird erklärt, wie das geht: Der Vertexbuffer Dieses kleine Programm stellt ein einfaches zweidimensionales Dreieck dar. Es demonstriert die Funktionsweise des Vertexbuffer. zu den Dateien


SelfDXD - Matrizen und Z-Buffer

SelfDXD --- [image] DirectXGraphic --- [image] Programme / Quellcodes / Klassen
 | Matrizen und Z-Buffer
 | Hier wird erklärt, wie das geht: Transformation und Tiefenbuffer In diesem Sample rotiert eine Kamera um zwei Zylinder, von denen sich einer bewegt. Das Programm demonstriert den Einsatz von verschiedenen Matrizen. Ausserdem wird hier zum ersten mal der Z-Buffer verwendet. zu den Dateien


SelfDXD - Indexbuffer

SelfDXD --- [image] DirectXGraphic --- [image] Programme / Quellcodes / Klassen
 | Indexbuffer
 | Hier wird erklärt, wie das geht: Der Indexbuffer Der Einsatz des Indexbuffers wird anhand einer Oberfläche demonstriert, die zwei Parabeln in sich vereint. zu den Dateien


SelfDXD - Lichtkasten

SelfDXD --- [image] DirectXGraphic --- [image] Programme / Quellcodes / Klassen
Lichtkasten

Hier wird erklärt, wie das geht: Licht einstellen

Der Lichtkasten ist ein Programm, in dem man zur Laufzeit alle erdenklichen Lichteinstellungen ändern und das Resultat direkt betrachten kann. Der Lichtkasten ist eine grosse Erleichterung um zu verstehen, welche Parameter welchen visuellen Effekt haben. Das Zusammenspiel der verschiedenen Einstellungen kann hier erforscht werden.

Beachten Sie, dass es von Land zu Land Unterschiede in der Trennung von Vor- und Nachkommastellen geben kann.

zu den Dateien


SelfDXD - Texturfilterkasten

SelfDXD --- [image] DirectXGraphic --- [image] Programme / Quellcodes / Klassen
 | Texturfilterkasten
 | Hier wird erklärt, wie das geht: Textur Filtering Im Texturfilterkasten können Sie zur Laufzeit sämtliche Texturfilter einstellen. Dies betrifft auch die Filter für die Mipmaps. Im linken Teil des Fensters rotiert eine Textur. Gleichzeitg bewegt Sie sich vom Betrachter weg und zum Betrachter hin. Damit lassen sich alle Effekte, die man mit den Texturfiltern einstellen kann, in der Anwendung betrachten. zu den Dateien


SelfDXD - Textur: Gittermuster zur Laufzeit erstellen

SelfDXD --- [image] DirectXGraphic --- [image] Programme / Quellcodes / Klassen
 | Textur: Gittermuster zur Laufzeit erstellen
 | DXPaint_Texture( )

DXPaint_Texture( )

Mit der folgenden Funktion lassen sich zur Laufzeit Texturen erstellen, die ein Gittermuster (zaunähnlich) haben. Durch mehrmaliges Ausführen der Prozedur mit unterschiedlichen Parametern, lassen sich diverse Effekte erzielen. Am einfachsten lässt sich die Prozedur verwenden, wenn Sie damit 16-Bit-Texturen bearbeiten wollen, wobei jeweils 4 Bits jedem Farbkanal (einschliesslich Alpha-Kanal) zugeordnet sind.

procedure DXPaint_Texture(_textur: IDirect3DTexture8;
                          _breite, _abstand, _sx, _sy: integer;
                          _color: word; _overwrt: boolean);
var
  d3dlr: TD3DLocked_Rect;
  cursor: pWord;
  x, y: integer;
  YInc: longint;
  summe: integer;
  hr: HRESULT;
begin
  summe := _breite + _abstand;

  hr := _textur.LockRect(0, d3dlr, nil, 0);
  if hr = D3D_OK then
  begin
    YInc := (d3dlr.Pitch shr 1);

    cursor := d3dlr.pBits;

    for x := 1 to YInc do
    begin
      for y := 1 to YInc do
      begin
        if ((y + x - _sx) mod summe) <= _breite then
        begin
          cursor^ := _color;
        end
        else
        begin
          if _overwrt then
          begin
            cursor^ := $f000;
          end;
        end;
        if ((y + Yinc - x - _sy) mod summe) <= _breite then
        begin
          cursor^ := _color;
        end;
        inc(cursor);
      end;
    end;
  end
  else
  begin
    hHalt(PChar(errtostr(hr)), Application);
  end;
  _textur.UnlockRect(0);
end;

_textur: die Textur, die bearbeitet werden soll. Beachten Sie, dass man nur Texturen locken kann, die mit D3DPOOL_MANAGED erstellt worden sind.

_breite, _abstand: Die Breite der Linien und der Abstand zwischen zwei Linien.

_sx, _sy: Werte, um das Gitter zu verschieben.

_color: Farbe der Linien. Die freien Stellen werden, sofern _overwrt auf TRUE gesetzt wurde, mit einem transparenten Schwarz gefüllt.

_overwrt: Gibt an, ob die freien Stellen mit einem transparenten Schwarz übermal werden sollen, oder nicht. Diese Auswahl erlaubt es Ihnen, die Prozedur mehrmals aufzurufen, und so mehrere Gitter "übereinander" zu malen (zum Beispiel für Schatteneffekte).


SelfDXD - Licht: Direktes Licht über Funktionen einstellen

SelfDXD --- [image] DirectXGraphic --- [image] Programme / Quellcodes / Klassen
Licht: Direktes Licht über Funktionen einstellen
Einleitung
SetDirectionalLight( )
SetSpotLight( )
SetPointLight( )

Einleitung

Das Einstellen einer Lichtquelle erfordert das Ausfüllen eines speziellen Records, welches dem Device übergeben wird. Damit Sie sich das aufwendige Parameter-dem-Record-zuweisen sparen können, kopieren Sie einfach die folgenden Funktionen in Ihr Programm rein. Sie müssen dann lediglich nur eine dieser drei Funktionen auswählen, dieser alle nötigen Parameter übergeben, und sich ansonsten um nichts mehr kümmern.

Beachten Sie jedoch, dass Sie, wenn Sie die nachfolgenden Codes übernehmen, gegebenenfalls den Namen des Direct3DDevice8 ändern müssen.

SetDirectionalLight( )

Diese Funktion erzeugt ein gradliniges Licht:

procedure SetDirectionalLight( _dr, _dg, _db,
                               _ar, _ag, _ab,
                               _sr, _sg, _sb: Single;
                               _dir : TD3DXVector3;
                               _index: integer );
var
  light : TD3DLIGHT8;
begin
  ZeroMemory( @light, sizeof( TD3DLIGHT8 ) );

  with light do
  begin
    _Type := D3DLIGHT_DIRECTIONAL;
    Diffuse.r := _dr;
    Diffuse.g := _dg;
    Diffuse.b := _db;
    Ambient.r := _ar;
    Ambient.g := _ag;
    Ambient.b := _ab;
    Specular.r := _sr;
    Specular.g := _sg;
    Specular.b := _sb;
    Direction := _dir;
  end;
  d3ddev8.SetLight( _index, light );
end;

SetSpotLight( )

Diese Funktion erzeugt ein Spotlight.

procedure SetSpotLight( _dr, _dg, _db,
                        _ar, _ag, _ab,
                        _sr, _sg, _sb,
                        _at0, _at1, _at2: Single;
                        _pos, _Dir: TD3DXVector3;
                        _theta, _phi,
                        _falloff, _range: single;
                        _index: cardinal);
var
  light : TD3DLIGHT8;
begin
  ZeroMemory( @light, Sizeof( TD3DLIGHT8 ) );
  with light do begin
    _Type := D3DLIGHT_SPOT;
    Diffuse.r := _dr;
    Diffuse.g := _dg;
    Diffuse.b := _db;
    Ambient.r := _ar;
    Ambient.g := _ag;
    Ambient.b := _ab;
    Specular.r := _sr;
    Specular.g := _sg;
    Specular.b := _sb;
    attenuation0 := _at0;
    attenuation1 := _at1;
    attenuation2 := _at2;
    position := _pos;
    direction := _dir;
    theta := _theta;
    phi := _phi;
    falloff := _falloff;
    range := _range;
  end;
  d3ddev8.SetLight( _index, light );
end;

SetPointLight( )

Diese Funktion erzeugt ein Punktlicht.

procedure SetPointLight( _dr, _dg, _db,
                         _ar, _ag, _ab,
                         _sr, _sg, _sb,
                         _at0, _at1, _at2,
                         _range: single;
                         _pos: TD3DXVector3;
                         _index: integer );
var
  light : TD3DLIGHT8;
begin
  ZeroMemory( @light, Sizeof( TD3DLIGHT8 ) );

  with light do
  begin
    _Type := D3DLIGHT_POINT;
    Diffuse.r := _dr;
    Diffuse.g := _dg;
    Diffuse.b := _db;
    Ambient.r := _ar;
    Ambient.g := _ag;
    Ambient.b := _ab;
    Specular.r := _sr;
    Specular.g := _sg;
    Specular.b := _sb;
    attenuation0 := _at0;
    attenuation1 := _at1;
    attenuation2 := _at2;
    range := _range;
    position := _pos;
  end;
  d3ddev8.SetLight( _index, light );
end;


SelfDXD - Material: Funktion zum Einstellen des Materials

SelfDXD --- [image] DirectXGraphic --- [image] Programme / Quellcodes / Klassen
 | Material: Funktion zum Einstellen des Materials
 | SetMaterial( )

SetMaterial( )

Damit das Eingeben der Materialeigenschaften ein wenig leichter fällt, bietet sich folgende Funktion an, bei der Sie nur noch gegebenenfalls den Namen des Direct3DDevice8 ändern müssen.

procedure SetMaterial( _ar, _ag, _ab, _aa,
                       _dr, _dg, _db, _da,
                       _sr, _sg, _sb, _sa,
                       _er, _eg, _eb, _ea,
                       _power: Single);
var
  mtrl : TD3DMATERIAL8;
begin
  ZeroMemory( @mtrl, sizeof( TD3DMATERIAL8 ) );

  with mtrl do
  begin
    Ambient.r := _ar;
    Ambient.g := _ag;
    Ambient.b := _ab;
    Ambient.a := _aa;
    Diffuse.r := _dr;
    Diffuse.g := _dg;
    Diffuse.b := _db;
    Diffuse.a := _da;
    Specular.r := _sr;
    Specular.g := _sg;
    Specular.b := _sb;
    Specular.a := _sa;
    Emissive.r := _er;
    Emissive.g := _eg;
    Emissive.b := _eb;
    Emissive.a := _ea;
    Power := _power;
  end;

  d3ddev8.SetMaterial( mtrl );
end;


SelfDXD - tDXHeightmap

SelfDXD --- [image] DirectXGraphic --- [image] Programme / Quellcodes / Klassen
 | tDXHeightmap
 | Einleitung .Create( ) .Destroy .GenerateTerrain( ) .LoadHeightmapFromFile( ) .LoadHeightmapFormTexture( ) .Draw

Einleitung

Die Klasse TDXHeightmap erlaubt es Ihnen, in nur wenigen Schritten eine flexible Heightmap einzusetzen und diese auch nachträglich zur Laufzeit zu verändern. Dabei ist es fast egal, in welchem Format die Bilddatei mit den Höheninformationen vorhanden ist. Sie können sogar farbige Bilder verwenden, um diese als Höhenkarte zu benutzen. Desweiteren beinhaltet das 3D-Objekt Texturkoordinaten, so dass Sie eine Textur über die Heightmap legen können.

.Create( )

Mit TDXHeightmap.Create erstellen Sie die Heightmap:

constructor Create(_Owner: IDirect3DDevice8);

_Owner: Das Direct3DDevice, unter dem die Heightmap später angezeigt werden soll.

.Destroy

Der Destructor der Heightmap. Wenn Sie die Heightmap freigeben wollen, dann sollten Sie jedoch in jedem Fall TDXHeightmap.Free verwenden, da diese Funktion automatisch kontrolliert, ob überhaupt die jeweilige Instanz erstellt wurde und dann automatisch Destroy aufruft.

destructor Destroy;

.GenerateTerrain( )

Mit dieser Funktion wird die eigentliche Heightmap erst erstellt. Dazu können Sie verschiedene Parameter eingeben, um das Resultat auf Ihre Bedürfnisse abzustimmen. Solange diese Funktion nicht ausgeführt wird, kann keine Heightmap angezeigt werden.

function GenerateTerrain(_heightmap: string;
  _mapsize_x,
  _mapsize_z,
  _height_f: single;
  _points_x,
  _points_z: cardinal;
  _tu_f,
  _tv_f: single
): HRESULT;

_heightmap: Der Pfad und Dateiname der Bilddatei, aus der die Höheninformationen gelesen werden sollen.

_mapsize_x, _mapsize_z: Die Grösse der Heightmap in Ihrer 3D-Anwendung.

_height_f: Der Höhenfaktor. Dieser Parameter wird mit dem Höhenwerten aus der Bilddatei multipliziert. 0.5 halbiert zum Beispiel alle Höhenwerte.

_points_x, _points_z: Die Anzahl der Vertices in der X- und Z-Achse. Mit diesen Parametern können Sie im Grunde die Auflösung der Höhenmap einstellen. Verwenden Sie nicht mehr als 110 Vertices pro Achse, da es sonst zu Systemabstürzen kommen kann.

_tu_f, _tv_f: Faktoren, mit denen Sie bestimmen können, wie oft die Textur auf der Heightmap gekachelt werden soll. Verwenden Sie 1, 1 um die Textur über die ganze Map zu strecken.

LoadHeightmapFromFile( )

Wenn Sie zur Laufzeit die Heightmap wechseln wollen, dann können Sie dies tun, in dem Sie einfach eine neue Bilddatei laden.

function LoadHeightmapFromFile(_heightmap: string): HRESULT;

_heightmap: Der Pfad und Dateiname der Bilddatei, aus der die Höheninformationen gelesen werden sollen.

LoadHeightmapFormTexture( )

Wahlweise können Sie die Höhenwerte ändern, in dem Sie eine bereits erstellte Textur der Klasse übergeben.

function LoadHeightmapFromTexture(_hmap: IDirect3DTexture8): HRESULT;

_hmap: Ein IDirect3DTexture8-Interface mit den Höheninformationen, die in die Heightmap übertragen werden sollen.

.Draw

Einfacher gehts doch kaum, oder? Einfach diese Funktion aufrufen, und die Heightmap wird gerendert. Sie müssen lediglich dran denken, die richtigen Transformationen, Lichtverhältnisse und Texturen eingestellt zu haben. Dann dürfte es keine Probleme geben.

function draw: HRESULT;


SelfDXD - tDXSkybox

SelfDXD --- [image] DirectXGraphic --- [image] Programme / Quellcodes / Klassen
 | tDXSkybox
 | Einleitung .Create( ) .Destroy .Draw( )

Einleitung

Mit der Klasse tDXSkybox können Sie erstellen Sie im handumdrehen eine kegelartige Skybox. Eine Skybox ist ein 3D-Körper, der immer von innen gerendert wird. Da beim Rendervorgang der Z-Buffer ausgeschaltet wird, werden Objekte, die sich ausserhalb der Skybox befinden, auch angezeigt. Verwenden Sie eine Skybox, wenn Sie einen Himmel anzeigen wollen. Beachten Sie jedoch, dass es einen Boden in dieser Skybox nicht gibt. Schauen Sie also nach unten, dann sehen Sie nichts von der Skybox.

Der Screenshot zeigt eine Blume von schrägunten. Die Kamera ist nach oben gerichtet. Perspektivisch korrekt wird der Himmel in Form einer Textur angezeigt.

.Create( )

Mit TDXSkyBox.Create erstellen Sie die Skybox:

constructor Create( _pdevice : IDirect3DDevice8; _textur : string );

_pdevice: Das Direct3DDevice, unter dem die Skybox später angezeigt werden soll.

_textur: Der Dateiname (mit Pfad) einer Textur, die angezeigt werden soll.

.Destroy

Der Destructor der Skybox. Wenn Sie die Skybox freigeben wollen, dann sollten Sie jedoch in jedem Fall TDXSkybox.Free verwenden, da diese Funktion automatisch kontrolliert, ob überhaupt die jeweilige Instanz erstellt wurde und dann automatisch Destroy aufruft.

destructor Destroy;

.Draw( )

Rufen Sie die Draw-Prozedur der Skybox vor allen anderen Rendervorgängen auf. Damit stellen Sie sicher, dass nachher auch alle Objekte angezeigt werden, denn beim Rendern der Skybox wird der Z-Buffer ausgeschaltet. Das heisst, alle Tiefeninformationen werden nicht beachtet. Damit die Skybox perspektivisch korrekt angezeigt wird, müssen Sie der Prozedur noch ein paar Parameter übergeben.

procedure Draw( _pos, _dest, _up: TD3DVector );

_pos, _dest, und up sind die drei Vektoren, die Sie auch verwenden, um die Kameraeinstellungen vorzunehmen. Diese müssen der .Draw-Prozedur übergeben werden, damit die Skybox auch richtig angezeigt werden kann.


SelfDXD

Inhaltsverzeichnis
CHM-Hilfe erstellt von Henrik Haftmann