Last active
May 20, 2024 02:00
-
-
Save SalvatorePreviti/0ec6a73cb14cd33f12350ae27468f2e7 to your computer and use it in GitHub Desktop.
GetFrustumLineIntersection - find the intersection of an infinite line with a view frustum in Unity (improved, with tolerance, stable on corner cases)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <summary> | |
/// Computes the intersection points between a frustum and an infinite line. | |
/// Finds the visible part of a segment in respect of a camera frustum. | |
/// Returns false if the line is not visible at all. | |
/// </summary> | |
/// <example> | |
/// var planes = GeometryUtility.CalculateFrustumPlanes(camera); | |
/// if (GetFrustumLineIntersection(camera, planes, ray, out d1, out d2)) { | |
/// Gizmos.DrawLine(ray.GetPoint(d1), ray.GetPoint(d2)); | |
/// } | |
/// </example> | |
/// <param name="planes">Frustum planes array. Can be generated using GeometryUtility.CalculateFrustumPlanes(camera)</param> | |
/// <param name="ray">The ray to intersect</param> | |
/// <param name="tolerance">The tolerance to use (the cilinder around the ray is checked for validation)</param> | |
/// <param name="d1">Output, minimum distance from the ray origin</param> | |
/// <param name="d1">Output, maximum distance the ray origin</param> | |
/// <returns>True if the segment is visible, false if not.</returns> | |
public static bool GetFrustumLineIntersection (Plane[] frustum, Ray ray, Vector3 tolerance, out float d1, out float d2) | |
{ | |
d1 = 0f; | |
d2 = 0f; | |
float d1Angle = 0f, d2Angle = 0f; | |
bool d1Valid = false, d2Valid = false; | |
for (int i = 0; i < frustum.Length; ++i) { | |
// Find the angle between a frustum plane and the ray. | |
var angle = Mathf.Abs (Vector3.Angle (frustum [i].normal, ray.direction) - 90f); | |
if (angle < 2f) | |
continue; // Ray almost parallel to the plane, skip the plane. | |
if (angle < d1Angle && angle < d2Angle) | |
continue; // The angle is smaller than a previous angle that was better, skip the plane. | |
// Cast a ray onto the plane to find the distance from ray origin where it happens. | |
// Compute also the direction the ray hits the plane, backward or forward (dir) ignoring the ray direction. | |
float d; | |
var dir = frustum [i].Raycast (ray, out d) ^ (frustum [i].GetDistanceToPoint (ray.origin) >= 0); | |
// Update d1 or d2, depending on the direction. | |
if (dir) { | |
d1Angle = angle; | |
if (!d1Valid || d > d1) { // Choose the maximum value | |
d1 = d; | |
d1Valid = true; | |
} | |
} else { | |
d2Angle = angle; | |
if (!d2Valid || d < d2) { // Choose the minimum value | |
d2 = d; | |
d2Valid = true; | |
} | |
} | |
} | |
if (!d1Valid || !d2Valid) | |
return false; // Points are not valid. | |
// Sort points | |
if (d1 > d2) { | |
var t = d1; | |
d1 = d2; | |
d2 = t; | |
} | |
// Check whether points are visible in the frustum. | |
var p1 = ray.GetPoint (d1); | |
var p2 = ray.GetPoint (d2); | |
var bb = new Bounds (); | |
bb.SetMinMax (Vector3.Min (p1, p2) - tolerance, Vector3.Max (p1, p2) + tolerance); | |
return GeometryUtility.TestPlanesAABB (frustum, bb); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment