使用迭代逼近的方式求解线段与圆柱体的交点

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用迭代逼近的方式求解线段与圆柱体的交点相关的知识,希望对你有一定的参考价值。

需求来源:

UE4.15版本

我们的VR项目中,需要实现护盾的功能,玩家的左手柄方向的延长线与玩家周围的一个圆柱体相交的位置,即为护盾的位置,而护盾的方向,与圆柱体相切,如下图所示.

绿色为护盾,红色的为圆柱体,蓝色的线从左手柄射出.

技术分享

玩家的左手一定会在圆柱体内,那么要解决的问题就变为,求解线段与圆柱体的交点.

如何使用迭代的方式求解呢,思路如下:

在玩家的左手柄方向的延长线上选择一点,此点的位置要在圆柱体外.设为PointOut.玩家左手柄位置为PointIn,求出两个点的中点:PointMiddle.那么交点一定位于[PointIn, PointMiddle] 或者[PointIn, PointOut]之间,以此类推,就可以不断逼近交点.

 1 UCLASS()
 2 class SHOOTERGAME_API UShooterBlueprintFunctionLibrary : public UBlueprintFunctionLibrary
 3 {
 4     GENERATED_UCLASS_BODY()
 5     //求解一个点是否在圆柱体内
 6     //LowCircleCenter:圆柱体底面的圆心位置
 7     //HightCircleCenter:圆柱体的顶面的圆心位置
 8     //R:圆柱体的半径
 9     //TestPoint需要测试的点
10     UFUNCTION(BlueprintPure, Category = "Math")
11     static bool IsInCylinder(FVector LowCircleCenter, FVector HighCircleCenter, float R, FVector TestPoint);
12 
13     //线段与圆柱的交点 采用迭代逼近的方式
14     //两个点必须一个在圆柱体内,一个在圆柱体外
15     //LowCircleCenter:圆柱体底面的圆心位置
16     //HightCircleCenter:圆柱体的顶面的圆心位置
17     //R:圆柱体的半径
18     //StartPoint:线段起始点
19     //EndPoint:线段终点
20     //Tolerance:迭代精度
21     //HasIntersection:是否有交点
22     //InSurface:交点的位置,在柱面上 0,在底面 -1,在顶面 1
23     UFUNCTION(BlueprintPure, Category = "Math")
24     static FVector LineAndCylinderIntersection(FVector LowCircleCenter, FVector HighCircleCenter, float R, FVector StartPoint, FVector EndPoint, float Tolerance, bool& HasIntersection, int32& InSurface);
25 }

 

 1 //求解一个点是否在圆柱体内
 2 //LowCircleCenter:圆柱体底面的圆心位置
 3 //HightCircleCenter:圆柱体的顶面的圆心位置
 4 //R:圆柱体的半径
 5 //TestPoint需要测试的点
 6 
 7 bool UShooterBlueprintFunctionLibrary::IsInCylinder(FVector LowCircleCenter, FVector HighCircleCenter, float R, FVector TestPoint)
 8 {
 9     FVector MiddlePoint = (LowCircleCenter + HighCircleCenter) / 2;
10 
11     FVector CylinderDir = (HighCircleCenter - LowCircleCenter);
12 
13     float Height = CylinderDir.Size();
14 
15     CylinderDir.Normalize();
16 
17     FVector StartDir = TestPoint - MiddlePoint;
18 
19     float StartProj = FVector::DotProduct(StartDir, CylinderDir);
20     //判断此点是否在圆柱体的高度范围内
21     if (FMath::Abs(StartProj) > Height / 2)
22     {
23         return false;
24     }
25     
26     //判断此点是否在圆柱体的横向距离内
27     float TestDist = FMath::PointDistToLine(TestPoint, CylinderDir, MiddlePoint);
28 
29     return TestDist <= R;
30 
31 }
 1 //求解线段与圆柱体的交点,两个点必须一个在圆柱体内,一个在圆柱体外
 2 //LowCircleCenter:圆柱体底面的圆心位置
 3 //HightCircleCenter:圆柱体的顶面的圆心位置
 4 //R:圆柱体的半径
 5 //StartPoint:线段起始点
 6 //EndPoint:线段终点
 7 //Tolerance:迭代精度
 8 //HasIntersection:是否有交点
 9 //InSurface:交点的位置,在柱面上 0,在底面 -1,在顶面 1
10 FVector UShooterBlueprintFunctionLibrary::LineAndCylinderIntersection(FVector LowCircleCenter, FVector HighCircleCenter, float R, FVector StartPoint, FVector EndPoint, float Tolerance, bool& HasIntersection, int32& InSurface)
11 {
12     Tolerance = Tolerance <= 0.1f ? 0.1f : Tolerance;
13     //两个点必须一个在圆柱体内,一个在圆柱体外
14     if (!(IsInCylinder(LowCircleCenter, HighCircleCenter, R, StartPoint) ^ IsInCylinder(LowCircleCenter, HighCircleCenter, R, EndPoint)))
15     {
16         HasIntersection = false;
17         return FVector::ZeroVector;
18     }
19 
20     HasIntersection = true;
21 
22     FVector Point1 = StartPoint;
23     FVector Point2 = EndPoint;
24 
25     FVector MiddlePoint = (Point1 + Point2) / 2;
26 
27     float DiffSize = (Point1 - Point2).SizeSquared();
28 
29     int32 Times = 0;
30     //迭代算法
31     while (DiffSize > Tolerance)
32     {
33         //选择下一个迭代点
34         if (IsInCylinder(LowCircleCenter, HighCircleCenter, R, Point1) ^ IsInCylinder(LowCircleCenter, HighCircleCenter, R, MiddlePoint))
35         {
36             Point2 = MiddlePoint;
37         }
38         else
39         {
40             Point1 = MiddlePoint;
41         }
42 
43         MiddlePoint = (Point1 + Point2) / 2;
44 
45         DiffSize = (Point1 - Point2).SizeSquared();
46 
47         Times++;
48 
49         if (Times >= 40)
50         {
51             break;
52         }
53     }
54 
55     FVector CircleCenterMiddlePoint = (LowCircleCenter + HighCircleCenter) / 2;
56     FVector CircleCenterDir = (HighCircleCenter - LowCircleCenter);
57     CircleCenterDir.Normalize();
58 
59     float TestDist = FMath::PointDistToLine(MiddlePoint, CircleCenterDir, CircleCenterMiddlePoint);
60     //交点在侧面,那么交点与圆柱体中心线的距离小于Tolerance
61     if ((TestDist -  R) * (TestDist - R) <= Tolerance)
62     {
63         InSurface = 0;
64     }
65     else
66     {
67         //可以依据交点与中心点的方向来判断交点在底面还是顶面
68         if (FVector::DotProduct(MiddlePoint - CircleCenterMiddlePoint, CircleCenterDir) >= 0.0f)
69         {
70             InSurface = 1;
71         }
72         else
73         {
74             InSurface = -1;
75         }
76     }
77 
78     return MiddlePoint;
79 }

 

以上是关于使用迭代逼近的方式求解线段与圆柱体的交点的主要内容,如果未能解决你的问题,请参考以下文章

直观理解牛顿迭代法

平面/射线交点与点/平面投影的差异

线段与线段的交点

梯度寻优

高斯-牛顿迭代

求直线与线段的交点