Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 14 Next capture
2021 2022 2023
2 captures
12 Jun 22 - 14 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
0
Question by EmmetOT · Jan 30, 2020 at 02:14 PM · cameraviewportmatrix4x4viewporttoworldpoint

Code behind 'Camera.ViewportPointToRay'?

I have a situation where I'm dealing with just the matrices behind camera transformations, but not actual cameras. I already have working equivalents for the ViewportToWorld and WorldToViewport methods, as well as a camera projection matrix, a worldToLocal matrix, and a localToWorld matrix.

I'd like to have the functionality of the method 'ViewportPointToRay' in this class also, does anyone know how it might work behind the scenes?

Alternatively, if anyone knows how to find the world coordinates for where the corners of the viewport intersect with a plane at y = 0 using only matrices and not the 'ViewportPointToRay' method, that would also work!

For reference here is a gizmo I'm drawing just to show the frustum where y = 0. I basically want to not have to reference a camera in this method.

 public void OnDrawGizmos()
 {
     Plane plane = new Plane(Vector3.up, Vector3.zero);
 
     Vector3 bottomLeftViewport = new Vector3(0f, 0f, 0f);
     Vector3 topLeftViewport = new Vector3(0f, 1f, 0f);
     Vector3 topRightViewport = new Vector3(1f, 1f, 0f);
     Vector3 bottomRightViewport = new Vector3(1f, 0f, 0f);
 
     Ray ray;
     float dist;
 
     Vector3[] maxZoomFrustum = new Vector3[4];
 
     ray = m_camera.ViewportPointToRay(bottomLeftViewport);
     plane.Raycast(ray, out dist);
     maxZoomFrustum[0] = ray.GetPoint(dist);
 
     ray = m_camera.ViewportPointToRay(topLeftViewport);
     plane.Raycast(ray, out dist);
     maxZoomFrustum[1] = ray.GetPoint(dist);
 
     ray = m_camera.ViewportPointToRay(topRightViewport);
     plane.Raycast(ray, out dist);
     maxZoomFrustum[2] = ray.GetPoint(dist);
 
     ray = m_camera.ViewportPointToRay(bottomRightViewport);
     plane.Raycast(ray, out dist);
     maxZoomFrustum[3] = ray.GetPoint(dist);
 
     Handles.color = Color.green.SetAlpha(0.3f);
     Handles.DrawAAConvexPolygon(maxZoomFrustum);
 }
Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

2 Replies

· Add your reply
  • Sort: 
avatar image
1
Best Answer

Answer by Bunny83 · Jan 31, 2020 at 01:44 PM

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;


I've tested this method with a perspective and an orthographic camera and it works like expected.

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Bunny83 · Jan 31, 2020 at 01:52 PM 1
Share

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.


edit

Here's a more optimised method which calculates a single "unproject matrix":

 private static Matrix4x4 m_View2NDC = Matrix4x4.Translate(-Vector3.one) * Matrix4x4.Scale(Vector3.one * 2);
 public static Matrix4x4 CalculateUnprojectMatrix(Matrix4x4 aProj, Matrix4x4 aWorld2Cam)
 {
     var m = aProj * aWorld2Cam;
     return m.inverse * m_View2NDC;
 }
 public static Ray ViewportPointToRay(Vector3 aP, Matrix4x4 aUnprojectMatrix)
 {
     aP.z = 0;
     var p0 = aUnprojectMatrix.MultiplyPoint(aP);
     aP.z = 1;
     var p1 = aUnprojectMatrix.MultiplyPoint(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.


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.

avatar image
1

Answer by Namey5 · Jan 31, 2020 at 06:18 AM

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.

Comment
Add comment · Show 4 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image EmmetOT · Jan 31, 2020 at 09:52 AM 0
Share

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.

avatar image Bunny83 EmmetOT · Jan 31, 2020 at 12:10 PM 0
Share

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 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; ...).


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.


Second issue is that Unity's ViewportToRay 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.


Properly "unprojecting" is quite tricky, especially just using the matrices like you can see in this question. 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. Note that the projected NDC z value usually goes from -1 to 1. That's actually true for all 3 components (x,y,z).

avatar image Bunny83 Bunny83 · Jan 31, 2020 at 02:02 PM 0
Share

Small correction to my comment. I just had another look and $$anonymous$$ultiplyPoint actually performs a homogeneous divide and assumes an inco$$anonymous$$g w value of 1. So it does the projection correctly. Though it sill would not work with an orthographic camera.

Show more comments

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

185 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Viewport to World Coordinates 2 Answers

ViewportToWorldPoint with multiple cameras 0 Answers

ViewportToWorldPoint relative to camera angle 2 Answers

How to Rotate Viewport? 0 Answers

How to force the game to be stretched to fit the screen? 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges