Platform specifics: Windows
Compiler errors and gl.h
If you get compiler errors pointing to gl.h, this is because there are some things like WINGDI which are used in gl.h but are only defined in some other header files. Include windows.h before including gl.h
If you have link errors, then you need to figure out how to link with your specific compiler/IDE.
Example : with VC++6 (which is very old by now), you go to Project->Settings and click on the links tab.
Add opengl32.lib to the Object/library modules list.
If you use other libs like GLU, add glu32.lib.
Other compiler/IDE mind need to link with opengl32.a
What should I do before the window is created?
Be sure to use the CS_OWNDC flag.
If you are doing plain old WinMain coding, RegisterClassEx() takes the WNDCLASSEX structure.
WNDCLASSEX.style should have the CS_OWNDC
If you are using MFC (SDI or MDI project), override PreCreateWindow and add the CS_OWNDC to cs.
For CDialog based project, PreCreateWindow doesn't get called.
This flag is really needed for Win9x systems.
Since DCs (device contexts) consume resources, the OS does not assign a DC but might share it between multiple windows and GUI elements.
On WinNT and its derivatives, it is said that resources are not a problem and it assigns a unique DC per window.
For good practice, I recommend using CS_OWNDC.
When is a good time to create the GL context?
When the WM_CREATE message is sent.
This is when the window is created (has a valid HWND and DC).
Normally, the window is visible at this point. Even if you create a invisible window, this is the place to create the GL context.
This is also a good place to make the context current, setup some GL states and setup some things for your program.
For MFC, you override the OnCreate function for your "View" class.
It's flickering! Why?
Windows sends a WM_ERASEBKGND message when the background needs to be erased.
Tell Windows that you handled the message by returning a non-zero number (TRUE).
On MFC, override OnEraseBkgnd and just return TRUE.
When do I render my scene?
Answer to this question depends on the situation:
- For application like CAD, 3D model viewer or anything that doesn't require constant updating, render when you receive the WM_PAINT message
- For high performance game do not use WM_PAINT and render directly from your "infinite loop"
Besides this simple division, please consider following advices:
- If you are developing game or dynamic visualisation in window (not fullscreen), consider setting up a timer and render only when receiving WM_TIMER message. You will be still able to get high framerate, but your program will not eat so much CPU resources
- If you are developing fullscreen application, do not render when user ALT-TABs from your program. You can detect this by handling WM_ACTIVATEAPP. This will again prevent CPU "hogging" when it is not necessary
To render image on the screen, you can follow this simple scheme:
glClear( ... ) // Clear the color/depth/stencil buffer // // Draw your geometry using OpenGL commands here // SwapBuffers( ... ) // Swap buffers to make geometry visible on screen
To swap buffers, use SwapBuffers instead of wglSwapBuffers.
For MFC, override OnPaint or OnDraw (depending on the parent class)
When do I destroy the GL context?
Some people do it when the WM_DESTROY message is received. In my experience, this is not correct because at this point, the window resources and DC are destroyed already. Functions like wglMakeCurrent may fail.
Try the following: call wglMakeCurrent(NULL, NULL) followed by wglMakeCurrent(hdc, hglrc).
The second call will(might) fail.
Instead, destroy the GL context when the WM_CLOSE message is received. With MFC, override OnClose.
Don't forget to deallocate your textures, display lists, VBOs, PBOs, FBOs, shaders, etc before destroying the GL context.
It is good programming practice to release resources!
Call wglMakeCurrent(NULL, NULL) to make the GL context non-current.
Call wglDeleteContext(glrc) to destroy the GL context.
If you have allocated a DC for your window, release it and/or destroy it (ReleaseDC and/or DeleteDC)
How many times can I call SetPixelFormat?
For each window, once. According to MSDN, it would lead to significant complications if they allowed for more flexibility.
Never call GetDC(NULL) and then call SetPixelFormat. This gives the DC for the entire desktop. Instead, create a fullscreen window.
This flag is for making an offscreen render buffer onto with you render with your GL commands. Typically, people do this so that they can BitBlt the result to a window or a BitBlt to a printer device context or so that they can use some Win32 commands on that same surface.
A few source code examples from 1995 show how to use PFD_DRAW_TO_BITMAP.
You should avoid using this flag during context creation, as it may cause you to not get a hardware-accelerated rendering context..
If your project opens other windows and you want GL on the other window as well, in each function that you have, call wglMakeCurrent(hdc, glrc) and when
you are done call wglMakeCurrent(NULL, NULL)
wglMakeCurrent(hdc, glrc); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); RenderThis(); RenderThat(); SwapBuffers(hdc); wglMakeCurrent(NULL, NULL);
This is because a GL context can be current to 1 thread. Since window 1 has a GL context and window 2 has a GL context, both can't be current at the same time. You can either create a window in another thread, this way each context is in its own thread, or if both windows use the same pixelformat, use 1 GL context for both windows. In the 2 window 1 context case, you still have to call wglMakeCurrent(hdc, glrc) wglMakeCurrent(NULL, NULL) because the hdc would be different for each window.
According to MSDN, wglShareLists shares display list space between 2 GL contexts. This is incomplete, as this function allows the sharing of other objects In addition to display lists, this function allows sharing of textures, renderbuffers, and buffer objects. Only these object types are shared among GL contexts.
The best time to call wglShareLists is after creating the GL contexts you want to share, but before you create any objects in either of the contexts. If you create objects, then there is a chance that wglShareLists will fail.
Sharing can also be done as part of context creation, using wglCreateContextAttribsARB. This is analogous to how *nix systems do it, and you don't have to worry about not creating objects in the new context, since creation and sharing are done simultaneously.
SwapInterval aka vsync
If you need information on WGL_EXT_swap_control which gives wglSwapIntervalEXT, read
SwapInterval aka vsync
This function is for getting a pointer to a function. Since on Windows, opengl32.dll will never get updated, opengl32.dll only provides the GL 1.1 functionality subset. To get the higher ones, you need wglGetProcAddress. Note that wglGetProcAddress gives a direct pointer to the real opengl driver implemented by your IHV, so you get to bypass opengl32.dll. This doesn't mean your GL code will run faster. In the case of modern games, there are often other bottlenecks. For example, they are often fill rate limited due to the enormous about of pixels they need to render.
An example for wglGetProcAddress would be :
First, I'm going to look at glext.h which defines all the functions in GL 1.2 and above and also extensions. Or, we could look at gl3.h (whatever you prefer).
Let's say, I want to get a pointer for glCompressedTexImage3D. I can see the definition for glCompressedTexImage3D in glext.h.
I would put the above line in one of my cpp files (If I'm programming in C++). I would probably name the file allmy_gl_functions.cpp. I would like for all my other cpp files to see that function definition therefore I would create a header file and call it allmy_gl_functions.h. I would declare this in my header file
extern PFNGLCOMPRESSEDTEXIMAGE3DPROC glCompressedTexImage3D;
and now I can include that header file in all my other cpp files so that both the compiler and linker would know what is to be done. The only thing left to do is to create a GL context and then get the function pointer
glCompressedTexImage3D = (PFNGLCOMPRESSEDTEXIMAGE3DPROC)wglGetProcAddress("glCompressedTexImage3D");
So, wglGetProcAddress takes a string as argument and looks into a table and returns an address. The only thing left for you to do is load all the hundreds of functions of GL.
Fortunately, there are already several projects that do all that for us. GLEW is one example. http://www.opengl.org/wiki/Getting_started
What about the WGL functions?
We call them the "wiggle" functions.
There are many wgl functions and some aren't even documented in MSDN.
Mostly, you will only need :
If you are writing an GL extension loader, you will make plenty use of wglGetProcAddress.
I don't recommend that you use some of these wgl functions :
- Use ChoosePixelFormat instead of wglChoosePixelFormat
- Use DescribePixelFormat instead of wglDescribePixelFormat
- Use SetPixelFormat instead of wglSetPixelFormat
- Use GetPixelFormat instead of wglGetPixelFormat
- Use SwapBuffers instead of wglSwapBuffers
glX vs wgl
This is a list of glX functions and there equivalent wgl functions. There migth be some missing entries.
glXChooseVisual --- ChoosePixelFormat
glXCopyContext —-- NULL
glXCreateContext --- wglCreateContext
glXCreateGLXPixmap --- CreateDIBitmap and CreateDIBSection
glXDestroyContext --- wglDeleteContext
glXDestroyGLXPixmap --- DeleteObject
glXGetConfig --- DescribePixelFormat
glXGetCurrentContext --- wglGetCurrentContext
glXGetCurrentDrawable --- wglGetCurrentDC
glXIsDirect —-- NULL
glXMakeCurrent --- wglMakeCurrent
glXQueryExtension ---- GetVersion
glXQueryVersion --- GetVersion
glXSwapBuffers --- SwapBuffers
glXUseXFont wglUseFontBitmaps and wglUseFontOutlines
glXWaitGL —-- NULL
glXWaitX --— NULL
XGetVisualInfo --- GetPixelFormat
XCreateWindow --- CreateWindow/CreateWindowEx and GetDC/BeginPaint
NULL —-- SetPixelFormat
glXGetProcAddress --— wglGetProcAddress
NULL —-- wglShareLists