3D Screenshot

Started by
9 comments, last by circlesoft 18 years, 10 months ago
Is there any program that allow me to save a 3d screenshot of some frame in DirectX? I like to see how much the oclusion system is actually culling so I want to find a program that allow me to press some key and get a .x, .obj, .3ds, or whatever with the actual frame object. Is out there any thing similar?
God is Real unless is declared Integer
Advertisement
That'd be a difficult thing to do I would imagine, I'm not aware of anything like that out there. :
A good solution might be to add the ability 'lock off' all your occlusion calculations (So the scene basically freezes with the current values and doesn't do any more occlusion updates until it's unlocked) and go into a free camera. You can then use that to fly around and assess how well it's working.

This would probably be easier than having to back out of the program entirely and open the results in another program anyway.
Try looking at PIX, which comes with newer SDKs (and has been updated quite a bit in the last few releases). I haven't played with the PC version of PIX for more than 5 minutes, so I'm not sure what it's capable of right now. The XBox version would capture each DX call and you can trace through a frame of your app, draw by draw. It may be useful, but there are potentially better ways to see the data.

Another approach would be to pick some simple text based model format, and just write out your transformed vertices, then load it into your favorite 3D package. It shouldn't take long to implement, but still captures just a single frame per run. Nice, but far from perfect.

Another approach is to make your occulsion system and camera seperable. You can then press a key to lock your occlusion system's idea of the camera into place. Now move your real camera, and you'll see only what was accepted for draw from where you were. You could even have a "debug occlusion mode" where you actually do draw everything, but color code it. Color frustum rejected items one color, visibility culled objects a second color, and items that should be drawn either normally, or a third color. At any point in your app just press a key to toggle this system on and you'll see how well you're doing. Throw in a wireframe of the occlusion camera frustum. Now it's actually a pretty useful tool.

As a bonus to making it seperable, you can now do visiblity tests of AIs.
You can program it into your engine. For instance, some variable that keeps the frustrum/matrix/whatever from which your world is rendered/occluded can be stored with a command and the engine keeps occluding from that position, but still renders from the position of the camera. Then you can simply walk around and see what is being occluded and what isnt. What you will see wont be pretty, but it should be good for what you want.

You might also consider implementing wireframe drawing for this purpose.
Here this is for openGL but u can easily convert this into DX. U have to call SaveScreenshot() on some key like 'S' to save current frame.


// WriteBitmapFile()
// desc: takes image data and saves it into a 24-bit RGB .BMP file
// with width X height dimensions
int WriteBitmapFile(char *filename, int width, int height, unsigned char *imageData)
{
FILE *filePtr; // file pointer
BITMAPFILEHEADER bitmapFileHeader; // bitmap file header
BITMAPINFOHEADER bitmapInfoHeader; // bitmap info header
int imageIdx; // used for swapping RGB->BGR
unsigned char tempRGB; // used for swapping

// open file for writing binary mode
filePtr = fopen(filename, "wb");
if (!filePtr)
return 0;

// define the bitmap file header
bitmapFileHeader.bfSize = sizeof(BITMAPFILEHEADER);
bitmapFileHeader.bfType = 0x4D42;
bitmapFileHeader.bfReserved1 = 0;
bitmapFileHeader.bfReserved2 = 0;
bitmapFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

// define the bitmap information header
bitmapInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmapInfoHeader.biPlanes = 1;
bitmapInfoHeader.biBitCount = 24; // 24-bit
bitmapInfoHeader.biCompression = BI_RGB; // no compression
bitmapInfoHeader.biSizeImage = width * abs(height) * 3; // width * height * (RGB bytes)
bitmapInfoHeader.biXPelsPerMeter = 0;
bitmapInfoHeader.biYPelsPerMeter = 0;
bitmapInfoHeader.biClrUsed = 0;
bitmapInfoHeader.biClrImportant = 0;
bitmapInfoHeader.biWidth = width; // bitmap width
bitmapInfoHeader.biHeight = height; // bitmap height

// switch the image data from RGB to BGR
for (imageIdx = 0; imageIdx < bitmapInfoHeader.biSizeImage; imageIdx+=3)
{
tempRGB = imageData[imageIdx];
imageData[imageIdx] = imageData[imageIdx + 2];
imageData[imageIdx + 2] = tempRGB;
}

// write the bitmap file header
fwrite(&bitmapFileHeader, 1, sizeof(BITMAPFILEHEADER), filePtr);

// write the bitmap info header
fwrite(&bitmapInfoHeader, 1, sizeof(BITMAPINFOHEADER), filePtr);

// write the image data
fwrite(imageData, 1, bitmapInfoHeader.biSizeImage, filePtr);

// close our file
fclose(filePtr);

return 1;
}

int frames = 0;

// SaveScreenshot()
// desc: takes a snapshot of the current window contents by retrieving
// the screen data with glReadPixels() and writing the data to a file
void SaveScreenshot()
{
frames++;
if (frames <= 50)
{
char *filename;
if (frames == 1) filename = "frame1.bmp";
else if (frames == 2) filename = "frame2.bmp";
else if (frames == 3) filename = "frame3.bmp";
else if (frames == 4) filename = "frame4.bmp";
else if (frames == 5) filename = "frame5.bmp";
else if (frames == 6) filename = "frame6.bmp";
else if (frames == 7) filename = "frame7.bmp";
else if (frames == 8) filename = "frame8.bmp";
else if (frames == 9) filename = "frame9.bmp";
else if (frames == 10) filename = "frame10.bmp";
else if (frames == 11) filename = "frame11.bmp";
else if (frames == 12) filename = "frame12.bmp";
else if (frames == 13) filename = "frame13.bmp";
else if (frames == 14) filename = "frame14.bmp";
else if (frames == 15) filename = "frame15.bmp";
else if (frames == 16) filename = "frame16.bmp";
else if (frames == 17) filename = "frame17.bmp";
else if (frames == 18) filename = "frame18.bmp";
else if (frames == 19) filename = "frame19.bmp";
else if (frames == 20) filename = "frame20.bmp";
else if (frames == 21) filename = "frame21.bmp";
else if (frames == 22) filename = "frame22.bmp";
else if (frames == 23) filename = "frame23.bmp";
else if (frames == 24) filename = "frame24.bmp";
else if (frames == 25) filename = "frame25.bmp";
else if (frames == 26) filename = "frame26.bmp";
else if (frames == 27) filename = "frame27.bmp";
else if (frames == 28) filename = "frame28.bmp";
else if (frames == 29) filename = "frame29.bmp";
else if (frames == 30) filename = "frame30.bmp";
else if (frames == 31) filename = "frame31.bmp";
else if (frames == 32) filename = "frame32.bmp";
else if (frames == 33) filename = "frame33.bmp";
else if (frames == 34) filename = "frame34.bmp";
else if (frames == 35) filename = "frame35.bmp";
else if (frames == 36) filename = "frame36.bmp";
else if (frames == 37) filename = "frame37.bmp";
else if (frames == 38) filename = "frame38.bmp";
else if (frames == 39) filename = "frame39.bmp";
else if (frames == 40) filename = "frame40.bmp";
else if (frames == 41) filename = "frame41.bmp";
else if (frames == 42) filename = "frame42.bmp";
else if (frames == 43) filename = "frame43.bmp";
else if (frames == 44) filename = "frame44.bmp";
else if (frames == 45) filename = "frame45.bmp";
else if (frames == 46) filename = "frame46.bmp";
else if (frames == 47) filename = "frame47.bmp";
else if (frames == 48) filename = "frame48.bmp";
else if (frames == 49) filename = "frame49.bmp";
else if (frames == 50) filename = "frame50.bmp";

imageData = malloc(SCREENWIDTH*SCREENHEIGHT*3); // allocate memory for the imageData
memset(imageData, 0, SCREENWIDTH*SCREENHEIGHT*3); // clear imageData memory contents

// read the image data from the window
glReadPixels(0, 0, SCREENWIDTH-1, SCREENHEIGHT-1, GL_RGB, GL_UNSIGNED_BYTE, imageData);

// write the image data to a file
WriteBitmapFile(filename, SCREENWIDTH, SCREENHEIGHT, (unsigned char*)imageData);

// free the image data memory
free(imageData);
}
}
Here this is for openGL but u can easily convert this into DX. U have to call SaveScreenshot() on some key like 'S' to save current frame.


// WriteBitmapFile()
// desc: takes image data and saves it into a 24-bit RGB .BMP file
// with width X height dimensions
int WriteBitmapFile(char *filename, int width, int height, unsigned char *imageData)
{
FILE *filePtr; // file pointer
BITMAPFILEHEADER bitmapFileHeader; // bitmap file header
BITMAPINFOHEADER bitmapInfoHeader; // bitmap info header
int imageIdx; // used for swapping RGB->BGR
unsigned char tempRGB; // used for swapping

// open file for writing binary mode
filePtr = fopen(filename, "wb");
if (!filePtr)
return 0;

// define the bitmap file header
bitmapFileHeader.bfSize = sizeof(BITMAPFILEHEADER);
bitmapFileHeader.bfType = 0x4D42;
bitmapFileHeader.bfReserved1 = 0;
bitmapFileHeader.bfReserved2 = 0;
bitmapFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

// define the bitmap information header
bitmapInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmapInfoHeader.biPlanes = 1;
bitmapInfoHeader.biBitCount = 24; // 24-bit
bitmapInfoHeader.biCompression = BI_RGB; // no compression
bitmapInfoHeader.biSizeImage = width * abs(height) * 3; // width * height * (RGB bytes)
bitmapInfoHeader.biXPelsPerMeter = 0;
bitmapInfoHeader.biYPelsPerMeter = 0;
bitmapInfoHeader.biClrUsed = 0;
bitmapInfoHeader.biClrImportant = 0;
bitmapInfoHeader.biWidth = width; // bitmap width
bitmapInfoHeader.biHeight = height; // bitmap height

// switch the image data from RGB to BGR
for (imageIdx = 0; imageIdx < bitmapInfoHeader.biSizeImage; imageIdx+=3)
{
tempRGB = imageData[imageIdx];
imageData[imageIdx] = imageData[imageIdx + 2];
imageData[imageIdx + 2] = tempRGB;
}

// write the bitmap file header
fwrite(&bitmapFileHeader, 1, sizeof(BITMAPFILEHEADER), filePtr);

// write the bitmap info header
fwrite(&bitmapInfoHeader, 1, sizeof(BITMAPINFOHEADER), filePtr);

// write the image data
fwrite(imageData, 1, bitmapInfoHeader.biSizeImage, filePtr);

// close our file
fclose(filePtr);

return 1;
}

int frames = 0;

// SaveScreenshot()
// desc: takes a snapshot of the current window contents by retrieving
// the screen data with glReadPixels() and writing the data to a file
void SaveScreenshot()
{
frames++;
if (frames <= 50)
{
char *filename;
if (frames == 1) filename = "frame1.bmp";
else if (frames == 2) filename = "frame2.bmp";
else if (frames == 3) filename = "frame3.bmp";
else if (frames == 4) filename = "frame4.bmp";
else if (frames == 5) filename = "frame5.bmp";
else if (frames == 6) filename = "frame6.bmp";
else if (frames == 7) filename = "frame7.bmp";
else if (frames == 8) filename = "frame8.bmp";
else if (frames == 9) filename = "frame9.bmp";
else if (frames == 10) filename = "frame10.bmp";
else if (frames == 11) filename = "frame11.bmp";
else if (frames == 12) filename = "frame12.bmp";
else if (frames == 13) filename = "frame13.bmp";
else if (frames == 14) filename = "frame14.bmp";
else if (frames == 15) filename = "frame15.bmp";
else if (frames == 16) filename = "frame16.bmp";
else if (frames == 17) filename = "frame17.bmp";
else if (frames == 18) filename = "frame18.bmp";
else if (frames == 19) filename = "frame19.bmp";
else if (frames == 20) filename = "frame20.bmp";
else if (frames == 21) filename = "frame21.bmp";
else if (frames == 22) filename = "frame22.bmp";
else if (frames == 23) filename = "frame23.bmp";
else if (frames == 24) filename = "frame24.bmp";
else if (frames == 25) filename = "frame25.bmp";
else if (frames == 26) filename = "frame26.bmp";
else if (frames == 27) filename = "frame27.bmp";
else if (frames == 28) filename = "frame28.bmp";
else if (frames == 29) filename = "frame29.bmp";
else if (frames == 30) filename = "frame30.bmp";
else if (frames == 31) filename = "frame31.bmp";
else if (frames == 32) filename = "frame32.bmp";
else if (frames == 33) filename = "frame33.bmp";
else if (frames == 34) filename = "frame34.bmp";
else if (frames == 35) filename = "frame35.bmp";
else if (frames == 36) filename = "frame36.bmp";
else if (frames == 37) filename = "frame37.bmp";
else if (frames == 38) filename = "frame38.bmp";
else if (frames == 39) filename = "frame39.bmp";
else if (frames == 40) filename = "frame40.bmp";
else if (frames == 41) filename = "frame41.bmp";
else if (frames == 42) filename = "frame42.bmp";
else if (frames == 43) filename = "frame43.bmp";
else if (frames == 44) filename = "frame44.bmp";
else if (frames == 45) filename = "frame45.bmp";
else if (frames == 46) filename = "frame46.bmp";
else if (frames == 47) filename = "frame47.bmp";
else if (frames == 48) filename = "frame48.bmp";
else if (frames == 49) filename = "frame49.bmp";
else if (frames == 50) filename = "frame50.bmp";

imageData = malloc(SCREENWIDTH*SCREENHEIGHT*3); // allocate memory for the imageData
memset(imageData, 0, SCREENWIDTH*SCREENHEIGHT*3); // clear imageData memory contents

// read the image data from the window
glReadPixels(0, 0, SCREENWIDTH-1, SCREENHEIGHT-1, GL_RGB, GL_UNSIGNED_BYTE, imageData);

// write the image data to a file
WriteBitmapFile(filename, SCREENWIDTH, SCREENHEIGHT, (unsigned char*)imageData);

// free the image data memory
free(imageData);
}
}
char fileName[64];sprintf(fileName, "frame%i.bmp", frames);


That should save you some time.
____________________________________________________________Programmers Resource Central
now, that chunk of ifs is a good candidate for thedailywtf.com :)
Agreed.

It would be better to just have a while loop with a counter start at 0. Then use sprintf as in the example above to write that to a string including a file name prefix.. say "sshot%03d.bmp". If this file exists, increment the counter, and start again. Do this until a file does not exist. You just found your open slot.

The problem with that big block of 'ifs' is that it will overwrite (or fail) if a screen shot exists of the same name from a previous session. Well, that's not the only problem...
Quote:
Try looking at PIX, which comes with newer SDKs

That was the first place where I look into; PIX allow me to see how all frames are rendered but don't allow me to change the camera, so I can see actually how much I'm culling.

Quote:
You might also consider implementing wireframe drawing for this purpose.

Yes, I had used wireframe, but that way I can't see the geometry that is rendered outside the frustum.

Quote:
Another approach is to make your occulsion system and camera seperable.

I guess that's the ultimate approach that solves my problem, but it represents making a few more lines of code. If I don't found another way, I will do as you described: culling with one camera and rendering with other.

But It will be cool if there is any program that allow to do that by themselves. I know that there is at least one that do it for opengl, (I think is glDebugger) which allow to press a key that stop the game and move around the rendered world with a virtual camera so you can see how the culling process is performing.
God is Real unless is declared Integer

This topic is closed to new replies.

Advertisement