Comments and answers for "Code behind 'Camera.ViewportPointToRay'?"
http://answers.unity.com/questions/1695275/code-behind-cameraviewportpointtoray.html
The latest comments and answers for the question "Code behind 'Camera.ViewportPointToRay'?"Comment by Namey5 on Namey5's comment
http://answers.unity.com/comments/1695781/view.html
Indeed. The assumption here was that we would be using a perspective projection matrix, although I did note that you may want to find the origin on the near plane rather than using the camera's origin as a small optimisation. I do have to ask though, the second assumption I made was that Unity uses OpenGL convention for its matrix representations in engine. Because all of this only occurs on the CPU, surely we can assume that the matrices will be fixed rather than converting to the API equivalents and having to manually account for the differences?Sat, 01 Feb 2020 02:27:53 GMTNamey5Comment by Bunny83 on Bunny83's comment
http://answers.unity.com/comments/1695595/view.html
Small correction to my comment. I just had another look and [$$anonymous$$ultiplyPoint actually performs a homogeneous divide and assumes an incoming w value of 1][1]. So it does the projection correctly. Though it sill would not work with an orthographic camera.
[1]: https://github.com/Unity-Technologies/UnityCsReference/blob/master/Runtime/Export/$$anonymous$$ath/$$anonymous$$atrix4x4.cs#L284Fri, 31 Jan 2020 14:02:56 GMTBunny83Comment by Bunny83 on Bunny83's answer
http://answers.unity.com/comments/1695593/view.html
Please note that this method is only a proof of concept and if you want to use it as it is it shouldn't be used more than a couple of times pre frame. If you need to do this conversion several times you really should cache the "mInv" matrix since combining and inverting a matrix isn't that cheap. Though that's one of the main point why we use matrices in the first place. You set them up once and use them on a large amount of data / points.
<br>
***edit***
Here's a more optimised method which calculates a single "unproject matrix":
private static $$anonymous$$atrix4x4 m_View2NDC = $$anonymous$$atrix4x4.Translate(-Vector3.one) * $$anonymous$$atrix4x4.Scale(Vector3.one * 2);
public static $$anonymous$$atrix4x4 CalculateUnproject$$anonymous$$atrix($$anonymous$$atrix4x4 aProj, $$anonymous$$atrix4x4 aWorld2Cam)
{
var m = aProj * aWorld2Cam;
return m.inverse * m_View2NDC;
}
public static Ray ViewportPointToRay(Vector3 aP, $$anonymous$$atrix4x4 aUnproject$$anonymous$$atrix)
{
aP.z = 0;
var p0 = aUnproject$$anonymous$$atrix.$$anonymous$$ultiplyPoint(aP);
aP.z = 1;
var p1 = aUnproject$$anonymous$$atrix.$$anonymous$$ultiplyPoint(aP);
return new Ray(p0, (p1-p0).normalized);
}
I just cached the view to NDC conversion since it's the same all the time. So when constructing the unproject matrix we only need to perform 2 matrix multiplication and one inverse. Once we have the unproject matrix for the current camera view we can use it as often as we want to translate a viewport point (coordinate range 0 - 1 for all axis) to worldspace point.
<br>
All the ViewportPointToRay method does now is calculating the world point for the given viewport point on the near and far clipping plane and construct the ray for those points.Fri, 31 Jan 2020 13:52:50 GMTBunny83Answer by Bunny83
http://answers.unity.com/answers/1695591/view.html
I've just tested this one and it works quite well. The numerical error compared to Unity's own ViewportPointToRay method is at the 5th decimal place. Drawing the rays 100 world units into the scene you can barely see the error. Unity might use some different internal order. However the results are pretty spot on.
public static Ray ViewportPointToRay(Vector2 aP, Matrix4x4 aProj, Matrix4x4 aCam)
{
var m = aProj * aCam;
var mInv = m.inverse;
// near clipping plane point
Vector4 p = new Vector4(aP.x*2-1, aP.y*2-1, -1, 1f);
var p0 = mInv * p;
p0 /= p0.w;
// far clipping plane point
p.z = 1;
var p1 = mInv * p;
p1 /= p1.w;
return new Ray(p0, (p1-p0).normalized);
}
Note that aProj need to be the camera's projection matrix and aCam need to be the camera's worldToCameraMatrix. Also note that worldToCameraMatrix is just the camera's worldToLocal matrix but with the z axis inverted like this:
var w2c = Matrix4x4.Scale(new Vector3(1, 1, -1)) * cam.transform.worldToLocalMatrix;
<br>
I've tested this method with a perspective and an orthographic camera and it works like expected.Fri, 31 Jan 2020 13:44:12 GMTBunny83Comment by Bunny83 on Bunny83's comment
http://answers.unity.com/comments/1695567/view.html
CameraToWorld is essentially just the localToWorld matrix of the camera transform. However depending on the platform and API (OpenGL, DirectX, ...) certain things might be inverted. Likewise Unity has the method [GL.GetGPUProjection$$anonymous$$atrix][1] to get the right projection matrix that is used / required on the actual GPU / shader. There also might be tiny differences (z inverted so right handed / left handed; or maybe y inverted; sometimes the depth buffer value might be inverted; ...).
<br>
Note that there a few issues with this approach. . First of all this probably won't work with perspective projection since the projection is achieved through the w component of the homogeneous coordinates and the additional normalization(homogeneous divide). So just using $$anonymous$$ultiplyPoint with a Vector3 position won't work. It probably should work for orthographic cameras.
<br>
Second issue is that Unity's [ViewportToRay][2] method places the ray origin at the near clipping plane and not just the camera origin. It's just a tiny difference but might be important. For example using just using `cam.transform.position` as origin won't work for orthographic cameras.
<br>
Properly "unprojecting" is quite tricky, especially just using the matrices like you can see [in this question][3]. Usually the projection matrix is defined for a right handed system with z being inverted. In that case the projection matrix looks like the one mentioned in [this question][4]. Note that the projected NDC z value usually goes from -1 to 1. That's actually true for all 3 components (x,y,z).
[1]: https://docs.unity3d.com/ScriptReference/GL.GetGPUProjection$$anonymous$$atrix.html
[2]: https://docs.unity3d.com/ScriptReference/Camera.ViewportPointToRay.html
[3]: https://answers.unity.com/questions/1039589/how-can-i-make-a-unproject-method-in-unity.html
[4]: https://answers.unity.com/questions/1359718/what-do-the-values-in-the-matrix4x4-for-cameraproj.htmlFri, 31 Jan 2020 12:10:39 GMTBunny83Comment by EmmetOT on EmmetOT's answer
http://answers.unity.com/comments/1695537/view.html
Great! This is exactly what I'm looking for. I just need to figure out how to get the 'cameraToWorld$$anonymous$$atrix' and I'm golden.Fri, 31 Jan 2020 09:52:23 GMTEmmetOTAnswer by Namey5
http://answers.unity.com/answers/1695491/view.html
ViewportToRay would work very similar to ViewportToWorld, but with the added step of finding the direction of said point from the camera;
Ray ViewportPointToRay (Vector3 pos, Camera cam)
{
//Remap to NDC-space [-1,1]
pos = pos * 2.0f - Vector3.one;
pos.z = 1f;
//Find the world-space position of the point at the camera's far plane
Vector3 worldPos = cam.cameraToWorldMatrix.MultiplyPoint (cam.projectionMatrix.inverse.MultiplyPoint (pos));
//The ray's origin is just the camera's position. Alternatively, you could use the same
//matrix logic above and find the same point at the camera's near plane and use that instead
Vector3 origin = cam.transform.position;
return new Ray (origin, worldPos - origin);
}
The Unity documentation for the internal function makes a very interesting point; the z-position is ignored. In this case, because distance is irrelevant in relation to a ray, we just assume the viewport point lies on the camera's far plane.Fri, 31 Jan 2020 06:18:53 GMTNamey5