reedbeta at July 30th, 2009 02:02 — #1
Today, I was working on a project where I wanted to do some OpenGL rendering to an offscreen buffer and save it as a bitmap. I googled around a bit for information on creating an offscreen rendering context (in Windows). The usual way to do it, according to the Internet (see for example this GameDev post) seems to be to create an offscreen device context with CreateCompatibleDC, and use a PFD_DRAW_TO_BITMAP pixel format.
However, when I tried this I found that the GL context thus created is not hardware accelerated, but falls back to Windows' built-in GL 1.1 software driver.
The other obvious way to do it is create a hidden window and set up GL normally, with PFD_DRAW_TO_WINDOW. This gets hardware accelerated and works (you can draw and retrieve the image without ever showing the window on-screen). It seems a bit extravagant though. Both methods "should" work equally well since it just comes down to drawing to a memory area that doesn't happen to be on-screen.
I'm curious about other peoples' experience here - if you've built a program that uses offscreen rendering, have you encountered similar issues? Are there configurations out there for which offscreen-DC does work, or hidden-window doesn't? Is there a different way of setting up the offscreen DC to make this work?
dk2 at July 30th, 2009 03:50 — #2
What about a framebuffer object (EXT_framebuffer_object), which can render to an offscreen framebuffer? Since it's windowing-system-agnostic, you wouldn't need any window-specific information and it should be accelerated by the hardware if it's supported. You can then use glReadPixels or glCopyTexImage2D to read back the contents of the render buffer. Seems like this should work in your case, unless I misunderstood what you're trying to do.
This page has good information about them.
martinsm at July 30th, 2009 06:50 — #3
You anyway need valid OpenGL context to create and use framebuffer objects.
thenut at July 30th, 2009 08:56 — #4
Like what Dia said, I too would recommend just using a render to texture option and then you can pull / save the bitmap data however you like. It's pretty fast too, since I've recorded video feeds in real-time using it (if you're worried about glCopyTeximage being slow). Its my preferred choice over off-screen rendering / p-buffers, which also runs into numerous portability issues.
reedbeta at July 30th, 2009 12:08 — #5
I'm aware of framebuffer objects, but those don't solve my problem - like martinsm says, you need to have an OpenGL context already to use them. My question is about how you get the OpenGL context in the first place. If you are writing an interactive app or game and already have a context for your main window, great, but I'm writing a console app to do some offline processing and am not showing anything on the screen, so I don't have a main window.
Update: I found that while the hidden-window method works in Vista, it does not seem to work in XP - with the window hidden, attempting to read back the framebuffer just grabs whatever is on-screen at the window's location.
I can probably still use a framebuffer object together with the hidden window to get hardware offscreen rendering. It's even more rigmarole, though.
martinsm at July 30th, 2009 15:51 — #6
I usually use hidden window + FBO approach for some GPGPU stuff I do. Now of course I wait for OpenCL
thenut at July 30th, 2009 19:34 — #7
Yep, a hidden window is the only way to go. Here's some code, which works fine in XP. Resolutions are of course limited to your desktop, but you can add a quick FBO in there and you're well off into the races.
LRESULT CALLBACK WndProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam)
return DefWindowProc(hWnd, Message, wParam, lParam);
int main (int argc, char **argv)
// Window properties
int width = 640;
int height = 480;
wndClass.cbSize = sizeof(WNDCLASSEX);
wndClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS;
wndClass.lpfnWndProc = WndProc;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hInstance = 0;
wndClass.hIcon = 0;
wndClass.hCursor = LoadCursor(0, IDC_ARROW);
wndClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wndClass.lpszMenuName = 0;
wndClass.lpszClassName = "WndClass";
wndClass.hIconSm = 0;
// Style the window and remove the caption bar (WS_POPUP)
DWORD style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_POPUP;
// Create the window. Position and size it.
HWND wnd = CreateWindowEx(0,
CW_USEDEFAULT, CW_USEDEFAULT, width, height,
0, 0, 0, 0);
HDC dc = GetDC(wnd);
// Setup OpenGL
memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
pfd.nSize = sizeof(pfd);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 32;
pfd.cDepthBits = 16;
pfd.cStencilBits = 8;
pfd.iLayerType = PFD_MAIN_PLANE;
int pixelFormat = ChoosePixelFormat(dc, &pfd);
SetPixelFormat(dc, pixelFormat, &pfd);
HGLRC rc = wglCreateContext(dc);
// Initialize your OpenGL properties and states here.
// Do a loop or call display() once, whatever rocks your boat
while ( true )
// Save yer image here.
reedbeta at July 30th, 2009 22:11 — #8
Thanks. BTW, you can actually specify DefWindowProc directly as the window procedure, with no need for the passthrough routine.
thenut at July 31st, 2009 06:08 — #9
I just ripped parts and pieces from my engine so I didn't bother cleaning it up much
dpatte at April 19th, 2010 15:32 — #10
I'm slightly confused with the idea of rendering openGL to an off-screen buffer.
I am trying to generate a skin using openGL that will be used as a source bitmap by UpdateLayeredWindow. It takes a source HDC as an input.
I used the code you provided above, and it works fine, but only if I leave the new openGL window visible (SW_SHOW). If I hide the new openGL window the hdc doesnt seem to contain the image I generated, and updateLayeredWindow is not what I expect.
Am I missing something?