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?
Offscreen GL contexts and hardware acceleration
Started by Reedbeta, Jul 30 2009 06:02 AM
9 replies to this topic
#2
Posted 30 July 2009 - 07:50 AM
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.
This page has good information about them.
#3
Posted 30 July 2009 - 10:50 AM
You anyway need valid OpenGL context to create and use framebuffer objects.
#4
Posted 30 July 2009 - 12:56 PM
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.
http://www.nutty.ca - Being a nut has its advantages.
#5
Posted 30 July 2009 - 04:08 PM
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.
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.
reedbeta.com - developer blog, OpenGL demos, and other projects
#6
Posted 30 July 2009 - 07:51 PM
I usually use hidden window + FBO approach for some GPGPU stuff I do. Now of course I wait for OpenCL :)
#7
Posted 30 July 2009 - 11:34 PM
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;
WNDCLASSEX wndClass;
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;
RegisterClassEx(&wndClass);
// 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,
"WndClass",
"",
style,
CW_USEDEFAULT, CW_USEDEFAULT, width, height,
0, 0, 0, 0);
HDC dc = GetDC(wnd);
// Setup OpenGL
PIXELFORMATDESCRIPTOR pfd;
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);
wglMakeCurrent(dc, rc);
// Initialize your OpenGL properties and states here.
// Do a loop or call display() once, whatever rocks your boat
while ( true )
{
display();
Sleep(33);
SwapBuffers(dc);
// Save yer image here.
}
}
http://www.nutty.ca - Being a nut has its advantages.
#8
Posted 31 July 2009 - 02:11 AM
Thanks. BTW, you can actually specify DefWindowProc directly as the window procedure, with no need for the passthrough routine.
reedbeta.com - developer blog, OpenGL demos, and other projects
#9
Posted 31 July 2009 - 10:08 AM
I just ripped parts and pieces from my engine so I didn't bother cleaning it up much ;)
http://www.nutty.ca - Being a nut has its advantages.
#10
Posted 19 April 2010 - 07:32 PM
Hi 'TheNut'
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?
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?
2 user(s) are reading this topic
0 members, 2 guests, 0 anonymous users












