Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

depth buffer got by glReadPixels is always 1

I'm using glReadPixels to get depth value of select pixel, but i always get 1, how can i solve it? here is the code:

    glEnable(GL_DEPTH_TEST);
    ..
    glReadPixels(x, viewport[3] - y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, z);

Do I miss anything? And my rendering part is shown below. I use different shaders to draw different part of scene, so how should i make it correct to read depth value from buffer?

void onDisplay(void)
{
// Clear the window and the depth buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

// calculate the view matrix.
GLFrame eyeFrame;
eyeFrame.MoveUp(gb_eye_height);
eyeFrame.RotateWorld(gb_eye_theta * 3.1415926 / 180.0, 1.0, 0.0, 0.0);
eyeFrame.RotateWorld(gb_eye_phi * 3.1415926 / 180.0, 0.0, 1.0, 0.0);
eyeFrame.MoveForward(-gb_eye_radius);
eyeFrame.GetCameraMatrix(gb_hit_modelview);
gb_modelViewMatrix.PushMatrix(gb_hit_modelview);

// draw coordinate system
if(gb_bCoord)
{
    DrawCoordinateAxis();
}

if(gb_bTexture)
{

    GLfloat vEyeLight[] = { -100.0f, 100.0f, 150.0f };
    GLfloat vAmbientColor[] = { 0.2f, 0.2f, 0.2f, 1.0f };
    GLfloat vDiffuseColor[] = { 1.0f, 1.0f, 1.0f, 1.0f};

    glUseProgram(normalMapShader);
    glUniform4fv(locAmbient, 1, vAmbientColor);
    glUniform4fv(locDiffuse, 1, vDiffuseColor);
    glUniform3fv(locLight, 1, vEyeLight);
    glUniform1i(locColorMap, 0);
    glUniform1i(locNormalMap, 1);
    gb_treeskl.Display(SetGeneralColor, SetSelectedColor, 0);
}
else
{
    if(!gb_bOnlyVoxel)
    {
        if(gb_bPoints)
        {
            //GLfloat vPointColor[] = { 1.0, 1.0, 0.0, 0.6 };
            GLfloat vPointColor[] = { 0.2, 0.0, 0.0, 0.9 };
            gb_shaderManager.UseStockShader(GLT_SHADER_FLAT, gb_transformPipeline.GetModelViewProjectionMatrix(), vPointColor);
            gb_treeskl.Display(NULL, NULL, 1);
        }
        if(gb_bSkeleton)
        {
            GLfloat vEyeLight[] = { -100.0f, 100.0f, 150.0f };
            glUseProgram(adsPhongShader);
            glUniform3fv(locLight, 1, vEyeLight);
            gb_treeskl.Display(SetGeneralColor, SetSelectedColor, 0);
        }
    }
    if(gb_bVoxel)
    {
        GLfloat vEyeLight[] = { -100.0f, 100.0f, 150.0f };
        glUseProgram(adsPhongShader);
        glUniform3fv(locLight, 1, vEyeLight);
        SetVoxelColor();
        glPolygonMode(GL_FRONT, GL_LINE);
        glLineWidth(1.0f);
        gb_treeskl.DisplayVoxel();
        glPolygonMode(GL_FRONT, GL_FILL);
    }
}
//glUniformMatrix4fv(locMVP, 1, GL_FALSE, gb_transformPipeline.GetModelViewProjectionMatrix());
//glUniformMatrix4fv(locMV, 1, GL_FALSE, gb_transformPipeline.GetModelViewMatrix());
//glUniformMatrix3fv(locNM, 1, GL_FALSE, gb_transformPipeline.GetNormalMatrix());
//gb_sphereBatch.Draw();
gb_modelViewMatrix.PopMatrix();

glutSwapBuffers();

}

like image 435
Philip Zhang Avatar asked May 27 '13 07:05

Philip Zhang


2 Answers

I think you are reading correctly the only problem is that you are not linearize the depth from buffer back to <znear...zfar> range hence the ~1 value for whole screen due to logarithmic dependence of depth (almost all the values are very close to 1).

I am doing this like this:

double glReadDepth(double x,double y,double *per=NULL)                  // x,y [pixels], per[16]
    {
    GLfloat _z=0.0; double m[16],z,zFar,zNear;
    if (per==NULL){ per=m; glGetDoublev(GL_PROJECTION_MATRIX,per); }    // use actual perspective matrix if not passed
    zFar =0.5*per[14]*(1.0-((per[10]-1.0)/(per[10]+1.0)));              // compute zFar from perspective matrix
    zNear=zFar*(per[10]+1.0)/(per[10]-1.0);                             // compute zNear from perspective matrix
    glReadPixels(x,y,1,1,GL_DEPTH_COMPONENT,GL_FLOAT,&_z);              // read depth value
    z=_z;                                                               // logarithmic
    z=(2.0*z)-1.0;                                                      // logarithmic NDC
    z=(2.0*zNear*zFar)/(zFar+zNear-(z*(zFar-zNear)));                   // linear <zNear,zFar>
    return -z;
    }

Do not forget that x,y is in pixels and (0,0) is bottom left corner !!! The returned depth is in range <zNear,zFar>. The function is assuming you are using perspective transform like this:

void glPerspective(double fovy,double aspect,double zNear,double zFar)
    {
    double per[16],f;
    for (int i=0;i<16;i++) per[i]=0.0;
    // original gluProjection
//  f=divide(1.0,tan(0.5*fovy*deg))
//  per[ 0]=f/aspect;
//  per[ 5]=f;
    // corrected gluProjection
    f=divide(1.0,tan(0.5*fovy*deg*aspect));
    per[ 0]=f;
    per[ 5]=f*aspect;
    // z range
    per[10]=divide(zFar+zNear,zNear-zFar);
    per[11]=-1.0;
    per[14]=divide(2.0*zFar*zNear,zNear-zFar);
    glLoadMatrixd(per);
    }

Beware the depth accuracy will be good only for close to camera object without linear depth buffer. For more info see:

  • How to correctly linearize depth in OpenGL ES in iOS?

If the problem persist there might be also another reason for this. Do you have Depth buffer in your pixel format? In windows You can check like this:

  • Getting a window's pixel format

Missing depth buffer could explain why the value is always 1 (not like ~0.997). In such case you need to change the init of your window enabling some bits for depth buffer (16/24/32). See:

  • What is the proper OpenGL initialisation on Intel HD 3000?

For more detailed info about using this technique (with C++ example) see:

  • OpenGL 3D-raypicking with high poly meshes
like image 71
Spektre Avatar answered Sep 30 '22 15:09

Spektre


Well, you missed to past the really relevent parts of the code. Also the status of the depth testing unit has no influence on what glReadPixels delivers. How about you post your rendering code as well.

Update

After a buffer swap SwapBuffers the contents of the back buffer are undefined and the default state for frame buffer reads is to read from the back buffer. Technically double buffering happens on only the color component, not the depth and stencil component. But you might run into a driver issue with that.

I suggest two tests to rule out those:

  • Do a read of the depth buffer with glReadBuffer(GL_BACK); right before the SwapBuffers.

  • Select the front buffer with glReadBuffer(GL_FRONT); for reading after SwapBuffers

Also please specify in which context (program, not OpenGL, well the later, too) you did your glReadPixels when this problem occours. Also check if you can read color value correctly.

like image 29
datenwolf Avatar answered Sep 30 '22 14:09

datenwolf