如何找到两个平面之间的交线?
我知道数学思想,我做了平面法向量之间的叉积
但是如何以编程方式从结果向量中获取线
平面的方程是ax + by + cz + d = 0
,其中 (a,b,c) 是平面的法线,d 是到原点的距离。这意味着满足该方程的每个点 (x,y,z) 都是平面的成员。
给定两个平面:
P1: a1x + b1y + c1z + d1 = 0
P2: a2x + b2y + c2z + d2 = 0
两者之间的交集是验证两个方程的点集。要沿着这条线找到点,您可以简单地为 x 选择一个值,任何值,然后求解 y 和 z 的方程。
y = (-c1z -a1x -d1) / b1
z = ((b2/b1)*(a1x+d1) -a2x -d2)/(c2 - c1*b2/b1)
如果你 make x=0
,这会变得更简单:
y = (-c1z -d1) / b1
z = ((b2/b1)*d1 -d2)/(c2 - c1*b2/b1)
要获得 2 个平面的交点,您需要在线上的一个点和该线的方向。
找到那条线的方向真的很容易,只需穿过相交的 2 个平面的 2 个法线。
lineDir = n1 × n2
但是那条线穿过原点,沿着你的平面交叉点延伸的线可能不会。因此,Martinho 的回答为在相交线上找到一个点(基本上是两个平面上的任何点)提供了一个很好的开端。
如果您想查看如何解决这个问题的推导,这里是它背后的数学:
首先让 x=0。现在我们在 2 个方程中有 2 个未知数,而不是在 2 个方程中有 3 个未知数(我们任意选择了一个未知数)。
那么平面方程是(因为我们选择 x=0,所以消除了 A 项):
B 1 y + C 1 z + D 1 = 0
B 2 y + C 2 z + D 2 = 0
我们希望 y 和 z 使得这些方程对于给定的 B 1、 C 1都正确求解(=0) 。
因此,只需将顶部 eq 乘以 (-B 2 /B 1 ) 即可得到
-B 2 y + (-B 2 /B 1 )*C 1 z + (-B 2 /B 1 )*D 1 = 0
B 2 y + C 2 z + D 2 = 0
添加 eqs 得到
z = ( (-B 2 /B 1 )*D 1 - D 2 ) / (C 2 * B 2 /B 1 )*C 1 )
现在将您找到的 z 放入第一个等式中以找到 y 为
y = (-D 1 - C 1 z) / B 1
请注意,使 0的最佳变量是系数最低的变量,因为它无论如何都不携带任何信息。因此,如果 C 1和 C 2都为 0,则选择 z=0(而不是 x=0)将是更好的选择。
如果 B 1 =0(这不太可能),上述解决方案仍然会搞砸。您可以添加一些 if 语句来检查 B 1 =0,如果是,请务必改为求解其他变量之一。
根据用户的回答,3 个平面相交的封闭形式解决方案实际上在 Graphics Gems 1 中。公式为:
P_intersection = (( point_on1 • n1 )( n2 × n3 ) + ( point_on2 • n2 )( n3 × n1 ) + ( point_on3 • n3 )( n1 × n2 )) / det(n1,n2,n3)
实际上 point_on1 • n1 = -d1 (假设你写你的飞机 Ax + By + Cz + D=0,而不是 =-D)。因此,您可以将其重写为:
P_intersection = (( -d1 )( n2 × n3 ) + ( -d2 )( n3 × n1 ) + ( -d3 )( n1 × n2 )) / det(n1,n2,n3)
与 3 个平面相交的函数:
// Intersection of 3 planes, Graphics Gems 1 pg 305
static Vector3f getIntersection( const Plane& plane1, const Plane& plane2, const Plane& plane3 )
{
float det = Matrix3f::det( plane1.normal, plane2.normal, plane3.normal ) ;
// If the determinant is 0, that means parallel planes, no intn.
if( det == 0.f ) return 0 ; //could return inf or whatever
return ( plane2.normal.cross( plane3.normal )*-plane1.d +
plane3.normal.cross( plane1.normal )*-plane2.d +
plane1.normal.cross( plane2.normal )*-plane3.d ) / det ;
}
证明它有效(黄点是 rgb 平面的交点)
一旦你有两个平面共有的交点,这条线就行了
P + t*d
其中 P 是交点,t 可以从 (-inf, inf) 出发,d 是方向向量,它是两个原始平面的法线的叉积。
红色和蓝色平面之间的交线如下所示
据我计算,“稳健”(第二种方式)需要 48 个基本操作,而第一种方式(x,y 的隔离)使用 36 个基本操作。这两种方式之间的稳定性和 # 计算之间存在权衡。
在 B 1为 0 并且您没有检查的情况下,从调用第一种方式返回 (0,inf,inf) 将是非常灾难性的。因此,添加if
语句并确保第一种方式不除以 0 可能会给您带来稳定性,但代价是代码膨胀和添加的分支(这可能非常昂贵)。3 平面相交法几乎是无分支的,不会给你无穷大。
添加此答案以确保完整性,因为在撰写本文时,此处的答案均不包含直接解决该问题的工作代码示例。
虽然这里的其他答案已经涵盖了这些原则。
可以使用 3 平面相交算法的简化版本来计算两个平面之间的线。
bobobobo 的答案中的第二个“更强大的方法”引用了 3 平面相交。
虽然这适用于 2 个平面(其中第 3 个平面可以使用前两个平面的叉积计算),但对于 2 个平面版本,该问题可以进一步减少。
包括这个代码示例,因为它可能不会立即显而易见。
// Intersection of 2-planes: a variation based on the 3-plane version.
// see: Graphics Gems 1 pg 305
//
// Note that the 'normal' components of the planes need not be unit length
bool isect_plane_plane_to_normal_ray(
const Plane& p1, const Plane& p2,
// output args
Vector3f& r_point, Vector3f& r_normal)
{
// logically the 3rd plane, but we only use the normal component.
const Vector3f p3_normal = p1.normal.cross(p2.normal);
const float det = p3_normal.length_squared();
// If the determinant is 0, that means parallel planes, no intersection.
// note: you may want to check against an epsilon value here.
if (det != 0.0) {
// calculate the final (point, normal)
r_point = ((p3_normal.cross(p2.normal) * p1.d) +
(p1.normal.cross(p3_normal) * p2.d)) / det;
r_normal = p3_normal;
return true;
}
else {
return false;
}
}
只要两个平面不平行,这种方法就可以避免除以零。
如果这些是飞机:
A1*x + B1*y + C1*z + D1 = 0
A2*x + B2*y + C2*z + D2 = 0
1) 找到一个平行于交线的向量。这也是垂直于其他两个平面的第三个平面的法线:
(A3,B3,C3) = (A1,B1,C1) cross (A2,B2,C2)
2) 形成一个由 3 个方程组成的系统。这些描述了在一点相交的 3 个平面:
A1*x1 + B1*y1 + C1*z1 + D1 = 0
A2*x1 + B2*y1 + C2*z1 + D2 = 0
A3*x1 + B3*y1 + C3*z1 = 0
3) 求解它们以找到 x1,y1,z1。这是相交线上的一个点。
4) 相交线的参数方程为:
x = x1 + A3 * t
y = y1 + B3 * t
z = z1 + C3 * t
基于行列式的方法很简洁,但很难理解它为什么有效。
这是另一种更直观的方式。
这个想法是首先从原点到第一个平面 ( p1
) 上的最近点,然后从那里到两个平面相交线上的最近点。(沿着我在v
下面调用的向量。)
Given
=====
First plane: n1 • r = k1
Second plane: n2 • r = k2
Working
=======
dir = n1 × n2
p1 = (k1 / (n1 • n1)) * n1
v = n1 × dir
pt = LineIntersectPlane(line = (p1, v), plane = (n2, k2))
LineIntersectPlane
==================
#We have n2 • (p1 + lambda * v) = k2
lambda = (k2 - n2 • p1) / (n2 • v)
Return p1 + lambda * v
Output
======
Line where two planes intersect: (pt, dir)
这应该与基于行列式的方法给出相同的观点。几乎可以肯定两者之间存在联系。n2 • v
如果我们应用“标量三重积”规则,至少分母是相同的。因此,就条件数而言,这些方法可能是相似的。
不要忘记检查(几乎)平行平面。例如:if (dir • dir < 1e-8)
如果使用单位法线,应该可以正常工作。
线的叉积是相交线的方向。现在您需要在交叉点上找到一个点。
您可以通过在叉积上取一个点,然后减去平面 A 的法线 * 到平面 A 的距离和平面 B 的法线 * 到平面 b 的距离来做到这一点。清洁器:
p = 叉积上的点
交点 = ([p] - ([平面 A 的法线] * [p 到平面 A 的距离]) - ([平面 B 的法线] * [从 p 到平面 B 的距离]))
编辑:
您有两个具有两个法线的平面:
N1 and N2
叉积是相交线的方向:
C = N1 x N2
上面的类有一个计算点和平面之间距离的函数。用它来获得 C 上某个点 p 到两个平面的距离:
p = C //p = 1 times C to get a point on C
d1 = plane1.getDistance(p)
d2 = plane2.getDistance(p)
相交线:
resultPoint1 = (p - (d1 * N1) - (d2 * N2))
resultPoint2 = resultPoint1 + C