3D 中射线与平面相交检测
本篇讨论的射线是无限长,平面无限大
下图展示的几种位置关系:相交、不相交
相交包括:只有一个交点 (射线3)、射线在平面内有无数交点 (射线4)
不相交:射线与平面平行 (射线1),射线与平面不平行也不相交 (射线2、射线5)
判断射线2和射线5和平面不相交,添加辅助线如下
在平面上任取一点C
射线2中:P1为射线起点,向量P1A 是射线的方向向量, 连接P1C
点乘:Dot(向量P1A,平面法向量 Normal) > 0
点乘:Dot(向量P1C,平面法向量 Normal < 0
两个点乘结果异号(一个大于0,一个小于0) 则判定射线2 与平面不相交
射线5中:P2为射线起点,向量 P2B 是射线的方向向量,连接P2C
点乘:Dot(向量P2B,平面法向量 Normal) < 0
点乘:Dot(向量P2C,平面法向量 Normal > 0
两个点乘结果异号(一个大于0,一个小于0) 则判定射线2 与平面不相交
在平面上任取一点C
射线1中:P1为射线起点,向量P1A 是射线的方向向量, 连接P1C
点乘:Dot(向量P1A,平面法向量 Normal) = 0 说明 P1A 垂直于平面法向量,则P1A平行于平面
点乘:Dot(向量P1C,平面法向量 Normal < 0 说明 P1C 至少一个端点不在平面上
两个点乘结果(一个等于0,一个小于0) 则判定射线2 与平面平行但不在平面内
射线4中:P2为射线起点,向量 P2B 是射线的方向向量,连接P2C
点乘:Dot(向量P2B,平面法向量 Normal) = 0 说明 P2B 垂直于平面法向量,则P2B平行于平面
点乘:Dot(向量P2C,平面法向量 Normal = 0 说明 P2C 垂直于平面法向量,则P2C平行于平面
则 P2BC 所组成的平面和原平面平行,C点又是原平面中的点,则P2BC和平面重合,则P2B在原平面内
结论射线4在平面内
射线3的相交检测,如果相交,求交点坐标。看下图辅助线以及标记
平面上任取一点C
PB 为从点P到平面做的垂线,h 为向量PB 在平面法向量 Normal 的投影长度
O为射线3与平面的交点,f 为向量PO的长度,
O点坐标 = P坐标 + 向量PO
向量PO = rayDirection * f
O点坐标 = P坐标 + rayDirection * f
我们通过点C来求解 f
在三角形 PBC 中
h = Dot(向量PC,平面法向量Normal)
在三角形 PBO 中
h = Dot(向量PO,平面法向量Normal)
h = Dot(rayDirection * f,平面法向量Normal), 其中 f 是标量,可以提取出来
h = f * Dot(rayDirection , 平面法向量 Normal)
f = h 除以 Dot(rayDirection, 平面法向量 Normal)
代码逻辑如下
///
/// 射线与平面相交检测
///
public class RayPlaneCollision
{
float dot_rayDir_planeNormal;
float dot_pc_planeNormal;
///
/// 射线与平面相交检测
///
/// 射线起点坐标
/// 射线方向
/// 平面上任一点坐标
/// 平面法向量
///
public RayPlaneCollisionEnum IsCollision(Vector3 source, Vector3 rayDirection, Vector3 planePos, Vector3 planeNormal)
{
Vector3 PC = planePos - source;
dot_rayDir_planeNormal = Dot(rayDirection, planeNormal);
dot_pc_planeNormal = Dot(PC, planeNormal);
// 射线平行于平面
if (dot_rayDir_planeNormal == 0)
{
if (dot_pc_planeNormal == 0)
{
// 射线和平面平行并且射线在平面内
return RayPlaneCollisionEnum.IN_PLANE;
}
// 射线和平面平行但是射线不在平面内
return RayPlaneCollisionEnum.SEPARATION;
}
if (dot_rayDir_planeNormal > 0 && dot_pc_planeNormal > 0)
{
return RayPlaneCollisionEnum.COLLISION;
}
if (dot_rayDir_planeNormal < 0 && dot_pc_planeNormal < 0)
{
return RayPlaneCollisionEnum.COLLISION;
}
return RayPlaneCollisionEnum.SEPARATION;
}
public Vector3 CollisionPosition(Vector3 source, Vector3 rayDirection, Vector3 planePos, Vector3 planeNormal)
{
if (IsCollision(source, rayDirection, planePos, planeNormal) != RayPlaneCollisionEnum.COLLISION)
{
return Vector3.zero;
}
float length = dot_pc_planeNormal / dot_rayDir_planeNormal;
Debug.LogError(dot_pc_planeNormal + " " + dot_rayDir_planeNormal);
return source + rayDirection * length;
}
public float Dot(Vector3 vector1, Vector3 vector2)
{
return vector1.x * vector2.x + vector1.y * vector2.y + vector1.z * vector2.z;
}
}
本篇开始说的限制条件 射线无限长、平面无限大,如果射线限制了长度 s,平面限制了区域(有N个顶点的多边形),对于不相交的判定没有影响
对于射线和平面相交的,如果射线限制长度,则 根据上边计算的长度 f 和 限制长度 s 比较,如果 s 小于 f,则射线长度不够,不相交
如果 s >= f,计算出 O 点坐标,然后判断 O点坐标是否在平面范围内
对于射线在平面内的思考下