2

I have tried using both soil and devil image libraries but the screenshots created are completely black images. For devil I used the function found here Take screenshot with openGL and save it as png but the image is still black screen.

Any ideas about saving screenshot or exporting opengl output?

Community
  • 1
  • 1
kdarbs
  • 65
  • 4

3 Answers3

4

Try this:

#include <vector>
#include <GL/glut.h>
#include <SOIL/SOIL.h>

using namespace std;

bool save = false;
void display()
{
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    glOrtho( -2, 2, -2, 2, -1, 1);

    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity();

    glColor3ub( 255, 0, 0 );
    glBegin( GL_QUADS );
    glVertex2i( -1, -1 );
    glVertex2i(  1, -1 );
    glVertex2i(  1,  1 );
    glVertex2i( -1,  1 );
    glEnd();

    if( save )
    {
        int w = glutGet( GLUT_WINDOW_WIDTH );
        int h = glutGet( GLUT_WINDOW_HEIGHT );
        vector< unsigned char > buf( w * h * 3 );

        glPixelStorei( GL_PACK_ALIGNMENT, 1 );
        glReadPixels( 0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, &buf[0] );

        int err = SOIL_save_image
            (
            "img.bmp",
            SOIL_SAVE_TYPE_BMP,
            w, h, 3,
            &buf[0]
            );

        save = false;
    }

    glutSwapBuffers();
}

void keyboard( unsigned char key, int x, int y )
{
    if( key == 's' ) 
    {
        save = true;
        glutPostRedisplay();
    }
}

int main( int argc, char **argv )
{
    glutInit( &argc, argv );
    glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE );
    glutInitWindowSize( 640, 480 );
    glutCreateWindow( "GLUT" );
    glutDisplayFunc( display );
    glutKeyboardFunc( keyboard );
    glutMainLoop();
    return 0;
}
genpfault
  • 51,148
  • 11
  • 85
  • 139
  • 1
    I tried this function, even though it works fine, but the image I get is upside down. Any idea why is that? – pslayer89 Jul 10 '15 at 15:43
  • @pslayer89: Whoops, I should have tested a scene that didn't have vertical symmetry :) That's how the bits come out of OpenGL. Try `SOIL_SAVE_TYPE_BMP | SOIL_FLAG_INVERT_Y`. – genpfault Jul 10 '15 at 16:02
  • @genpfault Doing that, it just doesn't saves any image :( – pslayer89 Jul 10 '15 at 20:04
  • 1
    @pslayer89: You're right, sorry about that, I should have read the SOIL header harder :( It looks like `SOIL_save_screenshot()` will do flipping internally if you're willing to lose some flexibility. – genpfault Jul 10 '15 at 20:46
  • @genpfault I was actually using SOIL_save_screenshot earlier, but apparently my application crashes when I try to capture a screenshot using a resolution higher than 1024 x 768. That's why I was searching for a good alternative for that and I stumbled on to this post. – pslayer89 Jul 10 '15 at 21:20
1

The biggest problem when saving screenshots from OpenGL renderings is, that the framebuffer of a regular system Window (which you normally use as drawing canvas for OpenGL) is a extremely unreliable source for data. If it's (partially) occluded by other windows some parts may contain no defined data at all. After a buffer swap the contents of the back buffer are undefined, etc. etc.

A few basic rules:

If capturing screenshots from the regular window, capture them from either

  • the back buffer right after the drawing finished but before the buffer swap (glReadPixels implieas a flush and finish of all drawing operations in the pipeline, as it introduces a synchronization point)

  • the front buffer after the buffer swap

To get reliable captures of OpenGL renderings, you should render to a framebuffer object. There the image is well defined and will not "vanish" in some hard to trace and debug race conditions between window invalidations and animation loops or other weird situations. To display the picture you then blit from the FBO to the main framebuffer.

datenwolf
  • 159,371
  • 13
  • 185
  • 298
1

While genpfault's answer does the job nicely, it led me to a much cleaner solution:

if (!SOIL_save_screenshot("screenshot.bmp",
    SOIL_SAVE_TYPE_BMP, 0, 0, width, height)) {
    // Handle error here
}

This way you avoid having to set up a buffer, call glReadPixels, etc. SOIL can handle all of that for you automatically!

Also be aware that as per genpfault's response to pslayer89's comment, you need to add SOIL_FLAG_INVERT_Y to the flags passed in the second parameter (unless you're already inverting the Y-axis with your projection/model matrix).

Kenny83
  • 769
  • 12
  • 38