Ein erstes Dreieck mit OpenGL unter C++

Inhaltsverzeichnis[Verbergen]

Im ersten Tutorial haben wir ein OpenGL Fenster erstellt. Da ein schwarzes Fenster nicht undbedingt sehr aufregend zum Anschauen ist, wird es jetzt langsam einmal Zeit, dass wir etwas Farbe ins Spiel bringen und etwas in das Fenster zeichnen.
Bei einem normalen Konsolenprogramm hätten wir jetzt "Hallo Welt." ausgegeben, was in einem Fenster nicht so leicht möglich ist. Deshalb werden wir in diesem Tutorial, das typische bunte Dreieck zeichnen, das quasi das Gegenstück zum "Hello World." der Konsolenprogramme ist.

1. Erstellen des Fensters

Wie wir genau ein Fenster für OpenGL erstellen haben wir bereits im vorigen Tutorial durch besprochen. Ab diesem Tutorial verwenden wir dafür eine eigene Klasse, die wir über den folgenden Link downloaden können.

icon Klasse zum Initialisieren von OpenGL (3.42 kB 2008-07-13 00:52:46)

Mit dieser Klasse können wir nun einfach ein Fenster mit OpenGL initialisieren.

#include <iostream>
#include "SDL.h"
#include "SDL_opengl.h"
 
#include "application_window.h" // Hier ist die neue Klasse drinnen

 
int main(int argc, char **argv)
{
  if( SDL_Init( SDL_INIT_VIDEO ) != 0 ) // Initialisieren des SDL Video Subsystems
  {
    printf("Die SDL konnte nicht initalisiert werden (%s)\n", SDL_GetError());
    return 1;
  }
 
  // Ein Fenster mit 800*600 Pixeln bei 32 Bit Farbtiefe anlegen
  TomprogsTutorial_::ApplicationWindow *game_window =
    new TomprogsTutorial_::ApplicationWindow( 800, 600, 32 );
 
  // Den Fenstertitel setzen
  game_window->setCaption( "Tomprogs OpenGL Tutorial 02" );
 
  // Und schließlich das Fenster erzeugen lassen
  game_window->create();
 
  // Und wieder die Endlosschleife
  while(true)
  {
    // Nachrichten verarbeiten
    // (in eine Funktion ausgelagert - siehe nächstes Codestück)
    if( !pollEvents() ) break;
  }
 
  SDL_Quit();
 
  return 0;
}

Damit unser Code etwas übersichtlicher wird lagern wir das Polling von den Nachrichten in eine Funktion aus. Dadurch können wir später auch einfach weitere Events abfangen, ohne dass die main-Funktion zu unübersichtlich wird. Diese neue Funktion schau wie folgt aus und dürfte wohl selbsterklärend sein, wie sie funktioniert, da es praktisch der gleiche Code wie im ersten Tutorial ist.

// Die neue Polling-Funktion
bool pollEvents()
{
  SDL_Event event;
 
  while( SDL_PollEvent(&event) ) // Nächste Nachricht holen
  {
    switch(event.type)
    {
      case SDL_QUIT:
        return false; // Programm sollte beendet werden
      default:
        break;
    }
  }
 
  return true;
}

Dieses Mal müssen wir beim kompilieren und linken auch noch die Fensterklasse in der entsprechenden Datei application_window.cpp miteinbeziehen. Der Aufruf schaut also unter Linux dieses Mal so aus:

g++ tomprogs_opengl_tutorial_01.cpp application_window.cpp -o Tutorial01 -Wall -lSDL -lGL

2. OpenGL

Jetzt ist endlich soweit, wir sind an dem Punkt angelangt, an dem wir wirklich beginnen OpenGL zu verwenden. Damit wir es auch richtig verwenden können sollten wir immer daran denken, dass OpenGL ein Zustandsautomat ist. Das bedeutet wenn wir irgendeinen Parameter auf einen bestimmten Wert setzten, dann bleibt dieser solange erhalten, bis wir ihn mit einem neuen Wert überschreiben.
Doch jetzt fangen wir an. Alle Operationen mit OpenGL werden wir innerhalb der Nachrichtenschleife nach dem Behandeln der Nachrichten einbauen. Als erstes müssen wir den Bildpuffer löschen, damit nicht noch vorhandene Bilder zu ungewollten Ergebnissen führen können.

glClearColor( 1.0, 0.0, 0.0, 0.0 ); // Farbe zum Löschen setzen
glClear( GL_COLOR_BUFFER_BIT );     // Aktuellen Bildpuffer löschen
 
// Alle Zeichenoperationen kommen hier dazwischen hinein.
 
 
 
 
 
SDL_GL_SwapBuffers(); // Bildpuffer vertauschen
                      // Wichtig ist, dass wir die Puffer erst nach
                      // der letzten Zeichenoperation tauschen.

Mit "glClearColor" haben wir die Farbe festgelegt, auf die der Bildpuffer beim Löschen gesetzt werden soll. Wir könnten diese Farbe natürlich auch außerhalb der Nachrichtenschleife setzten, da sie ja solange Erhalten bleibt, bis sie geändert wird. So können wir sie aber in jedem Schleifendurchlauf verändern und dadurch ganz nette Farbübergänge erhalten.
Mit "glClear" sagen wir OpenGL, dass es den Bildpuffer auf die Löschfarbe setzen soll. Und mit "SDL_GL_SwapBuffers" vertauschen wir schließlich noch die beiden Bildpuffer, und können dadurch im nächsten Schleifendurchgang auf den zweiten Puffer zeichnen, wärend der erste noch angezeigt wird.
Wer mit diesem Code etwas herumspielen will, der kann die Hintergrundfarbe mit zum Beispiel einer Sinusfunktion in jedem Schleifendurchlauf verändern und so einen Fenster mit schönen Farbwechseln erhalten.

3. Die Projektion setzen

So, jetzt haben wir aber genug geübt und werden jetzt ein erstes Dreieck auf den Bildschirm zeichen. Auch später werden wir nicht viel mehr als jede Menge Dreiecke zeichnen, da man meistens alle Objekte im Spiel aus vielen Dreiecken aufbaut. Diese Technik ist auch unter dem Namen 'Polygongrafik' bekannt.
Bevor wir jetzt ein Dreieck zeichnen können, sollten wir allerdings noch eine Projektionsmatrix setzen, damit OpenGL auch weiß wohin es unser Dreieck zeichnen soll. Damit es nicht zu kompliziert wird verwenden wir jetzt einmal nur eine einfache orthogonale Projektion, bei der es noch keine perspektivischen Verzerrungen gibt. Für alle Matrizenoperationen gibt es bei OpenGL mehrere Matrizenstacks, auf die man neue Matrizen hinauflegen kann. Bei allen Matrizenfunktionen wird das Ergebnis multiplikativ mit der obersten Matrix verknüpft.

glMatrixMode( GL_PROJECTION ); // Stack für Projektionsmatrix als
                               // aktiven Matrixstack setzen
glLoadIdentity();              // Identitätsmatrix auf den Stack laden
 
// Eine orthogonale Projektionsmatrix zum Stack
// dazu multiplizieren.
glOrtho( 0, 800, 600, 0, -1, 1 );

Mit diesem Code haben wir zuerst Stack mit den Projektionsmatrizen ausgewählt und auf den obersten Platz die Einheitsmatrix geladen. Die Einheitsmatrix hat die besondere Eigenschaft, dass sie bei einer Multiplikation mit einer anderen Matrix diese Matrix unverändert lässt. Dadurch können wir im nächsten Schritt die Matrix für die orthogonale Projektion auf den Stack hinauf multiplizieren. Mit dieser Projektion haben wir festgelegt, dass der am Bildschirm angezeigte Bereich in x-Richtung den Koordinatenwerten von 0 bis 800 und in y-Richtung den Werten 0 bis 600 entspricht. Zusätzlich haben wir die Anzeige auf Punkt mit den z-Koordinaten zwischen -1 und 1 beschränkt.
Wir dürfen nicht vergessen, dass wir diesen Code vor dem Vertauschen der Bildpuffer in den Code einbauen, da wir sonst keine schöne Ausgabe zusammenbringen werden.

4. Das erste Dreieck

Jetzt kommen wir zum spannendsten Teil dieses Tutorials und zeichnen unser erstes Dreieck. Alles was wir jetzt noch dazu tun müssen, ist es OpenGL mitzuteilen dass wir ein Dreieck zeichnen wollen, und dann noch die Koordinaten und Farben für die Eckpunkte setzen.

glBegin( GL_TRIANGLES ); // Wir wollen ein Dreieck zeichnen
 
  glColor3f(1,0,0);      // Ab jetzt werden alle gezeichneten Punkte rot
  glVertex3f(400,100,0); // Der erste Eckpunkt ist mittig und 100 Pixel
                         // vom oberen Rand entfernt
 
  glColor3f(0,1,0);      // Ab jetzt werden alle gezeichneten Punkte grün
  glVertex3f(750,500,0); // Der zweite Eckpunkt ist 50 Pixel vom rechten
                         // und 100 Pixel vom unteren Rand entfernt
 
  glColor3f(0,0,1);      // Ab jetzt werden alle gezeichneten Punkte blau
  glVertex3f(50,500,0);  // Der dritte Eckpunkt ist 50 Pixel vom linken
                         // und 100 Pixel vom unteren Rand entfernt
 
glEnd(); // Wir sind fertig mit dem Zeichnen

5. Noch einmal die Nachrichtenschleife

Da man vor allem am Anfang leicht Fehler einbauen kann, und man diese meistens nur schwer findet, schauen wir uns noch den gesamten Code der Nachrichtenschleife an. Den Code sollten wir jetzt eigentlich schon kennen, da es ja nur der Code aus den vorherigen Codestücken ist.

//...
 
// Und noch einmal die Endlosschleife
while(true)
{
  //------------------------- 
  // Nachrichten verarbeiten:
  //-------------------------
 
  if( !pollEvents() ) break;
 
  //----------------------------------
  // Den aktuellen Bildpuffer löschen:
  //----------------------------------
 
  glClearColor( 1.0, 0.0, 0.0, 0.0 ); // Farbe zum Löschen setzen
  glClear( GL_COLOR_BUFFER_BIT );     // Aktuellen Bildpuffer löschen
 
  //--------------------------
  // Die Projektion festlegen:
  //--------------------------
 
  glMatrixMode( GL_PROJECTION ); // Stack für Projektionsmatrix als
                                 // aktiven Matrixstack setzen
  glLoadIdentity();              // Identitätsmatrix auf den Stack laden
 
  // Eine orthogonale Projektionsmatrix zum Stack
  // dazu multiplizieren.
  glOrtho( 0, 800, 600, 0, -1, 1 );
 
  //----------------------
  // Das Dreieck zeichnen:
  //----------------------
 
  glBegin( GL_TRIANGLES ); // Wir wollen ein Dreieck zeichnen
 
    glColor3f(1,0,0);      // Ab jetzt werden alle gezeichneten Punkte rot
    glVertex3f(400,100,0); // Der erste Eckpunkt ist mittig und 100 Pixel
                         // vom oberen Rand entfernt
 
    glColor3f(0,1,0);      // Ab jetzt werden alle gezeichneten Punkte grün
    glVertex3f(750,500,0); // Der zweite Eckpunkt ist 50 Pixel vom rechten
                         // und 100 Pixel vom unteren Rand entfernt
 
    glColor3f(0,0,1);      // Ab jetzt werden alle gezeichneten Punkte blau
    glVertex3f(50,500,0);  // Der dritte Eckpunkt ist 50 Pixel vom linken
                         // und 100 Pixel vom unteren Rand entfernt
 
  glEnd(); // Wir sind fertig mit dem Zeichnen
 
  SDL_GL_SwapBuffers(); // Bildpuffer vertauschen (als Letztes)
}
 
//...

6. Ergebnis

Wenn wir jetzt unser Programm kompilieren und ausführen, dann haben wir schon ein richtiges OpenGL Programm. Wir werden zwar noch keine drei dimenionalen Gegenstände erkennen können, und auch noch kein Spiel spielen können, aber wir haben bereits Doublebuffering kennen gelernt, den Bildpuffer gelöscht und schließlich noch ein Dreieck gezeichnet.

Screenshot OpenGL Tutorial 02

Das Bild zeigt wie das Programm auf meinem Computer ausschaut. So in etwa sollte es bei dir auch ausschauen. Das Fenster kann natürlich einen ganz anderen Stil haben, da ja jedes Betriebssystem eine andere Oberfläche hat. Der mit OpenGL generierte Teil sollte hingegen auf jedem Computer gleich ausschauen.

7. Ausblick

Jetzt haben wir schon wieder das Ende eines Tutorials erreicht, und wir haben schon einige wichtige Hürden beim Programmieren mit OpenGL geschafft.
Im nächsten Tutorial werden wir uns etwas mit dem Verwenden einer perspektivischen Projektion beschäftigen, um endlich dreidimensionale Szenen zu rendern. Bevor wir weitermachen kann ich jedem nur empfehlen mit dem bereits erarbeiteten Code herumzuexperimentieren, um sich mit OpenGL vertraut zu machen. Schließlich heist es ja nicht umsonst "Übung macht den Meister".

Nächstes Tutorial: C++ OpenGL Tutorials - Perspektive und der Z-Buffer

8. Quellcode

Hier gibts noch den vollständigen Quellcode dieses Tutorials zum Downloaden.

icon Quellcode OpenGL Tutorial 02 (4.74 kB 2008-07-14 21:27:17)

Und hier ist noch einmal der Quellcode der Klasse zum Erstellen eines OpenGL Fensters.

icon Klasse zum Initialisieren von OpenGL (3.42 kB 2008-07-13 00:52:46)


Wünsche, Anregungen und Kritik bitte über das Kontaktformular oder direkt an Diese E-Mail-Adresse ist gegen Spambots geschützt! Sie müssen JavaScript aktivieren, damit Sie sie sehen können. an mich schicken.

Aktualisiert ( Freitag, 19. November 2010 um 18:07 Uhr )