FVector FMath::LinePlaneIntersection
inline FVector FMath::LinePlaneIntersection ( const FVector &Point1, const FVector &Point2, const FPlane &Plane ) { return Point1 + (Point2-Point1) * ((Plane.W - (Point1|Plane))/((Point2 - Point1)|Plane)); }
该功能还有一个实现,在KismetMathLibrary.h里:
bool UKismetMathLibrary::LinePlaneIntersection(const FVector& LineStart, const FVector& LineEnd, const FPlane& APlane, float& T, FVector& Intersection) { FVector RayDir = LineEnd - LineStart;
// Check ray is not parallel to plane if ((RayDir | APlane) == 0.0f) { T = -1.0f; Intersection = FVector::ZeroVector; return false; }
T = ((APlane.W - (LineStart | APlane)) / (RayDir | APlane));
// Check intersection is not outside line segment if (T < 0.0f || T > 1.0f) { Intersection = FVector::ZeroVector; return false; }
// Calculate intersection point Intersection = LineStart + RayDir * T;
return true; }
这两个实现其实是一样的原理,第二个可以当作第一个实现的详细注释版。
解释一下:其中(Vector | Plane)的操作是合法的,是因为FPlane是FVector的派生类型,因此(Vector | Plane)其实就是向量点乘,具体到这里就是Point点乘(Plane的Normal)。【|】运算符是对FVector.DotProduct的重载
就以Kismet里的蓝图函数来说:
- RayDir是从LineStart指向LineEnd的方向向量,(RayDir | APlane)是RayDir点乘APlane的Normal,若结果为0,表示RayDir⊥Normal,或说RayDir // APlane。
- 由 FPlane::PlaneDot 函数的解释可以知道(LineStart | APlane) - APlane.W是LineStart到APlane的有向距离,RayDir | APlane【即RayDir.Dot(APlane.Normal)】是RayDir在Normal上的投影,当RayDir与Normal基本同向时(是指RayDir与Normal正向夹角小于90°),投影值为正,否则为负。
- 取一种典型情况来分析:当LineStart在APlane分割出来的正空间中,LineEnd在负空间中,此时,LineStart与LineEnd连线与APlane有交点X,比例T=|X - LineStart| / |LineEnd - LineStart|. 这个比例根据比例理论,等于[(LineStart | APlane) - APlane.W] / |[RayDir | APlane]|,因为LineStart与LineEnd分处APlane分割的正负空间中,RayDir | APlane此时为负,所以 |[RayDir | APlane]| = - RayDir | APlane,最终得到比例T = [APlane.W - (LineStart | APlane)] / RayDir | APlane.
- 还有几种其他情形,比如LineStart与LineEnd分别在负空间和正空间,或都在正空间,或都在负空间,只要分析清楚每种情形下LineStart到APlane的有向距离值的符号,RayDir到APlane.Normal的投影值的符号,最终都会得出上面关于T的结论。
- 接下来函数检测了T值的范围,只关注了直线<LineStart, LineEnd>与APlane交点在LineStart与LineEnd连线之间的情况,简单点说就是这个函数只关注了线段[LineStart, LineEnd]与APlane有交点的情况,交点可以是两端点。这是这个蓝图函数与FMath::LinePlaneIntersection函数唯一的区别。FMath::LinePlaneIntersection函数也处理T在线段外的情况。因此,准确的说,FMath::LinePlaneIntersection函数名副其实,求解的是直线与平面的交点。而这里的蓝图函数求解的是线段与平面的交点,如果线段与平面没有交点,则返回false。
- 在FMath::LinePlaneIntersection()的实现中:当从P1出发射向P2的射线与Plane有交点时,T值为正,即((Plane.W - (Point1|Plane))/((Point2 - Point1)|Plane))>0;当从P2出发射向P1的射线与Plane有交点时,T值为负,即((Plane.W - (Point1|Plane))/((Point2 - Point1)|Plane))<0。并有:当P1与P2在Plane同一边时,|T|>1;当P1与P2分属Plane的两边时,|T|<1