OpenGL Screenies, How To?
Ok, this next bit is going to sound strange, so bare with me please.
I am trying to capture every frame of a OpenGL program in jpg/gif/tga format (pref jpg/gif). I am going to be using these to shrink down, and import into a flash website, FRAME BY FRAME... to horribly and painstakingly create the opengl animation in flash.
What i do not know how to do, is any commands to capture a frame and print it in a specific format. My texture image data is all TGA, so i can live with that, however i REALLY need to make OpenGL save each frame.
So, can anyone tell me how to do this? Links are much appreciated, explanations with those are awesome.
I am a total nub so please spell it out as if i were 5, and if you know of any better ways please enlighten me. I think flash can take movie formats, but i am just concerned with getting it done in a manner i know how to, i dont know how well flash likes movie formats and how the quality will be.
Thanks to any replies!
I'm using SDL for setup stuff, and they have a convenient BMP save function, so using that:
You'd need to include some other libs if you wanted JPEG.
hope it helps.
void Write_BMP (char *FName){ char *Pixels; SDL_Surface *Temp; Uint32 rmask, gmask, bmask, amask; int w, h; int i, j, Top, Bottom; char Swap; w = SDL_GetVideoSurface ()->w; h = SDL_GetVideoSurface ()->h; Pixels = new char [4*w*h]; if (SDL_BYTEORDER == SDL_BIG_ENDIAN) { rmask = 0xff000000; gmask = 0x00ff0000; bmask = 0x0000ff00; amask = 0x000000ff; } else { rmask = 0x000000ff; gmask = 0x0000ff00; bmask = 0x00ff0000; amask = 0xff000000; } // Get the data from OpenGL glReadPixels (0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, Pixels); // Flip it vertically for (j = (h >> 1) - 1; j >= 0; --j) { Top = j * w * 4; Bottom = (h - j - 1) * w * 4; for (i = (w << 2) - 1; i>= 0; --i) { Swap = Pixels [Top + i]; Pixels [Top + i] = Pixels [Bottom + i]; Pixels [Bottom + i] = Swap; } } // Copy it into a SDL surface Temp = SDL_CreateRGBSurfaceFrom (Pixels, w, h, 32, 4 * w, rmask, gmask, bmask, amask); // And save it SDL_SaveBMP (Temp, FName); // And free the memory delete [] Pixels; SDL_FreeSurface (Temp);}
You'd need to include some other libs if you wanted JPEG.
hope it helps.
If you really need the screenshot in Targa format, here's some code to do it:
If you want to convert the files in a batch conversion I recommend using Irfanview.
Hope that helped.
#include <windows.h>#include <gl/gl.h>#include <stdio.h>struct CTGAHeader_ForSaving{ char IDCount; char ColorMap; char ImageCode; unsigned short CMOrigin; char CMLength; char CMBitDepth; unsigned short IMXOrigin; unsigned short IMYOrigin; unsigned short IMWidth; unsigned short IMHeight; char IMBitDepth; char IMDescByte;};/* Takes a screenshot of the current opengl buffer. Be sure to include the extension in the filename, i.e. 'shot.tga' NOT 'shot'.*/void TakeScreenshot(const char* filename){ /* grab OpenGL buffer */ int VPort[4],FSize,PackStore; unsigned char *PStore; // get viewport dims (x,y,w,h) glGetIntegerv(GL_VIEWPORT,VPort); // allocate space for framebuffer in rgb format FSize = VPort[2]*VPort[3]*3; PStore = new unsigned char[FSize]; // store unpack settings glGetIntegerv(GL_PACK_ALIGNMENT, &PackStore); // setup unpack settings glPixelStorei(GL_PACK_ALIGNMENT, 1); // this actually gets the buffer pixels glReadPixels(VPort[0],VPort[1],VPort[2],VPort[3],GL_RGB,GL_UNSIGNED_BYTE,PStore); // restore unpack settings glPixelStorei(GL_PACK_ALIGNMENT, PackStore); /* dump data to disk */ CTGAHeader_ForSaving Header; int loop; FILE *pFile; unsigned char SwapByte; // setup header Header.IDCount = 0; Header.ColorMap = 0; Header.ImageCode = 2; // uncompressed rgb image Header.CMOrigin = 0; Header.CMLength = 0; Header.CMBitDepth = 0; Header.IMXOrigin = 0; Header.IMYOrigin = 0; Header.IMWidth = VPort[2]; Header.IMHeight = VPort[3]; Header.IMBitDepth = 24; // rgb with no alpha Header.IMDescByte = 0; // swap red and blue for (loop=0;loop<FSize;loop+=3) { SwapByte = PStore[loop]; PStore[loop] = PStore[loop+2]; PStore[loop+2] = SwapByte; } // open file pFile = fopen( filename,"wb" ); if (pFile==NULL) { delete[] PStore; return; } // write to disk fwrite( &Header,sizeof(CTGAHeader_ForSaving),1,pFile ); // header fwrite( PStore,FSize,1,pFile ); // image data // close file fclose( pFile ); // free memory delete[] PStore;}
If you want to convert the files in a batch conversion I recommend using Irfanview.
Hope that helped.
The glReadPixels function reads the pixels on the screen. This data can be exported by the above means, but a problem you will run into is speed. Writing to the hard drive once per frame takes CPU cycles. Play a game and save a screenshot to see the delay of the FPS. You could use glReadPixels and save the info into buffers, then save them file by file, but that depends on how much memory you have to do so. If you are limited by memory, you will have to save the buffers every so often, but writing to the hard drive isn't so bad if you do it all at once. The writing isn't so bad quantity wise. Writing 20 files and writing 1 file take close to the same amount of time, but only if you do the 20 files all at once. Doing it frame by frame will make the program/game unplayable. There are programs that read the screen buffer into videos, but they do basicaly the same thing with buffers.
hey, this is a dumb c++ questions, but i am not that good with data formats yet in c++ either.
How can i combine the string structure of a name "screenie" with an integer variable, so i can do it like "screenie0", screenie1, screenie2, ect..
And speed isnt a problem. It couldbe as slow as 1 frame per 10seconds, all i need to do is capture a full rotation of my movie(it loops obviously) and import it to flash.
I am more worried about what the massive frames will do in flash lol, hopefully the shrunked image size will help me out.
How can i combine the string structure of a name "screenie" with an integer variable, so i can do it like "screenie0", screenie1, screenie2, ect..
And speed isnt a problem. It couldbe as slow as 1 frame per 10seconds, all i need to do is capture a full rotation of my movie(it loops obviously) and import it to flash.
I am more worried about what the massive frames will do in flash lol, hopefully the shrunked image size will help me out.
To avoid having large jumps between frames use a set interval instead of steady motion from a timer, only moving onto the next frame when the previous one is rendered and saved to disk, or use buffering like kburkhart84 suggests.
The code I gave you is from a class which does exactly what you want (auto-naming) but I ripped it out as I didn't think it would be useful, I'll give you the source presently, just remember that the way I combine the filename and increment variable is a *very* bad way to do it as you could have a filename longer than the buffer which is bad news, it'll work fine for shorter (< 256 including number and extension) names. Really I should convert this to STL but I don't have a lot of spare time so I'll give you it as it stands now:
Header:
Source:
They are both direct ports from my Delphi classes which I did when I started with C++ so they're probably done in a very bad way, I'd be interested if anyone with more experience would care to comment on them as I need to learn as much C++ as possible and I want to do it in the correct way.
Anyway, hope that helps you Zeusbwr.
The code I gave you is from a class which does exactly what you want (auto-naming) but I ripped it out as I didn't think it would be useful, I'll give you the source presently, just remember that the way I combine the filename and increment variable is a *very* bad way to do it as you could have a filename longer than the buffer which is bad news, it'll work fine for shorter (< 256 including number and extension) names. Really I should convert this to STL but I don't have a lot of spare time so I'll give you it as it stands now:
Header:
/* Name: gl_screenshot Author: eSCHEn Contact: eschen@gibbering.net http://legion/gibbering.net/evillution/ Date: 16 Jul 04 Copyright: Use it, have fun. Author retains copyright. If you use this in a project then send me a mail - I'd love to see what you're doing with it :) Released under MPL 1.1, see 'mpl.txt' for details. Description: Exports a class 'CScreenshot', which takes a screenshot of an OpenGL context and saves it to a specified targa file. Usage of Class: =============== Methods: CounterPosition -> Current position of internal counter. SetCounter -> Sets the initial position of internal counter. SetFileName -> Sets the base filename for saving. TakeScreenshot -> Takes the screenshot using internal filename. TakeScreenshot -> Takes the screenshot, <Filename> overrides internal filename. Properties: AutoIncrement -> Set to true if auto-incrementing of counter is needed, will add auto-numbering to the end of the internal filename.*/struct CTGAHeader_ForSaving{ char IDCount; char ColorMap; char ImageCode; unsigned short CMOrigin; char CMLength; char CMBitDepth; unsigned short IMXOrigin; unsigned short IMYOrigin; unsigned short IMWidth; unsigned short IMHeight; char IMBitDepth; char IMDescByte;};class CScreenshot{ public: // constructor CScreenshot(); // destructor virtual ~CScreenshot(); // methods int CounterPosition(); void SetCounter(int CountValue); void SetFileName(char* FileName); void TakeScreenshot(); void TakeScreenshot(char* FileName); char* LastFilename() {return fLastFileName;}; // variables bool AutoIncrement; // set to false to stop auto-numbering private: // variables char* fDefaultFileName; int fFileCount; char* fLastFileName;};
Source:
#include <windows.h>#include <gl/gl.h>#include <stdio.h>#include "gl_screenshot.h"/* constructor */CScreenshot::CScreenshot(){ // some sensible default parameters fDefaultFileName = "shot"; AutoIncrement = true; fFileCount = 0;}/* destructor */CScreenshot::~CScreenshot(){ // nowt to do}int CScreenshot::CounterPosition(){ // get current counter position return fFileCount;} void CScreenshot::SetCounter(int CountValue){ // copy start position of counter fFileCount = CountValue;} void CScreenshot::SetFileName(char* FileName){ // copy filename fDefaultFileName = FileName;} void CScreenshot::TakeScreenshot(){ /* grab OpenGL buffer */ int VPort[4],FSize,PackStore; unsigned char *PStore; // get viewport dims (x,y,w,h) glGetIntegerv(GL_VIEWPORT,VPort); // allocate space for framebuffer in rgb format FSize = VPort[2]*VPort[3]*3; PStore = new unsigned char[FSize]; // store unpack settings glGetIntegerv(GL_PACK_ALIGNMENT, &PackStore); // setup unpack settings glPixelStorei(GL_PACK_ALIGNMENT, 1); // this actually gets the buffer pixels glReadPixels(VPort[0],VPort[1],VPort[2],VPort[3],GL_RGB,GL_UNSIGNED_BYTE,PStore); // restore unpack settings glPixelStorei(GL_PACK_ALIGNMENT, PackStore); /* dump data to disk */ CTGAHeader_ForSaving Header; int loop; FILE *pFile; unsigned char SwapByte; char NewFileName[256]; // setup header Header.IDCount = 0; Header.ColorMap = 0; Header.ImageCode = 2; // uncompressed rgb image Header.CMOrigin = 0; Header.CMLength = 0; Header.CMBitDepth = 0; Header.IMXOrigin = 0; Header.IMYOrigin = 0; Header.IMWidth = VPort[2]; Header.IMHeight = VPort[3]; Header.IMBitDepth = 24; // rgb with no alpha Header.IMDescByte = 0; // swap red and blue for (loop=0;loop<FSize;loop+=3) { SwapByte = PStore[loop]; PStore[loop] = PStore[loop+2]; PStore[loop+2] = SwapByte; } // setup file name if (AutoIncrement) sprintf( NewFileName,"%s%d.tga",fDefaultFileName,fFileCount ); else sprintf( NewFileName,"%s.tga",fDefaultFileName ); // copy filename fLastFileName = NewFileName; // open file pFile = fopen( NewFileName,"wb" ); if (pFile==NULL) { delete[] PStore; return; } // write to disk fwrite( &Header,sizeof(CTGAHeader_ForSaving),1,pFile ); // header fwrite( PStore,FSize,1,pFile ); // image data // close file fclose( pFile ); // free memory delete[] PStore; // move to next file counter position if (AutoIncrement) fFileCount++;} void CScreenshot::TakeScreenshot(char* FileName){ // copy filename fDefaultFileName = FileName; // call overloaded screenshot method this->TakeScreenshot(); }
They are both direct ports from my Delphi classes which I did when I started with C++ so they're probably done in a very bad way, I'd be interested if anyone with more experience would care to comment on them as I need to learn as much C++ as possible and I want to do it in the correct way.
Anyway, hope that helps you Zeusbwr.
Quote:Original post by shadowwz
there is another way,trought WinApi - BitBlt.
Below is the Delphi method I use to Grap a screenshot in OpenGL
Notice that you can also grab only part of the screen if needed.
I have No idea how to implement this in c++ :| but this will lead you in the right direction
Oh, this saves to 24 Bit Bitmap, but since you are goin to shrink the stuff in any case, you can just save to whatever format you want.
procedure TRGLBaseScene.ScreenPartToBitmap(const aFileName : String; aX, aY, aWidth, aHeight : integer);var lBitmap : TBitmap;begin lBitmap := TBitmap.Create; try lBitmap.Width := aWidth; lBitmap.Height := aHeight; lBitmap.PixelFormat := pf24bit; BitBlt(lBitmap.Canvas.Handle, 0, 0, aWidth, aHeight, DeviceContext, aX, aY, SRCCOPY); lBitmap.SaveToFile(aFileName); finally FreeAndNil(lBitmap); end;end
Quote:Original post by Zeusbwr
How can i combine the string structure of a name "screenie" with an integer variable, so i can do it like "screenie0", screenie1, screenie2, etc.
// variablesint Frame_Num;char Frame_Name [1024];char Screen_Save_Name [1024];// do this oncestrcpy (Screen_Save_Name, "C:\\ScreenCap Directory\\screenie-");// do this every frame, then use Frame_Name as the file namesprintf (Frame_Name, "%s%.5d.bmp", Screen_Save_Name, Frame_Num++);
or you can save your file name into an array as such..
char file_name[30];
include <string.h> header file.
strcpy(file_name, "screenie");
then in your loop going from 0 - whatever use this as your counter per frame then increment loop each frame. i have actually done this using .bmp's but yes, frame by frame it was really slow. anyway convert the 0 to ascii, using the itoa function, then do a strcat() to append the result onto your string. someones sprintf does the same thing, depending on what the counter is. this is just another way to do it or how i did it for mine.
char file_name[30];
include <string.h> header file.
strcpy(file_name, "screenie");
then in your loop going from 0 - whatever use this as your counter per frame then increment loop each frame. i have actually done this using .bmp's but yes, frame by frame it was really slow. anyway convert the 0 to ascii, using the itoa function, then do a strcat() to append the result onto your string. someones sprintf does the same thing, depending on what the counter is. this is just another way to do it or how i did it for mine.
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement