Lektion 34
Willkomen zu einem weiteren aufregendem Tutorial! Der Code für dieses Tutorial wurde von Ben Humphrey geschrieben und basiert auf dem Framework aus Lektion 1. Von nun an, sollten Sie ein GL Experte sein {grins} und den Code in Ihren eigenen Basis-Code einzufügen, sollte ein Leichtes sein!
Dieses Tutorial bringt Ihnen bei, wie Sie ein cool aussehendes Terrain aus einer Height Map erzeugen. Für die unter Ihnen, die sich nichts unter einer Height Map vorstellen können, versuche ich eine grobe Erklärung abzugeben. Eine Height Map ist einfach... eine Ersetzung einer Oberfläche. Für die unter Ihnen, die sich immer noch den Kopf kratzen und sich selbst fragen "worüber redet der Kerl überhaup!?!"... Auf deutsch: unsere Height Map repräsentiert tiefe und hohe Punkte unserer Landschaft. Es liegt völlig bei Ihnen zu entscheiden, welche Schattierungen hohe Punkte und welche Schattierungen tiefe Punkte repräsentieren. Es ist auch wichtig zu erwähnen, dass Height Maps keine Bilder sein müssen... Sie können eine Height Map erzeugen, indem Sie einfach irgendwelche Daten verwenden. Sie können zum Beispiel einen Audio Stream verwenden um eine visuelle Height Map Repräsentation zu erzeugen. Wenn Sie immer noch verwirrt sind... lesesn Sie weiter... Sie werden es am Ende des Tutorials begriffen haben :)
#include <windows.h> // Header Datei für Windows
#include <stdio.h> // Header Datei für Standard Input/Output ( NEW )
#include <gl\gl.h> // Header Datei für die OpenGL32 Library
#include <gl\glu.h> // Header Datei für die GLu32 Library
#include <gl\glaux.h> // Header Datei für die Glaux Library
#pragma comment(lib, "opengl32.lib") // Linke OpenGL32.lib
#pragma comment(lib, "glu32.lib") // Linke Glu32.lib
Wir fangen damit an, einige wichtige Variablen zu definieren. MAP_SIZE ist die Dimension unserer Map. In diesem Tutorial wird die Map 1024x1024 groß sein. Die Schrittgröße STEP_SIZE ist die Größe eines jeden Quadrats, welches wir auf unserer Landschaft zeichnen. Indem wir die Schrittgröße verringern, wird unsere Landschaft detaillierter und weicher. Es ist wichtig zu erwähnen, dass, je kleiner die Schrittgröße, desto mehr Performance wird benötigt, insbesondere wenn Sie große Height Maps verwenden. HEIGHT_RATIO wird verwendet, um die Landschaft auf der Y-Achse zu skalieren. Je kleiner HEIGT_RATIO ist, desto flacher sind die Hügel. Je höher HEIGHT_RATIO, desto größer / sichtbarer sind die Hügel.
Später im Code werden Sie bRender entdecken. Wenn bRender auf True gesetzt ist (was es standardmäßig ist), werden wir solide Polygone zeichnen. Wenn wir bRender auf False setzen, wird die Landschaft als Drahtgittermodell gezeichnet.
#define MAP_SIZE 1024 // Größe unsereR .RAW Height Map ( NEU )
#define STEP_SIZE 16 // Breite und Höhe jedes Quadrats ( NEU )
#define HEIGHT_RATIO 1.5f // Verhältnis indem Y zu X und Z skaliert wird ( NEU )
HDC hDC=NULL; // Privater GDI Device Context
HGLRC hRC=NULL; // Permanenter Rendering Context
HWND hWnd=NULL; // Enthält unser Fenster-Handle
HINSTANCE hInstance; // Enthält die Instanz der Applikation
bool keys[256]; // Array das für die Tastatur Routine verwendet wird
bool active=TRUE; // Fenster Aktiv Flag ist standardmäßig auf TRUE gesetzt
bool fullscreen=TRUE; // Fullscreen Flag ist standardmäßig auf TRUE gesetzt
bool bRender = TRUE; // Polygon Flag ist standardmäßig auf TRUE gesetzt ( NEU )
Hier erstellen wir ein Byte-Array (g_HeightMap[]), das unsere Height Map Daten enthalten wird. Da wir eine .RAW-Datei lesen, die nur Werte zwischen 0 und 255 enthält, können wir die Werte als Höhen-Werte benutzen, mit 255 als höchsten Punkt und 0 als tiefsten Punkt. Wir erzeugen auch eine Variable namens scaleValue, um die gesamte Szene zu skalieren. Das gibt dem Benutzer die Möglichkeit heran und wegzuzoomen.
BYTE g_HeightMap[MAP_SIZE*MAP_SIZE]; // enthält die Height Map Daten ( NEU )
float scaleValue = 0.15f; // Skalierungswert für das Terrain ( NEU )
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // Deklaration For WndProc
Der ReSizeGLScene() Code ist der selbe wie in Lektion 1 mit der Ausnahme, dass die entfernteste Distanz von 100.0f auf 500.0f geändert wurde.
GLvoid ReSizeGLScene(GLsizei width, GLsizei height) // verändert die Größe und initialisiert das GL-Fenster
{
... SCHNITT ...
}
Der folgende Code lädt die .RAW DAtei. Nicht allzu komplex! Wir öffnen die Datei im binären Lesemodus. Wir überprüfen dann, ob die Datei gefunden wurden und das sie geöffnet werden konnte. Wenn es beim Öffnen der Datei irgend ein Problem gegeben haben, wird eine Fehlernachricht angezeigt.
// Lädt die .RAW Datei und speichert sie in pHeightMap
void LoadRawFile(LPSTR strName, int nSize, BYTE *pHeightMap)
{
FILE *pFile = NULL;
// öffne die Datei zum Lesen im binären Modus.
pFile = fopen( strName, "rb" );
// Überprüfe ob wir die Datei gefunden haben und ob wir sie öffnen konnten
if ( pFile == NULL )
{
// Zeige Fehlernachricht an und stoppe die Funktion
MessageBox(NULL, "Konnte die Height Map nicht finden!", "Error", MB_OK);
return;
}
Wenn wir bis hierher gekommen sind, können wir uns sicher sein, dass keine Probleme beim Öffnen der Datei aufgetreten sind, wir können die Daten jetzt lesen. Wir machen das mittels fread(). pHeightMap ist der Platz an dem die Daten gespeichert werden (Zeigt auf unser g_Heightmap Array). 1 ist die Anzahl der zu ladenden Elemente (1 Byte zur Zeit), nSize die maximale Anzahle an zu lesenden Elementen (die Bildgröße in Bytes - Breite des Bildes * Höhe des Bildes). Letztendlich ist pFile ein Zeiger auf unsere Datei-Struktur!
Nachdem wir die Daten eingelesen haben, überprüfen wir, ob irgendwelche Fehler aufgetreten sind. Wir speichern die Ergebnisse in result und überprüfen dann result. Wenn ein Fehler aufgetreten ist, zeigen wir eine Fehler-Nachricht an.
Als letztes schließen wir die Datei mit flcose(pFile) wieder.
// Hier laden wir die .RAW Datei in unser pHeightMap Daten Array
// Wir lesen nur '1' ein und die Größe ist (Breite * Höhe)
fread( pHeightMap, 1, nSize, pFile );
// Nachdem wir die Daten gelesen haben, ist es eine gute Idee, zu überprüfen, ob beim Lesen alles glatt verlief
int result = ferror( pFile );
// überprüfe, ob ein Fehler aufgetreten ist
if (result)
{
MessageBox(NULL, "Daten konnten nicht geholt werden!!", "Error", MB_OK);
}
// Schließe die Datei
fclose(pFile);
}
Der Init Code ist ziemlich einfach. Wir setzen die Hintergrundfarbe auf Schwarz, setzen Depth Testing, Polygon Smoothing, etc. Nach all dem laden wir unsere .RAW Datei. Um das zu machen, übergeben wir den Dateinamen ("Date/Terrain.raw"), die Dimensionen der .RAW Datei (MAP_SIZE * MAP_SIZE) und letztlich unser HeightMap Array (g_HeightMap) an LoadRawFile(). Damit springen wir in den Lade-Code von oben hinein. Die .RAW Datei wird geladen und die Daten werden in unserem Heightmap Array (g_HeightMap) gespeichert.
int InitGL(GLvoid) // Der ganze Setup Kram für OpenGL kommt hier rein
{
glShadeModel(GL_SMOOTH); // aktiviert weiches Shading (Smooth Shading)
glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // Schwarzer Hintergrund
glClearDepth(1.0f); // Depth Buffer Setup
glEnable(GL_DEPTH_TEST); // aktiviert Depth Testing
glDepthFunc(GL_LEQUAL); // Die Art des auszuführenden Depth Test
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // wirklich nette Perspektiven Berechnungen
// Hier lesen wir die Height Map aus der .RAW Datei ein und speichern sie in unserem
// g_HeightMap Array. Außerdem übergeben wir die Größe der .RAW Datei (1024).
LoadRawFile("Data/Terrain.raw", MAP_SIZE * MAP_SIZE, g_HeightMap); // ( NEU )
return TRUE; // Initialisierung war OK
}
Damit indexieren wir unser Height Map Array. Wann immer wir mit Arrays arbeiten, wollen wir sicher gehen, dass wir nicht die Grenzen überschreiten. Um sicher zu gehen, dass das nicht passiert, benutzen wir %. % verhindert, dass unsere x / y Werte außerhalb MAX_SIZE - 1 liegen.
Wir stellen sicher, dass pHeightMap auf gültige Daten zeigt und geben ansonsten 0 zurück.
Ansonsten geben wir den gespeicherten Wert bei x, y in unserer Heigt Map zurück. Sie sollten nun wissen, dass wir Y mit der Breite unseres Bildes MAP_SIZE multiplizieren um uns durch die Daten zu bewegen. Mehr darüber weiter unten!
int Height(BYTE *pHeightMap, int X, int Y) // Dies gibt die Höhe von einem Height Map Index zurück
{
int x = X % MAP_SIZE; // Überprüfe unseren X Wert
int y = Y % MAP_SIZE; // Überprüfe unseren Y Wert
if(!pHeightMap) return 0; // Stelle sicher, dass unsere Daten gültig sind
Wir müssen das einzelne Array wie ein 2D Array behandeln. Wir können die folgende Gleichung benutzen: index = (x + (y * arrayWidth) ). Damit nehme wir an, dass wir es wie: pHeightMap[x][y] visualisieren, ansonsten ist es das Gegenteil: (y + (x * arrayWidth) ).
Nun wo wir den korrekten Index haben, geben wir die Höhe an diesem Index zurück (Daten bei x, y in unserem Array).
return pHeightMap[x + (y * MAP_SIZE)]; // Index in unser Height Array und gebe die Höhe zurück
}
Hier setzen wir die Farbe für einen Vertex, basierend auf dem Höhen Index. Um ihn dunkler zu machen, beginne ich mit -0.15f. Wir bekommen den Anteil der Farbe zwischen 0.0f und 1.0f indem wir die Höhe durch 256.0f dividieren. Wenn dort keine Daten vorhanden sind, kehrt die Funktion zurück, ohne eine Farbe zu setzen. Wenn alles in Ordnung ist, setzen wir die Farbe auf eine bläuliche Schattierung, in dem wir glColor3f(0.0f, fColor, 0.0f) verwenden. Versuchen Sie fColor zu den roten oder grünen Punkten zu bewegen, um die Farbe der Landschaft zu verändern.
void SetVertexColor(BYTE *pHeightMap, int x, int y) // Dies setzt die Farbwerte für einen bestimmten Index
{ // Abhängig vom Height Index
if(!pHeightMap) return; // Stelle sicher, dass unsere Height Daten gültig sind
float fColor = -0.15f + (Height(pHeightMap, x, y ) / 256.0f);
// verbinde diese blaue Schattierung mit dem aktuellen Vertex
glColor3f(0.0f, 0.0f, fColor );
}
Dies ist der Code, der unsere Landschaft eigentlich zeichnet. X und Y werden benutzt, um durch die Height Map Daten zu gehen. x, y und z werden verwendet, um die Quadrate zu rendern, aus denen unsere Landschaft besteht.
Wie immer überprüfen wir, ob unsere Height Map (pHeightMap) Daten enthält. Wenn nicht, kehren wir zurück, ohne etwas zu machen.
void RenderHeightMap(BYTE pHeightMap[]) // Damit rendern wir die Height Map als Quadrate
{
int X = 0, Y = 0; // erzeuge einige Variablen, um das Array zu durchlaufen.
int x, y, z; // erzeuge einige Variablen zur besseren Lesbarkeit
if(!pHeightMap) return; // Stelle sicher, dass unsere Height Daten gültig sind
Da wir zwischen Linien und Quadraten hin und herwechseln können, überprüfen wir unseren Render-Status mit dem folgenden Code. Wenn bRender = True dann wollen wir Polygone rendern, ansonsten rendern wir Linien.
if(bRender) // was wir rendern wollen
glBegin( GL_QUADS ); // Rendere Polygons
else
glBegin( GL_LINES ); // Rendere Linien statt dessen
Als nächstes müssen wir unsere eigentliches Terrain aus der Height Map zeichnen. Um das zu machen, durchwandern wir einfach das Array der Höhen Daten und picken einige Höhen heraus um unsere Punkte zu zeichnen. Wenn wir sehen könnten, wie das geschieht, würde als erstes die erste Reihe (Y) gezeichnet werden, dann die Zeilen. BEachten Sie, dass wir STEP_SIZE haben. Das bestimmt wie detailiert unsere Height Map definiert ist. Je höher STEP_SIZE ist, umso klobiger würde unser Terrain aussehen und je kleiner der Wert wäre, umso runder (weicher) wird es. Wenn wir STEP_SIZE auf 1 setzen, würde es einen Vertex für jeden Pixel in unserer Height Map erzeugen. Ich habe 16 als eine gute Größe gewählt. Alles was wesentlich weniger ist, würde wahnsinnig langsam sein. Selbstverständlich können Sie die Nummer erhöhen, wenn Sie Beleuchtung einschalten. Die Vertex Beleuchtung würde die kantigen Formen etwas besser aussehen lassen. Anstatt Beleuchtung zu benutzen, verbinden wir einfach einen Farbwert mit jedem Polygon, um das Tutorial einfach zu halten. Je höher das Polygon ist, umso heller ist die Farbe.
for ( X = 0; X <(MAP_SIZE-STEP_SIZE); X += STEP_SIZE )
for ( Y = 0; Y <(MAP_SIZE-STEP_SIZE); Y += STEP_SIZE )
{
// ermittle die (X, Y, Z) Werte für den unteren linken Vertex
x = X;
y = Height(pHeightMap, X, Y );
z = Y;
// Setze den Farbwert des aktuellen Vertex
SetVertexColor(pHeightMap, x, z);
glVertex3i(x, y, z); // Sende diesen Vertex an OpenGL zum rendern
// ermittle die (X, Y, Z) Werte für den oberen linken Vertex
x = X;
y = Height(pHeightMap, X, Y + STEP_SIZE );
z = Y + STEP_SIZE ;
// Setze den Farbwert des aktuellen Vertex
SetVertexColor(pHeightMap, x, z);
glVertex3i(x, y, z); // Sende diesen Vertex an OpenGL zum rendern
// ermittle die (X, Y, Z) Werte für den oberen rechten Vertex
x = X + STEP_SIZE;
y = Height(pHeightMap, X + STEP_SIZE, Y + STEP_SIZE );
z = Y + STEP_SIZE ;
// Setze den Farbwert des aktuellen Vertex
SetVertexColor(pHeightMap, x, z);
glVertex3i(x, y, z); // Sende diesen Vertex an OpenGL zum rendern
// ermittle die (X, Y, Z) Werte für den unteren rechten Vertex
x = X + STEP_SIZE;
y = Height(pHeightMap, X + STEP_SIZE, Y );
z = Y;
// Setze den Farbwert des aktuellen Vertex
SetVertexColor(pHeightMap, x, z);
glVertex3i(x, y, z); // Sende diesen Vertex an OpenGL zum rendern
}
glEnd();
Nachdem wir fertig sind, setzen wir die Farbe zurück auf helles Weiß mit einem Alpha-Wert von 1.0f. Wenn andere Objekte auf dem Screen waren, wollen wir diese nicht BLAU zeigen :)
glColor4f(1.0f, 1.0f, 1.0f, 1.0f); // Resette die Farbe
}
Für die unter Ihnen, die gluLookAt() noch nicht verwendet haben, die Funktion positioniert die Kameraposition, Ihre Sicht und Ihren Vektor der nach oben zeigt. Hier setzen wir die Kamera etwas außerhalb, damit wir eine gute Sicht auf das Terrain haben. Um solch hohe Zahlen zu vermeiden, würden wir die Terrain Vertices durch eine Skalierungskonstante dividieren, wie wir es später bei glScalef() machen.
Die Werte bei gluLookAt() sind wie folgt: Die ersten drei Zahlen repräsentieren die Position der Kamera. So das die ersten drei Werte die Kamera 212 Einheiten auf der X-Achse, 60 Einheiten auf Y-Achse und 194 Einheiten auf der Z-Achse von unserem Mittelpunkt weg bewegen. Die nächsten 3 Werte repräsentieren wo unsere Kamera hinzeigt. In diesem Tutorial werden Sie bemerken, wenn Sie die Demo laufen lassen, dass wir etwas nach links schauen. Wir schauen auch etwas hinunter auf die Landschaft. 186 liegt links von 212 weshalb wir nach links schauen und 55 ist kleiner als 60, was uns den Eindruck vermittelt, dass wir höher als die Landschaft liegen und wir mit einer leichten Neigung auf diese herunterschauen. Der Wert 171 sagt wie weit das Objekt von der Kamera ist. Die letzten drei Werte teilen OpenGL mit, welche Richtung nach oben zeigt. Unsere Berge wandern auf der Y-Achse nach oben, weshalb wir den Wert auf der Y-Achse auf 1 setzen. Die anderen beiden Werte setzen wir auf 0.
gluLookAt kann anfangs recht einschüchternd wirken. Wenn Sie die grobe Erklärung eben gelesen haben, mag es sein, dass Sie immer noch irritiert sind. Mein Ratschlage ist es, dass Sie etwas mit den Werten herumspielen. Ändern Sie die Kameraposition. Wenn Sie beispielsweise die Y-Position der Kamera auf sagen wir 120 ändern, würden Sie mehr den oberen Teil der Landschaft sehen, da Sie ganz hinunter auf 55 sehen.
Ich bin mir nicht sicher, ob es Ihnen hilft, aber ich werde eins meiner oft kritisierten Real Life "Beispiel" Erklärung geben :) Sagen wir, Sie sind 6 Fuß und ein bißchen groß. Nehmen wir ebenso an, dass Ihre Augen sich bei der 6 Fuß Marke befinden (Ihre Augen repräsentieren die Kamera - 6 Fuß sind 6 Einheiten auf der Y-Achse). Wenn Sie nun vor einer Mauer stehen, die nur 2 Fuß hoch ist (2 Einheiten auf der Y-Achse), würde Sie auf die Mauer HINUNTER schauen und könnten den oberen Teil der Mauer sehen. Wenn die Mauer 8 Fuß hoch wäre, würden Sie nach OBEN schauen und Sie könnten NICHT auf den oberen Teil der Mauer schauen. Die Sicht würde sich abhängig davon, ob Sie rauf oder runter schauen würden, verändern (ob Sie nun größer oder kleiner als das Objekt sind, das Sie betrachten). Ich hoffe das macht wenigstens etwas Sinn!
int DrawGLScene(GLvoid) // Hier kommt der ganze Zeichnen-Kram hin
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Lösche den Bildschirm und den Depth-Buffer
glLoadIdentity(); // Resette die Matrix
// Position Sicht Vektor der nach oben zeigt
gluLookAt(212, 60, 194, 186, 55, 171, 0, 1, 0); // dies bestimmt die Kamera Position und Sicht
Das wird unser Terrain herunterskalieren, so dass es etwas einfacher zu betrachten und nicht so groß ist. Wir können den scaleValue verändern, indem wir die Pfeil nach oben oder Pfeil nach unten Taste drücken. Sie werden bemerken, dass wir den Y scaleValue mit HEIGHT_RATIO multiplizieren. Das geschieht, damit das Terrain höher und detaillierter aussieht.
glScalef(scaleValue, scaleValue * HEIGHT_RATIO, scaleValue);
Wenn wir die g_HeightMap Daten an unsere RenderHeightMap() Funktion übergeben, wird diese das Terrain als Quads zeichnen. Wenn Sie irgendwie Gebruach von dieser Funktion machen, wäre es eine gute Idee, einen (X, Y) Parameter einzuügen, um dort zu zeichnen oder benutzen Sie OpenGL's Matrix-Operationen (glTranslatef(), glRotatef(), etc) um das Land exakt dort zu positionieren, wo Sie es wollen.
RenderHeightMap(g_HeightMap); // Rendere die Height Map
return TRUE; // Weiter geht's
}
Der KillGLWindow() Code ist der selbe wir in Lektion 1.
GLvoid KillGLWindow(GLvoid) // Entferne das Fenster korrekt
{
}
Der CreateGLWindow() Code ist der selbe wie in Lektion 1.
BOOL CreateGLWindow(char* title, int width, int height, int bits, bool fullscreenflag)
{
}
Die einzige Änderung in WndProc() ist das Hinzufügen von WM_LBUTTONDOWN. Dadurch wird überprüft, ob die linke Maustaste gedrückt wurde. Falls ja, wechselt der Rendering-Status zwischen Polygon-Modus und Linien-Modus oder vom Linien-Modus zum Polygon-Modus.
LRESULT CALLBACK WndProc( HWND hWnd, // Handle für dieses Fenster
UINT uMsg, // Nachricht für dieses Fenster
WPARAM wParam, // Weitere Nachrichten Informationen
LPARAM lParam) // Weitere Nachrichten Informationen
{
switch (uMsg) // Überprüfe auf Fenster-Nachrichten
{
case WM_ACTIVATE: // Wenn Aktivierungs-Nachricht reinkommt
{
if (!HIWORD(wParam)) // Überprüfe Minimierungs-Status
{
active=TRUE; // Programm ist aktiv
}
else
{
active=FALSE; // Programm ist nicht länger aktiv
}
return 0; // Kehre zurück zur Nachrichten-Schleife
}
case WM_SYSCOMMAND: // hereinkommende System Befehle
{
switch (wParam) // Überprüfe auf System Aufrufe
{
case SC_SCREENSAVE: // versucht der Screensaver sich zu starten?
case SC_MONITORPOWER: // versucht der Monitor in den Energiesparmodus zu gehen?
return 0; // verhindere das
}
break; // beende
}
case WM_CLOSE: // Haben wir eine Nachricht zum Schließen erhalten?
{
PostQuitMessage(0); // Sende eine Beenden-Nachricht
return 0; // Springe zurück
}
case WM_LBUTTONDOWN: // haben wir einen linken Mausklick erhalten?
{
bRender = !bRender; // Ändere Render-Status zwischen Gefüllt/Drahtgittermodell
return 0; // Springe zurück
}
case WM_KEYDOWN: // Wird eine Taste gedrückt?
{
keys[wParam] = TRUE; // Wenn ja, markiere sie mit TRUE
return 0; // Springe zurück
}
case WM_KEYUP: // Wurde eine Taste losgelassen?
{
keys[wParam] = FALSE; // Wenn ja, markiere sie mit FALSE
return 0; // Springe zurück
}
case WM_SIZE: // ändere die Größe des Fenster
{
ReSizeGLScene(LOWORD(lParam),HIWORD(lParam)); // LoWord=Breite, HiWord=Höhe
return 0; // Springe zurück
}
}
// Übergebe alle nicht bearbeiteten Nachrichten an DefWindowProc
return DefWindowProc(hWnd,uMsg,wParam,lParam);
}
Keine großartigen Änderungen in diesem Codeabschnitt. Die einzige Änderung ist der Titel des Fensters. Alles andere bleibt beim selben, bis wir auf Tastendrücke überprüfen.
int WINAPI WinMain( HINSTANCE hInstance, // Instanz
HINSTANCE hPrevInstance, // vorherige Instanz
LPSTR lpCmdLine, // Kommandozeilen Parameter
int nCmdShow) // Fenster Anzeige Status
{
MSG msg; // Windows Nachrichten Struktur
BOOL done=FALSE; // Bool Variable um die Schleife zu beenden
// Frage den Benutzer, in welchen Modus er starten will
if (MessageBox(NULL,"Wollen Sie im Vollbildmodus starten?", "Start FullScreen?",MB_YESNO|MB_ICONQUESTION)==IDNO)
{
fullscreen=FALSE; // Fenster-Modus
}
// erzeuge unser OpenGL Fenster
if (!CreateGLWindow("NeHe & Ben Humphrey's Height Map Tutorial", 640, 480, 16, fullscreen))
{
return 0; // Beende, wenn Fenster nicht erzeugt wurde
}
while(!done) // Schleife die so lange läuft, bis done=TRUE
{
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) // Wartet eine Nachricht?
{
if (msg.message==WM_QUIT) // Haben wir eine Nachricht zum beenden erhalten?
{
done=TRUE; // Wenn ja done=TRUE
}
else // Wenn nicht, bearbeite die Fenster-Nachrichten
{
TranslateMessage(&msg); // Übersetze die Nachricht
DispatchMessage(&msg); // bearbeite die Nachricht
}
}
else // Wenn da keine Nachrichten sind
{
// Zeichne die Szene. Schau nach der ESC-Taste und Beendigungs-Nachrichten von DrawGLScene()
if ((active && !DrawGLScene()) || keys[VK_ESCAPE]) // Acktiv? Wurde Signal zum Beenden gesendet?
{
done=TRUE; // ESC oder DrawGLScene signalisiert uns, dass beendet werden soll
}
else if (active) // es ist noch nicht Zeit, zum beenden, aktualisiere den Screen
{
SwapBuffers(hDC); // Swap Buffers (Double Buffering)
}
if (keys[VK_F1]) // Wurde F1 gedrückt?
{
keys[VK_F1]=FALSE; // Wenn ja, setze Taste auf FALSE
KillGLWindow(); // Kill unser aktuelles Fenster
fullscreen=!fullscreen; // Wechsel zwischen Fullscreen und Fester-Modus
// Erzeuge unser OpenGL Fenster erneut
if (!CreateGLWindow("NeHe & Ben Humphrey's Height Map Tutorial", 640, 480, 16, fullscreen))
{
return 0; // Beenden, wenn das Fenster nicht erzeugt wurde
}
}
Der folgende Code lässt Sie scaleValue inkrementieren und dekrementieren. Indem Sie die Pfeil-Hoch-Taste drücken, wird scaleValue inkrementiert, was die Landschaft größer macht. Indem Sie die Pfeil-Runter-Taste drücken, dekrementieren Sie scaleValue und die Landschaft wird kleiner.
if (keys[VK_UP]) // wurde die Pfeil-Hoch-Taste gedrückt?
scaleValue += 0.001f; // Inkrementiere den Skalierungswert um heran zu zoomen
if (keys[VK_DOWN]) // Wurde die Pfeil-Runter-Taste gedrückt?
scaleValue -= 0.001f; // dekrementiere den Skalierungswert um raus zu zoomen
}
}
// Shutdown
KillGLWindow(); // Kill das Fenster
return (msg.wParam); // Beende das Programm
}
Das ist alles um schöne Height Mapped Landschaften zu erstellen. Ich hoffe, Sie wissen Ben's Arbeit zu schätzen. Wie immer, wenn Sie Fehler in dem Tutorial oder Code finden, mailen Sie mir bitte und ich werde versuchen, dass Problem zu korrigieren / das Tutorial verbessern.
Wenn Sie den Code verstanden haben, spielen Sie etwas herum damit. Etwas was Sie versuchen könnten, wäre das Hinzufügen eines kleinen Balls, der über die Oberfläche rollt. Sie wissen bereits die Höhe jeder Sektion der Landschaft, weshalb das Hinzufügen des Balls kein Problem sein sollte. Ander Dinge zum ausprobieren: Erzeugen Sie die Height Map manuell, machen Sie eine scrollende Landschaft, fügen Sie Farben zur Landschaft hinzu, um Spitzen mit Schnee / Wasser / etc. zu repräsentieren, fügen Sie Texturen hinzu, benutzen Sie einen Plasma-Effekt um die Landschaft kontinuierlich zu ändern. Die Möglichkeiten sind endlos :)
Ich hoffe, Sie haben das Tutorial genossen. Sie können Ben's Seite unter http://www.GameTutorials.com besuchen.
Ben Humphrey (DigiBen)
Jeff Molofee (NeHe)
* DOWNLOAD Visual C++ Code für diese Lektion.
* DOWNLOAD Borland C++ Builder 6 Code für diese Lektion. ( Conversion by Christian Kindahl )
* DOWNLOAD Code Warrior 5.3 Code für diese Lektion. ( Conversion by Scott Lupton )
* DOWNLOAD Delphi Code für diese Lektion. ( Conversion by Michal Tucek )
* DOWNLOAD Dev C++ Code für diese Lektion. ( Conversion by Dan )
* DOWNLOAD JoGL Code für diese Lektion. ( Conversion by Abdul Bezrati )
* DOWNLOAD LCC Win32 Code für diese Lektion. ( Conversion by Robert Wishlaw )
* DOWNLOAD Linux/GLX Code für diese Lektion. ( Conversion by Patrick Schubert )
* DOWNLOAD Mac OS X/Cocoa Code für diese Lektion. ( Conversion by Bryan Blackburn )
* DOWNLOAD Visual Studio .NET Code für diese Lektion. ( Conversion by Grant James )
Deutsche Übersetzung: Joachim Rohde
Der original Text ist hier zu finden.
Die original OpenGL Tutorials stammen von NeHe's Seite.