Kapitel 8: DirectX Graphics

Die Seite wird erstellt Helmut Nowak
 
WEITER LESEN
Kapitel 8: DirectX Graphics
Kapitel 8: DirectX Graphics
Nochmal etwas Theorie:

● Arbeitsweise einer 3D Engine
● Umrechnen von Welt- in Bildschirmkoordinaten

● Objekt-Culling

● Doppel- und Tiefenpufferung

Yipieh! Es geht los:

● Anwendungsgerüst für das Projekt
● Initialisierung von DirectX Graphics

● Textausgabe

● Punkte, Linien und Kreise zeichnen

● Einfaches Texture Mapping
Kapitel 8: DirectX Graphics
Kapitel 8: Die 3 Anwendungen
Teil 1

Beispiel 'FirstContact':
Das modularisierte Anwendungsgerüst

Beispiel 'Punkte, Linien und Kreise':
Die Implementierung von Primitiven in das Gerüst

Teil 2

Beispiel 'Texturen':
Die Implementierung von Textur-Funktionalität
Kapitel 8: DirectX Graphics
Was ist DirectX?
 ●   ist eine Sammlung von Komponenten, mit denen sich
     unter Windows lauffähige Multimediaanwendungen
     entwickeln lassen

 ●   ist sprachunabhängig

 ●   besitzt einzelne Komponenten (Component Object
     Model):
       ● DirectX Graphics (zusammengefaßt aus den

         früheren Komponenten Direct3D und DirectDraw)
       ● DirectX Audio

       ● DirectInput

       ● DirectPlay

       ● DirectShow

       ● DirectSetup

In Kapitel 8 befassen wir uns mit DirectX Graphics.
Kapitel 8: DirectX Graphics
Am Rande erwähnt: Das DirectX SDK
Es könnte hilfreich sein, in der mitgelieferten
Dokumentation des SDK zu stöbern...

Microsoft schenkt uns den DirectX Sample Browser ...
Am Rande erwähnt: Das DirectX SDK
... und die vollständige Dokumentation.

Dort findet man z.B. Beschreibungen von Funktionen ...
Am Rande erwähnt: Das DirectX SDK
... oder Erklärungen für interne Funktionsweisen.
Die Transformations- und
Beleuchtungspipeline
Für die Transormation und Beleuchtung wird eine
sogenannte Pipeline benutzt.

In einzelnen Schritten werden die Dinge abgearbeitet, die
benötigt werden, um ein abstraktes Modell als etwas
grafisches am Monitor darzustellen.
Welttransformation

Die Transormation von den Modellkoordinaten in die
Weltkoordinaten ist die Welttransformation.

Matrix multiplication that converts
vertices from model space to world space.

Die Welttransformation wird von DirectX durch diesen
Aufruf erzeugt:

SetTransform(D3DTS_WORLD, &TransformationsMatrix);

     HRESULT SetTransform (
        D3DTRANSFORMSTATETYPE State,
        CONST D3DMATRIX * pMatrix
     );

     Header: Declared in D3d9.h
Sichttransformation

Die gesamte 3D Szene wird durch die Kamera (= Player)
wahrgenommen:

    ●   Kameraposition
    ●   Blickrichtung

Die Objekte werden mit Hilfe einer Sichtmatrix (View-
Matrix) in den Kameraraum transformiert.

Die Sichttransformation wird (wie die Welttransformation
auch) von DirectX durch diesen Aufruf erzeugt:

SetTransform(D3DTS_View, &ViewMatrix);

Man beachte, dass hier wieder SetTransform, allerdings
mit anderem Transformationsstatetyp, aufgerufen wird.
siehe in der SDK-Dokumentation:
D3DTRANSFORMSTATETYPE bzw. SetTransform (Idirect3DDevice9)
Sichttransformation:
Prinzipielle Funktionsweise

Anstatt die Kamera zu bewegen, wird die Kamera im
Ursprung des Weltkoordinatensystems mit Blickrichtung in
die positive z-Achse aufgestellt.

Die Kamera wird nicht bewegt oder gedreht, sondern die
Objekte um sie herum in inverser Kamerabewegung.

    Beispiel:

    ●   Die Kamera soll sich eigentlich um 30° drehen,
        also wird das Objekt um - 30° gedreht.

    ●   Die Kamera soll sich eigentlich auf ein Objekt
        zubewegen, also wird das Objekt auf die Kamera
        zubewegt.

Die View-Matrix entspricht der inversen Kamera-
Transformationsmatrix.
Sichttransformation: Vorgehen 1/2

Im Prinzip wird die Sichttransformation so durchgeführt:

    ●   die Kamera-Transformationsmatrix berechnen
    ●   die Kamera-Transformationsmatrix invertieren
    ●   Sichtransformation durchführen

Aber das ist rechenaufwendig. Darum geht man etwas
anders vor ...

Die Kamera wird durch 4 Vektoren für deren Position und
Ausrichtung beschrieben. Mit diesen Vektoren kann man
dann die View-Matrix erstellen.
Sichttransformation: Vorgehen 2/2

DirectX erzeugt mit folgender Funktion eine (linkshändige)
View-Matrix:

D3DXMatrixLookAtLH(&ViewMatrix,
                   &PlayerPosition,
                   &PlayerFlugrichtung, // Look
                   &PlayerVertikale     // Up
                   );

D3DXMATRIX * D3DXMatrixLookAtLH(
  D3DXMATRIX * pOut,
  CONST D3DXVECTOR3 * pEye,
  CONST D3DXVECTOR3 * pAt,
  CONST D3DXVECTOR3 * pUp
);

Header: Declared in D3dx9math.h

       Setting Up a View Matrix
The D3DXMatrixLookAtLH and D3DXMatrixLookAtRH helper functions create a view matrix
based on the camera location and a look-at point.
The following example creates a view matrix for left-handed coordinates.
D3DXMATRIX out;
D3DXVECTOR3 eye(2,3,3);
D3DXVECTOR3 at(0,0,0);
D3DXVECTOR3 up(0,1,0);
D3DXMatrixLookAtLH(&out, &eye, &at, &up);
Sichttransformation: Optimierung

Die (inverse) Verschiebung der Kamera kann auch direkt
in die Welttransformation aller Objekte berechnet werden.

Beispiel 1:

Man hat eine Szene mit 2000 eigentlich nicht bewegten
Objekten, z.B. fixe Sterne im Hintergrund.

Die Sterne werden durch die Kamerabewegung bewegt,
aber das will man garnicht. Also muß man diese
Bewegung wieder zurückberechnen und das ist
schlecht.

Beispiel 2:

2D Objekte (Rauch, Partikel etc.) werden anhand von
sogenannter Billboard-Matrizen berechnet. Auch hier:
weniger Rechenaufwand, wenn die Kamerabewegung der
Billboard-Matrix nicht in der View-Matrix berechnet wird,
sondern direkt in der Welttransformation der Billboard-
Matrix.
Projektionstransformation

In der Projektionstransformation wird die 3D-Szene auf 2
Dimensionen herunterprojeziert und so auf dem Bildschirm
darstellbar.

Diese Projektion wird wieder durch SetTransform (hier mit
dem State D3DTS_PROJECTION) durchgeführt:

SetTransform(D3DTS_PROJECTION, &ProjectionMatrix);

You can think of the projection transformation
as controlling the camera's internals; it is
analogous to choosing a lens for the camera.

Bevor man die Projektionstransformation durchführen
kann, muß eine Projektionsmatrix erstellt werden.
Projektionstransformation:
Arbeitsweise der Projektionsmatrix 1/3
Der sichtbare Ausschnitt einer 3D-Szene (abhängig von
der Kamera) wird durch das Viewing Volume
beschrieben.

Es Viewing Volume besitzt:

    ●   eine maximale Sichtweite: Far Clipping Plane
    ●   eine minimale Sichtweite: Near Clipping Plane
    ●   ein Blickfeld (der Sichtwinkel): Field of View (FOV)
Projektionstransformation:
Arbeitsweise der Projektionsmatrix 2/3

Durch die Projektionsmatrix wird jedes Objekt der 3D
Szene auf der Projektionsfläche abgebildet.

Die Projektionsmatrix:

    ●   z-Achse ist Blickrichtung
    ●   die Eckpunkte der Clipping Planes als Vertices
        verwenden
    ●   ein Punkt auf der z-Achse als Vertex verwenden
    ●   für das FOV wird als Spezialfall 180° festgelegt
    ●   der Abstand der Far Clipping Plane strebt gegeb
        unendlich
    ●   der Abstand zwischen Projektionsfläche und Near
        Clipping Plane muß >= 1.0 sein, sonst kommt es zu
        Projektionsfehlern
Projektionstransformation:
Arbeitsweise der Projektionsmatrix 3/3

DirectX erzeugt durch folgende Funktion die
Projektionsmatrix:

D3DXMatrixPerspectiveFovLH(&matProj,
                           45*D3D_PI/180,
                           Aspect,
                           1.0f,
                           SizeOfUniverse);

siehe SDK-Dokumentation:
D3DXMATRIX * D3DXMatrixPerspectiveFovLH(
     D3DXMATRIX * pOut,
     FLOAT fovy,
     FLOAT Aspect,
     FLOAT zn,
     FLOAT zf
 );

pOut [in, out] Pointer to the D3DXMATRIX structure that is the
result of the operation.

fovy [in] Field of view in the y direction, in radians.

Aspect [in] Aspect ratio, defined as view space width divided by
height.

zn [in] Z-value of the near view-plane.

zf [in] Z-value of the far view-plane.
(Projektionstransformation) Clipping

Clipping bedeutet, dass die nicht sichtbaren Bereiche
eines Polygons weggeschnitten werden.

Nach der Projektionstransformation werden die Polygone
gegen die 6 Flächen des Viewing Volumes geclippt.

Anmerkung:

Aus Effizienzgründen wird das Viewing Volume in einen
Quader transformiert.
Umrechnung von Welt- in
Bildschirmkoordinaten 1/3

Hier wird eine Funktions vorgestellt, mit der die
Bildschirmkoordinaten eines Objektes berechnet werden
können.

Beispiel:

Ein Angriffsziel im 3D Raum wird mit Mauszeiger
ausgewählt.

    ●   1.Schritt: Bildschirmkoordinaten des Mauszeigers
        und des Angriffziels ermitteln.

    ●   2.Schritt: diese Koordinaten miteinander
        vergleichen

    ●   3.Schritt: Bei Übereinstimmung wird das Objekt
        selektiert

Diese Umrechnung geschieht unter Verwendung der
zugehörigen Sicht- und Projektionsmatrizen.
Umrechnung von Welt- in
Bildschirmkoordinaten 2/3

Wie geht das?

Für jedes Frame muss diejenige Matrix berechnet werden,
die für die Transformation der Weltkoordinaten eines
Objektes in die entsprechenden Bildschirmkoordinaten
verantwortlich ist:
ViewProjectionMatrix = mathView*mathProj;

(Diese Anweisung wird später im letzten Beispiel
'Simulation' in 3D_ScenarioClasses.h eingebaut.)

Um den Ortsvektor eines Objektes in die
Viewportkoordinaten zu transformieren, bedarf es einer
Matrizenmultiplikation.

Weil der jeweilige Ursprung der Viewportkoordinaten und
der Bildschirmkoordinaten nicht übereinstimmt, muss
anschließend ein klein wenig umgerechnet werden.

(siehe Funktion auf nächster Folie)
Umrechnung von Welt- in
Bildschirmkoordinaten 3/3

Die Funktion wird später in CoolMath.h implementiert und
sieht so aus:
inline void Calculate_Screen_Coordinates(   float* pScreenX,
                                            float* pScreenY,
                                            D3DXVECTOR3* pVec)
{
    tempX = pVec->x;
    tempY = pVec->y;
    tempZ = pVec->z;

    tempX2 =
    ViewProjectionMatrix._11*tempX +
    ViewProjectionMatrix._21*tempY +
    ViewProjectionMatrix._31*tempZ +
    ViewProjectionMatrix._41;

    tempY2 =
    ViewProjectionMatrix._12*tempX +
    ViewProjectionMatrix._22*tempY +
    ViewProjectionMatrix._32*tempZ +
    ViewProjectionMatrix._42;

    tempW2 = ViewProjectionMatrix._14*tempX +
    ViewProjectionMatrix._24*tempY +
    ViewProjectionMatrix._34*tempZ +
    ViewProjectionMatrix._44;

    tempInvW2 = 1.0f/tempW2;

    *pScreenX = (1.0f + (tempX2*tempInvW2))*0.5f*screenwidth;
    *pScreenY = (1.0f - (tempY2*tempInvW2))*0.5f*screenheight;
}
Object-Culling 1/3

Es braucht Rechenzeit, um ein Objekt durch die
Transformations- und Beleuchtungspipeline zu schicken
und anschließend zu clippen.

Mit Object-Culling sondert man daher vorher die sowieso
nicht sichtbaren Teile aus.

Die einfache Variante des Culling prüft, ob ein Objekt vor
oder hinter dem Betrachter (bzw. der Kamera) ist:
tempFloat = D3DXVec3Dot(&Ortsvektor,PlayerBlickrichtung);

Der Rückgabewert ist das Skalarprodukt der reingegebenen Werte.

if(tempFloat < 0.0f)                                     ... und prüfen
     visible = FALSE;                                    Objekt hinter dem Spieler

Statistisch sind jetzt schonmal etwa 50% der Objekte
ausgesondert.

                               Und nun weg mit dem Rest ...
Object-Culling 2/3

Jetzt werden die Objekte ermittelt, die uns durch die
Lappen gegangen sind.

Es geht entweder so:
Den Betrag des Ortsvektors ermitteln:
Betrag = Calculate3DVectorLength(&Ortsvektor);

So erhält man den Kosinus des Blickwinkels:
tempFloat = tempFloat/Betrag;

Bei einem Blickfeld von angenommenen 90° sind Objekte
nur in einem Blickwinkel von +45° bis – 45° sichtbar.

Aus Sicherheitsgründen nehmen wir einen höheren Wert,
nämlich jeweils 53°. (Sonst kann es zu vorzeitigem Culling
kommen und das ist nicht schön.)
if(tempFloat < 0.6f)
     visible = FALSE;

0.6f ist cos(53°) bzw. cos(-53°). Aber die Berechnung ist
aufwendig, also optimierbar.

Alles Beschriebene geht aber zusammengefaßt auch
anders, sodass man eine schnellere Berechnung erhält.

                                              Und das geht so ...
Object-Culling 3/3

Um die Wurzelberechnung einzusparen, kann man mit
dem quadratischen Betrag und dem quadratischen
Skalarprodukt arbeiten. (Das ist einfach schneller.)

Der Wert der if-Abfrage muss natürlich auch der
quadratische Wert von cos(53°) sein, also nicht 0.6f
sondern 0.36f.
Doppelpufferung

Ein Bild muss irgendwie auf den Monitor gelangen.

Im Speicher (RAM oder VRAM) wird ein Bild als linearer
Array gespeichert. Jedes Array-Element entspricht einem
bestimmten Bildpunkt und speichert als Inhalt den
dazugehörigen Farbwert.

Darum ist der Speicherbedarf abhängig von der
eingestellten Farbtiefe. Bei der Bilderzeugung wird direkt
auf einen bestimmten Bereich des VRAM zugegriffen.

(siehe InitD3D(): Abfrage der Farbtiefe ff.)

Zur Vermeidung von Doppelbild- oder Flacker-Effekten
wird die sogenannte Doppelpufferung verwendet.

Das gesamte Bild wird in einen zweiten Speicherbereich,
den sogenannten Backbuffer, aufgebaut und anschließend
auf die Display-Surface kopiert (Fenster) oder geflippt
(Fullscreen).

Flipping ist schneller als Kopieren. Dabei werden einfach
die Zeiger vertauscht:
Der Backbuffer wird zur neuen Display-Surface und
umgekehrt.
Tiefenpufferung

Verdeckte Oberflachen sind abhängig von der Anordnung
der Polygone im Raum zur Kamera:
Objekte, die näher zur Kamera liegen, können Teile der
Objekte, die weiter weg sind, verdecken.

Um dies in den Griff zu bekommen, benutzt man die
Tiefenpufferung. (Verfahren zur Tiefenpufferung werden
durch DirectX zur Verfügung gestellt.)

Beispiel z-Buffer:

Funktioniert wie ein Array mit den gleichen Dimensionen
der Bildschirmauflösung. Darin sind die
Tiefeninformationen der Polygone auf Pixelbasis
gespeichert.

Vor dem Rendern wird der gesamte z-Buffer mit dem
maximal möglichen Tiefenwert belegt.

Beim Rendern eines Polygons wird pro Pixel überprüft, ob
der an der entsprechenden Position im z-Buffer
gespeicherte Tiefenwert größer oder kleiner als der
Tiefenwert des Pixels ist. Ist der gespeicherte Tiefenwert
größer als der des Pixels, wird er mit dessen Tiefenwert
überschrieben.
Anwendungsgerüst

Das Anwendungsgerüst ist stark modularisiert und
beinhaltet erstmal eine scheinbar unverhältnismäßig große
Menge an Dateien.

Wenn man genauer hinschaut ...
Anwendungsgerüst

... die vorgestellten Funktionen aus Kapitel 1 ...
Anwendungsgerüst

... das stellt uns DirectX zur Verfügung ...
Anwendungsgerüst

... Header-Dateien mit Deklaration von globalen Variablen
und Externals ...
Anwendungsgerüst

... und schon sieht das Ganze nicht mehr ganz so wild
aus.
„Hello World“ in DirectX

Das Projekt „First Contact“ ist hier unsere DirectX-
Entsprechung für ein herkömmliches „Hello World“
Programm.

Die eigentliche Funktionalität des Programms

      ●   DirectX initialisieren
      ●   Text ausgeben

Die zentralen Dateien sind (hier) Space3D.cpp und der
Header Space.h.

In Space3D.h werden alle verwendeten Programmmodule
eingebunden und die in Space3D.cpp definierten
Funktionen deklariert.

Durch diese Modularisierung verfügbare Funktionalität:

  ●   DirectX-Makros und Helperfunktionen
  ●   Textausgabe
  ●   eine lauffähige DirectX-Anwendung
  ●   mathematische Funktionen
  ●   Funktionen für die Ausrichtung von Billoards
  ●   Spielsteuerung und Sound
  ●   Klassen für Erzeugung von und Umgang mit Texturen
Programmablauf

GameShell

GameRoutines

   void GameInitialisierungsRoutine(void)
   {
       InitRenderOptions();               // in GameRoutines
       InitSpieleinstellungen();
       InitD3D();
       Init_View_and_Projection_Matrix();
   }

   void GameCleanUpRoutine(void)
   {
       CleanUpD3D();
   }

   void GameMainRoutine(void)
   {
        [ ... ]

       Show_3D_Scenario();

       [ ... ]
   }

Space3D
Programmablauf

Space3D.h

#include   
#include   
#include   "d3dutil.h"
#include   "d3dfont.h"
#include   "dxutil.h"
#include   "DXSubstitutions.h"
#include   "GameExternals.h"
#include   "D3DGlobals.h"
#include   "CoolMath.h"
#include   "tempGlobals.h"
#include   "TextureClasses.h"
#include   "GameGlobals.h"
#include   "VertexFormats.h"
#include   "BillboardFunctions.h"

void   InitSpieleinstellungen(void);
void   CleanUpD3D(void);
void   InitD3D(void);
void   Init_View_and_Projection_Matrix(void);
void   Show_3D_Scenario(void);
Programmablauf

Space3D.cpp

inline void Player_Flugdrehung(float winkel)

inline void Player_Horizontaldrehung(float winkel)

inline void Player_Vertikaldrehung(float winkel)

3   void Init_View_and_Projection_Matrix(void)

2   void InitD3D(void)

5   void CleanUpD3D(void)

1   void InitSpieleinstellungen(void)

4   void Show_3D_Scenario(void)
1. InitSpieleinstellungen()
void InitSpieleinstellungen(void)
{
    FILE* pfile;

    if((pfile = fopen("SpielEinstellungen.txt","r")) == NULL)
         Game_Shutdown();

    fscanf(pfile,"%s", strBuffer );
    fscanf(pfile,"%f", &SizeOfUniverse );

    if( SizeOfUniverse < 1000.0f )
        SizeOfUniverse = 1000.0f;

    SizeOfUniverseSq = SizeOfUniverse*SizeOfUniverse;

    fclose(pfile);
}

Funktion liest alle relevanten Grafikeinstellungen aus
SpielEinstellungen.txt heraus.

Inhalt SpielEinstellungen.txt:
Sichtweite_3DScenario:           2000
2. InitD3D() : Pseudocode
void InitD3D(void)
{

    Kordinaten des Bildschirmmittelpunktes speichern

    Erzeugung des Direct3D-Objektes

    Aktuellen Display-Modus für eine Fensteranwendung
    feststellen

    Ein geeignetes Pixelformat für eine Fullscreen-Anwendungen
    suchen

    Präsentationsparameter festlegen

              z-Buffer anlegen

              Warten, bis der vertikale Elektronenstahl-
              Rücklauf des Monitors beendet ist.
              Surface-Flipping erfolgt synchron
              zum Aufbau des Monitorbilds.

    Grafikkarten-Objekt anlegen, über das man auf die Treiber
    der primären Grafikkarte zugreifen kann

              Verarbeiten der ausgelesenen RenderingOption und
              Etablieren einer Vertexverarbeitung

    SamplerStates für 2 Texturstufen festlegen

    RenderStates festlegen

    2 Objekte die für die Textausgabe initialisieren
              zugehörige Klasse: d3dfont.cpp

}
2. InitD3D() 1/9
                                                    void InitD3D(void)
                                                    {

                                                    Kordinaten des Bildschirmmittelpunktes speichern:

                                                    screenwidth und screenheight deklariert in GameRoutines.h und definiert in
                                                    GameRoutines.cpp
                                                    centerX und centerY deklariert in D3DGlobals.h

                                                    centerX = screenwidth/2.0f;
                                                    centerY = screenheight/2.0f;

Kordinaten des Bildschirmmittelpunktes speichern
                                                    1.Schritt
Erzeugung des Direct3D-Objektes                     Erzeugung des Direct3D-Objektes mit (selbstgwähltem) Namen g_pD3D:
Aktuellen Display-Modus für eine Fensteranwendung
feststellen                                         siehe D3DGlobals.h: LPDIRECT3D9 g_pD3D = NULL;
                                                    Der Funktion Direct3DCreate9 wird einfach nur als Parameter die Konstante
Ein geeignetes Pixelformat für eine Fullscreen-     D3D_SDK_VERSION übergeben.
Anwendungen suchen
                                                    g_pD3D = Direct3DCreate9(D3D_SDK_VERSION);
Präsentationsparameter festlegen

Grafikkarten-Objekt anlegen, über das man auf die
Treiber der primären Grafikkarte zugreifen kann     Aktuellen Display-Modus für eine Fensteranwendung feststellen:
                                                    (Vorbereitung 2.Schritt: Checken, ob Fenster oder Fullscreen)
Verarbeiten der ausgelesenen RenderingOption und
Etablieren einer Vertexverarbeitung
                                                    Falls das Programm als Fensteranwendung gestartet werden sollte (g_bFullScreen
SamplerStates für 2 Texturstufen festlegen          == FALSE), dann wird der Displaymodus (Auflösung, Farbtiefe und Format) abgefragt
                                                    und in der Variable d3ddm vom Typ D3DDISPLAYMODE abgespeichert.
RenderStates festlegen

2 Objekte die für die Textausgabe initialisieren    D3DDISPLAYMODE d3ddm;

                                                    if(g_bFullScreen == FALSE)
                                                    g_pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);

                                                    ...
2. InitD3D() 2/9
                                                    Falls das Programm Fullscreen gestartet werden sollte:
                                                    ein geeignetes Pixelformat für eine Fullscreen-Anwendungen suchen;
                                                    grober Überblick (Erklärung siehe nächste Folie)

                                                    if(Farbtiefe == 16)
                                                    {

                                                         if(SUCCEEDED( g_pD3D->CheckDeviceType( D3DADAPTER_DEFAULT,
                                                                                                D3DDEVTYPE_HAL,
                                                                                                D3DFMT_R5G6B5,
                                                                                                D3DFMT_R5G6B5,
                                                                                                FALSE)))
Kordinaten des Bildschirmmittelpunktes speichern         {
Erzeugung des Direct3D-Objektes                               g_d3dfmtFullscreen = D3DFMT_R5G6B5;
                                                         }
Aktuellen Display-Modus für eine Fensteranwendung        else if(SUCCEEDED( g_pD3D->CheckDeviceType(
feststellen                                                                                     D3DADAPTER_DEFAULT,
                                                                                                D3DDEVTYPE_HAL,
Ein geeignetes Pixelformat für eine Fullscreen-                                                 D3DFMT_X1R5G5B5,
Anwendungen suchen
                                                                                                D3DFMT_X1R5G5B5,
Präsentationsparameter festlegen                                                                FALSE)))
                                                         {
Grafikkarten-Objekt anlegen, über das man auf die             g_d3dfmtFullscreen = D3DFMT_X1R5G5B5;
Treiber der primären Grafikkarte zugreifen kann          }
                                                         else if(SUCCEEDED( g_pD3D->CheckDeviceType(
Verarbeiten der ausgelesenen RenderingOption und
Etablieren einer Vertexverarbeitung                                                             D3DADAPTER_DEFAULT,
                                                                                                D3DDEVTYPE_HAL,
SamplerStates für 2 Texturstufen festlegen                                                      D3DFMT_X1R5G5B5,
                                                                                                D3DFMT_A1R5G5B5,
RenderStates festlegen                                                                          FALSE)))
                                                         {
2 Objekte die für die Textausgabe initialisieren
                                                              g_d3dfmtFullscreen = D3DFMT_A1R5G5B5;
                                                         }
                                                    }
                                                    else if(Farbtiefe == 32)
                                                    {
                                                           ... ...
                                                    }
2. InitD3D() 3/9
                                                    Falls in Fullscreen Modus gestartet wird, müssen geeignete Display- und
                                                    Backbufferformate gesucht werden, die von der Grafikkarte unterstützt
                                                    werden. Das macht CheckDeviceType.
                                                    Der Prototyp sieht so aus:
                                                    HRESULT CheckDeviceType (
                                                                      UINT Adapter,                 welche Grafikkarte?
                                                                      D3DDEVTYPE DeviceType,        Hardwarebeschl./Softwareemu.?
                                                                      D3DFORMAT DisplayFormat,      Display-Format?
                                                                      D3DFORMAT BackBufferFormat,   Backbuffer-Format?
                                                                      BOOL Windowed                 Fenster oder Fullscreen?
                                                                            );
Kordinaten des Bildschirmmittelpunktes speichern
                                                    Mit den DirectX-Makros SUCCEEDED() und FAILED() läßt sich mit dem
Erzeugung des Direct3D-Objektes
                                                    Rückgabewert HRESULT überprüfen, ob ein Funktionsaufruf erfolgreich ist
Aktuellen Display-Modus für eine Fensteranwendung   oder nicht.
feststellen                                         (Siehe D3DGlobals.h für Deklaration von D3DFORMAT g_d3dfmtFullscreen.)
Ein geeignetes Pixelformat für eine Fullscreen-     if(Farbtiefe == 16)
Anwendungen suchen
                                                    {
Präsentationsparameter festlegen

Grafikkarten-Objekt anlegen, über das man auf die        if(SUCCEEDED( g_pD3D->CheckDeviceType( D3DADAPTER_DEFAULT,
Treiber der primären Grafikkarte zugreifen kann                                                 D3DDEVTYPE_HAL,
                                                                                                D3DFMT_R5G6B5,
Verarbeiten der ausgelesenen RenderingOption und                                                D3DFMT_R5G6B5,
Etablieren einer Vertexverarbeitung
                                                                                                FALSE)))
SamplerStates für 2 Texturstufen festlegen               {
                                                              g_d3dfmtFullscreen = D3DFMT_R5G6B5;
RenderStates festlegen                                   }
2 Objekte die für die Textausgabe initialisieren
                                                    ... ... ...

                                                    else if(Farbtiefe == 32)
                                                    {
                                                           ... ...
                                                    }

                                                    Für Displayformate (D3DFMT_R5G6B5 etc.) siehe SDK: D3DFORMAT
2. InitD3D() 4/9
                                                    2.Schritt
                                                    Präsentationsparameter festlegen:

                                                    D3DPRESENT_PARAMETERS d3dpp; Struktur namens d3dpp erzeugen

                                                    alle Parameter unten siehe SDK: D3DPRESENT_PARAMETERS

                                                    ZeroMemory( &d3dpp, sizeof(d3dpp) );
                                                    d3dpp.Windowed = !g_bFullScreen;
                                                    d3dpp.BackBufferCount = 1;

                                                    z-Buffer anlegen:
Kordinaten des Bildschirmmittelpunktes speichern    If this value is TRUE, Direct3D will manage depth buffers for the application.
                                                    d3dpp.EnableAutoDepthStencil = TRUE;
Erzeugung des Direct3D-Objektes
                                                    d3dpp.AutoDepthStencilFormat = D3DFMT_D16; // 16-Bit z-Buffer
Aktuellen Display-Modus für eine Fensteranwendung
feststellen                                         if( g_bFullScreen )                Fullscreen?
                                                    {
Ein geeignetes Pixelformat für eine Fullscreen-
Anwendungen suchen                                       Art der Erzeugung von Wechsel Backbuffer/Display Surface
                                                         d3dpp.SwapEffect = D3DSWAPEFFECT_FLIP;
Präsentationsparameter festlegen
                                                           The maximum rate at which the swap chain's back buffers
Grafikkarten-Objekt anlegen, über das man auf die
Treiber der primären Grafikkarte zugreifen kann            can be presented to the front buffer.
                                                           d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE;
Verarbeiten der ausgelesenen RenderingOption und
Etablieren einer Vertexverarbeitung                        d3dpp.hDeviceWindow            =   main_window_handle;
                                                           d3dpp.BackBufferWidth          =   screenwidth;
SamplerStates für 2 Texturstufen festlegen
                                                           d3dpp.BackBufferHeight         =   screenheight;
RenderStates festlegen                                     d3dpp.BackBufferFormat         =   g_d3dfmtFullscreen;
                                                    }
2 Objekte die für die Textausgabe initialisieren    else                                      Fenster?
                                                    {
                                                           d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE;
                                                           d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
                                                           d3dpp.BackBufferFormat = d3ddm.Format;
                                                    }
2. InitD3D() 5/9
                                                    3.Schritt
                                                    Grafikkarten-Objekt anlegen, über das man auf die Treiber der
                                                    primären Grafikkarte zugreifen kann

                                                    RenderOption wird aus RenderingAndMore.txt in GameRoutines.cpp mit der Funktion
                                                    InitRenderOptions(void) ermittelt.

                                                    FILE* file;

                                                    if((file = fopen("protokoll.txt","w")) == NULL)
                                                         Game_Shutdown();

Kordinaten des Bildschirmmittelpunktes speichern    if(RenderingOption == 0) Transform_And_Lightning_with_PureDevice(0)
                                                    {
Erzeugung des Direct3D-Objektes                               // Versuche, Pure Device zu etablieren,
Aktuellen Display-Modus für eine Fensteranwendung             // versuche, Hardware-Vertexverarbeitung zu etablieren,
feststellen                                                   // versuche, gemischte Vertexverarbeitung zu etablieren,
                                                              // Software-Vertexverarbeitung etablieren
Ein geeignetes Pixelformat für eine Fullscreen-     }
Anwendungen suchen

Präsentationsparameter festlegen
                                                    else if(RenderingOption == 1) Transform_And_Lightning_without(1)
                                                    {
Grafikkarten-Objekt anlegen, über das man auf die             // Versuche, Hardware-Vertexverarbeitung zu etablieren,
Treiber der primären Grafikkarte zugreifen kann               // versuche, gemischte Vertexverarbeitung zu etablieren,
Verarbeiten der ausgelesenen RenderingOption und
                                                               // Software-Vertexverarbeitung etablieren
Etablieren einer Vertexverarbeitung                 }

SamplerStates für 2 Texturstufen festlegen          else if(RenderingOption == 2) Mixed_Transform_And_Lightning(2)
                                                    {
RenderStates festlegen                                        // Versuche, gemischte Vertexverarbeitung zu etablieren,
2 Objekte die für die Textausgabe initialisieren               // Software-Vertexverarbeitung etablieren
                                                    }

                                                    else if(RenderingOption == 3) Non_Transform_And_Lightning(3)
                                                    {
                                                            // Software-Vertexverarbeitung etablieren
                                                    }

                                                    fclose(file);
2. InitD3D() 6/9
                                                    Mit CreateDevice() wird ein Objekt erzeugt, über das auf die Grafikkarte zugegriffen
                                                    werden kann.

                                                    Staffelung nach Verarbeitungsgeschwindigkeit:
                                                    0-     Pure Device
                                                           Transform_And_Lightning_with_PureDevice
                                                    1-     Hardware-Vertexverarbeitung
                                                           Transform_And_Lightning_without_PureDevice
                                                    2-     gemischte Vertexverarbeitung
                                                           Mixed_Transform_And_Lightning
                                                    3–     Software-Vertexverarbeitung
                                                           Non_Transform_And_Lightning
Kordinaten des Bildschirmmittelpunktes speichern
                                                    Für einzelne Parameter siehe SDK-Dokumetation: CreateDevice
Erzeugung des Direct3D-Objektes

Aktuellen Display-Modus für eine Fensteranwendung   Beispiel:
feststellen
                                                    // versuche, Pure Device zu etablieren
Ein geeignetes Pixelformat für eine Fullscreen-     if(SUCCEEDED(g_pD3D->CreateDevice(
Anwendungen suchen

Präsentationsparameter festlegen                           D3DADAPTER_DEFAULT,
                                                           D3DDEVTYPE_HAL,
Grafikkarten-Objekt anlegen, über das man auf die          main_window_handle,
Treiber der primären Grafikkarte zugreifen kann            D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE,
                                                           &d3dpp,
Verarbeiten der ausgelesenen RenderingOption und
Etablieren einer Vertexverarbeitung
                                                           &g_pd3dDevice)))

SamplerStates für 2 Texturstufen festlegen
                                                    // versuche, Hardware-Vertexverarbeitung zu etablieren
RenderStates festlegen                              if(SUCCEEDED(g_pD3D->CreateDevice(
2 Objekte die für die Textausgabe initialisieren
                                                           D3DADAPTER_DEFAULT,
                                                           D3DDEVTYPE_HAL,
                                                           main_window_handle,
                                                           D3DCREATE_HARDWARE_VERTEXPROCESSING,
                                                           &d3dpp,
                                                           &g_pd3dDevice)))
2. InitD3D() 7/9
                                                    SamplerStates für 2 Texturstufen festlegen

                                                    [in] The sampler stage index
                                                    [in] any member of the D3DSAMPLERSTATETYPE enumerated type
                                                    [in] State value to set

                                                    Verwendung von bilinearer Textur-Filterung:

                                                    g_pd3dDevice->SetSamplerState(                0,
                                                                                                  D3DSAMP_MINFILTER,
                                                                                                  D3DTEXF_LINEAR);

Kordinaten des Bildschirmmittelpunktes speichern    g_pd3dDevice->SetSamplerState(                0,
                                                                                                  D3DSAMP_MAGFILTER,
Erzeugung des Direct3D-Objektes                                                                   D3DTEXF_LINEAR);
Aktuellen Display-Modus für eine Fensteranwendung   ... ebenso für SetSamplerState mit index 1
feststellen

Ein geeignetes Pixelformat für eine Fullscreen-
Anwendungen suchen                                  TexturStageStates für 2 Texturstufen einstellen:
Präsentationsparameter festlegen                    Die zu verwendenen Texturkoordinaten festlegen:
Grafikkarten-Objekt anlegen, über das man auf die
Treiber der primären Grafikkarte zugreifen kann     [in]   Stage identifier of the texture for which the state value
                                                           is set.
Verarbeiten der ausgelesenen RenderingOption und    [in]   Texture state to set. This parameter can be any member of the
Etablieren einer Vertexverarbeitung
                                                           D3DTEXTURESTAGESTATETYPE enumerated type.
SamplerStates für 2 Texturstufen festlegen          [in]   State value to set.

RenderStates festlegen                              g_pd3dDevice->SetTextureStageState(                0,
                                                                                                       D3DTSS_TEXCOORDINDEX,
2 Objekte die für die Textausgabe initialisieren                                                       0);

                                                    ... ebenso für SetTextureStageState mit index 1
2. InitD3D() 8/9
                                                    Prototyp:
                                                    HRESULT SetRenderState(           D3DRENDERSTATETYPE State,
                                                                                      DWORD Value );

                                                    [in]   Device state variable that is being modified. This parameter can be any member
                                                           of the D3DRENDERSTATETYPE enumerated type.

                                                    [in]   New value for the device render state to be set. The meaning of this parameter is
                                                           dependent on the value specified for State. For example, if State were
                                                           D3DRS_SHADEMODE, the second parameter would be one member of the
                                                           D3DSHADEMODE enumerated type.

Kordinaten des Bildschirmmittelpunktes speichern    RenderStates festlegen: Festlegung der Steuerung der Renderzustände
Erzeugung des Direct3D-Objektes
                                                    Culling im Uhrzeigersinn:
Aktuellen Display-Modus für eine Fensteranwendung   g_pd3dDevice->SetRenderState( D3DRS_CULLMODE,
feststellen                                                                       D3DCULL_CW);
Ein geeignetes Pixelformat für eine Fullscreen-     Dithering verwenden - verbessert die grafische Qualität der Darstellung:
Anwendungen suchen
                                                    g_pd3dDevice->SetRenderState( D3DRS_DITHERENABLE,
Präsentationsparameter festlegen                                                           TRUE);

Grafikkarten-Objekt anlegen, über das man auf die   speculare Lichtanteile bei der Beleuchtung berücksichtigen:
Treiber der primären Grafikkarte zugreifen kann     g_pd3dDevice->SetRenderState( D3DRS_SPECULARENABLE,
                                                                                            TRUE);
Verarbeiten der ausgelesenen RenderingOption und
Etablieren einer Vertexverarbeitung
                                                    Beleuchtung einschalten:
SamplerStates für 2 Texturstufen festlegen          g_pd3dDevice->SetRenderState( D3DRS_LIGHTING,
                                                                                  TRUE);
RenderStates festlegen

2 Objekte die für die Textausgabe initialisieren    z-Buffer einschalten:
                                                    g_pd3dDevice->SetRenderState( D3DRS_ZENABLE,
                                                                                  TRUE);

                                                    natürlich kein RenderState... wird später gebraucht
                                                    deklariert in D3DGlobals.h
                                                    Aspect = (float)screenwidth/(float)screenheight;
2. InitD3D() 9/9
                                                    zwei Objekte Initialisieren, die für die Textausgabe verwendet werden können.
                                                    Die Implementierung der zugehörigen Klasse befindet sich in der Datei d3dfont.cpp.

                                                    Font1->InitDeviceObjects(g_pd3dDevice);
                                                    Font1->RestoreDeviceObjects();

                                                    Font2->InitDeviceObjects(g_pd3dDevice);
                                                    Font2->RestoreDeviceObjects();

                                                    Hier wird die Initialisierung von 2 Instanzen der Klasse CD3DFont vorbereitet.

Kordinaten des Bildschirmmittelpunktes speichern    Die eigentliche Initialisierung erfolgt unmittelbar nach Deklaration in D3DGlobals.h:

Erzeugung des Direct3D-Objektes                     ...
Aktuellen Display-Modus für eine Fensteranwendung   Objekte für die Textausgabe
feststellen                                         CD3DFont* Font1           = new CD3DFont(_T("Arial"), 20, D3DFONT_BOLD );
Ein geeignetes Pixelformat für eine Fullscreen-
                                                    CD3DFont* Font2           = new CD3DFont(_T("Arial"), 10, D3DFONT_BOLD );
Anwendungen suchen
                                                    ...
Präsentationsparameter festlegen

Grafikkarten-Objekt anlegen, über das man auf die
Treiber der primären Grafikkarte zugreifen kann

Verarbeiten der ausgelesenen RenderingOption und
Etablieren einer Vertexverarbeitung

SamplerStates für 2 Texturstufen festlegen

RenderStates festlegen                              ... soviel zu dieser sehr langen Initialisierung in InitD3D()
2 Objekte die für die Textausgabe initialisieren
Zur Erinnerung: Programmablauf

GameShell

GameRoutines

   void GameInitialisierungsRoutine(void)
   {
       InitRenderOptions();                 //   in GameRoutines
       InitSpieleinstellungen();            //   hatten wir schon
       InitD3D();                           //   hatten wir schon
       Init_View_and_Projection_Matrix();   //   kommt jetzt
   }

   void GameCleanUpRoutine(void)
   {
       CleanUpD3D();
   }

   void GameMainRoutine(void)
   {
        [ ... ]

       Show_3D_Scenario();

       [ ... ]
   }

Space3D               ...    jetzt kommt
                             Init_View_and_Projection_Matrix()
Kurzer Zwischenblick auf D3DGlobals.h

In D3DGlobals.h werden alle möglichen Dinge deklariert
und/oder definiert, die u.a. für die Funktion
Init_View_and_Projection_Matrix() in SpaceD3D.cpp
genutzt werden.
...

float SizeOfUniverse    = 1000.0f;     // momentane Blickweite

D3DXVECTOR3     PlayerPosition;

D3DXVECTOR3   PlayerVerschiebungsvektor;
D3DXVECTOR3   PlayerFlugrichtung    (0.0f, 0.0f, 1.0f);
D3DXVECTOR3   PlayerVertikale       (0.0f, 1.0f, 0.0f);
D3DXVECTOR3   PlayerHorizontale     (1.0f, 0.0f, 0.0f);

D3DXMATRIX identityMatrix;           // Einheitsmatrix
D3DXMATRIX matView;                  // Viewmatrix
                                        "Das Auge der Kamera"
D3DXMATRIX matProj;                  // Projektionsmatrix
                                        3D-Welt auf Bildschirm
D3DXMATRIX ViewProjectionMatrix;     // matView*matProj
                                        Wird für die Bestimmung der
                                        Bildschirmkoordinaten von
                                        3D-Vektoren oder Vertices
                                        benötigt

...
Init_View_and_Projection_Matrix()

Nach der Initialisierung durch die Funktion InitD3D()
werden nun die Sicht- und Projektionsmatrizen erzeugt
und die zugehörigen Transformationen durchgeführt.
void Init_View_and_Projection_Matrix(void)
{
     PlayerPosition            = NullVektor;
     PlayerVerschiebungsvektor = NullVektor;

    PlayerFlugrichtung = D3DXVECTOR3(0.0f, 0.0f, 1.0f);
    PlayerVertikale    = D3DXVECTOR3(0.0f, 1.0f, 0.0f);
    PlayerHorizontale = D3DXVECTOR3(1.0f, 0.0f, 0.0f);

    D3DXMatrixLookAtLH( &matView,                         D3DXMATRIX * pOut
                        &PlayerPosition,                  D3DXVECTOR3 * pEye
                        &PlayerFlugrichtung,              D3DXVECTOR3 * pAt
                        &PlayerVertikale);                D3DXVECTOR3 * pUp

    g_pd3dDevice->SetTransform(           D3DTS_VIEW,
                                          &matView);

    If the method succeeds, the return value is D3D_OK

    D3DXMatrixPerspectiveFovLH(           &matProj, D3DXMATRIX * pOut
    FLOAT fovy                            45*D3DX_PI/180,
    FLOAT Aspect                          Aspect,
    FLOAT z                               1.0f, // niemals < 1.0 !!!!
    FLOAT zf                              SizeOfUniverse);

    g_pd3dDevice->SetTransform(           D3DTS_PROJECTION,
                                          &matProj);

    If the method succeeds, the return value is D3D_OK.
}
Init_View_and_Projection_Matrix()

Nach der Initialisierung durch die Funktion InitD3D()
werden nun die Sicht- und Projektionsmatrizen erzeugt
und die zugehörigen Transformationen durchgeführt.
void Init_View_and_Projection_Matrix(void)
{
     PlayerPosition            = NullVektor;
     PlayerVerschiebungsvektor = NullVektor;

    PlayerFlugrichtung = D3DXVECTOR3(0.0f, 0.0f, 1.0f);
    PlayerVertikale    = D3DXVECTOR3(0.0f, 1.0f, 0.0f);
    PlayerHorizontale = D3DXVECTOR3(1.0f, 0.0f, 0.0f);

    D3DXMatrixLookAtLH( &matView,                         D3DXMATRIX * pOut
                        &PlayerPosition,                  D3DXVECTOR3 * pEye
                        &PlayerFlugrichtung,              D3DXVECTOR3 * pAt
                        &PlayerVertikale);                D3DXVECTOR3 * pUp

    g_pd3dDevice->SetTransform(           D3DTS_VIEW,
                                          &matView);

    If the method succeeds, the return value is D3D_OK

    D3DXMatrixPerspectiveFovLH(           &matProj, D3DXMATRIX * pOut
    FLOAT fovy                            45*D3DX_PI/180,
    FLOAT Aspect                          Aspect,
    FLOAT z                               1.0f, // niemals < 1.0 !!!!
    FLOAT zf                              SizeOfUniverse);

    g_pd3dDevice->SetTransform(           D3DTS_PROJECTION,
                                          &matProj);

    If the method succeeds, the return value is D3D_OK.
}
Zur Erinnerung: Programmablauf

GameShell

GameRoutines

   void GameInitialisierungsRoutine(void)   // fertig
   {
       InitRenderOptions();                 //   in GameRoutines
       InitSpieleinstellungen();            //   hatten wir schon
       InitD3D();                           //   hatten wir schon
       Init_View_and_Projection_Matrix();   //   hatten wir schon
   }

   void GameCleanUpRoutine(void)
   {
       CleanUpD3D();
   }

   void GameMainRoutine(void)
   {
        [ ... ]

       Show_3D_Scenario();                  // kommt jetzt

       [ ... ]
   }

Space3D               ...    jetzt kommt
                             Show_3D_Scenario()
Show_3D-Scenario()
void Show_3D_Scenario(void)
{
1.Schritt: Löschen der vorangegangenen Szene
g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER,
                            D3DCOLOR_XRGB(0,0,0), 1.0f, 0 );

2.Schritt: neue Szene beginnen
g_pd3dDevice->BeginScene();

Font1->DrawTextScaled(           -0.55f, 0.0f, 0.9f,           Position
                                 0.06f, 0.02f,                 Skalierung
                                 D3DCOLOR_XRGB(0,0,250),       Farbe
                                 "Hello to all Game Programmers",
                                 D3DFONT_FILTERED);

Textausgabe geht auch so:
sprintf( strBuffer, "Framerate: %03d", (long)FrameRate );
Font1->DrawTextScaled(-0.8f, -0.8f, 0.9f, 0.03f, 0.02f,
D3DCOLOR_XRGB(250,0,0), strBuffer, D3DFONT_FILTERED );

sprintf( strBuffer, "Resolution: %d, %d, %d", screenwidth,
screenheight, Farbtiefe);
Font1->DrawTextScaled(-0.8f, -0.7f, 0.9f, 0.03f, 0.02f,
D3DCOLOR_XRGB(250,0,0), strBuffer, D3DFONT_FILTERED );

sprintf( strBuffer, "Blickweite: %0.1f", SizeOfUniverse );
Font1->DrawTextScaled(-0.8f, -0.6f, 0.9f, 0.03f, 0.02f,
D3DCOLOR_XRGB(250,0,0), strBuffer, D3DFONT_FILTERED );

3.Schritt: neue Szene beenden
(Die 3D-Szene ist komplett aufgebaut und fertig für die Anzeige)
g_pd3dDevice->EndScene();

4.Schritt: neue Szene anzeigen
g_pd3dDevice->Present( NULL, NULL, NULL, NULL );

}
Zur Erinnerung: Programmablauf

GameShell

GameRoutines

   void GameInitialisierungsRoutine(void)   // fertig
   {
       InitRenderOptions();                 //   in GameRoutines
       InitSpieleinstellungen();            //   hatten wir schon
       InitD3D();                           //   hatten wir schon
       Init_View_and_Projection_Matrix();   //   hatten wir schon
   }

   void GameCleanUpRoutine(void)
   {
       CleanUpD3D();                        // kommt jetzt
   }

   void GameMainRoutine(void)               // fertig
   {
        [ ... ]

       Show_3D_Scenario();                  // hatten wir schon

       [ ... ]
   }

Space3D               ...    jetzt kommt
                             CleanUpD3D()
CleanUpD3D()
void CleanUpD3D(void)
{
     Font1->DeleteDeviceObjects();
     Font2->DeleteDeviceObjects();

    SAFE_DELETE(Font1)
    SAFE_DELETE(Font2)

    Wird von einigen Billboard-Transformationsfunktionen
    verwendet
    SAFE_DELETE(Zufallswinkel)

    SAFE_RELEASE(g_pd3dDevice)
    SAFE_RELEASE(g_pD3D)
}
Punkte, Linien und Kreise

Das Anwendungsgerüst wird um die Datei
3D_ScenarioClass.h erweitert.

Darin ist die Funktionalität der Klasse C3DScenario
implementiert.

Im Prinzip sieht 3D_ScenarioClass.h so aus:
Einbinden von 3D_ScenarioClass.h

GameRoutines.h
extern void Init3DScenario(void);
extern void CleanUp3DScenario(void);

GameRoutines.cpp
void GameInitialisierungsRoutine(void)
{
    InitRenderOptions();
    InitSpieleinstellungen();
    InitD3D();
    Init_View_and_Projection_Matrix();
    Init3DScenario();
}

void GameCleanUpRoutine(void)
{
    CleanUp3DScenario();
    CleanUpD3D();
}

Space3D.h
#include "3D_ScenarioClass.h"

Space3D.cpp

In Space3D.cpp erfolgt das Rendern von Punikten, Linien
und Kreisen aus der Funktion Show_3D-Scenario().
Die Funktion wird entsprechend erweitert ...
Einbinden von 3D_ScenarioClass.h
void Show_3D_Scenario(void)
{

g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER,
                    D3DCOLOR_XRGB(0,0,0), 1.0f, 0 );

g_pd3dDevice->BeginScene();

Scenario->Render_Points();
Scenario->Render_Circle();

D3DCOLOR Color = D3DCOLOR_XRGB(0, 200, 0);

temp1Vektor3   = D3DXVECTOR3(-2.0f, -2.0f, 5.0f);
temp2Vektor3   = D3DXVECTOR3(2.0f, 2.0f, 5.0f);

Scenario->Render_Line(&temp1Vektor3, &temp2Vektor3, &Color );

temp1Vektor3   = D3DXVECTOR3(-2.0f, 2.0f, 5.0f);
temp2Vektor3   = D3DXVECTOR3(2.0f, -2.0f, 5.0f);

Scenario->Render_Line(&temp1Vektor3, &temp2Vektor3, &Color );

Font1->DrawTextScaled(-0.55f, 0.0f, 0.9f, 0.06f, 0.02f,
D3DCOLOR_XRGB(0,0,250),
"Hello to all Game Programmers", D3DFONT_FILTERED);

sprintf( strBuffer, "Framerate:   %03d", (long)FrameRate );

Font1->DrawTextScaled(-0.8f, -0.8f, 0.9f, 0.03f, 0.02f,
D3DCOLOR_XRGB(250,0,0), strBuffer, D3DFONT_FILTERED );

... noch mehr Text

g_pd3dDevice->EndScene();
g_pd3dDevice->Present( NULL, NULL, NULL, NULL );

}
Was passiert in 3D_ScenarioClass.h?

Flexible Vertexformate (FVF)

Je nach Anwendungszweck müssen für einem Vertex
verschiedene Daten gespeichert werden. Dazu zählen:
  ● Position

  ● Normale (für die Lichtberechnung)

  ● Farbe (in diesem Fall wird auf Lichtberechnung

    verzichtet)
  ● ein oder mehrere Sätze von Texturkoordinaten

    (TextureMapping, Multitexturing)

Für jeden Verwendungszweck kann ein geeignetes
Vertexformat definiert werden. Dies geschieht hier in
VertexFormats.h.

Für die Darstellung von Linien und Punkte wird dieses
Vertexformat verwendet:
struct POINTVERTEX
{
     D3DXVECTOR3 position;
     D3DCOLOR    color;
};

#define D3DFVF_POINTVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE)

D3DFVF_XYZ: Die Vertices müssen noch transformiert werden
D3DFVF_DIFFUSE: Die Vertices sind bereits beleuchtet
Was passiert in 3D_ScenarioClass.h?

Vertexbuffer

Damit eine 3D-Szene möglichst effizient transformiert
und gerendert werden kann, werden die Vertexdaten in
einem geschützen Speicherbereich, den sogenannten
Vertexbuffer, abgelegt.
g_pd3dDevice->CreateVertexBuffer(
     [1]                 AnzahlPunkte*sizeof(POINTVERTEX),
     [2]                 D3DUSAGE_WRITEONLY,
     [3]                 D3DFVF_POINTVERTEX,
     [4]                 D3DPOOL_MANAGED,
     [5]                 &PunkteVB,
     [6]                 NULL );

[3] Das Vertexformat, für den der Vertexbuffer angelegt wird
[5] Adresse eines Zeigers, der auf den Vertexbuffer verweist

Der Vertexbuffer ist ein geschützer Speicherbereich.
Darum muss er vor jedem Zugriff mit Lock() verriegelt
werden. Nach allen Schreibvorgängen durchgeführt
wurden, muss er mit Unlock() wieder entriegelt werden:
POINTVERTEX* pPointVertices;

PunkteVB->Lock(0, 0, (VOID**)&pPointVertices, 0);

... irgendein Zugriff (siehe 3D_ScenarioClass.h)

PunkteVB->Unlock();
Was passiert in 3D_ScenarioClass.h?

Vertexdaten transformieren und rendern

Bevor Vertxdaten gerendert werden können, müssen sie
durch die Transformations- und Beleuchtungspipeline
geschickt werde.

Über die Methode SetFVF() wird ein vertexshader
ausgewählt, mit SetStreamSource() werden die zu
rendernden Vertexdaten ausgewählt.
Zum Rendern der Vertexdaten eines Vertexbuffers wird
DrawPrimitive() verwendet.

Beispiel aus 3D_ScenarioClass.h:
void Render_Points(void)
{
     g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, FALSE);
     g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE);

    g_pd3dDevice->SetFVF(D3DFVF_POINTVERTEX);

    g_pd3dDevice->SetStreamSource(     0, PunkteVB, 0,
                                       sizeof(POINTVERTEX));

    g_pd3dDevice->DrawPrimitive( D3DPT_POINTLIST, 0,
                                 AnzahlPunkte);

    g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, TRUE);
    g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, TRUE);
}
Was passiert in 3D_ScenarioClass.h?

Grafikprimitiven

Es gibt im Grunde nur Punkte, Linien und Dreiecke.
Was passiert in 3D_ScenarioClass.h?

Kreis rendern 1/2

Ein Kreis ist ein Vieleck und wird hier unter Verwendung
eines Line Strip (siehe Render_Circle() )gerendert.
C3DScenario()
{
     ... ... ...

RingSegmente = 80; // des Kreises

g_pd3dDevice->CreateVertexBuffer(
                              RingSegmente*sizeof(POINTVERTEX),
                              D3DUSAGE_WRITEONLY ,
                              D3DFVF_POINTVERTEX,
                              D3DPOOL_MANAGED,
                              &GreenCircleVB, NULL );

GreenCircleVB->Lock( 0, 0, (VOID**)&pPointVertices, 0 );

float SegmentTeilwinkel = 2*D3DX_PI/(RingSegmente-1);

for( i = 0; i < RingSegmente; i++ )
{
     pPointVertices[i].position =
     D3DXVECTOR3(2.0f*sinf(i*SegmentTeilwinkel),
     2.0f*cosf(i*SegmentTeilwinkel),5.0f );

    pPointVertices[i].color     = D3DCOLOR_XRGB(0,200,0);
}

GreenCircleVB->Unlock();

}

                                       weiter mit Render_Circle() ...
Was passiert in 3D_ScenarioClass.h?

Kreis rendern 2/2
void Render_Circle(void)
{
     g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, FALSE );
     g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE);

    g_pd3dDevice->SetStreamSource(     0, GreenCircleVB, 0,
                                       sizeof(POINTVERTEX));

    g_pd3dDevice->SetFVF(D3DFVF_POINTVERTEX);

    g_pd3dDevice->DrawPrimitive( D3DPT_LINESTRIP, 0,
                                 RingSegmente-1);

    g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, TRUE);
    g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, TRUE);
}

Für die Anzahl der RingSegmente in DrawPrimitive() muss
1 abgezogen werden, weil es genau eine Linie weniger
gibt als Eckpunkte.

(Ein Kreis mit 9 Punkten hat 8 Linien, weil Endpunkt gleich
Startpunkt ist – sonst wäre der Kreis nicht geschlossen.)
Das war Teil 1 zum Anwendungsgerüst.

Es folgt Teil 2 mit der Implementierung der Textur-
Funktionalität.
Texturen
Texturen sind Bitmap-Dateien, die im Videospeicher der
Grafikkarte gespeichert sind.

Im Zusammenhang von Texturen spricht man nicht von
Pixeln, sondern von Texeln.

Nach Transformation eines 3D-Objekts werden die
Dreiecke, aus denen das Objekt zusammengesetzt sind,
mit derTextur überzogen.

Dies geschieht gemäß der in Vertices gespeicherten
Texturkoordinaten.

Das Verarbeiten von Texturen ist rechenintensiv.

Erschwert wird das Texture Mapping dadurch, dass
Objekte frei beweglich sind und entsprechend ihrem
Abstand zur Kamera vergrößert oder verkleinert
erscheinen.
Texturen
Als Texturkoordinaten werden gewöhnlich die Parameter
tu und tv verwendet.

Zum vollständigen Mappen über ein Polygon müssen
Texturkoordinaten im Bereich vom 0 bis 1 verwendet
werden. Das Koordinatenpaar (0,0) ist die obere linke
Ecke und (1,1) die untere rechte Ecke der Bitmap.
Die neuen Dateien, Klassen und Funktionen
Das Anwendungsgerüst wird um die folgenden Dateien
erweitert:

BildschirmSchablonenClass.h
Für die Darstellung der Bildschirmschablone wird die
Klasse CSchablone verwendet.

GiveInput.h
... wird in Kapitel 9 besprochen: unsere DirectInput
Bibliothek für die Mausabfrage.

SimpleCursorClass.h
Die Klasse CCursor wird für die Erzeugung und
Darstellung eines einfachen Cursors benötigt.

SimpleStarfield.h
Die Klasse CStarfield wird für die Erzeugung und
Darstellung eines Sternenfelds (im Hintergrund) genutzt.
Das ist die ausgelagerte Funktionalität des
Punkthintergrundes aus 'Punkte, Linien und Kreise'.
Was machen die neuen Dateien?
SimpleStarfieldClass.h ist das Ergebnis einer
Entrümpelung der ehemaligen Datei 3D_ScenarioClass.h
von 'Punkte, Linien und Kreise'.

Jetzt gibt es allerdings eine Klasse CStarfield, die die
Punkte erzeugt.

(Die Instanz wird in 3D_Scenario.h erzeugt)

BildschirmSchabloneClass.h besitzt eine
KlasseCSchablone, die für die Initialisierung, Darstellung
und Zerstörung eines beleuchteten und transformierten
Quads von der Größe des Bildschirms verwendet wird.

SimpleCursor.h besitzt eine Klasse Ccursor, die für die
Initialisierung, Darstellung und Zerstörung des Cursor-
Quads verwendet wird.

Dort wird eine Textur auf ein beleuchtetes Quad
gerendert.

Damit der Cursor immer an der aktuellen Mausposition
gerendert wird, muss vor dem Rendern eine
Welttransformation durchgeführt werden.
Die Klasse CTexturPool in TextureClasses.h

In TextureClasses.h ist die Klasse CTexturPool zum
Umgang mit Texturen implementiert.

Im Prinzip sieht diese Klasse so aus:

Die Fu nktionen lassen einen unterschiedlichen Umgang mit Texturen zu, ansonsten
machen sie alle das Gleiche – nämlich Texturen erzeigen:

CreateTextur(): Textur mit vollständiger Mipmap-Kette
schwarze Bereiche sind bei eingeschaltetem Alpha Blending transparent

CreateNonTransparentTextur(): Textur mit vollständiger Mipmap-Katte ohne
Transparenz

CreateTextur_withoutMipmaps() /
CreateNonTransparentTextur_withoutMipmaps():
Textur ohne vollständige Mipmap-Kette mit / ohne Transparenz
Die Klasse CTexturPool in TextureClasses.h
Die 4 implementierten Methoden in der Klasse
CTexturPool rufen alle den Prototyp
D3DXCreateTextureFromFileEx() mit jeweils
verschiedenen Parametern auf.

Siehe SDK-Dokumentation: D3DXCreateTextureFromFileEx

Die Methode CreateTexture() beinhaltet z.B. folgenden
Aufruf:
D3DXCreateTextureFromFileEx(

g_pd3dDevice,           Zeiger auf Device Objekt
Speicherpfad,           TexturPfad
D3DX_DEFAULT,           Texturbreite in Pixeln
D3DX_DEFAULT,           Texturhoehe in Pixeln
D3DX_DEFAULT,           Anzahl Mipmaps
0,                      Verwendungszweck
D3DFMT_UNKNOWN,         Pixelformat
D3DPOOL_MANAGED,        Art der Texturverwaltung

                         für dieTtexturerstellung:
D3DX_FILTER_TRIANGLE|D3DX_FILTER_MIRROR|D3DX_FILTER_DITHER,
                         Mipmap-Filter:
D3DX_FILTER_BOX|D3DX_FILTER_MIRROR|D3DX_FILTER_DITHER,

D3DCOLOR_XRGB(0,0,0),   bei Alphablending transparente Farbe
0,                      unwichtig: NULL
0,                      unwichtig: NULL
&pTexture               Adresse des Texturzeigers

);
Die Klasse CTexturPoolEx in
TextureClasses.h

Eine Arbeitshilfe:

Mit Hilfe der Klasse CTexturPoolEx lassen sich mehrere
Texturen in einem Zug erzeugen. (Siehe Woche 3)

Dazu muss ein Textur-Stammname an ein Objekt der
Klasse CtexturPoolEx übergeben werden. Anschließend
wird die Methode InitTextures() aufgerufen.

Beispiel:
EineTextur1M1.bmp
EineTextur1M2.bmp
EineTextur1M3.bmp

MeineTextur = new CTexturPoolEx;
sprintf(MeineTextur->Speicherpfad, "EineTextur1");
MeineTextur->InitTextur();
Erzeugen von Texturen
Wenn eine Textur erzeugt werden soll, braucht man dafür
grundsätzlich 3 Schritte:

  ●   1. einTexturklassen Objekt erzeugen

      Beispiel: CursorTextur   = new CTexturPool;

  ●   2. Speicherpfad der Textur übergeben

      Beispiel: sprintf(CursorTextur->Speicherpfad,     "Cursor.bmp");

  ●   3. Textur erzeugen

      Beispiel: CursorTextur->CreateTextur();
(siehe SimpleCursorClass.h und BildschirmSchablone.h)
Einbinden in das Anwendungsgerüst
Die neue Funktionalität wird in folgende Dateien
eingebaut:

  ●   GameRoutine.h
  ●   GameRoutines.cpp
  ●   Space3D.h
  ●   Space3D.cpp
  ●   3D_ScenarioClass.h
Einbinden in das Anwendungsgerüst
GameRoutine.h

... ... ...
//---------------------------------------------------------
// EXTERNALS
//---------------------------------------------------------

extern HWND main_window_handle;
extern HINSTANCE hinstance_app;

//---------------------------------------------------------
// externe Funktionsprototypen
//---------------------------------------------------------

extern HRESULT InitDirectInput(HINSTANCE, HWND);
extern void FreeDirectInput(void);

extern void ReadImmediateDataMouse(void);
extern void ReadImmediateDataJoystick(void);
extern void ReadImmediateDataKeyboard(void);

...

extern void BuildIdentityMatrices(void);

 ... ... ...
Einbinden in das Anwendungsgerüst
GameRoutines.cpp
void GameInitialisierungsRoutine(void)
{

     if(InitDirectInput(hinstance_app, main_window_handle) ==
          S_FALSE)
    {
        Game_State = -1000; // nicht existierender Game-State
        SendMessage(main_window_handle,WM_CLOSE,0,0);
        return;
    }
     ... ... ...
    BuildIdentityMatrices();
     ... ... ...
}

void GameCleanUpRoutine(void)
{
    FreeDirectInput();
     ... ... ...
}

void GameMainRoutine(void)
{
    if(Game_State == -1)
    {
        start_time = GetTickCount();

        if(Pause == FALSE)
        {
            Show_3D_Scenario();
        }

        // Eingabegeräte (Joystick, Maus und Keyboard) abfragen:
        ReadImmediateDataMouse();

 ... ... ...
}
Einbinden in das Anwendungsgerüst
Space3D.h
 ... ... ...

//-------------------------------------------------------
// Funktionsprototypen
//-------------------------------------------------------
 ... ... ...

void BuildIdentityMatrices(void);

//-------------------------------------------------------
// zusätzliche Includes
//-------------------------------------------------------

#include   "SimpleStarfieldClass.h"
#include   "SimpleCursorClass.h"
#include   "BildschirmSchablonenClass.h"
#include   "3D_ScenarioClass.h"

#include "GiveInput.h"
Einbinden in das Anwendungsgerüst
Space3D.cpp
void BuildIdentityMatrices(void)
{
    D3DXMatrixIdentity(&g_ObjectKorrekturMatrix);
    D3DXMatrixIdentity(&identityMatrix);
    D3DXMatrixIdentity(&g_ScaleMatrix);
    D3DXMatrixIdentity(&g_VerschiebungsMatrix);
    D3DXMatrixIdentity(&g_VerschiebungsMatrix3);
}
void Show_3D_Scenario(void)
{
 ...
       Scenario->New_Scene(); Methode in 3d_Scenario.h
 ...
}

Bei Programmaufruf (siehe GameRoutines.cpp) werden
mit BuildIdentityMatrices() die global verwendeten
Matrizen als Eineitsmatrizen initialisiert.

Zur Erinnerung die Reihenfolge des Funktionsaufrufs in
GameRoutines.cpp:
InitRenderOptions();
InitSpieleinstellungen();
InitD3D();
Init_View_and_Projection_Matrix();
BuildIdentityMatrices();
Init3DScenario();
Einbinden in das Anwendungsgerüst
3D_ScenarioClass.h
void Init3DScenario(void);
void CleanUp3DScenario(void);

class C3DScenario
{
public:

    CStarfield* Starfield;
    CCursor*    Cursor;
    CSchablone* Schablone;

    C3DScenario()         alle Elemente der 3D-Szene werden initialisiert
    {
        Starfield = new CStarfield;
        Cursor     = new CCursor;
        Schablone = new CSchablone;
    }
    ~C3DScenario()        alle Elemente der 3D-Szene werden zerstört
    {
        SAFE_DELETE(Schablone)
        SAFE_DELETE(Starfield)
        SAFE_DELETE(Cursor)
    }

Diese Methode wird durch Show_3D_Scenario() in Space3D.cpp aufgerufen:
     void New_Scene(void)
     {
          Starfield->Render_Starfield();
          Cursor->Render_Cursor();
          Schablone->Render_Schablone();
     }
};
C3DScenario* Scenario = NULL;

void Init3DScenario(void)    { Scenario = new C3DScenario; }
void CleanUp3DScenario(void) { SAFE_DELETE(Scenario) }
Was machen die neuen Dateien?
SimpleStarfieldClass.h ist das Ergebnis einer
Entrümpelung der ehemaligen Datei 3D_ScenarioClass.h
von 'Punkte, Linien und Kreise'.

Jetzt gibt es allerdings eine Klasse CStarfield, die die
Punkte erzeugt.

(Die Instanz wird in 3D_Scenario.h erzeugt)

BildschirmSchabloneClass.h besitzt eine
KlasseCSchablone, die für die Initialisierung, Darstellung
und Zerstörung eines beleuchteten und transformierten
Quads von der Größe des Bildschirms verwendet wird.

SimpleCursor.h besitzt eine Klasse Ccursor, die für die
Initialisierung, Darstellung und Zerstörung des Cursor-
Quads verwendet wird.

Dort wird eine Textur auf ein beleuchtetes Quad
gerendert.

Damit der Cursor immer an der aktuellen Mausposition
gerendert wird, muss vor dem Rendern eine
Welttransformation durchgeführt werden.
Sie können auch lesen