Sie sind auf Seite 1von 4

Terrain in DirectX 9 und C++ - Teil 2

Dieses Tutorial baut auf dem ersten dieser Reihe auf, also bitte lest erst das Erste. So und nun gehts auch schon los. In diesem Teil beschftigen wir uns mit folgendem: 1. Das Terrain anhand der Hhe einfrben 2. Scaling die Gre des Terrains anpassen 3. Eine Color-Map 4. Die Detail-Map

1. Das Terrain anhand der Hhe einfrben


Nun werden wir das Terrain anhand der Hhe einfrben. Wir werden das Terrain ganz Unten blau einfrben, in der Mitte grn und Oben rot. Das werden wir hier ganz simpel machen. Man kann es natrlich viel komplizierter machen und auch ein paar schnere Ergebnisse erzielen, aber da wir im nchsten Schritt diese Farben sowieso wieder verwerfen werden, lohnt sich der Aufwand nicht. Also als erstes erweitern wir das Vertex-Format:

//Our terrain-vector struct STerrainVektor { Vector3 vPos; DWORD dwColor; static DWORD dwFVF; }; DWORD STerrainVektor::dwFVF = D3DFVF_XYZ|D3DFVF_DIFFUSE;
Auch das hier sollte klar sein: Wir fgen noch eine Variable fr die Farbe hinzu und eine statische die das FVF speichern soll. Nun ndern wir schonmal die SetFVF Methode so um, das sie immer das VertexFormat des Terrainvertex nutzt. Aus

g_pD3DDevice->SetFVF(D3DFVF_XYZ);
wird

g_pD3DDevice->SetFVF(STerrainVektor::dwFVF);
Anschlieend mssen wir nurnoch die Farbe entsprechend der Hhe setzten. Das ist ganz einfach und auch dafr erstellen wir eine eigene Funktion, um es spter einfacher zu haben. Die Funktion ist ganz einfach: Bis zu einer Hhe von 50 Einheiten ist das Terrain blau, bis zu einer Hhe von 160 Einheiten ist das Terrain grn und der Rest, der ber 160 Einheiten liegen muss ist rot.

void ComputeColorFromHeigh(STerrainVektor *pVertex) { if(pVertex->vPos.y<50.0f)// < 50m pVertex->dwColor = 0x0000ff; else if(pVertex->vPos.y<160.0f) // >= 40m & < 160m pVertex->dwColor = 0x00ff00; else // >= 160m pVertex->dwColor = 0xff0000; }
So, nun mssen wir natrlich noch die CreateVertieces-Funktion ndern. Um die Faben zu eintragen zu lassen, fgen wir einfach hinter die Zuweisung der Position eine for-Schleife ein, die fr jede der 6 Ecken die Farbe eintrgt:

pVertieces[Index+4].vPos = Vector3(x,pVertexData[(z+1)*SizePerSide+x],z+1); pVertieces[Index+5].vPos = Vector3(x+1,pVertexData[(z+1)*SizePerSide+x+1],z+1); for(int i=0;i<6;i++) { ComputeColorFromHeigh(&pVertieces[Index+i]); }


Und das war es auch schon. Jetzt sollte man die Position der Kamera nurnoch so anpassen das man mglichst viel des Terrains sieht. Beim mir ist es so Optimal:

D3DXMatrixLookAtLH(&mView,&D3DXVECTOR3(-60,130,-60),&D3DXVECTOR3(0.0f,130.0f,0.0f),&D3DXVECTOR3(0.0f,1.0f,0.0f));
Aber das muss nun jeder selbst vornehmen. Wie immer findet ihr den Sourceode zu diesem Abschnitt als Anlage unter dem Namen Step_03.zip. Und das ist mein Ergebniss:

2. Scaling die Gre des Terrains anpassen

Als nchstes geht es darum, das Terrain ein bisschen zu Scalieren. Wir werden hier unser Terrain auf doppelte Gre bringen, dabei aber die Hhe auf die Hlfte verringern. Ich denke statt einer mglichen Maximalhhe von 255 Einheiten (1 Byte = 255) reicht uns eine Maximalhhe von 127,5 voll aus. Auerdem ist das Detail des Terrains ein wenig hoch, wenn man bedenkt, dass ein Mensch o.. spter im Spiel 1,8 Einheiten gro ist, und ein Quadrat des Terrains gerade mal 1 Einheit Breit. Dazu verndern wir die Funktion CreateVertieces. Wir ndern sie so das wir ihr einen 3D-Vektor zustzlich bergeben und dieser dann mit der Positionsangabe mal genommen wird:

BOOL CreateVertieces( UINT SizePerSide, BYTE *pVertexData, Vector3 vScaling, STerrainVektor **ppVertieces,UINT *uiTriangleCount) { ... for(int i=0;i<6;i++) { ComputeColorFromHeigh(&pVertieces[Index+i]); pVertieces[Index+i].vPos *= vScaling; } ... }
Damit das auch alles so Funktioniert mssen wir unsere Vector3-Struktur nochmal etwas verndern. Wir fgen einen eigenen *= Operator an. Dieser gehrt in die Struktur!

Vector3& operator *= (Vector3 &v) { x*=v.x; y*=v.y; z*=v.z; return *this; }


Jetzt geben wir beim Aufruf der Funktion CreateVertieces noch den Vector ( 2 | 0.5 | 2 ) an.

if(!CreateVertieces(64,pHeightData,Vector3(2.0f,0.5f,2.0f),&pVertieces,&uiTriangleCount)) return FALSE;


Und wir sollten ungefhr folgendes Bild erhalten: (Projekt zu diesem Zeitpunkt zu finden im Anhang als Step_04.zip)

Ich finde das sieht doch schon wesentlich besser aus. Die Hhen stehen wesentlich besser im Verhltnis zu der Breite des Terrains.

3. Eine Color-Map
So als nchstes wollen wir auf unser Terrain eine erste Textur legen. Eine sogenannte Color-Map. Sie wird einmal ber das komplette Terrain gelegt und gibt ihm seine Grundfarbe. Meistens wird darber noch eine Detail-Map gelegt, die dem Terrain dann eine Struktur gibt. Auch wenn wir diese Technik spter nicht mehr verwenden werden, mchte ich sie doch einmal vorstellen. Als erstes passen wir unsere Kamera wieder auf unser Terrain an. Dies muss wieder jeder selbst machen fr seine HMAP. Es sollte mglichst viel des Terrains sichtbar sein. Bei mir ist folgendes in Ordnung:

D3DXMatrixLookAtLH(&mView,&D3DXVECTOR3(-30,60,-30),&D3DXVECTOR3(0.0f,60.0f,0.0f),&D3DXVECTOR3(0.0f,1.0f,0.0f));
Nun mssen wir unseren Vertex anpassen. Um eine Color-Map darber zu legen, fgen wir zu unserem Vertex zwei float Variablen hinzu, die die Texturkoordinaten des Vertex enthalten sollen und passen natrlich auch das FVF dementsprechend an:

//Our terrain-vector struct STerrainVektor { Vector3 vPos; DWORD dwColor; float fTexX,fTexY; static DWORD dwFVF; }; DWORD STerrainVektor::dwFVF = D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1;
Anschlieend mssen wir auch unsere CreateVertieces Funktion mal wieder bearbeiten. In der for-Schleife die das Scaling vornimmt fgen wir noch die Berechnung ein. Der Wert der X- bzw. Y-Koordinate soll 0 sein bei einer Position von 0 und 1 sein bei der grt mglichen Position, bei uns 128 (64*2). Folgendes schreiben wir also ans ende der Schleife:

pVertieces[Index+i].fTexX = (pVertieces[Index+i].vPos.x)/(SizePerSide*vScaling.x); pVertieces[Index+i].fTexY = (pVertieces[Index+i].vPos.z)/(SizePerSide*vScaling.z);


Auerdem lassen wir die Farbe nichtmehr von der Hhe bestimmen, sondern setzten sie erst einmal auf Wei (0xffffff). Nun sieht unsere for-Schleife so aus:

for(int i=0;i<6;i++) { pVertieces[Index+i].dwColor = 0xffffff; pVertieces[Index+i].vPos *= vScaling; //Compute texture-coordinats pVertieces[Index+i].fTexX = (pVertieces[Index+i].vPos.x)/(SizePerSide*vScaling.x); pVertieces[Index+i].fTexY = (pVertieces[Index+i].vPos.z)/(SizePerSide*vScaling.z); }
Als nchstes mssen wir natrlich noch eine Textur laden und sezten. Das tuen wir in der WinMain-Funktion nachdem wir die Vertieces erstellt haben. Ich benutze hier die Funktion D3DXCreateTextureFromFile.

//Load and set texture PDIRECT3DTEXTURE9 pColorMap; D3DXCreateTextureFromFileA(g_pD3DDevice,"ColorMap.bmp",&pColorMap); g_pD3DDevice->SetTexture(0,pColorMap);


Nun mssen wir nurnoch eine Color-Map erstellen und als ColorMap.bmp abspeichern. Dabei ist die Gre der Color-Map egal. Je hher die Auflsung desto schner sieht spter das Terrain aus. Statt eine Color-Map zu erstellen knnt ihr natrlich auch einfach ein Foto oder Bild benutzten zum Testen. Ich habe mal eine Color-Map zu meiner HMAP erstellt. Sie hat die gleiche Gre, was euch aber nicht abhalten soll eine Grere fr eure HMAP zu erstellen. Meine sieht so aus:

Tipp: Wir ihr seht ist meine Color-Map nicht ganz gleichmig gefrbt. Darauf solltet ihr unbedingt achten. Were alles grn jetzt nur in einer Frbe wre das Terrain wesentlich langweiliger. Am leichtesten geht sowas in Gimp oder PS mit Filtern. Soweit so gut. Ihr knnt das Projekt bis hierhin unter dem Namen Step_05.zip im Anhang finden. So sieht mein ergebnis aus:

4. Die Detail-Map
Wie ihr euch vielleicht schon denken knnt, dient die Detail-Map dazu, ein wenig Detail zum Terrain hinzu zu fgen. Oft ist die Detail-Map einfach nur ein Bild mit einer bestimmten Struktur die dann auf das Terrain gelegt wird. Also werft eure Zeichenprogramme an und erstellt euch eine Struktur. Hier gilt auch wieder: Je grer desto schner aber umso langsamer wird das Terrain gerendert. Meine Detail-Map sieht so aus:

Nun machen wir uns an das Programmieren. Als erstes erweitern wir unseren Vertex. Wir bentigen ein weiteres Paar Texturkoordinaten fr unsere Detail-Map und mssen wieder mal das FVF anpassen:

//Our terrain-vector struct STerrainVektor { Vector3 vPos; DWORD dwColor; float fTexX,fTexY; float fDetailX, fDetailY; static DWORD dwFVF; }; DWORD STerrainVektor::dwFVF = D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX2;
Als nchstes muss die Funktion mal wieder herhalten. Wir erweitern sie um einen weiteren Parameter der angiebt, wie oft unsere Detail-Map auf dem gesamten Terrain angeziegt wird.

BOOL CreateVertices( UINT SizePerSide, BYTE *pVertexData, Vector3 vScaling, D3DXVECTOR2 vDetailMap, STerrainVektor **ppVertices,UINT *uiTriangleCount)

Danach generieren wir die Texturkoordinaten fr unsere Detail-Map, indem wir die Position des Vertex durch die maximale Position teilen und mit der Anzahl der Wiederholungen mal nehmen. Folgendes schreiben wir ans Ende der for-Schleife die auch schon unsere anderen Texturkoordinaten eintrgt:

pVertices[Index+i].fDetailX = ((pVertices[Index+i].vPos.x)/(SizePerSide*vScaling.x))*vDetailMap.x); pVertices[Index+i].fDetailY = ((pVertices[Index+i].vPos.z)/(SizePerSide*vScaling.z))*vDetailMap.y);


Nun laden wir die Detail-Map und legen sie auf Stage 2 und auerdem setzten wir anschlieend die Farboperation von Stage 2 auf Modulate. Das ganze kommt direkt nach dem laden der Color-Map.

//Load and set Detail-Map PDIRECT3DTEXTURE9 pDetailMap; D3DXCreateTextureFromFileA(g_pD3DDevice,"DetailMap.bmp",&pDetailMap); g_pD3DDevice->SetTexture(1,pDetailMap); //compinate texture 1 and 2 g_pD3DDevice->SetTextureStageState(1,D3DTSS_COLOROP,D3DTOP_MODULATE);
Nun ndern wir den Aufruf der CreateVertices Funktion nurnoch, sodass unsere Detail-Map genau 128 mal in jede Richtung gekachelt wird:

CreateVertices(64,pHeightData,Vector3(2.0f,0.5f,2.0f),D3DXVECTOR2(128,128),&pVertices,&uiTriangleCount)
Und jetzt sind wir ausch schon fertig. Bewegt man die Kamera jetzt ganz nah an das Terrain, erkennt man schon eine Struktur. Natrlich ist das noch nicht reif fr ein Spiel, aber ich denke, man kann auch noch selbst ein bisschen optimieren wenn man sich dafr entscheiden sollte sein Terrain so einzufrben. Zum Beispiel knnte man durch einen Shader die DetailMap in grerer Entfernung weniger oft nebeneinander setzten, nahe bei der Kamera dann etwas fter. Dadurch wrde verhindet, dass man schon auf den ersten Blick erkennt, das die Detail-Textur ziemlich oft nebeneinander liegt. In einem der nchsten Tutorials werde ich dann auf das Einfrben des Terrain mit der Texture Splatting Technik, bevor wir uns dann daran machen eine vernnftige Klasse fr das ganze zu schreiben. Naja wie immer das Projekt zum jetzigen Zeitpunkt findet ihr im Anhang als Step_06.zip, und so sieht das ganze bei mir aus:

5. Anhang
Step_03.zip, Step_03.jpg Step_04.zip, Step_04.jpg Step_05.zip, Step_05.jpg Step_06.zip, Step_06.jpg ColorMap.bmp DetailMap.bmp