iea has asked for the wisdom of the Perl Monks concerning the following question:

I was wondering if its possible to capture a window using opengl. I am trying to capture a screenshot of an game using opengl. If its runing in software mode its no problem but when i change to opengl, i keep getting an black bmp. I tried to use opengl
use SDL::App; use SDL::OpenGL; my $file = $x.'screenshot.bmp'; glReadBuffer(GL_FRONT); my $data = glReadPixels(0, 0, '1024', '768', GL_BGR, GL_UNSIGNED_BYTE); SDL::OpenGL::SaveBMP($file, '1024', '768', 24, $data);
but it stays black. =(

Replies are listed 'Best First'.
Re: Capture opengl screen
by Corion (Patriarch) on Mar 23, 2008 at 23:04 UTC

    I use the following code (using FrameBufferObjects) to save screenshots. I render into texture $texture_id and then read that texture back into main memory:

    sub tick { my ($self,$texture_id) = @_; # save current frame from input fbo glBindTexture(GL_TEXTURE_2D, $texture_id); my $frame; for my $i (reverse (0..$self->height-1)) { glReadPixels_s(0,$i,$self->width,1,GL_RGB,GL_UNSIGNED_BYTE,my +$line); $frame .= $line; }; syswrite $self->stream, $frame, $self->width * $self->height * $se +lf->depth or die "Write failure"; };

    ... and here's the part that renders into an fbo:

    sub allocate_fbo_texture{ my ($class,$width,$height) = @_; (my $texture_id) = glGenTextures_p(1); (my $fbo) = glGenFramebuffersEXT_p(1); (my $render_buffer) = glGenRenderbuffersEXT_p(1); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, $fbo); glBindTexture(GL_TEXTURE_2D, $texture_id); glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, $render_buffer); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexImage2D_c(GL_TEXTURE_2D,0,GL_RGBA8,$width,$height,0,GL_RGBA,G +L_UNSIGNED_BYTE,0); glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0 +_EXT, GL_TEXTURE_2D, $texture_id, 0); glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24 +, $width,$height); glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHME +NT_EXT, GL_RENDERBUFFER_EXT, $render_buffer); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); return ($fbo,$texture_id, $render_buffer); }; sub enable { my $self = shift; glPushAttrib(GL_VIEWPORT_BIT); glViewport(0,0,$self->width, $self->height); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, $self->fbo); my $res = $self->shader->Enable(); if ($self->config->{enable}) { $self->config->{enable}->($self); }; };

    This is quite roughly ripped out of a larger framework, but it should give you at least a hint of how you could do it. I'm not sure why your approach of reading the memory doesn't work though.

Re: Capture opengl screen
by zentara (Cardinal) on Mar 24, 2008 at 16:02 UTC
    You can probably use Image Magick's import to capture the screen. Here is a test script you can play with.... it captures mozilla, just change the names to suit your needs.
    #!/usr/bin/perl use warnings; use strict; use Image::Magick; use X11::WMCtrl; use Data::Dumper; my $wmctrl = X11::WMCtrl->new; printf("window manager is %s\n", $wmctrl->get_window_manager->{name}); my @windows = $wmctrl->get_windows; my $wid; # x window id my $host; # virtual desktop foreach my $app(@windows){ my $title = $app->{title}; if( $title =~ /Mozilla/i){ print "$title\n"; print Dumper(\$app),"\n\n"; $wid = $app->{id}; $host = $app->{host}; } } $wmctrl->switch($host); #must be on same virtual desktop as the browser #works my $blob = `import -window $wid jpg:`; #my $blob = `import -`; #postscript produced #print "$blob\n"; # now $blob is in memory and you can do what you # want with it. my $output = Image::Magick->new(magick=>'jpg'); $output->BlobToImage( $blob ); $output->Resize(geometry=>'160x120'); $output->Write( $0.'.jpg'); exit; # png works too

    I'm not really a human, but I play one on earth. Cogito ergo sum a bum
      well i tried both solutions thank you very much ... but i keep on getting a black screen. I tried image magick bevor. I am using activestate perl under windows and i would be really happy if anybody has any solution to create screenshots of full screen directx games / applications. I tried many ways but i keep on getting black screens thanks

        Err - DirectX is quite different from OpenGL.

Re: Capture opengl screen
by Anonymous Monk on Jul 04, 2008 at 08:56 UTC
    - if you use framebufferObjects, you shall add glBindFramebuffer before glReadBuffer and glReadPixels but I don't thing the problem is there
    - have you checked glReadPixels doesn't sent an array of 0 ?
    - I use the lib Devil (new OpenIl), with this code :
    ilInit(); iluInit(); ... ILuint ratz; ilGenImages(1, &ratz); ilBindImage(ratz); vector<GLubyte> store; store.resize(Wide * Height * 4); glReadPixels(0, 0, Wide, Height, GL_RGBA, GL_UNSIGNED_BYTE, &store[0]) +; ilTexImage(Wide, Height, 1, 4, IL_RGBA, IL_UNSIGNED_BYTE, &store[0]); ilEnable(IL_FILE_OVERWRITE); ilSaveImage(sortie.c_str());

    where sortie is a string containing the file to write

    Hope it should help