www.codeworx.org/opengl-tutorials/Tutorial 11: Wellen-Bewegungen

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)