首页 > 解决方案 > 为什么在opengl中将gl_FragCoord.z与深度缓冲区一偏离?

问题描述

我一直在尝试寻找一种方法来读取特定鼠标坐标(x,y)的深度值。使用 opengl 4.x 在 win10 上一切正常,但不适用于 opengl es 3.x

我的方法:

  1. glReadPixels() 不适用于深度缓冲区的 openGL es
  2. 光线投射不适合,因为我使用的是大型地形模型
  3. 随后的方法就足够了,但不幸的是太不准确了,win10也是如此,但为什么呢?

    #version 420
    uniform vec2 screenXy;
    uniform vec2 screenSize;
    
    out vec4 fragColor;
    
    void main(void) {
    
    if((int(gl_FragCoord.x) == int(screenXy.x)) && ((int(screenSize.y) - int(gl_FragCoord.y)) == int(screenXy.y))) {
        fragColor.r = gl_FragCoord.z;
        } else {
            fragColor = vec4(1, 1, 1, 1.0);
        }
    }
    

我将鼠标 xy 坐标提交给片段着色器(screenXy)。如果单击的像素在行中,我将深度值写入颜色缓冲区。这可行,但值 gl_FragCoord.z 和深度缓冲区中的值并不完全相同(我知道深度缓冲区中的值是正确的)。虽然 gl_FragCoord.z 和深度缓冲区的值是浮动的,所以我认为是 32bit。

GLfloat zd; // from depth buffer
GLfloat zc[4]; // from color buffer
m_func->glReadPixels(xy.x(), m_pFbo->height() - xy.y(), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &zd);
m_func->glReadPixels(xy.x(), m_pFbo->height() - xy.y(), 1, 1, GL_RGBA, GL_FLOAT, zc);

原因:

  1. 偏差通过内部类型转换发生,但在哪里?
  2. 因为 GL_DEPTH_TEST 是在 fragmentshader gl_FragCoord.z 不是最近的(离相机)之后执行的,而是保存在深度缓冲区中。因此,将 gl_FragCoord.z 保存在单独的 Frambuffer 中也是没有意义的,因为它不是正确的值。

也许有人可以帮我解决这个问题,因为我找不到任何其他解释?

这里有一些测量值:

zc  0.984314
zd  0.985363

zc  0.552941
zd  0.554653

zc  1 -> extremly critical
zd  0.999181

标签: c++qtopengl-esglslopengl-es-3.0

解决方案


由于 0.984314 * 255.0 正好是 251.0,我假设颜色平面的内部格式是GL_RGBA8. 这意味着每个颜色通道有 1 个字节,并且zc只能有 256 个不同的值,从 0.0 到 1.0,步长为 1/256。

如果您使用的 OpenGL ES 版本支持它,那么您可以更改渲染缓冲区存储的格式(例如GL_R32F- 只有红色通道,但 32 位浮点)。

或者您可以将深度编码为 4 * 8 位颜色平面的 4 个通道:

vec4 PackDepth( in float depth )
{
    depth *= (256.0*256.0*256.0 - 1.0) / (256.0*256.0*256.0);
    vec4 encode = fract( depth * vec4(1.0, 256.0, 256.0*256.0, 256.0*256.0*256.0) );
    return vec4( encode.xyz - encode.yzw / 256.0, encode.w ) + 1.0/512.0;
}

....

fragColor = PackDepth(gl_FragCoord.z);

您可以在读取值后对其进行解码:

GLfloat zc[4]; // from color buffer
m_func->glReadPixels(xy.x(), m_pFbo->height() - xy.y(), 1, 1, GL_RGBA, GL_FLOAT, zc);

float depth = zc[0] + zc[1]/256.0 + zc[2]/(256.0*256.0) + zc[3]/(256.0*256.0*256.0);
depth = depth * (255.0f/256.0f) * (256.0*256.0*256.0) / (256.0*256.0*256.0 - 1.0);

推荐阅读