🔒 Locked
Off-screen OpenGL rendering...
Advertisement
Original Post
Hi. I need render OpenGL to off-screnn buffer and write them as BMP image into the disk. I need very large image (like 2500x2000), I know how to write to disk image that fit in to the screen but don't know how to do this which such a large image. Where to read about that problem ("feedback" will be good solutioin as I suspose....)???? PS.Sorry for my poor English language skill. PSS.I'am Physicist so I need some very simple example to understand how to do this. I use Borland Builder 6 + OpenGLAP 1.0.8...
You can use the offscreen renderer in Mesa3D; OSMesa. It's a software implementation of OpenGL 1.5, and includes lots of extensions. Unless rendering speed is of great importance, Mesa is, in my oppinion, an extremely good solution, especially for offscereen rendering.
Well, Mesa IS "naked" OpenGL*. There's no difference between Mesa and the implementation that ships by default with Windows or whatever platform you use (other that different feature sets, like version, extensions, ability for hardware acceleration, and so on). Mesa is not an add-on library or so, it is a stand-alone library you use instead of some other implementation. Just download it, compile it (unless there are some precompiled binaries), and link against it instead of your other OpenGL library (opengl32.lib on Windows).
* It is OpenGL in the sense that it fullt implements the specification. For legal reasons it can't be called an implementaiton since it doesn't have a license to do so.
* It is OpenGL in the sense that it fullt implements the specification. For legal reasons it can't be called an implementaiton since it doesn't have a license to do so.
the other option would be to draw a section of it at a time, copying it back to system memory after each frame is generated and assembling it in a block of memory on your own.
There was an artical recently about saving screen shots and videos, i'm pretty sure it had some code to allow you to save 'poster quality' shots which rendered the scene multiple times to generate a large image from lots of smaller ones.
The other option is that you could make a pBuffer of 2500*2000 and render directly to it and then readback from it using glReadPixels, however there is a thread on this page detailing the problem someone has had getting a pbuffer to work at large sizes on a GeForce card (although, iirc it was working fine at around 3000*3000 so even if a bug does exist w.r.t. the size it should be fine for your purposes), but assuming it works this would probably be the better choice.
As long as you dont try and do render to texture with it then you can have a viewport size of 4096*4096 even on an ATI card (max texture size is only 2048*2048 you see)
There was an artical recently about saving screen shots and videos, i'm pretty sure it had some code to allow you to save 'poster quality' shots which rendered the scene multiple times to generate a large image from lots of smaller ones.
The other option is that you could make a pBuffer of 2500*2000 and render directly to it and then readback from it using glReadPixels, however there is a thread on this page detailing the problem someone has had getting a pbuffer to work at large sizes on a GeForce card (although, iirc it was working fine at around 3000*3000 so even if a bug does exist w.r.t. the size it should be fine for your purposes), but assuming it works this would probably be the better choice.
As long as you dont try and do render to texture with it then you can have a viewport size of 4096*4096 even on an ATI card (max texture size is only 2048*2048 you see)
For reference, the OpenGL Superbible explains how to render to a bitmap (under windows).
In short, you need to create a DIB section, and bind it to the DC. In the pixel format selection stage, use PFD_DRAW_TO_BITMAP instead of PFD_DRAW_TO_WINDOW, and use the DC with the bitmap bound to it to create the GLRC. Then you just render as normal, and save the DIB.
In short, you need to create a DIB section, and bind it to the DC. In the pixel format selection stage, use PFD_DRAW_TO_BITMAP instead of PFD_DRAW_TO_WINDOW, and use the DC with the bitmap bound to it to create the GLRC. Then you just render as normal, and save the DIB.
does that allow you to use a hardware accelerated context as well?
I've no idea, try it out.
Try this class, and tweak the ChoosePixelFormat() method to get the pixel format you want. In my case, I chose to force it to use the software renderer because performance wasn't critical, and I needed something that would never mess up due to a bug in some vendor's OpenGL driver.
class CGLBitmapContext
{
protected:
BITMAPINFO m_bmi;
HBITMAP m_hBitmap;
void *m_pBits;
HDC m_hDC, m_hOldDC;
HGLRC m_hGLRC, m_hOldGLRC;
CGLFont m_font;
public:
CGLBitmapContext()
{
}
int GetWidth() { return m_bmi.bmiHeader.biWidth; }
int GetHeight() { return m_bmi.bmiHeader.biHeight; }
CGLFont &GetFont() { return m_font; }
bool ChoosePixelFormat()
{
int nMaxWeight = 0;
int nBestFormat = 0;
int nPixelFormat = 1;
PIXELFORMATDESCRIPTOR pfdDesc;
DWORD dwRequiredFlags = PFD_DRAW_TO_BITMAP | PFD_GENERIC_FORMAT | PFD_SUPPORT_OPENGL | PFD_SUPPORT_GDI;
DWORD dwRejectedFlags = PFD_GENERIC_ACCELERATED;
while(DescribePixelFormat(m_hDC, nPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfdDesc))
{
if(pfdDesc.cColorBits == 24 && pfdDesc.iLayerType == PFD_TYPE_RGBA &&
(pfdDesc.dwFlags & dwRequiredFlags) == dwRequiredFlags &&
(pfdDesc.dwFlags & dwRejectedFlags) == 0)
{
int nWeight = pfdDesc.cColorBits * 8 + pfdDesc.cDepthBits * 4 + pfdDesc.cStencilBits + pfdDesc.cAccumBits;
if(nWeight > nMaxWeight)
{
nMaxWeight = nWeight;
nBestFormat = nPixelFormat;
}
}
nPixelFormat++;
}
if(nBestFormat && ::SetPixelFormat(m_hDC, nBestFormat, &pfdDesc))
return true;
return false;
}
bool Init(HDC hWindowDC, int nWidth, int nHeight)
{
// Create the bitmap for this OpenGL context
memset(&m_bmi, 0, sizeof(BITMAPINFO));
m_bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
m_bmi.bmiHeader.biWidth = nWidth;
m_bmi.bmiHeader.biHeight = nHeight;
m_bmi.bmiHeader.biPlanes = 1;
m_bmi.bmiHeader.biBitCount = 24;
m_bmi.bmiHeader.biCompression = BI_RGB;
m_bmi.bmiHeader.biSizeImage = nWidth * nHeight * 3;
m_hBitmap = ::CreateDIBSection(hWindowDC, &m_bmi, DIB_RGB_COLORS, &m_pBits, NULL, (DWORD)0);
// Create the memory DC for the OpenGL context
m_hDC = ::CreateCompatibleDC(NULL);
::SelectObject(m_hDC, ::GetCurrentObject(hWindowDC, OBJ_FONT));
::SelectObject(m_hDC, m_hBitmap);
// Select a software-rendering pixel format that supports drawing to a bitmap
// (Must be 24-bit color because the bitmap is 24-bit color)
if(!ChoosePixelFormat())
return false;
// Initialize the OpenGL context
m_hGLRC = ::wglCreateContext(m_hDC);
m_hOldDC = ::wglGetCurrentDC();
m_hOldGLRC = ::wglGetCurrentContext();
::wglMakeCurrent(m_hDC, m_hGLRC);
// Initialize the CGLFont object and set up the viewport
m_font.Init(m_hDC, 16);
glViewport(0, 0, nWidth, nHeight);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, nWidth, 0, nHeight);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
return true;
}
void Cleanup()
{
m_font.Cleanup();
::wglMakeCurrent(NULL, NULL);
::wglDeleteContext(m_hGLRC);
::wglMakeCurrent(m_hOldDC, m_hOldGLRC);
::DeleteObject(m_hBitmap);
::DeleteDC(m_hDC);
}
};
class CGLBitmapContext
{
protected:
BITMAPINFO m_bmi;
HBITMAP m_hBitmap;
void *m_pBits;
HDC m_hDC, m_hOldDC;
HGLRC m_hGLRC, m_hOldGLRC;
CGLFont m_font;
public:
CGLBitmapContext()
{
}
int GetWidth() { return m_bmi.bmiHeader.biWidth; }
int GetHeight() { return m_bmi.bmiHeader.biHeight; }
CGLFont &GetFont() { return m_font; }
bool ChoosePixelFormat()
{
int nMaxWeight = 0;
int nBestFormat = 0;
int nPixelFormat = 1;
PIXELFORMATDESCRIPTOR pfdDesc;
DWORD dwRequiredFlags = PFD_DRAW_TO_BITMAP | PFD_GENERIC_FORMAT | PFD_SUPPORT_OPENGL | PFD_SUPPORT_GDI;
DWORD dwRejectedFlags = PFD_GENERIC_ACCELERATED;
while(DescribePixelFormat(m_hDC, nPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfdDesc))
{
if(pfdDesc.cColorBits == 24 && pfdDesc.iLayerType == PFD_TYPE_RGBA &&
(pfdDesc.dwFlags & dwRequiredFlags) == dwRequiredFlags &&
(pfdDesc.dwFlags & dwRejectedFlags) == 0)
{
int nWeight = pfdDesc.cColorBits * 8 + pfdDesc.cDepthBits * 4 + pfdDesc.cStencilBits + pfdDesc.cAccumBits;
if(nWeight > nMaxWeight)
{
nMaxWeight = nWeight;
nBestFormat = nPixelFormat;
}
}
nPixelFormat++;
}
if(nBestFormat && ::SetPixelFormat(m_hDC, nBestFormat, &pfdDesc))
return true;
return false;
}
bool Init(HDC hWindowDC, int nWidth, int nHeight)
{
// Create the bitmap for this OpenGL context
memset(&m_bmi, 0, sizeof(BITMAPINFO));
m_bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
m_bmi.bmiHeader.biWidth = nWidth;
m_bmi.bmiHeader.biHeight = nHeight;
m_bmi.bmiHeader.biPlanes = 1;
m_bmi.bmiHeader.biBitCount = 24;
m_bmi.bmiHeader.biCompression = BI_RGB;
m_bmi.bmiHeader.biSizeImage = nWidth * nHeight * 3;
m_hBitmap = ::CreateDIBSection(hWindowDC, &m_bmi, DIB_RGB_COLORS, &m_pBits, NULL, (DWORD)0);
// Create the memory DC for the OpenGL context
m_hDC = ::CreateCompatibleDC(NULL);
::SelectObject(m_hDC, ::GetCurrentObject(hWindowDC, OBJ_FONT));
::SelectObject(m_hDC, m_hBitmap);
// Select a software-rendering pixel format that supports drawing to a bitmap
// (Must be 24-bit color because the bitmap is 24-bit color)
if(!ChoosePixelFormat())
return false;
// Initialize the OpenGL context
m_hGLRC = ::wglCreateContext(m_hDC);
m_hOldDC = ::wglGetCurrentDC();
m_hOldGLRC = ::wglGetCurrentContext();
::wglMakeCurrent(m_hDC, m_hGLRC);
// Initialize the CGLFont object and set up the viewport
m_font.Init(m_hDC, 16);
glViewport(0, 0, nWidth, nHeight);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, nWidth, 0, nHeight);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
return true;
}
void Cleanup()
{
m_font.Cleanup();
::wglMakeCurrent(NULL, NULL);
::wglDeleteContext(m_hGLRC);
::wglMakeCurrent(m_hOldDC, m_hOldGLRC);
::DeleteObject(m_hBitmap);
::DeleteDC(m_hDC);
}
};
ah, i thought that was the case
Are you certain that hardware acceleration is not supported? Unless it's a limitation enforced by Microsoft's OpenGL implementation, the decision should be up to the driver. It's not difficult to imagine a back buffer being created, and a glReadPixels call being made after a hardware-accelerated render (it's how impostors used to be done).
The only reason I bring this up is that when I've used the class I posted earlier, hardware-accelerated contexts were listed. When I selected those contexts, they produced visual artifacts that didn't show up when I selected a software-only context. (It's an old chipset with crappy drivers.)
Sean
The only reason I bring this up is that when I've used the class I posted earlier, hardware-accelerated contexts were listed. When I selected those contexts, they produced visual artifacts that didn't show up when I selected a software-only context. (It's an old chipset with crappy drivers.)
Sean
Two things :
- Even if you don't care about the eye candy, I think you'd better draw to a hidden window (simply do not call ShowWindow after you create it) and blit back the color buffer to a DIB with glReadPixels : believe me, it will be waysss faster than the DRAW_TO_BITMAP thingy.
- One last thing : when dealing with huge bitmaps (DIB), it may help to use a file mapping instead of normal memory.
- Even if you don't care about the eye candy, I think you'd better draw to a hidden window (simply do not call ShowWindow after you create it) and blit back the color buffer to a DIB with glReadPixels : believe me, it will be waysss faster than the DRAW_TO_BITMAP thingy.
- One last thing : when dealing with huge bitmaps (DIB), it may help to use a file mapping instead of normal memory.
|
Topic Locked
This topic has been locked by a moderator. New replies are not allowed.
Sign in to reply to this topic.
Advertisement