OpenGL:glReadPixels 对于非投影不准确

OpenGL: glReadPixels inaccurate for unprojection

我有自己的 unproject 函数来执行屏幕点的反向投影。代码如下(用OpenTK编写):

public static Vector3 UnProject(Point screenLocation, float depth)
{
    int[] viewport = GetViewport();
    Vector4 pos = new Vector4();

    // Map x and y from window coordinates, map to range -1 to 1 
    pos.X = (screenLocation.X - viewport[0]) / (float)viewport[2] * 2.0f - 1.0f;
    pos.Y = 1 - (screenLocation.Y - viewport[1]) / (float)viewport[3] * 2.0f;
    pos.Z = depth * 2.0f - 1.0f;
    pos.W = 1.0f;

    Vector4 pos2 = Vector4.Transform(pos, Matrix4.Invert(GetModelViewMatrix() * GetProjectionMatrix()));
    Vector3 pos_out = new Vector3(pos2.X, pos2.Y, pos2.Z);

    return pos_out / pos2.W;
}

基本上,您会为我的函数提供所需的非投影深度,它会为您提供相应的屏幕点世界坐标。假设这可以正常工作(我 99% 确定它确实如此),我在将屏幕点转换为世界坐标时遇到了问题。这种非投影适用于拾取:我会调用我的 unproject 函数两次(一次深度 = 0,另一次深度 = 1)以将屏幕点转换为射线。我执行 ray/triangle 相交以确定哪个对象与射线相交,并基于此执行拾取(非常准确)。

另外一个操作(暂且称之为操作X),我只需要知道屏幕点的世界坐标(假设鼠标光标在屏幕上的一个对象上)。为此,我使用 glReadPixel 函数获取光标下的深度。问题是我感觉读取深度缓冲区得到的Z值有点偏差。如果我计算与光线投射的交集,我会得到准确的结果,但这对于操作 X 是不可行的,因为每次触发 MouseMoved 时都需要执行操作 X。

为了证明准确性不足,这是我获得的两个数字:
glReadPixel + Unprojection 产生 (0.886105343709181, 0.12422376198582, 0.998496665566841) 作为光标下的世界坐标。
光线投射 + 相交产生 (0.885407337013061, 0.124174778008613, 1) 作为光标下的世界坐标。

Z 值中的这个 0.0015 误差对于操作 X 来说太大了(因为它对小数字非常敏感)。 glReadPixels 有什么问题我应该知道吗?发生这种情况是因为 glReadPixels 只能读取 float 值吗?

我不认为 glReadPixels 是罪魁祸首。我认为 Z 缓冲区精度是问题所在。默认情况下,您通常有一个 24 位定点深度缓冲区。如果您使用 32 位浮点深度缓冲区可能会有所帮助,但您可能需要一个 FBO。