Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
869 views
in Technique[技术] by (71.8m points)

opengl - 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();

}

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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:

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:

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:

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


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...