继续上一节的内容,本节主要讲解三维空间中射线、线段与平面及三维物体的交点及距离的计算,它们在碰撞检测和可见性剔除等应用中是必不可少的。首先给出3D空间下点乘和叉乘的定义与定理的推导,再谈如何应用到程序编码的工作中。
设三维空间中任意两矢量V(v1,v2,v3)和W(w1,w2,w3)
![](https://i-blog.csdnimg.cn/blog_migrate/7e90cdc710c40028e35d2ead8ad83370.jpeg)
由三角形的余弦公式:
cosα = (a2 + b2 – c2)/2ab
则两矢量夹角α的余弦 : cosα = {(v1*v1 + v2*v2 + v3*v3) + (w1*w1+w2*w2+w3*w3)- [ (w1 - v1)2+( w2 - v2)2+( w3 - v3)2] } / 2 |V| |W| = (v1w1+ v2w2 + v3w3)/ |V| |W|
由于将矢量的点乘定义为 : V·W = v1w1+ v2w2 + v3w3 ;
所以 |V| |W| cosα = V·W;
由上面的结论引申,可知道如果两矢量的点乘为0,则说明夹角α的余弦为0,即矢量的夹角是90度,互相垂直。
再来看两矢量的叉乘,与点乘的计算结果定义为一数值不同,叉乘的结果定义为另外一个矢量 U (u1,u2,u3) ,其中:
u1 = v2w3 - v3w2;
u2 = v3w1 - v1w3;
u3 = v1w2 - v2w1;
由这个定义我们来推导:
V·U = v1(v2w3 - v3w2) + v2(v3w1 - v1w3) + v3(v1w2 - v2w1) = v1v2w3 - v1 v3w2 + v2v3w1-v2 v1w3 + v3v1w2-v3 v2w1 = 0;两矢量的点乘为0,说明它们互相垂直。
W·U = w1(v2w3 - v3w2) + w2(v3w1 - v1w3) + w3(v1w2 - v2w1) = 0,说明它们也是互相垂直的。
在三维空间中,U与V、W都垂直,说明U是垂直与V与W构成的平面,也就是这个平面的法向量。
U虽然是矢量,但其模|U|依旧是一个数值,表明其长度。
|U|2 = u1*u1 + u2*u2 + u3*u3 = (v2w3 - v3w2)2+( v3w1 - v1w3)2+( v1w2 - v2w1)2;
同时考虑,在α是V、W两矢量的夹角条件下,(|V| |W|sinα)2= |V|2|W|2(1-cos2α)= |V|2|W|2- |V|2|W|2cos2α = |V|2|W|2- (V·W)2= (v1*v1 + v2*v2 + v3*v3)( w1*w1+w2*w2+w3*w3) - (v1w1+ v2w2 + v3w3)2
通过化解,可以得到一个数学等式,即:
(bz-cy)2+(cx-az)2+(ay-bx)2=(a2+b2+c2)(x2+y2+z2)-(ax+by+cz)2
所以 (v2w3 - v3w2)2+( v3w1 - v1w3)2+( v1w2 - v2w1)2 = (v1*v1 + v2*v2 + v3*v3)( w1*w1+w2*w2+w3*w3) - (v1w1+ v2w2 + v3w3)2;
即 |U|2 = (|V| |W|sinα)2
|U| = |V| |W|sinα
在完成三维空间的点乘和叉乘定义与公式推导后,可以直接运用结论到程序的编写中,这些结论包括利用点乘来求得夹角的余弦,利用点乘为0来求得垂直于某向量的另一向量,利用两向量的叉乘来求得平面的法向量,利用叉乘的模来求得三角形的面积等。
三维空间中点到直线、射线和线段的距离。即从点P作垂直于线段(P0,P1)的垂线,求得垂心T后,计算两点之间的距离。
设置T点为参数表达方式: T = P0 + t(P1 –P0);将T点看作是从P0开始往P1移动的动态点,则矢量PT为PP0与P0T两个矢量之和,当PT垂直于P0P1时,利用两个矢量的点乘为0列等式,求得参数t的数值。
(PP0 + P0T) ·P1P0 = 0;
即 (P-P0)·(P1 –P0) + t(P1 –P0) ·(P1 –P0) = 0;
t = - (P-P0)·(P1 –P0) / (P1 –P0) ·(P1 –P0)
可以看出分子是需要计算两个矢量的点乘,而分母是矢量P0P1点乘自身,即为模的平方。
double CDEMAlgorithm::dist_Point_to_Line( Point P, Line L)
{
Vector v = L.P1 - L.P0; //顶点相减得到矢量
Vector w = P - L.P0; //顶点相减得到矢量
double c1 = Dot(w,v); //两矢量的点乘
double c2 = Dot(v,v); //两矢量的点乘
double b = c1 / c2; //求得参数
Point Pb = L.P0 + b * v; //解得参数后,得到垂心位置
return d(P, Pb); //返回两点之间的距离
}