Ray ScreenPointToRay(Vector2 viewPortPos) { Rect viewport = new Rect(0, 0, Application.width, Application.height); Ray ray = new Ray(); Vector3 o; Matrix4x4 clipToWorld; GetClipToWorldMatrix(out clipToWorld); Matrix4x4 camToWorld = GetCameraToWorldMatrix(); if (!CameraUnProject(new Vector3(viewPortPos.x, viewPortPos.y, m_NearClip), camToWorld, clipToWorld, viewport, out o)) { return(new Ray(transform.position, new Vector3(0, 0, 1))); } ray.origin = o; if (m_Orthographic) { // In orthographic projection we get better precision by circumventing the whole projection and subtraction. ray.direction = Vector3.Normalize(-camToWorld.GetAxisZ()); } else { // We need to sample a point much further out than the near clip plane to ensure decimals in the ray direction // don't get lost when subtracting the ray origin position. if (!CameraUnProject(new Vector3(viewPortPos.x, viewPortPos.y, m_NearClip + 1000), camToWorld, clipToWorld, viewport, out o)) { return(new Ray(transform.position, new Vector3(0, 0, 1))); } Vector3 dir = o - ray.origin; ray.direction = (Vector3.Normalize(dir)); } return(ray); }
bool CameraUnProject(Vector3 p, Matrix4x4 cameraToWorld, Matrix4x4 clipToWorld, Rect viewport, out Vector3 outP) { // pixels to -1..1 Vector3 in_v; in_v.x = (p.x - viewport.x) * 2.0f / viewport.width - 1.0f; in_v.y = (p.y - viewport.y) * 2.0f / viewport.height - 1.0f; // It does not matter where the point we unproject lies in depth; so we choose 0.95, which // is further than near plane and closer than far plane, for precision reasons. // In a perspective camera setup (near=0.1, far=1000), a point at 0.95 projected depth is about // 5 units from the camera. in_v.z = 0.95f; Vector3 pointOnPlane; if (clipToWorld.PerspectiveMultiplyPoint3(in_v, out pointOnPlane)) { // Now we have a point on the plane perpendicular to the viewing direction. We need to return the one that is on the line // towards this point, and at p.z distance along camera's viewing axis. Vector3 cameraPos = cameraToWorld.GetPosition(); Vector3 dir = pointOnPlane - cameraPos; // The camera/projection matrices follow OpenGL convention: positive Z is towards the viewer. // So negate it to get into Unity convention. Vector3 forward = -cameraToWorld.GetAxisZ(); float distToPlane = Vector3.Dot(dir, forward); if (Mathf.Abs(distToPlane) >= 1.0e-6f) { bool isPerspective = clipToWorld.IsPerspective(); if (isPerspective) { dir *= p.z / distToPlane; outP = cameraPos + dir; } else { outP = pointOnPlane - forward * (distToPlane - p.z); } return(true); } } outP = new Vector3(0.0f, 0.0f, 0.0f); return(false); }
bool CameraProject(Vector3 p, Matrix4x4 cameraToWorld, Matrix4x4 worldToClip, Rect viewport, out Vector3 outP) { Vector3 clipPoint; outP = new Vector3(0, 0, 0); if (worldToClip.PerspectiveMultiplyPoint3(p, out clipPoint)) { Vector3 cameraPos = cameraToWorld.GetPosition(); Vector3 dir = p - cameraPos; // The camera/projection matrices follow OpenGL convention: positive Z is towards the viewer. // So negate it to get into Unity convention. Vector3 forward = -cameraToWorld.GetAxisZ(); float dist = Vector3.Dot(dir, forward); outP.x = viewport.x + (1.0f + clipPoint.x) * viewport.width * 0.5f; outP.y = viewport.y + (1.0f + clipPoint.y) * viewport.height * 0.5f; //outP.z = (1.0f + clipPoint.z) * 0.5f; outP.z = dist; return(true); } outP.Set(0.0f, 0.0f, 0.0f); return(false); }