The demo operates by creating a borderless, captionless popup window covering the entire screen. Initially, this window is transparent because it does not paint itself. Then an OpenGL context is created that has no back buffer, so all draw calls operate directly on the displayed image. Each frame, a full-screen alpha-blended rectangle is drawn, fading the image gradually toward black. Any color (or texture) could be chosen, but black is probably the best in most cases. After the fadeout is complete, the application can switch video modes and create its main window, and then destroy the fadeout window. The demo program creates a new OpenGL context and renders a spinning cube until you press Escape.
The core of the effect is in this message handler for the fadeout window:
LRESULT CALLBACK FadeoutWndProc (HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
static int frames = 0;
static DWORD nextFrameTime;
switch (message)
{
case WM_CREATE:
{
// Set up a basic OpenGL environment for the fadeout, with no
// backbuffer, zbuffer, stencil, or destination-alpha
g_pRenderEnvironment = CreateOpenGLEnvironment(hWnd, false, false,
false, false);
if (!g_pRenderEnvironment)
{
MessageBox(hWnd, L"Could not set up OpenGL!",
L"Error", MB_ICONEXCLAMATION);
DestroyWindow(hWnd);
PostQuitMessage(1);
return 0;
}
ShowCursor(false);
// Setup OpenGL state
glEnable(GL_BLEND);
glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, 1, 0, 1, -1, 1);
nextFrameTime = GetTickCount() + FADEOUT_TIME / 256;
return 0;
}
case WM_PAINT:
{
if (GetTickCount() > nextFrameTime)
{
++frames;
nextFrameTime += FADEOUT_TIME / 256;
// Fade out the screen one more step
glColor4f(0, 0, 0, (float)(255 - frames) / (256 - frames));
glRectf(0, 0, 1, 1);
glFinish();
if (frames == 256)
{
// Finished fading out, so validate the window rectangle
// (so Windows stops sending us paint messages)
ValidateRect(hWnd, NULL);
// Get rid of the existing render environment, as we will
// be creating a new one for the main window
delete g_pRenderEnvironment;
// Create the main window before destroying the fadeout
// window, so as not to allow the desktop to visibly
// flicker between them
CreateMainWindow();
DestroyWindow(hWnd);
}
}
return 0;
}
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
I have tried the program on an nVIDIA GeForce 6800 and on an ATI Radeon 9600, but I would love to hear if it works (or fails) on other cards. :)
The full source code and binary are available here, with the source distributed under the BSD license. The source also demonstrates some parts of a lightweight, extensible framework for graphics applications that I've been developing for an engine I'm working on.
Some improvements that could be made to the code:
* Sometimes on ATI cards the taskbar isn't affected by the fadeout, though it disappears a few seconds after the main window is created. You might be able to fix this by making the fadeout window "topmost," though I haven't gotten around to trying it.
* Some useful work, like loading resources, could be done in the background while the fadeout is occurring. Currently the program just enters a busy wait between frames: since the window is not validated, Windows spams us with WM_PAINT messages, which are ignored until it's time for the next fadeout frame. This could be done with a timer instead.












