Einleitung:
Mit OpenGL kannst du Grafiken auf den Bildschirm ausgeben. Sogar Spiele kannst du damit programmieren (wie, erkläre ich in einen späteren Workshop). Nun werden wir uns mit den Grundlagen vertraut machen. Das Ziel dieses Workshops wird es sein ein rotierenden Würfel auszugeben.
Vorraussetztungen:
Erstmal solltest du schon wissen, was die WinAPI ist und musst schon damit umzugehen wissen Du solltest schon etwas mit C++ vertraut sein. Einen C++ Compiler mit OpenGL musst du auch haben. Falls du noch keinen hast, dann lade dir einen unter http://www.borland.com herunter.
Kapitel 1: OpenGL Fenster
Nun werden wir ein OpenGL taugliches Fenster erstellen. Ich werde mich bemühen jeden Befehl einzeln durch ein Kommentar zu erklären. Am Ende jedes Kapitels werde ich den fertigen Quellcode für das komplette Programm zusammenfassen, so, dass du ihn dir direkt abschreiben kannst und die Kompilierung durchführen kannst. Ich werde jedes mal davon ausgehen, dass du den Borland BCC 5.5 Compiler benutzts. Es wird immer empfohlen die OpenGL Programme mit den Borland BCC 5.5 Compiler so zu kompilieren:
1. Lege nach der Installation des Borland BCC 5.5
Compilers im Bin Verzeichnis eine Datei Namens Build.bat (sorge dafür, dass es eine Batch Datei ist).
2. Die wird mit einen belibigen Editor (am besten den standard Windows Editor) so bearbeitet:
quellcode musst du mit den Namen deiner Cpp Datei ersetzen. Nach diesen Schritten, kannst du ein Quelltext kompilieren. So, jetzt fangen wir mit den wesentlichen an. Für jedes OpenGL Programm, solltest du folgende Include Dateien einbinden:
1 2 3 4
#include <windows.h> // Definitionen für ein Fenster
#include <gl/gl.h> // Definitionen für OpenGL
#include <gl/glu.h> // Definitionen für OpenGL
#include <gl/glaux.h> // Definitionen für OpenGL
Mit diesen Include Dateien kannst du bereits alle OpenGL Befehle benutzten und musst dich nicht mehr darum kümmern, ob die richtigen Include Dateien eingebunden sind. Aber falls du z.B. Mathematische Befehle wie Sinus benutzen willst, dann musst du die entsprechenden Include Dateien einbinden. Nun sollten wir folgende globale Variablen deklarieren:
1 2 3 4 5 6 7 8 9
HDC hDC=NULL; // Eine Variable für das OpenGl Fenster
HGLRC hRC=NULL; // Eine Variable für das OpenGl Fenster
HWND hWnd=NULL; // Eine Variable für das normale Windows Fenster
HINSTANCE hInstance; // Die Instanz des Fensters
bool fullscreen=true; // Soll der Fullscreen Modus Aktiviert werden?
bool active=true; // Reagiert das Programm?
bool bQuit=false; // Soll das Programm beendet werden?
bool keys[256]; // Welche Tasten sind gedrückt?
Das in der ersten Zeile definierte hDC nennt sich auch Device Context. Das hDC verbindet das Fenster mit den GDI (Graphics Device Interface). Die zweite Zeile definiert das hRC, das Rendering Context. Dies stellt eine Verbindung zu den DC her. Jetzt deklarieren wir unsere Funktionen:
Nun kommt die Haupt Funktion, die als erstes gestartet wird und in der Objekt Datei c0w32.obj, die bei den Link Vorgang eingebunden wird, als Startfunktion gesetzt wird:
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR, int nCmdShow) // WinMain Funktion definieren
{
DWORD style=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; // Den Typ des Fensters festlegen
hInstance=hInst; // Den Parameter von WinMain global machen
WNDCLASSEX wincls; // Die Window Klasse erstellen
MSG msg; // Den Window Message erstellen
if (fullscreen) // Den Fullscreen aktivieren falls ja
{
DEVMODE dmScreenSettings; // Den Fullscreen einstellen
memset(&dmScreenSettings,0,sizeof(dmScreenSettings));
dmScreenSettings.dmSize=sizeof(dmScreenSettings);
dmScreenSettings.dmPelsWidth = 640; // 640
dmScreenSettings.dmPelsHeight = 480; // 480
dmScreenSettings.dmBitsPerPel = 32;
dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;
if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL) // Wenn nicht erfolgreich, dann zurücksetzen
{
if (MessageBox(NULL,"Wollen sie ohne Vollbild fortfahren?","GL",MB_YESNO|MB_ICONEXCLAMATION)==IDYES)
{
fullscreen=FALSE;
}
else
{
MessageBox(NULL,"Fehler!","ERROR",MB_OK|MB_ICONSTOP);
return 0;
}
}
}
if (fullscreen) // Wenn erfolgreich, dann Fenster ändern
{
style=WS_BORDER | WS_VISIBLE | WS_POPUPWINDOW; // Den Typ besser anpassen
ShowCursor(FALSE); // Mauszeiger verstecken
}
So, das war etwas viel auf einmal. Aber ich habe ja alles
gut auskommentiert. Jetzt wenden wir uns der Funktion zum
Übersetzen der Messages zu, die in der WNDCLASSEX Klasse
auf lpfnWndProc gesetzt wurde.
void EnableOpenGL(HWND hWnd, HDC *hDC, HGLRC *hRC) // Wir nennen sie EnableOpenGL
{
int iFormat; // Für das Pixelformat
PIXELFORMATDESCRIPTOR pfd; // Für den DESCRIPTOR
Am ende des Programmes müssen wir noch das Fenster "zerstören", damit keine Reste vom Programm im Speicher bleiben. Dies geschieht mit dieser Funktion:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
void KillGLWindow()
{
if (fullscreen) // Wenn Vollbild, dann zurücksetzten
{
ChangeDisplaySettings(NULL,0);
ShowCursor(TRUE); // Mauszeiger zeigen
}
if (hRC) // Sonstiges löschen
{
wglMakeCurrent(NULL,NULL);
wglDeleteContext(hRC);
hRC=NULL;
}
ReleaseDC(hWnd,hDC);
}
Folgende Funktion heben wir uns auf für das
rendern der Grafiken:
1 2 3 4 5
void DrawGLScene()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Den Bildschirm säubern
glLoadIdentity(); // Die Modelview Matrix zurücksetzten
}
So, wir haben alles fertig behandelt, das heißt, dass ich nun den ganzen Code zum direkten Kopieren in eine Cpp Datei aufschreiben werde:
1 2 3 4 5 6 7 8 9
HDC hDC=NULL; // Eine Variable für das OpenGl Fenster
HGLRC hRC=NULL; // Eine Variable für das OpenGl Fenster
HWND hWnd=NULL; // Eine Variable für das normale Windows Fenster
HINSTANCE hInstance; // Die Instanz des Fensters
bool fullscreen=true; // Soll der Fullscreen Modus Aktiviert werden?
bool active=true; // Reagiert das Programm?
bool bQuit=false; // Soll das Programm beendet werden?
bool keys[256]; // Welche Tasten sind gedrückt?
0
Kapitel 2: Erste Polygone
Jetzt werden wir ein Dreieck rendern. Wir müssen aus dem Fenster die vorherigen Polygone, die wegen der Schleife übrig bleiben, löschen oder einfach auch übermalen. Dies können wir mit diesen Befehlen machen:
1 2 3 4 5 6 7 8 9
HDC hDC=NULL; // Eine Variable für das OpenGl Fenster
HGLRC hRC=NULL; // Eine Variable für das OpenGl Fenster
HWND hWnd=NULL; // Eine Variable für das normale Windows Fenster
HINSTANCE hInstance; // Die Instanz des Fensters
bool fullscreen=true; // Soll der Fullscreen Modus Aktiviert werden?
bool active=true; // Reagiert das Programm?
bool bQuit=false; // Soll das Programm beendet werden?
bool keys[256]; // Welche Tasten sind gedrückt?
1
Nun kann es mit dem Zeichnen losgehen. Die obere linke Ecke des Fensters entspricht den Koordinaten -1, -1. Die obere rechte Ecke 1, -1. Die untere linke Ecke -1, 1. Die untere rechte Ecke 1, 1. Und die Mitte des Fensters ist dann 0, 0. Ist doch logisch. Ich nehme jetzt an, dass du es bis hierhin kapiert hast. Nun nehmen wir die z Achse dazu. Nehmen wir mal an, dass ihr Wert gerade 10 beträgt. Dies hat zu folgen, dass sie nicht mehr angezeigt wird, weil sie dann aus den Bildschirm in deine Richtung bei positiven Werten geht. Wenn ihr Wert -10 beträgt, dann wird sie im Bildschirm angezeigt. Nehmen wir mal an, dass du das ganze Fenster mit einen Viereck ausgefüllt hast und dessen z Wert 0 beträgt. Wenn wir diesen Wert auf z.B. -15 setzten, dann wird das Viereck kleiner, da es sich von dir entfernt. Ok, das solltest du schon verstanden haben. Da immer angenommen wird, dass dein einziger Polygon -1, -1 bis 1, 1 groß ist, kann man diesen wunderbar irgendwo setzten. Dies geschieht so:
glTranslatef(1.5f,0.0f,-6.0f);
Mit diesen Befehl habe ich jetzt den darauf folgenden Polygon um 1.5 Felder nach links und 6 Felder nach Hinten positioniert. Jetzt zeichnen wir ein Dreieck. Dies muss mit folgendem Befehl eingeleitet werden:
1 2 3 4 5 6 7 8 9
HDC hDC=NULL; // Eine Variable für das OpenGl Fenster
HGLRC hRC=NULL; // Eine Variable für das OpenGl Fenster
HWND hWnd=NULL; // Eine Variable für das normale Windows Fenster
HINSTANCE hInstance; // Die Instanz des Fensters
bool fullscreen=true; // Soll der Fullscreen Modus Aktiviert werden?
bool active=true; // Reagiert das Programm?
bool bQuit=false; // Soll das Programm beendet werden?
bool keys[256]; // Welche Tasten sind gedrückt?
2
Nun können wir hintereinanderweg drei Vertex Befehle eintippen, da ein Dreieck aus drei verschiedenen Punkten besteht. Diese werden nacheinander miteinander verbunden. Aber vorher fügen wir noch Farbe hinzu. Dies geschieht mit diesem Befehl:
1 2 3 4 5 6 7 8 9
HDC hDC=NULL; // Eine Variable für das OpenGl Fenster
HGLRC hRC=NULL; // Eine Variable für das OpenGl Fenster
HWND hWnd=NULL; // Eine Variable für das normale Windows Fenster
HINSTANCE hInstance; // Die Instanz des Fensters
bool fullscreen=true; // Soll der Fullscreen Modus Aktiviert werden?
bool active=true; // Reagiert das Programm?
bool bQuit=false; // Soll das Programm beendet werden?
bool keys[256]; // Welche Tasten sind gedrückt?
3
Ich habe die Farbe auf rot gesetzt. Das erste Argument bestmmt den Rot-Anteil, das zweite den Grün-Anteil und das dritte den Blau-Anteil. Wir können vor jedem Vertex (siehe den darauffolgenden Befehl) die Farbe neu setzten, damit ein Vermischungseffekt entsteht. Dies kann man jedoch nur einmal setzten oder auch gar nicht, damit wir einfaches Weiß erhalten. Nun zu den Vertex Befehl. Wie gesagt brauchen wir davon 3, da ein Dreieck aus drei Punkten, die miteinander verbunden werden, besteht. Es basiert auf den Koordinatensystem, das ich zuvor erklärt habe:
1 2 3 4 5 6 7 8 9
HDC hDC=NULL; // Eine Variable für das OpenGl Fenster
HGLRC hRC=NULL; // Eine Variable für das OpenGl Fenster
HWND hWnd=NULL; // Eine Variable für das normale Windows Fenster
HINSTANCE hInstance; // Die Instanz des Fensters
bool fullscreen=true; // Soll der Fullscreen Modus Aktiviert werden?
bool active=true; // Reagiert das Programm?
bool bQuit=false; // Soll das Programm beendet werden?
bool keys[256]; // Welche Tasten sind gedrückt?
4
Dies waren die Befehle, die man für ein ordentliches Dreieck anwenden sollte. Zwischen denen kannst du den Befehl für eine neue Farbe einsetzten. Dadurch erhälst du ein tollen Vermischungseffekt. Da wir jetzt mit den Dreick fertig sind, müssen wir den Codeblock abschließen und den Backbuffer anzeigen:
1 2 3 4 5 6 7 8 9
HDC hDC=NULL; // Eine Variable für das OpenGl Fenster
HGLRC hRC=NULL; // Eine Variable für das OpenGl Fenster
HWND hWnd=NULL; // Eine Variable für das normale Windows Fenster
HINSTANCE hInstance; // Die Instanz des Fensters
bool fullscreen=true; // Soll der Fullscreen Modus Aktiviert werden?
bool active=true; // Reagiert das Programm?
bool bQuit=false; // Soll das Programm beendet werden?
bool keys[256]; // Welche Tasten sind gedrückt?
5
Du kannst die Anweisungen von glBegin() bis glEnd sooft wiederholen, wie du willst. Das Ergebnis sind dann mehrere Dreiecke. Zum Schluss fasse ich das ganze zusammen. Diesmal jedoch musst du nur von der letzten Zusammenfassung in Kapitel 1 die Funktion void DrawGLScene() mit dieser ersetzten:
1 2 3 4 5 6 7 8 9
HDC hDC=NULL; // Eine Variable für das OpenGl Fenster
HGLRC hRC=NULL; // Eine Variable für das OpenGl Fenster
HWND hWnd=NULL; // Eine Variable für das normale Windows Fenster
HINSTANCE hInstance; // Die Instanz des Fensters
bool fullscreen=true; // Soll der Fullscreen Modus Aktiviert werden?
bool active=true; // Reagiert das Programm?
bool bQuit=false; // Soll das Programm beendet werden?
bool keys[256]; // Welche Tasten sind gedrückt?
6
Kapitel 3: Rotierender 3D Würfel
Nun kommen wir langsam auf das Ende des Workshops zu. In diesen Kapitel, wie die Überschrift schon alles sagt, werden wir einen rotierenden 3D Würfel ausgeben. Erst brauchen wir noch eine globale Variable oben. Diese soll den rotatins Winkel speichern. Wir deklarieren sie so:
1 2 3 4 5 6 7 8 9
HDC hDC=NULL; // Eine Variable für das OpenGl Fenster
HGLRC hRC=NULL; // Eine Variable für das OpenGl Fenster
HWND hWnd=NULL; // Eine Variable für das normale Windows Fenster
HINSTANCE hInstance; // Die Instanz des Fensters
bool fullscreen=true; // Soll der Fullscreen Modus Aktiviert werden?
bool active=true; // Reagiert das Programm?
bool bQuit=false; // Soll das Programm beendet werden?
bool keys[256]; // Welche Tasten sind gedrückt?
7
Nach diesem Schritt wenden wir uns der gewohnten Funktion void DrawGLScene(). Hier machen wir dasselbe wie letztes Mal. Also erst den Bildschirm leeren, die Abhängigkeit zurücksetzten usw. Wir halten bei den Translate Befehl an. Hier werden wir gleich den Würfel drehen im Winkel von float alpha Grad. Dazu gibt es ein einfachen Befehl:
1 2 3 4 5 6 7 8 9
HDC hDC=NULL; // Eine Variable für das OpenGl Fenster
HGLRC hRC=NULL; // Eine Variable für das OpenGl Fenster
HWND hWnd=NULL; // Eine Variable für das normale Windows Fenster
HINSTANCE hInstance; // Die Instanz des Fensters
bool fullscreen=true; // Soll der Fullscreen Modus Aktiviert werden?
bool active=true; // Reagiert das Programm?
bool bQuit=false; // Soll das Programm beendet werden?
bool keys[256]; // Welche Tasten sind gedrückt?
8
Das erste Argument ist der Winkel, in dem sich der Würfel drehen soll, das Zweite um die X, das Dritte um die Y und das viert Argument um die Z Achse. Du kannst mit diesen Befehl selber mal herumexperimentieren. Nun können wir den Würfel zeichnen:
1 2 3 4 5 6 7 8 9
HDC hDC=NULL; // Eine Variable für das OpenGl Fenster
HGLRC hRC=NULL; // Eine Variable für das OpenGl Fenster
HWND hWnd=NULL; // Eine Variable für das normale Windows Fenster
HINSTANCE hInstance; // Die Instanz des Fensters
bool fullscreen=true; // Soll der Fullscreen Modus Aktiviert werden?
bool active=true; // Reagiert das Programm?
bool bQuit=false; // Soll das Programm beendet werden?
bool keys[256]; // Welche Tasten sind gedrückt?
9
Dieser Code gibt ein perfekten Würfel aus. Natürlich kannst du ihn etwas verändern. Da sich der Rotationswinkel irgendwann verändern oder auch erhöhen muss, addieren wir zu alpha einen Wert so zwischen 1 und 2 hinzu. Es soll ja nicht zu schnell und auch nicht zu langsam sein:
So, jetzt fasse ich nur die Funktion void DrawGLScenes zusammen. Du kannst sie direkt in dein Quelltext übernehmen. Denke daran, dass du vorher die Variable float alpha definiert hast.
Ende:
Ich habe mich bemüht den ganzen Beispiel Code auszukommentieren. Alle Beispiele habe ich erfolgreich mit den Borland BCC 5.5 getestet. In meinen nähsten Workshop werde ich euch zeigen, wie man Texturen einbindet. Bis dahin noch viel Spass und Erfolg!