射线与圆、球相交检测
本篇讨论2D中射线和圆的相交检测,本方法同样适用于3D中射线和球的相交检测,这是因为可以在包含射线和球心的平面中进行检测,从而将3D问题转化为2D问题。如果射线穿过球心,那么平面不是唯一的,但是不影响问题的解。
下面直接降阶为 2D讨论射线与圆的相交检测。
射线与圆的三种关系:相交、相切、相离
其中相切是相交的特殊情况
看下图,
如何判断射线与圆是否相交?
如果相交,如何计算交点坐标?
下图中是射线与圆相交的一般情形。交点为E,其实相交时交点是两个,行切时交点是一个,我们只讨论第一个,感兴趣的同学可以自行求解两个不同交点坐标
上图中加了辅助线和字母标记,帮助我们清晰理解求解过程
Vector3 source 射线起点坐标
Vector3 rayDirection 射线方向
Vector3 sphereCenter 球中心点坐标
float sphereRadius 球半径
P 射线起点坐标
O 球心坐标
E 射线与球的交点
A 球心到射线画垂线的交点
e = PO长度:射线与球中心连线长度
a = PA长度:向量PO 到 射线方向rayDirection 上的投影长度 Dot(PO, rayDirection)
b = OA长度:球中心到射线的距离, e、a、b 构成一个直角三角形,e 为斜边,a、b为直角边
如果射线与球相交才会有交点 E
t (小写字母T)= PE长度:射线源点到 射线与球的交点距离
f (小写字母F) = EA长度
PA = PE + EA
上边 PAO 三个点构成一个直角三角形,A为直角
上边 EAO 三个店构成一个直角三角形,A为直角
r 为圆半径
则
e x e = a x a + b x b
r x r = f x f + b x b
推导出
f x f = r x r - e x e + a x a
t = a - f
射线与球交点坐标为 source + rayDirection * t
逻辑代码如下
/// <summary>
/// 射线与球的相交检测
/// </summary>
///
public class RaySphereCollision
{
private float a;
private float e;
public bool IsCollision(Vector3 source, Vector3 rayDirection, Vector3 sphereCenter, float sphereRadius)
{
rayDirection.Normalize();
Vector3 offset = sphereCenter - source;
e = offset.magnitude;
a = Dot(offset, rayDirection);
return sphereRadius * sphereRadius - (e * e - a * a) >= 0;
}
public Vector3 CollisionPosition(Vector3 source, Vector3 rayDirection, Vector3 sphereCenter, float sphereRadius)
{
if (!IsCollision(source, rayDirection, sphereCenter, sphereRadius))
{
return Vector3.zero;
}
// 如果不相交 sphereRadius * sphereRadius - e * e + a * a < 0
float f = (float)Math.Sqrt(sphereRadius * sphereRadius - e * e + a * a);
float t = a - f;
return source + rayDirection * t;
}
public float Dot(Vector3 vector1, Vector3 vector2)
{
return vector1.x * vector2.x + vector1.y * vector2.y + vector1.z * vector2.z;
}
}