OpenGL – Initialisierungsschritte im Detail

Im heutigen Artikel werden wir die einzelnen Initialisierungsschritte einer OpenGL-Anwendung im Detail beleuchten. Unser Augenmerk richtet sich dabei voll und ganz auf die CreateOpenGLWindow()-Funktion. Bevor wir uns jedoch der eigentlichen Implementierung zuwenden, verschaffen wir uns zunächst eine Übersicht über die einzelnen Schritte:



Schritt 1: Eigenschaften des Anwendungsfensters festlegen.

Schritt 2: Anwendungsfenster registrieren lassen.

Schritt 3: Falls die Anwendung im Vollbildmodus laufen soll, dann dessen Eigenschaften festlegen und die Anzeige-Einstellungen dementsprechend ändern.

Schritt 4: Anwendungsfenster erzeugen.

Schritt 5: Gewünschtes Pixelformat festlegen.

Schritt 6: Gerätekontext anfordern.

Schritt 7: Überprüfen, ob das gewünschte Pixelformat vom Gerätekontext unterstützt wird.

Schritt 8: Temporären OpenGL-Compatibility-Profile-Renderkontext erstellen, den wir jedoch nicht zum Rendern verwenden werden. Bei Verwendung von älteren Catalyst-Treibern (vor der offiziellen Unterstützung von OpenGL 3.3 und 4.0 seitens ATI) wurde an dieser Stelle ein OpenGL-2.x-Renderkontext erzeugt. Den temporären Renderkontext benötigen wir für die Erstellung eines Forward-Compatible-Renderkontext, den wir im weiteren Verlauf zum Rendern nutzen.

Hinweis:
Im Gegensatz zu einem Forward-Compatible-Renderkontext soll ein Compatibility-Profile-Renderkontext die Abwärtskompatibilität mit älteren OpenGL-Versionen gewährleisten.

Schritt 9: GLEW-Library initialisieren.

Schritt 10: Attribute für den OpenGL-Renderkontext festlegen. Wir verwenden aus Performancegründen einen Forward-Compatible-Renderkontext, der jedoch keine Abwärtskompatibilität mehr gewährleistet.

Schritt 11: Überprüfen, ob der gewünschte Renderkontext erstellt werden kann.

Schritt 12: Den Compatibility-Profile-Renderkontext löschen und den Forward-Compatible-Renderkontext zum neuen Renderkontext der Anwendung machen.


Hier nun der zugehörige Source Code:

BOOL CreateOpenGLWindow(LPCWSTR title)
{

// Struktur zum Speichern der Fenstereigenschaften
WNDCLASS wc;
DWORD    dwExStyle;   // Aussehen des Fensters (Erweiterungen)
DWORD    dwStyle;     // Aussehen des Fensters
RECT     WindowRect;  // Fenstergröße

// Fenstergröße festlegen:
WindowRect.left   = 0;
WindowRect.right  = g_screenwidth_Loaded;
WindowRect.top    = 0;
WindowRect.bottom = g_screenheight_Loaded;

// Instanz der Windows Anwenung holen:
g_ApplicationInstanceHandle = GetModuleHandle(NULL);

// Fenstereigenschaften festlegen:

// Aussehen des Fensters festlegen:
wc.style    = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;

// Callback Funktion für die Nachrichtenverarbeitung:
wc.lpfnWndProc    = (WNDPROC) WndProc;

// weitere Eigenschaften:
wc.cbClsExtra    = 0;
wc.cbWndExtra    = 0;
wc.hInstance     = g_ApplicationInstanceHandle;
wc.hIcon         = LoadIcon(NULL, IDI_WINLOGO);
wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName  = NULL;
wc.lpszClassName = L"OpenGL";

// Fenster registrieren lassen:
if(!RegisterClass(&wc))
{
    Add_To_Log("Failed To Register The Window Class.");
    return FALSE;
}

// Einstellugen für den Vollbildmodus festlegen:
if(g_FullScreen)
{
    DEVMODE dmScreenSettings;

    // Speicherbereich leeren:
    memset(&dmScreenSettings,0,sizeof(dmScreenSettings));
    // benötigten Speicherplatz ermitteln:
   
dmScreenSettings.dmSize       = sizeof(dmScreenSettings);
    // Bildschirmbreite festlegen:
    dmScreenSettings.dmPelsWidth  = g_screenwidth_Loaded;
    // Bildschirmhöhe festlegen:
    dmScreenSettings.dmPelsHeight = g_screenheight_Loaded;
    // Farbtiefe festlegen:
   
dmScreenSettings.dmBitsPerPel = g_ColorDepth;

    dmScreenSettings.dmFields = DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;

    // Anzeigeeinstellungen für den Vollbildmodus ändern:
   
if(ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=
       DISP_CHANGE_SUCCESSFUL)
    {
        g_FullScreen = FALSE;
    }
}

// Der Wechsel in den Vollbildmodus war erfolgreich:
if(g_FullScreen)
{
    dwExStyle=WS_EX_APPWINDOW; // Fenster wird in der Task-Leiste Angezeigt
   
dwStyle=WS_POPUP;          // Fenster ohne Rahmen verwenden
   
ShowCursor(FALSE);         // Mauszeiger nicht anzeigen
}
else
{
    // Fenster wird in der Task-Leiste Angezeigt,Fenster,
    // mit gehobener Kante:
   
dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;

    // Fenster-Buttons für Minimieren, Maximieren und Schließen anzeigen:
   
dwStyle=WS_OVERLAPPEDWINDOW;
}

// Fenster justieren:
AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);

// Anwendungsfenster erzeugen:
if(!(g_MainWindowHandle = CreateWindowEx(dwExStyle, L"OpenGL", title,
                          dwStyle | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
                          0, 0, WindowRect.right-WindowRect.left,
                          WindowRect.bottom-WindowRect.top, NULL,
                          NULL, g_ApplicationInstanceHandle, NULL)))
{
    KillOpenGLWindow();
    Add_To_Log("Window Creation Error!");
    return FALSE;
}

// unser gewünschtes Pixelformat festlegen:
static PIXELFORMATDESCRIPTOR pfd =
{
    sizeof(PIXELFORMATDESCRIPTOR), 1,
    PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
    PFD_TYPE_RGBA, g_ColorDepth, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    g_ZBufferDepth, 0, 0, PFD_MAIN_PLANE, 0, 0, 0, 0
};

// Device (Geräte) Context (DC) holen:
if(!(g_DeviceContextHandle = GetDC(g_MainWindowHandle)))
{
    KillOpenGLWindow();
    Add_To_Log("Can't Create A GL Device Context!");
    return FALSE;
}

// Zwischenspeichern des beim Rendern zu verwendenden Pixelformats
// Farbtiefe, Genauigkeit des Tiefenpuffers, etc.
GLuint PixelFormat;

// Überprüfen, ob das gewünschte Pixelformat unterstützt wird:
if(!(PixelFormat=ChoosePixelFormat(g_DeviceContextHandle, &pfd)))
{
    KillOpenGLWindow();
    Add_To_Log("Can't Find A Suitable PixelFormat!");
    return FALSE;
}

// Pixelformat festlegen:
if(!SetPixelFormat(g_DeviceContextHandle,PixelFormat,&pfd))
{
    KillOpenGLWindow();
    Add_To_Log("Can't Set The PixelFormat!");
    return FALSE;
}

// zunächst einen temporären
Compatibility-Profile-Renderkontext erstellen,
// den wir jedoch nicht zum Rendern verwenden werden
:
HGLRC tempContext = wglCreateContext(g_DeviceContextHandle);
wglMakeCurrent(g_DeviceContextHandle, tempContext);

//
GLEW-Library initialisieren
glewInit();

Add_To_Log((char*)glGetString(GL_VERSION));

// Attribute für den OpenGL 3.3 Render-Context festlegen:
int attribList[] =
{
    WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
    WGL_CONTEXT_MINOR_VERSION_ARB, 3,
    WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, 0
};

// Überprüfen, ob ein OpenGL 3.3 Render-Context erstellt werden kann:
if(wglewIsSupported("WGL_ARB_create_context") == 1)
{
// OpenGL 3.3 Render-Context erstellen:
// Hierfür wird die wglCreateContextAttribsARB()-Methode benötigt, die
// ihrerseits den
Compatibility-Profile-Context benötigt:
   
g_OpenGLRenderContextHandle = wglCreateContextAttribsARB(
                                  g_DeviceContextHandle,0, attribList);

    // temporären
Compatibility-Profile-Context löschen:
   
wglMakeCurrent(NULL,NULL);
    wglDeleteContext(tempContext);

    // OpenGL 3.3 Render-Context zum Rendern verwenden:
   
wglMakeCurrent(g_DeviceContextHandle,g_OpenGLRenderContextHandle);
    Add_To_Log( (char*)glGetString(GL_VERSION) );
}

ShowWindow(g_MainWindowHandle, SW_SHOW); // Fenser anzeigen
SetForegroundWindow(g_MainWindowHandle); // im Vordergrund

// Tastatureingaben sollen von unserer Anwendung verarbeitet werden:
SetFocus(g_MainWindowHandle);

// Größe des Betrachtungsfensters (Viewport) festlegen
// und Projektionsmatrizen berechnen:
ReSizeOpenGLScene(g_screenwidth, g_screenheight);

if(!Set_Initial_OpenGL_RenderParameter(&g_BackgroundScreenColor))
{
    KillOpenGLWindow();
    Add_To_Log("Can't Activate The GL Rendering Context.");
    return FALSE;
}

return TRUE;
}