- Home /
Camera position in Viewport coordinates
According to the documentation the range of viewport coordinates are:
"Viewport space is normalized and relative to the camera. The bottom-left of the camera is (0,0); the top-right is (1,1). The z position is in world units from the camera."
However, when I perform the following operation, the result is always Vector3.zero (0,0,0).
Camera.current.WorldToViewportPoint(Camera.current.transform.position);
While this would make sense to me if the viewport range were [-1,+1], I don't see how this works with a range of [0,+1]. The (0,0,0) vector indicates that the camera is at the bottom left of the viewport. Why isn't the camera's viewspace coordinate (0.5f,0.5f,0.0f), right in the MIDDLE of it's viewport?
I suspect I'm missing some critical piece of information about viewport space, but my online research has not helped me find it.
Additional info: I discovered this because I was trying to determine which of two viewspace coordinates (with the same z coord) are closer to the CENTER of the viewspace output. Should I compare their distances to (0,0,z) or (0.5,0.5,z)?
Answer by Glurth · Jul 15, 2016 at 11:25 PM
Sir Owen, you got my thinking back on the right track, here is what's going on.
Consider the usual viewing frustum. As show in this world-space image, we have a NEAR PLANE. This near plane distance is in-fact the Z coordinate of the "Screen" in veiwport space. The plane, at this z coordinate, has a rectangular shape, with some non-zero distance between the clipping planes.
In this world-space image, we can see what it looks like if we ignore the near-plane: All our clipping planes(top,bottom,left,right) meet at a single point, the camera's position, a.k.a our viewport space z=0. The effectively renders any x,y coordinate at z=0, in view-space, meaningless: Since there is zero "distance" between the left and right clipping planes at z=0, an x value that represents a fraction of how far between them, is also, always zero.
Ah, yes. The near clipping plane is just an example, right? But, yes, step one is to "slide" that rectangle along the frustum until your point is on it. If that rectangle collapses to a point, the math makes no sense. All of the points on the same plane as the camera have that same problem.
Looking at it another way, you get a division by 0 error at the camera.
And, haven't tested, but of course this problem doesn't exist with an ortho-camera.
Just an example? Well, yes and no: it's an example as far as explaining why it's not at z=0, and why the center is (x,y,z)= (0.5,0.5,nearPlaneDist). But it's also an important number. You mentioned in your post about adding a small amount to the camera's z coordinate- well how small, what should that small number be? (float.epsilon is TOO small, for example). I used the near clipping plane distance for that number. This way, any computations (that are more complex than the OP "closer to middle") are done on the plane the screen lies on.
Hmm, Division by zero: I thought this would yield undefined numbers (float.isNaN), not zero's. Perhaps it's actually $$anonymous$$ULTIPLYING (x,y) by the viewport z-coord? Or is some special checking converting the undefined numbers to zeros?
Well, again, the important thing is, as you wrote, that there is no valid answer on (or on a plane with) the camera. Exactly on the camera is the center, and left side, and right side of the size-0 rect where the frustum lines cross.
But the near viewing plane really doesn't matter. The math looks at a plane where ever the point you're checking happens to be. For fun, test by moving the near-viewing plane around.
It is dividing by zero. The math is to divide your x by the edge x (at the frustrum line for where-ever you are.) Results that make no sense, like viewport coords exactly on the camera, are usually a hint there's a division by zero somewhere. If you get a nice zero result, that usually means the value makes sense, and really is 0
In a computer, dividing by zero just happens to report NaN or infinity or whatever, but, yes, math gals know that's sometimes just a special case. For example, atan2 (if x is 0, you know not to divide by it. The answer is really +/-90 degrees.) In other words, giving the funny 000 result is just a "built-in functions shouldn't ever crash" value.
Hehe, back when I was in school, divide by zero operations actually generated an interrupt on the hardware! Ah, I see what you mean: Rather than a gradual change from the center of (0.5,0.5,z) to (0,0,0), it's is a sudden jump for z=zero and ONLY z=zero. That sudden jump is the big divide by zero clue. "For fun, test by moving the near-viewing plane around." based on my playing- it feels like the near plane is a necessary evil. I don't WANT to clip ANYTHING in front of the camera, but the "screen" needs to be SO$$anonymous$$E distance in front of the camera for the projection math to work. I'm having trouble wrapping my head around where objects with z greater than 0 but less than the near clip plane dist. They are in front of the camera, but behind the "screen": odd.
The near-clipping-plane is a standard 3D hack to make the depth buffer work -- reduce the amount of z-fighting.
Answer by Owen-Reynolds · Jul 14, 2016 at 05:11 PM
Looks like a very small glitch in Unity's worldToViewPoint. It doesn't work when you give it the exact world position of the camera. Moving it a tiny amount fixes it:
Transform T=Camera.current.transform;
Vector3 pp=T.position; // exact camera pos. gives incorrect 00
// pp += T.forward*0.01f; // <- fixes it
// pp -= T.forward*0.01; // <- also fixes it
Debug.Log( Camera.current.WorldToViewportPoint(pp) );
With pp as the exact camera coords, you get 000. But moving it a tiny bit forward or backward is fine.
I'm wondering if technically the exact camera pos doesn't have a viewport point -- but I think you're right. It's (0.5, 0.5, 0) by definition.
Thanks Owen, was starting to question my sanity, when I got this result for a "sanity check". Interesting resolution! But I think I just hard code (0.5f,0.5f,0) for the camera viewspace position.