OpenGL-Initialisierung: Funktionen und Variablen, Renderkontext und Gerätekontext

Die Implementierung aller an der OpenGL-Initialisierung und -Freigabe beteiligten Funktionen finden Sie in der Datei OpenGL_Initialisiation_And_Shutdown.cpp. Es ist mit Sicherheit ein mehr als nur zähes Unterfangen, bis man sich endlich durch den kompletten Source Code durchgearbeitet hat. Für die Mehrheit der Programmierer ist derlei Code darüber hinaus recht uninteressant und gehört in die Kategorie „Kopieren, Einfügen und Vergessen“. Auch wenn diese Haltung durchaus nachvollziehbar ist, sollte man sich zumindest ein grobes Verständnis über die einzelnen Initialisierungsschritte bei Programmstart und über die notwendigen Aufräumarbeiten bei Programmende aneignen. Aus diesem Grund beginnen wir unsere Betrachtungen mit einer Beschreibung der verwendeten Funktionen und Variablen. Selbstverständlich steht es jedem Leser frei, sich im weiteren Verlauf dieser Artikelserie tiefer in die Materie einzuarbeiten.


Für jede OpenGL-Anwendung müssen wir zunächst einen Render- und einen Gerätekontext anlegen. Der Gerätekontext (Device Context, DC) stellt die Schnittstelle zur Grafikkarte dar und ist damit für die Verarbeitung aller OpenGL-Aufrufe verantwortlich. Eine OpenGL-Anwendung hat jedoch keinen direkten Zugriff auf den Gerätekontext, sondern ist über einen Renderkontext mit diesem verbunden. Im Zuge dessen speichert der Renderkontext alle benötigten Informationen (Bildschirmauflösung, Farbtiefe, Tiefenpuffer-Genauigkeit, usw.), damit die 3D-Szene korrekt dargestellt werden kann. Soll nun ein Renderkontext mit einem Gerätekontext verbunden werden, kommt die wglMakeCurrent()-Funktion zum Einsatz:

// Renderkontext mit Gerätekontext verbinden:
wglMakeCurrent(g_DeviceContextHandle,g_OpenGLRenderContextHandle);

Hinweis:
Für eine OpenGL-Anwendung lassen sich selbstverständlich auch mehrere Renderkontexte erstellen. Müssen beispielsweise zur Laufzeit Texturen oder 3D-Modelle nachgeladen werden, dann bietet sich die Verwendung eines zweiten Renderkontexts an, der im Rahmen eines Loader-Threads zum Einsatz kommt. Wichtig ist jedoch, dass beide Kontexte die erzeugten Objekte (Texturen, Vertexbuffer, Indexbuffer, usw.) miteinander teilen. Zuständig hierfür ist die wglShareLists()-Funktion:

wglShareLists(g_OpenGLRenderContextHandle,
              g_OpenGLRenderContextHandle_Loader_Thread);

Kommen wir nun zur Deklaration von Render- und Gerätekontext. Beide Variablen werden global verwendet, genau wie die Handles der zugrunde liegenden Windows-Anwendung:

// Rendering Context (RC):
HGLRC g_OpenGLRenderContextHandle = NULL;

// Device (Geräte) Context (DC):
HDC g_DeviceContextHandle = NULL;

// Handle der Windows Anwendung:
HWND g_MainWindowHandle = NULL;

// Handle auf die Instanz der Windows Anwendung:
HINSTANCE g_ApplicationInstanceHandle = NULL;


Hier nun eine Übersicht über die einzelnen Funktionen, die im Verlauf der Initialisierung und beim Beenden einer OpenGL-Anwendung zum Einsatz kommen:


Die InitResolutionAndRenderOptions()-Funktion öffnet eine Konfigurationsdatei (ResolutionAndRendering.txt) und liest alle renderspezifischen Informationen in das Programm ein.

Funktionsprototyp:
void InitResolutionAndRenderOptions(char* pFileName);

Betrachten wir ein Beispiel für solch eine Konfigurationsdatei:

ZBufferDepth(Bit): 24
Windows/Fullscreen(0/1): 0
Frame/TimeBased(1/2): 1
MaxFrameRate(FrameBased): 35.0
MinimalAcceptableFrameRate: 35.0
FOV_Angle(Blickfeld): 60.0
ViewDistance: 15000.0
screenwidth: 1024
screenheight: 768
colordepth(Bit): 32

. . .

Nachdem alle renderspezifischen Parameter eingelesen worden sind, kann die OpenGL-Anwendung mithilfe der CreateOpenGLWindow()-Funktion initialisiert werden.

Funktionsprototyp:
BOOL CreateOpenGLWindow(LPCWSTR title);

Alle OpenGL-Render-Parameter (bsp. Tiefenpuffer-Einstellungen), die im weiteren Verlauf genutzt werden sollen, werden im Zuge der Initialisierungsarbeiten innerhalb der Set_Initial_OpenGL_RenderParameter()-Funktion eingestellt.

Funktionsprototyp:
BOOL Set_Initial_OpenGL_RenderParameter(D3DXVECTOR4*
                                        pBackgroundScreenColor);

Die ReSizeOpenGLScene()-Funktion dient zum Einstellen der Viewport-Eigenschaften (glViewport(), Bildschirmauflösung bsp. 1024 x 768). Darüber hinaus werden die Projektionsmatrizen berechnet (g_matProj, g_matProj_ZBiased), mit deren Hilfe in den Vertex-Shader-Programmen später die Transformation der 3D-Modell-Vertices aus dem Kameraraum in den Bildraum (auf den Bildschirm) erfolgt.

Funktionsprototyp:
void ReSizeOpenGLScene(long width, long height);

Bevor eine OpenGL-Anwendung wieder beendet werden kann, müssen Render- und Gerätekontext wieder freigegeben werden. Hier kommt die KillOpenGLWindow()-Funktion ins Spiel.

Funktionsprototyp:
void KillOpenGLWindow(void);