Lektion 11: Wellen-Bewegungen
Hallo,
für alle die wissen wollen was sie erwartet schaut euch doch einfach das Ende
meiner Demo "Worthless" an. Ich bin Bosco und werde mein bestes geben um euch
zu erklären wie man so ein animiertes Sinus-Kurven Bild erstellt. Dieses Tutorial
baut auf NeHe's Tutorial #6 auf, welches ihr um fortzufahren gut verstanden
haben müsstest. Du solltest dir das Sourcepacket zu diesem Tutorial runterladen
und das Bitmap in das Unterverzeichnis "Data" kopieren. Oder du benutzt einfach
euer eigenes Bild, wenn es die geignete Größe hat um als Textur für OpenGL zu
fungieren.
Den Anfang zu erst. Öffne Tutorial #6 und füge folgende include Anweisung nach
den anderen #include Anweisungen hinzu.
Der Befehl #include <math.h> erlaubt uns komplexe mathematische Formeln
anzuwenden z.B. Sinus und Cosinus.
#include <math.h>
Wir werden das Array points verwenden um die x, y & z Koordinaten unseres Gitters
zu speichern. Das Gitter hat die Größe von 45 mal 45 Punkten, was genau 44 mal
44 Quadrate entstehen lässt. wiggle_count wird dazu benutzt um festzustellen wie
schnell sich die Textur bewegen soll. Alle 3 Frames sieht ganz gut aus, und die
Variable hold würd einen Realwert beinhalten der das ganze etwas flüssiger aussehen
lässt. Diese drei Linien können direkt am Anfang des Programms, nach der letzten
include Anwiesung und vor der Zeile "GLuint texture[1] " eingefügt werden.
float points[ 45 ][ 45 ][3];
// The Array For The Points On The Grid Of Our "Wave"
int wiggle_count = 0;
// Counter Used To Control How Fast Flag Waves
GLfloat hold;
// Temporarily Holds A Floating Point Value
Weiter unten bei der Funktion LoadGLTextures() wollen wir das Bild Tim.bmp laden.
Finde LoadBMP("Data/NeHe.bmp") und ersetze es durch LoadBMP("Data/Tim.bmp").
if (TextureImage[0]=LoadBMP("Data/Tim.bmp"))
// Load The Bitmap
Nun fügen wir folgenden Code ans Ende der InitGL() Funktion vor der Zeile "return
TRUE" ein.
glPolygonMode( GL_BACK, GL_FILL );
// Back Face Is Filled In
glPolygonMode( GL_FRONT, GL_LINE );
// Front Face Is Drawn With Lines
Das bedeutet das die wir die Rückseite des Polygons vollends ausgefüllt aber
die Vorderseite nur umrandet haben möchten. Das hat etwas mit der Reihenfolge
der einzelnen Koordinaten zu tun. Schaut ins Red Book wenn ihr mehr darüber
wissen wollt. Als jemand der es gerade liest, kann ich nur sagen das es eine
treibende Kraft ist, die mich anspornt OpenGL zu erlernen (natürlich NeHe's
Seite nicht zu vergessen). Danke NeHe. Kauft "The Programmer's Guide to OpenGL"
von Addison-Wesley. Es ist eine unverzichtbare Ressource, davon bin ich überzeugt.
Aber nun zurück zum Tutorial.
Direkt unter dem eben eingefügtem Code kommen nun folgende Zeilen.
// Loop Through The X Plane
for(int x=0; x<45; x++)
{
// Loop Through The Y Plane
for(int y=0; y<45; y++)
{
// Apply The Wave To Our Mesh
points[x][y][0]=float((x/5.0f)-4.5f);
points[x][y][1]=float((y/5.0f)-4.5f);
points[x][y][2]=float(sin((((x/5.0f)*40.0f)/360.0f)*3.141592654*2.0f));
}
}
Danke an Graham Gibbons für seinen Vorschlag mit der Integer-Schleife um das
kleine Loch in der Welle verschwinden zu lassen.
Die beiden Schleifen initialisieren die Punkte unseres Gitters. Ich deklariere
die Schleifen-Variablen innerhalb der Schleife um sie in meinem Kopf leichter
als diese identifizieren zu können. Ich weiss nicht ob man das so machen kann.
Wir benutzen eine Integer-Schleife um Graphische Fehler zu vermeiden die auftreten
wenn wir mit Realzahlen rechnen würden. Wir dividieren die x und y Variable
durch 5 (da 45 / 9 = 5) und ziehen 4,5 von beiden ab um die "Welle" in die Mitte
zu rücken. Das selbe könnte man auch durch eine translate-Anweisung machenl,
aber ich bevorzuge diese Methode.
Die Werte in points[x][y][2] sind unsere Sinus Werte. Die sin() Funktion benötigt
Radianten. Wir nehmen also unseren Winkel, welcher float_x enspricht und multiplizieren
ihn mit 40.0f. Einmal getan wandeln wir den Winkel nun in einen Radianten um
indem wir ihn durch 360.0f dividieren und dann mit PI und 2 multiplizieren.
Ich schreibe die DrawGLScene Funktion völlig neu, also löscht schonmal alles
was da drin steht und ersetzt es durch folgenden Code.
int DrawGLScene(GLvoid)
// Draw Our GL Scene
{
int x, y;
// Loop Variables
float float_x, float_y, float_xb, float_yb;
// Used To Break The Flag Into Tiny Quads
Die verschiedene Variablen werden zum Teil als Schleifen-Variablen oder um Werte
zwischen zu speichern benutzt. Sie sind nicht "extra" für diese "Welle".
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Clear The Screen And Depth Buffer
glLoadIdentity();
// Reset The Current Matrix
glTranslatef(0.0f,0.0f,-12.0f);
// Translate 17 Units Into The Screen
glRotatef(xrot,1.0f,0.0f,0.0f);
// Rotate On The X Axis
glRotatef(yrot,0.0f,1.0f,0.0f);
// Rotate On The Y Axis
glRotatef(zrot,0.0f,0.0f,1.0f);
// Rotate On The Z Axis
glBindTexture(GL_TEXTURE_2D, texture[0]);
// Select Our Texture
Das ist das gleiche wie in Tutorial #6 ausser das ich die Kamera noch ein Stück
weiter weg bewegt habe.
glBegin(GL_QUADS);
// Start Drawing Our Quads
for( x = 0; x < 44; x++ )
// Loop Through The X Plane 0-44 (45 Points)
{
for( y = 0; y < 44; y++ )
// Loop Through The Y Plane 0-44 (45 Points)
{
Hier beginnt die Schleife um die verschiedenen Polygone zu zeichnen. ich benutze
Integer-Variablen um die konvertierung mittel int() Funktion zu vermeiden, so
wie ich es früher getan habe um das Array richtig ansprechen zu können
float_x = float(x)/44.0f;
// Create A Floating Point X Value
float_y = float(y)/44.0f;
// Create A Floating Point Y Value
float_xb = float(x+1)/44.0f;
// Create A Floating Point Y Value+0.0227f
float_yb = float(y+1)/44.0f;
// Create A Floating Point Y Value+0.0227f
Wir benutzen die vier Variablen um die Texturkoordinaten zu speichern. Jedes unserer
Polygone (ein Quadrat im Gitter) hat 1/44 x 1/44 der Größe der gesamten Textur.
Die Schleife gibt uns die Position Links/unten. Um die anderen drei Positionen
zu erhalten erhöhen wir einfach dem entsprechend x oder y um 1.
glTexCoord2f( float_x, float_y);
// First Texture Coordinate (Bottom Left)
glVertex3f( points[x][y][0], points[x][y][1], points[x][y][2] );
glTexCoord2f( float_x, float_yb );
// Second Texture Coordinate (Top Left)
glVertex3f( points[x][y+1][0], points[x][y+1][1], points[x][y+1][2] );
glTexCoord2f( float_xb, float_yb );
// Third Texture Coordinate (Top Right)
glVertex3f( points[x+1][y+1][0], points[x+1][y+1][1], points[x+1][y+1][2] );
glTexCoord2f( float_xb, float_y );
// Fourth Texture Coordinate (Bottom Right)
glVertex3f( points[x+1][y][0], points[x+1][y][1], points[x+1][y][2] );
}
}
glEnd(); // Done Drawing Our Quads
Die eben kopierten Zeilen zeichnen jetzt alle Daten die wir bis jetzt besprochen
haben. Jeweils vier Funktionsaufrufe von glTexCoord2f() und glVertex3f(). Die
Vierecke wurden im Uhrzeigersinn erstellt, das heisst du siehst bei der initialisierung
die Rückseite. Die Rückseite ist ganz ausgefüllt während die Vorderseite nur
aus Linien besteht.
Wenn man die Quadrate gegen den Uhrzeigersinn gezeichnet hätte würde man zuerst
die Vorderseite sehen.
if( wiggle_count == 2 )
// Used To Slow Down The Wave (Every 2nd Frame Only)
{
Wenn wir das Bild zwei mal gezeichnet haben, wollen wir das es sich ein Stück
weiter bewegt.
for( y = 0; y < 45; y++ )
// Loop Through The Y Plane
{
hold=points[0][y][2];
// Store Current Value One Left Side Of Wave
for( x = 0; x < 44; x++)
// Loop Through The X Plane
{
// Current Wave Value Equals Value To The Right
points[x][y][2] = points[x+1][y][2];
}
points[44][y][2]=hold;
// Last Value Becomes The Far Left Stored Value
}
wiggle_count = 0;
// Set Counter Back To Zero
}
wiggle_count++;
// Increase The Counter
Wir speichern hier den ersten Wert jeder Linie, dann bewegen wir die ganze
Bild nach Links, was den Welleneffekt ausmacht. Den Wert den wir gespeichert
hatten wird nun am Ende des Bildes wieder angefügt, so das wir eine niemals
endende Welle auf der Textur erzeugt haben. Dann wird die Variable wiggle_count
auf 0 gesetzt um unsere Animation fortfahren zu lassen.
Der Code wurde im Februar 2000 von NeHe modifiziert um eine kleine Unebenheit
aus der Welle zu bekommen. Die Welle ist nun glatt.
xrot+=0.3f; // Increase The X Rotation Variable
yrot+=0.2f; // Increase The Y Rotation Variable
zrot+=0.4f; // Increase The Z Rotation Variable
return TRUE; // Jump Back
}
Die Standart Variablen um das ganze rotieren zu lassen.
Das wars auch schon. Einfach compilieren und schon sollte man ein hübsch aussehendes,
sich wellenförmig bewegendes Bild haben. Ich weiss nicht was ich dazu noch sagen
kann ausser , WOW ... Das war LANG ! Aber ich hoffe das ihr mir folgen konntet
oder wenigstens etwas daraus gelernt habt. Wenn ihr noch irgendwelche Fragen
habt, das ich noch irgendetwas deutlicher erkläre oder ihr mir einfach nur sagen
wollt wie verdammt gut , *lol*, ich code dann schreibt mir.
Das war sehr Energie- und Zeitraubend. Es steigert meine Bewunderung an NeHe's
Tutorials um einiges.
Bosca (bosco4@home.com)
Die Source Codes und Ausführbaren Dateien zu den Kursen liegen auf der Neon
Helium Website
Übersetzung von Delax & ChaosAngel/ Sundancer
Inc.
(Präsentiert von www.codeworx.org)