Search code examples
c#openglglreadpixels

OpenGL: glReadPixels "fails" although glGetError returns 0


Problem summary:

I use OpenGL glReadPixels to get a screenshot but the buffer is unchanged. If I use glGetError to get the error from the last function, it returns 0, as if everything is fine.

I've researched online throughly and haven't found anyone facing a problem as this one.

In details:

I'm using the CsGL.dll, which basically just wraps OpenGL to C#, and I have made the required initializations for using GL: DC from hWnd, ChoosePixelFormat for DC using PixelFormatDescriptor and setting the it as it's format, creating RC for the DC and calling wglMakeCurrent(RC, DC).


Note that I am using simple wrappers for the Windows API functions. Here is the code for this initialization I have used:

public unsafe void Init(IntPtr hWnd)
{
        this.DC = (IntPtr)User.GetDC(this.HWnd = hWnd);

        var pfd = new PIXELFORMATDESCRIPTOR();
        var sizeOf = Marshal.SizeOf(pfd);

        Kernel.ZeroMemory(new IntPtr(&pfd), sizeOf);
        pfd.nSize = (short)sizeOf;
        pfd.nVersion = 1;
        pfd.dwFlags = (int)(PixelFormatDescriptorFlagsEnum.PFD_DRAW_TO_WINDOW |
                            PixelFormatDescriptorFlagsEnum.PFD_SUPPORT_OPENGL |
                            PixelFormatDescriptorFlagsEnum.PFD_DOUBLEBUFFER);
        pfd.iPixelType = PIXELFORMATDESCRIPTOR.PFD_TYPE_RGBA;
        pfd.cColorBits = 24;
        pfd.cDepthBits = 16;
        pfd.iLayerType = PIXELFORMATDESCRIPTOR.PFD_MAIN_PLANE;

        var iFormat = GDI.ChoosePixelFormat(this.DC, ref pfd);
        GDI.SetPixelFormat(this.DC, iFormat, ref pfd);

        this.RC = wglCreateContext(this.DC);

        wglMakeCurrent(this.DC, this.RC);
}

I've sent User.GetForegroundWindow() as the hWnd.

After this initialization I try getting a screenshot into in image (and I have also tried to read it into a simple byte array)

Short psuedo code of glReadPixels usage:

var area = new Rectangle(0, 0, 100, 100);
var bmp = new Bitmap(area.Width, area.Height);
var data = bmp.LockBits(area, ILM.WriteOnly, PF.24bppRgb);

glReadBuffer(BACK);
glReadPixels(0, 0, area.Width, area.Height, BGR_EXT /*also tried RGB and RGBA*/, UNSIGNED_BYTE, data.Scan0);

bmp.UnlockBits(data);
bmp.Save(@"C:\Back.bmp");

data = bmp.LockBits(area, ILM.WriteOnly, PF.24bppRgb);

glReadBuffer(FRONT);
glReadPixels(0, 0, area.Width, area.Height, BGR_EXT /*also tried RGB and RGBA*/, UNSIGNED_BYTE, data.Scan0);

bmp.UnlockBits(data);
bmp.Save(@"C:\Front.bmp");

Trying it with a simple and small byte[] was done like this:

var bytes = new byte[10 * 10 * 3];
glReadPixels(0, 0, 10, 10, RGB, UNSIGNED_BYTE, bytes);

And the byte[] was all zero's. I've also tried it with bigger size for the array (keeping the 0, 0, 10, 10), but still to no avail.


In both cases, the result is the same. The buffer doesn't change at all, while a glGetError call returns 0 after each GL function call.

Both Back.bmp and Front.bmp are totally black.

Please tell me what am I doing wrong? Thanks


Solution

  • Are you trying to create a screenshot of something you rendered, or a screenshot of the screen in general?

    glReadPixels is assured to work only for contents of the OpenGL framebuffer. It's not suited for taking screenshots of anything else (there used to be a time, where this was possible, but ever since the advent of compositing window managers those times are gone).

    So if you're trying to do general screen capture, glReadPixels is the wrong way to go.

    If however you're trying to take a screenshot of what you rendered, then you must make sure that the context you rendered with is also active when calling glReadPixels.