Sign in to follow this  
swordfish

gluUnProject() Question

Recommended Posts

(Note: I am using my own equivalent of the glu library for portability reasons; the function is pretty much exactly the same) I am confused what the purpose of the "WinZ" parameter to gluUnProject(WinXYZ,...,ObjXYZ&,...) is? As far as I know, the WinZ is supposed to be the distance I want the 'unprojected' world-space point (ObjXYZ) to be from the camera near plane, in "camera space" [is that the right terminology?] (0.0 = near, 1.0 = far). With the camera facing +Z from Z=0, with a frustum near-far of 0.1-5000.0, I pass a WinXYZ with WinZ as 1.0. The returned ObjXYZ point is around the far-plane at Z=~5000.0 (the result seems to be slightly inaccurate - I'll assume that's normal behavior for now). When I do WinZ=0.0, the returned ObjXYZ point is on the near-plane at Z=~0.1. Everything seems normal so far. But when I pass a WinXYZ with WinZ=0.5, the returned ObjXYZ remains on the near-plane at Z=~0.1, instead of what I wanted: Z=~2500.0. Anything other than WinZ=1.0 results in the ObjXYZ remaining on the near-plane. Q) Is this supposed to happen? Q) Is the slight inaccuracy in gluUnProject() normal? Thanks. My gluUnProject() equivalent, just in case there's an error I may have overlooked: The difference is that the inverse composed projection and modelview matrices are precomputed outside of the function
bool UnProjectVector(Vector3f& rkVec, Matrix4f& rkInvComposedMatrix, float afViewport[4])
{

    Vector4f kIn;
    Vector4f kOut;
    
    kIn[0] = rkVec[0];
    kIn[1] = rkVec[1];
    kIn[2] = rkVec[2];
    kIn[3] = 1.0f;
    
    kIn[0] = (kIn[0] - afViewport[0]) / afViewport[2];
    kIn[1] = (kIn[1] - afViewport[1]) / afViewport[3];
    
    kIn[0] = kIn[0]*2.0f-1.0f;
    kIn[1] = kIn[1]*2.0f-1.0f;
    kIn[2] = kIn[2]*2.0f-1.0f;
    
    kOut = rkInvComposedMatrix*kIn;
    if(kOut[3]==0.0)
	return false;
    
    kOut[0] /= kOut[3];
    kOut[1] /= kOut[3];
    kOut[2] /= kOut[3];
    
    rkVec = Vector3f(kOut[0],kOut[1],kOut[2]);
    return true;
}




Share this post


Link to post
Share on other sites
The reason is because the Z-axis transformation is non-linear, and your near plane value is extremely bad. A good near-to-far value ratio range is about 0.001-0.002, while your ratio is .00002! If you have a graphing calculator (or any graphing software), graph the function (-FN/(F-N))(1/x) + (F/(F-N)), which is the projection transformation for the Z-axis, with N=0.1 and F=5000. Choose your viewing window to be x=[N,F] and y=[0,1]. You'll see that the graph is almost vertical around the near plane, and about 90% of your precision is between 0.1 and 1. So when you do the reverse transformation, WinZ is the y value in this graph, and you're determining the x value. Again, you can see that almost the entire y range between 0 and 1 is spanned within the first couple of units of x.

Now let N=10 and F=10000 (N/F = 0.001). You'll notice that the graph rises much slower and that there's a smooth rounded curve more reminiscent of the inverse-linear graph 1/x we're used to. As far as testing your function, an N/F of about 0.5 will give you a very good mapping. While you'd never actually use that ratio in a real application, it gives you something almost linear that's easy to test.

Share this post


Link to post
Share on other sites
@Zipster: are you saying the the near/far ratio will adversely affect the outcome of the ObjXYZ?

I changed my near/far to 1.0 and 1000.0f, and I just noticed something new. If I pass WinZ as 1.0, I get the ObjXYZ on the far plane. If I pass WinZ as 0.0, I get the ObjXYZ on the near plane. If I pass WinZ as 0.5, I don't get an ObjXYZ half-way between the near and far planes, as I'd expect it to. Instead, as WinZ goes from 0.5 to 1.0, the ObjXYZ depth increase exponentially. A WinZ of 0.999 seems to return what I'd expect a WinZ of 0.5 to return ("half-way"). Am I missing something?

[Edited by - swordfish on October 21, 2006 1:50:59 PM]

Share this post


Link to post
Share on other sites
It's not increasing exponentially, it's increasingly inverse-linearly, according to the equation I posted before. It's not an "adverse" effect, it's just the way the transformation works. I definitely recommend taking a look at the graph of that function. It's guaranteed to answer all your questions [smile]

Share this post


Link to post
Share on other sites
Okay, I see what you mean by 'inverse linear'. I was on the right track at first, but I forgot to invert my Y screen coord, which caused a domino effect of confusion.

So in conclusion, WinZ is 1.0/Near. And don't forget to invert your Y-coord (1-WinY).

[EDIT]
Wrong once again. I did some more research (sorry, I'm not good with graphing) and found this: z = (1/Zn - 1/Z) / (1/Zn - 1/Zf)

I can see where many people would get confused here.
There is a difference between "Screen Space Z" and "World Space Z".

WinZ = (1.0f/fNear-1.0f/fDistance)/(1.0f/fNear-1.0f/fFar)
Where fNear/fFar are the near/far plane distances, and fDistance is the distance you want the unprojected point to be from the camera.

[Edited by - swordfish on October 26, 2006 12:23:25 PM]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this