Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Efficient way of reading depth values from depth buffer

Tags:

opengl

For an algorithm of mine I need to be able to access the depth buffer. I have no problem at all doing this using glReadPixels, but reading an 800x600 window is extremely slow (From 300 fps to 20 fps)

I'm reading a lot about this and I think dumping the depth buffer to a texture would be faster. I know how to create a texture, but how do I get the depth out?

Creating an FBO and creating the texture from there might be even faster, at the moment I am using an FBO (but still in combination with glReadPixels).

So what is the fastest way to do this?

(I'm probably not able to use GLSL because I don't know anything about it and I don't have much time left to learn, deadlines!)

edit: Would a PBO work? As described here: http://www.songho.ca/opengl/gl_pbo.html it can go a lot faster but I can not change buffers all the time as in the example.

Edit2: How would I go about putting the depth data in the PBO? At the moment I do:

glGenBuffersARB(1, &pboId);
glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pboId);
glBufferDataARB(GL_PIXEL_PACK_BUFFER_ARB, 800*600*sizeof(GLfloat),0,     GL_STREAM_READ_ARB);

and right before my readpixels i call glBindbuffer again. The effect is that I read nothing at all. If I disable the PBO's it all works.

Final edit: I guess I solved it, I had to use:

glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pboId);
glReadPixels( 0, 0,Engine::fWidth, Engine::fHeight, GL_DEPTH_COMPONENT,GL_FLOAT, BUFFER_OFFSET(0));

GLuint *pixels = (GLuint*)glMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY);

This gave me a 20 FPS increase. It's not that much but it's something.

So, I used 2 PBO's but I'm still encountering a problem: My code only gets executed once.

glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pboIds[index]);  
std::cout << "Reading pixels" << std::endl;
glReadPixels( 0, 0,Engine::fWidth, Engine::fHeight, GL_DEPTH_COMPONENT,GL_FLOAT, BUFFER_OFFSET(0));
std::cout << "Getting pixels" << std::endl;
//  glBufferDataARB(GL_PIXEL_PACK_BUFFER_ARB, 800*600*sizeof(GLfloat), 0, GL_STREAM_DRAW_ARB);
GLfloat *pixels = (GLfloat*)glMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY);
int count = 0;
for(int i = 0; i != 800*600; ++i){
    std::cout << pixels[i] << std::endl;
}

The last line executes once, but only once, after that it keeps on calling the method (which is normal) but stops at the call to pixels.


I apparently forgot to load glUnMapBuffers, that kinda solved it, though my framerate is slower again..


I decided giving FBO's a go, but I stumbled across a problem: Initialising FBO:

glGenFramebuffersEXT(1, framebuffers);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffers[0]);
std::cout << "framebuffer generated, id: " << framebuffers[0] << std::endl;
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);

glGenRenderbuffersEXT(1,renderbuffers);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffers[0]);
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, 800, 600);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, renderbuffers[0]);
bool status = checkFramebufferStatus();
    if(!status)
        std::cout << "Could not initialise FBO" << std::endl;
    else
        std::cout << "FBO ready!" << std::endl;

glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

My drawing loop:

GLenum errCode; const GLubyte *errString;

if ((errCode = glGetError()) != GL_NO_ERROR) {
    errString = gluErrorString(errCode);
   fprintf (stderr, "OpenGL Error: %s\n", errString);
}

++frameCount;
// -----------  First pass to fill the depth buffer  -------------------
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffers[0]);

std::cout << "FBO bound" << std::endl;
//Enable depth testing
glEnable(GL_DEPTH_TEST);
glDisable(GL_STENCIL_TEST);
glDepthMask( GL_TRUE );
//Disable stencil test, we don't need that for this pass
glClearStencil(0);
glEnable(GL_STENCIL_TEST);

//Disable drawing to the color buffer
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);

//We clear all buffers and reset the modelview matrix
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glLoadIdentity();

//We set our viewpoint
gluLookAt(eyePoint[0],eyePoint[1], eyePoint[2], 0.0,0.0,0.0,0.0,1.0,0.0);
//std::cout << angle << std::endl;
std::cout << "Writing to FBO depth" << std::endl;
//Draw the VBO's, this does not draw anything to the screen, we are just filling the depth buffer
glDrawElements(GL_TRIANGLES, 120, GL_UNSIGNED_SHORT, BUFFER_OFFSET(0));

After this I call a function that calls glReadPixels() The function does not even get called. The loop restarts at the function call.


Apparently I solved this as well: I had to use

glReadPixels( 0, 0,Engine::fWidth, Engine::fHeight, GL_DEPTH_COMPONENT,GL_UNSIGNED_SHORT, pixels);

With GL_UNSIGNED_SHORT instead of GL_FLOAT (or any other format for that matter)

like image 469
Kevin Avatar asked Aug 23 '11 17:08

Kevin


People also ask

How is depth buffer calculated?

z' = (2^d -1 ) * ((far + near)/(2 * (far - near) + (1 / z) * (-far * near) / (far - near) + 1/2) when d is the depth of the z-buffer (24 bits at my case) and z is the z value of the vertex.

Why does the depth buffer need to be reset each frame?

Why? Because otherwise all the new pixels will be compared against the depth values from the previous frame.

How do you Linearize depth?

To linearize the sampled depth-buffer value, we can multiply the native device coordinates (ndc) vector by the inverse projection matrix and divide the result by the w coordinate (as the result is a homogenous vector).

What does GL depth test do?

When depth testing is enabled, OpenGL tests the depth value of a fragment against the content of the depth buffer. OpenGL performs a depth test and if this test passes, the fragment is rendered and the depth buffer is updated with the new depth value. If the depth test fails, the fragment is discarded.


2 Answers

The fastest way of doing this is using asynchronous pixel buffer objects, there's a good explanation here:

http://www.songho.ca/opengl/gl_pbo.html

like image 143
Andreas Brinck Avatar answered Oct 03 '22 02:10

Andreas Brinck


I would render to a FBO and read its depth buffer after the frame has been rendered. PBOs are outdated technology.

like image 30
Razzupaltuff Avatar answered Oct 01 '22 02:10

Razzupaltuff