PDA

View Full Version : Offscreen rendering



David Doria
02-25-2009, 04:37 PM
I would like to get the contents of the color buffer and depth buffer that would have been displayed but without displaying them. I read that you can use frame buffer objects (FBOs) but all of the FBO examples I saw were talking about textures and all kinds of stuff that I don't need. I just want to save an image of a very basic model from different viewpoints without displaying it on the screen. Does anyone have an example of that / any examples?

Thanks,

Dave

scratt
02-25-2009, 06:22 PM
Hi Dave,

When you create an FBO you have to attach textures and render buffers to it so that you have something to render into. An FBO is just a structure to hold all of those things together.

The minimum useful requirement for an FBO is a layer of some sort to draw into. The easiest way to achieve this is with a texture IMO. Textures can be added for colour and depth layers and so on..

When reading data back from an FBO you actually interrogate the textures which are attached to it.

This is quite a good tutorial IMO..
http://www.gamedev.net/reference/articles/article2331.asp

Hope that helps.

Abdallah DIB
02-26-2009, 01:20 AM
if u want to save only the image on the hard disk that mean that u want to make a transfer from ur graphics card to central memory than to hard disk. if u want to do a simple transfer u have to use the ugly glReadPixels.

to save an image of ur model without displaying ur model on ur screen than u have to comment the line that swap ur buffers(if u are using double buffering) (for ex in SDL comment the line : SDL_GL_SwapBuffers)but now u have to read the Back buffer and not the front buffer using the command glReadBuffer(GL_BACK) before ur glReadPixels,

finally, it is better to use fbo ( best performance)

David Doria
02-26-2009, 06:48 AM
If I simply don't swap the buffers, I still have to have an active window, right? What I want is to save the depth buffer of what would have been rendered without even opening a window. I'm looking into FBOs - but they can do that, right?

ZbuffeR
02-26-2009, 06:51 AM
yes

Abdallah DIB
02-26-2009, 07:15 AM
opengl is not attached to any window or rendering surface , it is a just a library that contain functions to draw objects in 3D space. check ur SDL or glut or Qt or whetever

ZbuffeR
02-26-2009, 07:33 AM
opengl is not attached to any window or rendering surface
This is true is theory, but the pixel ownership rule makes that fragile. The non-visible part(s) of the framebuffer is undefined.
Quote from OpenGL FAQ 14.070 (http://www.opengl.org/resources/faq/technical/rasterization.htm#rast0070) :
"If a window is obscured by another window, it doesn't have to store pixel data for the obscured region. Therefore, a glReadPixels() call can return undefined data for the obscured region."

The answer : FBO.

Abdallah DIB
02-26-2009, 07:50 AM
i agree with u !
but the question i am asking is the Operating system wich decide not to store pixels or the rendering surface( sdl ... ) ?
i think it is sdl no ?

David Doria
02-26-2009, 09:46 AM
I followed the tutorial scratt gave. There is one part that says to check


GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);

if(status != GL_FRAMEBUFFER_COMPLETE_EXT)
{
cout << gluErrorString(status) << endl;
}


It is returning "no error", but I guess that is different than "GL_FRAMEBUFFER_COMPLETE_EXT" ?

here is my complete attempt (almost every line is from the tutorial)



//Create handle to FBO
GLuint fbo;
glGenFramebuffersEXT(1, &amp;fbo);

//bind FBO (what does this mean?)
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);

//create depth buffer for the FBO
GLuint depthbuffer;
glGenRenderbuffersEXT(1, &amp;depthbuffer);

//bind the renderbuffer
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);

//specify size of the depth buffer
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, width, height);

//attach the depth buffer to the frame buffer
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depthbuffer);

//attach a texture to the FBO
//------------
//create a texture
GLuint img;
glGenTextures(1, &amp;img);
glBindTexture(GL_TEXTURE_2D, img);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);

//attach the texture to the framebuffer
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, img, 0);

//check that everything went ok
GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);

if(status != GL_FRAMEBUFFER_COMPLETE_EXT)
{
cout << gluErrorString(status) << endl;
}

glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
glPushAttrib(GL_VIEWPORT_BIT);
glViewport(0,0,width, height);


// Render as normal here
// output goes to the FBO and it’s attached buffers
LookAt();
DrawCube();
ReadDepthBuffer();

glPopAttrib();
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

//clean up
glDeleteFramebuffersEXT(1, &amp;fbo);
glDeleteRenderbuffersEXT(1, &amp;depthbuffer);



LookAt() is simply


void LookAt()
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(70, 1, 1, 100);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

//gluLookAt(2, 2, 10, 2, 0, 0, 0, 1, 0);
gluLookAt(0, 0, 10, 0, 0, 0, 0, 1, 0);

}

And my read and write buffer are



void ReadDepthBuffer()
{

float depths[width*height];
glReadPixels(0, 0, width, height, GL_DEPTH_COMPONENT, GL_FLOAT, depths);
WriteDepthBuffer("DepthBuffer.txt", depths);
}

void WriteDepthBuffer(const string &amp;Filename, float* depths)
{
ofstream fout(Filename.c_str());

for(unsigned int im_x = 0; im_x < width; im_x++)
{
for(unsigned int im_y = 0; im_y < height; im_y++)
{
double CurrentDepth = depths[width*im_y + im_x];
fout << im_x << " " << im_y << " " << CurrentDepth << endl;
}
}

fout.close();

cout << "Done Writing." << endl;
}


The output looks nothing like the depth buffer of the cube. Any thoughts?

Thanks,
Dave

Y-tension
02-26-2009, 10:31 AM
Perhaps glReadBuffer(GL_COLOR_ATTACHMENT0_EXT); will help?

Abdallah DIB
02-26-2009, 10:37 AM
add glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
befrore glreadpixels.
bcoz the default read buffer is the front buffer,
but now u are not rendering to the fron buffer but to ATTACHMENT0 of the fbo .

gd luck

Y-tension
02-26-2009, 10:39 AM
Don't think so...in the spec:
"For application-created framebuffer objects, the initial setting for ReadBuffer is COLOR_ATTACHMENT0_EXT."

Anyway, you can always try binding a depth texture and reading from it..

And try making a window and use it for visual display of your buffers. Much better than .txt files!

David Doria
02-26-2009, 11:01 AM
I tried this



float depths[width*height];
glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
glReadPixels(0, 0, width, height, GL_DEPTH_COMPONENT, GL_FLOAT, depths); //(x,y, width, height, format, type, *pixels)
WriteDepthFileVIL("buffer.jpg", depths);


but it's still just junk. I took your advice though and wrote a jpg instead of a text file (which before I was interpreting in matlab).

I thought I had already bound a depth buffer with


//create depth buffer for the FBO
GLuint depthbuffer;
glGenRenderbuffersEXT(1, &amp;depthbuffer);

//bind the renderbuffer
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);

//specify size of the depth buffer
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, width, height);

//attach the depth buffer to the frame buffer
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depthbuffer);



So how would I read from that one?

Thanks,
Dave

scratt
02-26-2009, 11:13 AM
Dave you really need to put some checking in your code...

In most examples, including I think the one I posted, they talk about checking if the FBO has been created properly. They can be a bit pernickety, so to save yourself a lot of time wrap the error checking around the creation stuff.

Y-tension
02-26-2009, 11:17 AM
To be honest, I think reading it the way you did initially should work, but I haven't got the hardware to test it right now..What I meant is use glFrameBufferTexture2D with a depth texture and a depth attachment(yes, works, that's how shadowmaps work), and read the texel data(glGetTexImage) after rendering.

David Doria
02-26-2009, 11:18 AM
I did try that check they recommended, but it failed... but the error code was "no error", so I thought it was ok? And now I called this after every line:


void CheckErrors()
{
GLenum error = glGetError();
cout << gluErrorString(error) << endl;
}


But everything said "no error".

Abdallah DIB
02-27-2009, 01:21 AM
use this fbo class from gpgpu site
or @ least try to compare ur code to this class .to clean any mistake
http://sourceforge.net/project/showfiles.php?group_id=104004&package_id=158526&release_id=463385

scratt
02-27-2009, 01:42 AM
The final job to do in setup is to check that the FBO is ‘complete’. Completeness refers to the state of the FBO being one which, given the current OpenGL state and its attachments, all is correct for you to render to it.

This test is done via a single function which returns the status of the currently bound FBO

GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
If all has gone well then status will equal GL_FRAMEBUFFER_COMPLETE_EXT and your FBO is ready to be rendered to. Other error codes, as found in the spec, indicate other problems which might well have occurred when you tried to setup the FBO.

David Doria
02-27-2009, 06:14 AM
abdallah - thanks, i'll check it out.

Scratt - I did try that function but it seemed to return "no error".

scratt
02-27-2009, 06:34 AM
Does the "no error' value you get equate to GL_FRAMEBUFFER_COMPLETE_EXT though?

David Doria
02-27-2009, 07:30 AM
nope. However, I added
glDrawBuffer(GL_NONE);

and then the COMPLETE test passed.

So now I need to get the values of the depth buffer - I was trying:



glReadBuffer(GL_FRAMEBUFFER_EXT);
glReadPixels(0, 0, width, height, GL_DEPTH_COMPONENT, GL_FLOAT, depths);



but they are all just black. How should I get the values into an array?

Thanks,
Dave

Abdallah DIB
02-27-2009, 02:24 PM
i dont understand this line
glReadBuffer(GL_FRAMEBUFFER_EXT);
did u mean :
glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);