PDA

View Full Version : View Coverage / Occlusion Testing ?



Z-Knight
09-29-2008, 01:38 PM
Given a 3D model in a screen, what percentage of the view is the model and what percentage is the background?

Basically, I have a 3D model that I want to calculate the cross sectional area of the model as it flies through space...basically, imagine a vehicle flying, how much of it is flying into the wind (effectively the cross-sectional area)?

I posted a similar question in the JOGL forum since I'm using JOGL for my development but this is more accurately an OpenGL question. One thing I do know is that I need to perform this calculation while in an orthographic view instead of perspective...that's besides the point. One suggestion I got was to search for the "Occlusion Query" topic and I have found some info and in particular this demo code: http://www.codesampler.com/oglsrc/oglsrc_7.htm#ogl_occlusion_query. That example is nice but it seems to me like the occlusion query concept is a hardware (video card) solution but I'm looking for something that is not dependent on the hardware, per se.

Basically, I want to code some solution that would run in the background without changing my perspective view to orthographic view...something that runs in the background and effectively takes a screen shot of the view that I want and then gets me the buffer of pixel data which I could then search myself and count the number of background and model pixels. Worst case I would need some image capture code that could retrieve the buffered view before it is rendered and store that data for me to search and then clear the buffer so the orthographic view is not rendered but then my perspective view gets rendered.

The main problem that I have is my lack of OpenGL vocabulary and so searching for this concept is hard...do you have any suggestions? gluUnproject() is one thing that I saw but I am still investigating...do you know what else to search for? Do you know a possible solution?

I hope all of that was clear...I would appreciate any info. Thanks.

Z-Knight
10-01-2008, 02:15 PM
I think I figured out something and I'd appreciate if you could poke at it to see if it will work right...

Basically, in my display() method, whenever I want to calculate the area of the vehicle in the view I switch from a PERSPECTIVE projection to an ORTHOGRAPHIC projection, then I just display the vehicle in the attitude I want, read the pixels and count the background ones and foreground ones, and then clear the display bits and reset the view back to PERSPECTIVE and then draw the normal scene.

So basically I have this:

display() {
SetOrthoView()
clearColorAndDepthBuffers()
DISPLAYVEHICLE()
glReadPixels() // Calculate the area from the pixel array

SetPerspectiveView()
clearColorAndDepthBuffers() // Resets the view to nothing
glLoadIdentity()
DisplayNormalScene()
}

Right now I do this every few cycles because I save the view to a PNG file to see how it looks, but it seems fast enough to do every cycle if I take out the write out to a PNG file.

Does all of that make sense? It seems to work right now, and it seems simple enough which is why is surprises me that it works.

ZbuffeR
10-01-2008, 03:30 PM
Nothing wrong with this, fairly common to render temp stuff without showing it to the user.

You can make it more complex and more robust : your current solution will output wrong area if another window comes above the rendering window.
Use FBO (framebuffer object) to get an actual offscreen surface, independant from occlusion by other windows.

Z-Knight
10-01-2008, 03:35 PM
ZbuffeR, wow, thanks! I didn't realize that overlaying another window conflicts like that...I'm guessing that's a limitation of glReadPixels the way I'm using it. The way I was testing always had the window on top and so I would have never caught that problem.

I guess I have some reading about FBOs to do...thanks, yet again.

ZbuffeR
10-02-2008, 03:05 AM
The problem does not come from glReadPixels.
The GL spec explicitely says that hidden parts of GL window are undefined.
You can see that if you swapbuffers when another window moves over the GL frame, a small band of garbage can be seen following the moving window.

And 'undefined' means that some implementations can keep the correct pixels values in certain conditions, but you should not rely on it.

For FBO, I recommend these 2 tutorials, very nicely done :
http://www.gamedev.net/reference/programming/features/fbo1/
http://www.gamedev.net/reference/programming/features/fbo2/

Z-Knight
10-02-2008, 08:02 AM
Would the same "undefined" region exist if the window is moved off-screen? For example, if I move it down and only see the top portion? I ask because I tried that and I was still able to read the pixels correctly and everything was defined correctly (I output the pixels to a PNG file so I could verify visually)...the only thing I have not tested is to actually have a window pop above mine to see if the region remains undefined. I've not done it because I'm not using an OpenGL loop per se...I'm using JOGL and I disabled the animator which does the loop and instead I update the display when needed to save CPU cycles.

Anyway, thanks again for the info...and the links.

ZbuffeR
10-02-2008, 11:26 AM
Would the same "undefined" region exist if the window is moved off-screen?
Yes, it is the same thing. So even if it works for you now, keep in mind your are in "undefined" territory, that may break on a driver or OS update.

Z-Knight
10-02-2008, 12:48 PM
One last thing...so far the FBO tutorials focus on adding textures to the FBO and then rendering to the texture. I have two question regarding this:

1) Do I have to render to a texture because this is how I get the buffer of the scene, or is there something else that I should render to if I don't actually need to store a texture object. In my case I'm just using the glReadPixels() to read the pixel data in the current buffer so that's basically all I need. But since the examples all show textures I'm wondering if that is more than I really need.

2) If I do have to render to a texture is the rendering part going to be any different from I currently do? I mean, currently I effectively have a different display() method in which I render the scene with only the object/vehicle in it and another display() method where I display my normal scene. The first display() method also changes screen modes from perspective to orthographic and back (after rendering and pixel reading), so would the same calls be used for rendering to the texture?

I tried to do the following but got a black image and no vehicle was rendered... was supposed to get a green background and a vehicle...basically I:

bind the FBO, convert to Orthographic projection, draw the vehicle by itself, read pixels of current buffer (or so I thought), unbind the FBO, convert back to Perspective projection, draw the full scene



if (isFboSupported) {
gl.glBindFramebufferEXT(GL.GL_FRAMEBUFFER_EXT, fboID);
gl.glPushAttrib(GL.GL_VIEWPORT_BIT);
}

//
// Convert to ORTHOGRAPHIC Projection
//
gl.glViewport(0, 0, frameWidth, frameHeight);
gl.glClearColor(0.0f, 1.0f, 0.0f, 0.0f); // GREEN background
gl.glMatrixMode(GL.GL_PROJECTION);
gl.glLoadIdentity();
gl.glOrtho(-model.getRadius(),
model.getRadius(),
-model.getRadius(),
model.getRadius(),
model.getRadius(),
-model.getRadius());
gl.glMatrixMode(GL.GL_MODELVIEW);
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
gl.glLoadIdentity();

//
// Draw the model by itself
//
model.drawModel(gl);

if (isFboSupported) {
saveFrameAsPNG(gl, "frame.png");
gl.glPopAttrib();
gl.glBindFramebufferEXT(GL.GL_FRAMEBUFFER_EXT, 0);
}

//
// Convert back to PERSPECTIVE Projection
//
gl.glViewport(0, 0, frameWidth, frameHeight);
gl.glMatrixMode(GL.GL_PROJECTION);
gl.glLoadIdentity();
glu.gluPerspective(65.0, frameWidth/frameHeight, 0.1, 20000.0);
gl.glMatrixMode(GL.GL_MODELVIEW);

// Draw the entire scene
drawScene(gl);


In the saveFrameAsPNG() code I basically do glReadPixels() and loop through the resulting buffer to count the pixels.

thanks.

ZbuffeR
10-03-2008, 04:25 AM
1) no

As explained in the first tutorial, you need to "Attach a colour Renderbuffer to the FBO" (or attach a texture if you would have wanted a texture). I must admit I only used this for textures.

Check for GL errors, and check to completeness of your FBO before rendering to it :
GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);

Z-Knight
10-03-2008, 09:28 AM
Someone suggested to me it might be possible to read the pixels from the currently bound buffer so that is why I didn't use textures and the "attach a colour renderbuffer to the FBO" was a little above me - ie I didn't understand it yet. I guess I need to figure out how to "attach the RenderBuffer" to get this to work....more searching since the tutorial skips that part.

I foolishly didn't check the status, thinking that it was created ok since the 'isFboSupported' check I did was successful.

In my init() method I get a successful fbo when I create it using: glGenFramebuffersEXT()

But, when I did the status check in my display() method I get this error GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT which I need to track down...that error has something to do with "There is at least one image attached to the framebuffer."

Searching on the web it seems that status error may be ok...someone posted this: "When you first create an FBO, it is always incomplete. It's only after you have attached all targets, including your depth cubemap face, that the FBO will be complete." at: http://www.gamedev.net/community/forums/viewreply.asp?ID=3312895



So I did some checking and I get the following statuses:
init() method
- Gen FBO
- Status: Complete

display() method
- Bind buffer (before rendering)
- Status: Incomplete, Missing attachment

- After rendering, before unbinding
- Status: Incomplete, Missing attachment

- After unbinding
- Status: Complete

So that verifies the comments I found...so the FBO may be working ok but I just either can't simply read the pixels directly or I first need to attach a texture or a color RenderBuffer for it to work correctly.

Z-Knight
10-03-2008, 12:29 PM
I'm wondering if how I read the pixels is what the problem may be... I have the following code that works when NOT using FBOs, where I read from the BACK buffer (or so it seems...not my own code).



gl.glReadBuffer( GL.GL_BACK );
gl.glPixelStorei( GL.GL_PACK_ALIGNMENT, 1 );
gl.glReadPixels( 0, 0, frameWidth, frameHeight,
GL.GL_RGB, GL.GL_UNSIGNED_BYTE,
pixelsRGB );


Maybe having the glReadBuffer() set to GL_BACK is wrong...so what should it be?

Z-Knight
10-06-2008, 11:45 AM
So I still can't figure out how to create and attach renderbuffers and all of the sites out there show how to do textures and say that renderbuffers is the other option but none of them show squat...I have no idea what I'm doing a reading a spec isn't going to tell me anything because I don't speak opengl spec speak - this is really frustrating. Also, I need a color render buffer, only mention I've seen out there was of depth render buffers and colors are always secondary...sigh.

I tried the following in my init() method to create the render buffer:


if (isFboSupported) {
// Create Frame buffer
gl.glGenFramebuffersEXT(1, framebufferName, 0);

// Create Render buffer
gl.glGenRenderbuffersEXT(1, renderbufferName, 0);
}


In my display() method I try to bind it this way:


if (isFboSupported) {
// Bind frame buffer
gl.glBindFramebufferEXT(GL.GL_FRAMEBUFFER_EXT, framebufferName[0]); //fboID);

// Bind Render buffer
gl.glBindRenderbufferEXT(GL.GL_RENDERBUFFER_EXT, renderbufferName[0]);

// Render buffer storage
gl.glRenderbufferStorageEXT(GL.GL_RENDERBUFFER_EXT , GL.GL_RGB32F_ARB, frameWidth, frameHeight);

// Set viewport bit
gl.glPushAttrib(GL.GL_VIEWPORT_BIT);
}

// RENDER SCENE
renderScene();

// Save buffer to image
saveBuffer();

...


Is the above even correct?? Did I create and bind the render buffer correctly?

The saveBuffer() method is where I want to process the render buffer by reading the pixels using:


...
// Read Frame back into our ByteBuffer.
gl.glReadBuffer( GL.GL_BACK );
gl.glPixelStorei( GL.GL_PACK_ALIGNMENT, 1 );
gl.glReadPixels( 0, 0, frameWidth, frameHeight,
GL.GL_RGB, GL.GL_UNSIGNED_BYTE,
pixelsRGB );
...


Do I need to flag the ReadBuffer with some other flag? i.e. GL_FRONT, or something else?

dletozeun
10-06-2008, 01:47 PM
Perhaps the code above is incomplete, but you should attach the renderbuffer to the framebuffer. A renderbuffer alone is useless.

So you need to call glFramebufferRenderbufferEXT after setting the renderbuffer storage (don't forget to bind the framebuffer).

Then to select the read buffer call glReadBuffer after binding the framebuffer with for example GL_DEPTH_ATTACHMENT_EXT or GL_COLOR_ATTACHMENT[0..N]_EXT as parameter like explained in the gamedev tutorials.

Sorry if it is already done or already said, I have fell on this topic and read it quickly.

Z-Knight
10-06-2008, 03:02 PM
it is all starting to become a bit clearer and thanks for the explanation, dletozeun.

I'm still not getting it to work and I'm not sure where the problems are currently, so I'll lay out my code and print out where I get the fbo status errors.

First of this is the code I use to create my FBO and the Color RenderBuffer...note my previous post was probably off and I've changed it around some since then.

Initialization code ... in an init() method is:

public void init() {
isFboSupported = gl.isExtensionAvailable("GL_EXT_framebuffer_object");
if (isFboSupported) {
// Create the frame buffer
gl.glGenFramebuffersEXT(1, framebufferName, 0);

// Bind the frame buffer for use
gl.glBindFramebufferEXT(GL.GL_FRAMEBUFFER_EXT,fram ebufferName[0]);

// Check status of frame buffer
checkFboStatus(gl); Status: GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT

// Create the render buffer
gl.glGenRenderbuffersEXT(1, renderbufferName, 0);

// Bind the render buffer for use
gl.glBindRenderbufferEXT(GL.GL_RENDERBUFFER_EXT,re nderbufferName[0]);

// Set the render buffer storage
gl.glRenderbufferStorageEXT(GL.GL_RENDERBUFFER_EXT , GL.GL_RGB, frameWidth, frameHeight);

// Attach the render buffer to the frame buffer
gl.glFramebufferRenderbufferEXT(GL.GL_FRAMEBUFFER_ EXT, GL.GL_COLOR_ATTACHMENT0_EXT, GL.GL_RENDERBUFFER_EXT, renderbufferName[0]);
// Unbind the frame buffer
gl.glBindFramebufferEXT(GL.GL_FRAMEBUFFER_EXT, 0);
}
}

Now in the display loop I do the following

public void display() {
...
...
if (isFboSupported) {
// Set the viewport bit attribute
gl.glPushAttrib(GL.GL_VIEWPORT_BIT);

// Bind the frame buffer for use
gl.glBindFramebufferEXT(GL.GL_FRAMEBUFFER_EXT, framebufferName[0])

// Check status of frame buffer
checkFboStatus(gl); Status: GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT

// Set the viewport
gl.glViewport(0, 0, frameWidth, frameHeight);

// Clear the color to GREEN
gl.glClearColor(0.0f, 1.0f, 0.0f, 0.0f);

// Draw the background scene
drawBackgroundScene();

// Check status of frame buffer
checkFboStatus(gl); Status: GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT

// Read the frame...read pixels
gl.glReadBuffer(GL.GL_COLOR_ATTACHMENT0_EXT);
gl.glPixelStorei( GL.GL_PACK_ALIGNMENT, 1 );
gl.glReadPixels( 0, 0, frameWidth, frameHeight, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, pixelsRGB );

// Unbind the frame buffer
gl.glBindFramebufferEXT(GL.GL_FRAMEBUFFER_EXT, 0);

// Unset the viewport bit attribute
gl.glPopAttrib();

// Check status of frame buffer
checkFboStatus(gl); Status: GL_FRAMEBUFFER_COMPLETE_EXT
}
...

// Reset the view and Draw the main scene
drawMainScene();
...
}

Doing this, and saving the pixels, I end up getting a black screen...ie the pixels are all black. They should be GREEN and some white (for the vehicle in the view) but I get all black....and I'm not sure the FBO status errors I get (the ones in red) are valid for that point of the code or if they are true errors that require a fix?


Unless I'm missing something with the FBO creation and RenderBuffer creation, etc I think I have everything correct, except probably for how I read the pixels? I don't know what else it could be....I'm likely not retrieving the right buffer?

Z-Knight
10-06-2008, 05:06 PM
well, I feel like a damn idiot ... I never set frameWidth and frameHeight before the init() method and so that was causing the darn problem...everything is working now. geez

Thank you to all that helped or tried to help. :) At any rate, the code above is valid (written for JOGL) so I hope it helps someone in their FBO work.

dletozeun
10-07-2008, 02:13 PM
:D, Bugs are always where we don't look.