简体   繁体   中英

OpenGL: glReadPixels inaccurate for unprojection

I have my own unproject function for performing reverse projection of a screen point. The code is as follows (written in 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;
}

Basically, you'd provide the desired unprojection depth to my function, and it will give you the corresponding world coordinate of the screen point. Assuming that this works correctly (which I am 99% sure think it does), I'm having problems converting screen points to world coordinates. This unprojection works fine for picking: I'd call my unproject function twice (once with depth = 0 and another time with depth = 1) to convert the screen point to ray. I perform ray/triangle intersection to determine which object intersects with the ray and based on that I perform picking (which works very accurately).

For another operation (let's call it operation X), I only need to know the world coordinate of the screen point (assuming that the mouse cursor is over an object on the screen). For that, I am obtaining the depth under the cursor by using the glReadPixel function. The problem is that I feel the Z value obtained by reading the depth buffer is a little bit off. If I calculate the intersection with ray casting, I get accurate results, but that is not viable for operation X as operation X needs to be performed every time MouseMoved is triggered.

To demonstrate the lack of accuracy, here are the two numbers I obtained:
glReadPixel + Unprojection yields (0.886105343709181, 0.12422376198582, 0.998496665566841) as the world coordinate under the cursor.
Ray casting + intersection yields (0.885407337013061, 0.124174778008613, 1) as the world coordinate under the cursor.

This 0.0015 error in the Z value is too much for operation X (as it is very sensitive to small numbers). Is there something wrong with glReadPixels that I should know about? Is this happening because glReadPixels is only capable of reading float values?

I don't think that glReadPixels is to blame here. I think that the Z buffer precision is the issue. By default, you typically have a 24 bit fixed-point depth buffer. Maybe it helps if you use a 32 bit floating point depth buffer, but you probably need an FBO for that.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM