PDA

View Full Version : save texture to a file



bliss
10-11-2010, 12:16 AM
I need to make a "PrintScreen" function im my application.
The thing is that i need it to be done by opengl (not windows printscreen or api functions). In addition, later I will need to save a depth buffer as well.

After drawing some objects on the screen, I want to copy the screen and save it to a file. The easiest way by glReadPixels seems not working. So, I am trying now to use glCopyTexImage2D.

The thing is that I am stuck with obtaing a pointer to a raw texture data.



GLint m_viewport[4];
glGetIntegerv( GL_VIEWPORT, m_viewport );

GLuint texture;
glGenTextures( 1, &texture );

glBindTexture(GL_TEXTURE_2D, texture);

glCopyTexImage2D(GL_TEXTURE_2D,0,GL_RGB,m_viewport[0],
m_viewport[1], m_viewport[2], m_viewport[3],0);

// now when framebuffer is copied, i need to link a
//pointer to a raw texture data:
BYTE *raw_img = ...;

// save using, for example libpng
free(raw_img);


Any suggestions on how to do it?
I wonder is it after all a correct way to do?

Alfonse Reinheart
10-11-2010, 01:54 AM
The easiest way by glReadPixels seems not working.

In what way is it not working?


The thing is that I am stuck with obtaing a pointer to a raw texture data.

You can't do that. You can get a copy of the pixel data by using glGetTexImage. However, if you can't make glReadPixels work, I doubt you'd be able to get glGetTexImage to work for the same reasons.

bliss
10-11-2010, 02:00 AM
i have found out a problem with glReadPixels.
i just needed to add

glPixelStorei(GL_PACK_ALIGNMENT, 1);
right before glReadPixels.
Just in case smb. needs - a good and simple piece of code is given in
http://www.gamedev.net/community/forums/topic.asp?topic_id=436385
by Muhammad Haggag


Anyway, I need to save a depth buffer to an image as well. And for that it seems i still should make glCopyTexImage2D() work.

Also with depth I tried
http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=253578

but it returns just gray screen (actually the color of my background, but no object on it). I've tried to play with offsets, but this did not help...

Maybe some lines of code will help ;)

bliss
10-11-2010, 02:03 AM
glGetTexImage() seems what I was looking for.
I will try it!

zeoverlord
10-11-2010, 10:55 AM
here is a simple way of saving screenshots into tga files.
http://www.flashbang.se/archives/155

mhagain
10-11-2010, 05:05 PM
Be aware that using GL_RGB as your format is going to be sloooooow. I guess it's OK for screenshots, but if it's something you ever want to do every frame, it's not a good idea.

bliss
10-11-2010, 06:07 PM
mhagain, then what is the faster way to do it?

by now just screen shots are fine, but it is probable that later i will need to stream the RGB data together with Depth data.

bliss
10-12-2010, 02:15 AM
So, my final code for grabbing RGB image looks like this (just for reference):


GLint m_viewport[4];
glGetIntegerv( GL_VIEWPORT, m_viewport );
*width = m_viewport[2];
*height = m_viewport[3];

GLuint texture;
glGenTextures( 1, &texture );
glBindTexture(GL_TEXTURE_2D, texture);

// rgb image
glCopyTexImage2D(GL_TEXTURE_2D,0,GL_RGB,m_viewport[0],
m_viewport[1], m_viewport[2], m_viewport[3],0);

glPixelStorei(GL_PACK_ALIGNMENT, 1);
BYTE *raw_img = (BYTE*) malloc(sizeof(BYTE) * *width * *height * 3);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, raw_img);

return raw_frame;


Thanks for the hint about glGetTexImage.

Now I want to grab Depth image. Actually, in docs it is said that glGetTexImage does not accept GL_DEPTH_COMPONENT. However, in the same manner as for RGB, I grabbed the depth component by glCopyTexImage2D


glCopyTexImage2D(GL_TEXTURE_2D,0,GL_DEPTH_COMPONEN T,m_viewport[0],
m_viewport[1], m_viewport[2], m_viewport[3],0);

and then got a pointer to a depth texture.


glGetTexImage(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, raw_depth);


I also tried to use GL_LUMINANCE in glGetTexImage but it does not work and produces error in next redraw cycle by glClearColor.

1. I wonder why GL_DEPTH_COMPONENT in glGetTexImage works?

2. The returned depth seems to be not a really detailed one. All values are in range 248-255. I also tried to put objects close but this does not help much. Why this happens?

Alfonse Reinheart
10-12-2010, 03:07 AM
Actually, in docs it is said that glGetTexImage does not accept GL_DEPTH_COMPONENT.

In what docs?


The returned depth seems to be not a really detailed one. All values are in range 248-255. I also tried to put objects close but this does not help much. Why this happens?

Because you asked for the data to be compressed down to GL_UNSIGNED_BYTEs in size. A more appropriate format would be GL_UNSIGNED_INTs.

Though again, there's no reason that you can't use glReadPixels to do exactly this.

mhagain
10-12-2010, 08:51 AM
mhagain, then what is the faster way to do it?
GL_BGRA. Optionally with GL_UNSIGNED_INT_8_8_8_8_REV as the type.

(1) Your framebuffer is going to be 32-bit anyway; using a 24-bit format means that OpenGL must discard the extra 8 bits before sending it to your application.

(2) Your framebuffer layout is most likely going to be in BGRA format so using an RGB format means that OpenGL needs to swizzle the components before sending it to your application.

(3) Your framebuffer is 32 bits so using a 32 bit type can provide a hint to OpenGL to do a faster copy (32 bits at a time rather than 8 bits at a time). GL_UNSIGNED_INT_8_8_8_8_REV matches the in-hardware layout. It's not always needed but it doesn't hurt to play nice.

These ensure that there is the highest possible chance of the read operation going through as few software stages as possible before your app gets the data.

http://www.opengl.org/wiki/Common_Mistakes#Texture_upload_and_pixel_reads

And if you are interested, most GPUs like chunks of 4 bytes. In other words, RGBA or BGRA is prefered. RGB and BGR is considered bizarre since most GPUs, most CPUs and any other kind of chip don't handle 24 bits. This means, the driver converts your RGB or BGR to what the GPU prefers, which typically is BGRA.

Similarly, if you read a buffer with glReadPixels, you might get similar problems. There is a GL_PACK_ALIGNMENT just like the GL_UNPACK_ALIGNMENT. The default GL_PACK_ALIGNMENT is 4 which means each horizontal line must be a multiple of 4 in size. If you read the buffer with a format such as BGRA or RGBA you won't have any problems since the line is already a multiple of 4. If you read it in a format such as BGR or RGB then you risk running into this problem.

bliss
10-12-2010, 06:59 PM
To Alfonse Reinheart:


In what docs?
I checked here: http://www.opengl.org/sdk/docs/man/xhtml/glGetTexImage.xml
It says




void glGetTexImage( GLenum target,
GLint level,
GLenum format,
GLenum type,
GLvoid * img);


format
Specifies a pixel format for the returned data. The supported formats are GL_RED,GL_GREEN,GL_BLUE,GL_ALPHA,GL_RGB, GL_BGR,GL_RGBA,GL_BGRA,GL_LUMINANCE, and GL_LUMINANCE_ALPHA.

Because the internal texture image is an RGBA image, pixel formats GL_COLOR_INDEX, GL_STENCIL_INDEX, and GL_DEPTH_COMPONENT are not accepted, and pixel type GL_BITMAP is not accepted.


Anyway, it works;)
Also, I changed format to GL_UNSIGNED_INT and it became exactly what i need..


Though again, there's no reason that you can't use glReadPixels to do exactly this.
Yeah, it works, as i wrote before. I had to add glPixelStorei.
But I read somewhere (while just surfing around) that glCopyTexImage2D works faster. Is it correct?


To mhagain:
Thanks for sharing, i will try it and see the difference.

Alfonse Reinheart
10-12-2010, 09:32 PM
I checked here: http://www.opengl.org/sdk/docs/man/xhtml/glGetTexImage.xml

That page is wrong. The OpenGL 2.1 specification clearly says on page 252 that the function can take GL_DEPTH_COMPONENT.


But I read somewhere (while just surfing around) that glCopyTexImage2D works faster. Is it correct?

It depends on what you want.

glCopyTexImage2d is good if your goal is to get data from the framebuffer into a texture. That is, you shouldn't do glReadPixels followed by glTexImage2d. But if your goal is to get framebuffer data into a block of CPU accessible memory, then you may as well do glReadPixels.