glReadPixels获得的深度缓冲区始终为1

时间:2013-05-27 07:14:22

标签: opengl opengl-3

我正在使用glReadPixels来获取选择像素的深度值,但我总是得到1,我该如何解决?这是代码:

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

我错过了什么吗?我的渲染部分如下所示。我使用不同的着色器来绘制场景的不同部分,那么我应该如何正确地从缓冲区读取深度值呢?

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

}

2 个答案:

答案 0 :(得分:3)

我认为您没看错,唯一的问题是您没有将缓冲区的深度线性化回到<znear...zfar>范围,因此由于深度的对数依赖性,整个屏幕的~1值(几乎所有该值非常接近1)。

我正在这样做:

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;
    }

不要忘记x,y以像素为单位,(0,0)是左下角!返回的深度处于{{ 1}}。该函数假定您正在使用像这样的透视变换:

<zNear,zFar>

请注意,只有在没有线性深度缓冲区的情况下,接近相机对象的深度精度才是好的。有关更多信息,请参见:

如果问题仍然存在,则可能还有其他原因。您是否有像素格式的深度缓冲区?在Windows中,您可以像这样检查:

缺少深度缓冲区可以解释为什么值始终为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); } (不像1)。在这种情况下,您需要更改窗口的初始化,为深度缓冲(~0.997)启用一些位。参见:

有关使用此技术的更多详细信息(以C ++示例为例),请参见:

答案 1 :(得分:2)

好吧,你错过了代码的真正相关部分。此外,深度测试单元的状态对glReadPixels提供的内容没有影响。你也可以发布你的渲染代码。

更新

在缓冲区交换SwapBuffers之后,后台缓冲区的内容未定义,帧缓冲区读取的默认状态是从后台缓冲区读取。 技术双缓冲仅在颜色组件上发生,而不在深度和模板组件上发生。但是你可能会遇到驱动程序问题。

我建议用两个测试来排除这些:

  • 在 SwapBuffers之前,使用glReadBuffer(GL_BACK);读取深度缓冲区。

  • 在SwapBuffers

  • 之后选择带glReadBuffer(GL_FRONT);的前缓冲区进行读取

另外,请在出现此问题时指定您在glReadPixels中使用的上下文(程序,而不是OpenGL,以及后者)。另外,检查是否可以正确读取颜色值。