三维空间中三角面片碰撞检测TTI
空间中三角面片碰撞检测,本质上是检测其中一个三角形面片的边是否与另一个三角形面片发生碰撞——即线段与三角形面片碰撞(三角形1与三角形2以及三角形2与三角形1)。
相关代码打包:三角形碰撞检测TTI.zip
采用符号标记为:三角形t1(p1,p2,p3)与t2(p4,p5,p6)以及分别所在平面f1,f2。线段与平面交点p
首先,转换成线段所在直线与三角形面片所在平面的碰撞检测,当直线
因此,碰撞检测可以分为如下几种情况:
- 线段与平面平行,但不在平面内
- 线段与平面平行,且在平面内
- 线段与平面不平行,存在交点
1.线段与平面平行,但不在平面内
![](https://img-blog.csdnimg.cn/34e3068d0a7c4cc4a32ff206acf5eed4.png)
如上图所示,显而易见该线段与平面无交点,舍弃
2.线段与平面平行,但在平面内
![](https://img-blog.csdnimg.cn/477b43ad5d2c492a9215faa5b1351467.png)
如上图所示,当线段在三角形t1所在平面f1内,分为三种情况处理
- 线段与三角形面片无交点,舍弃
- 线段一个端点在三角形面片内,面片相交
- 线段两个端点都在三角形面片内,面片相交
3.线段与平面不平行,存在交点
对于线段与平面相交,采用如下流程判断:
按照上图流程图所示,对于空间中与平面发生相交的线段,首先求得交点p是否在线段内(包含端点),若不在,则返回无交点。若在,则继续判断交点p是否在三角形面片内(不包含边界),若不在则返回无交点,否则,存在交点。
如图所示,对于线段2,交点p2不在三角形面片内,因此无交点;对于线段3,交点p3不在线段内部,因此也无交点。
测试结果
sphere.m模型,无交点
test.m测试模型,存在交点。
部分函数代码
- 直线与平面交点,p1,p2,p3表示平面三点,p4,p5表示直线两点,p返回交点
int ESLib::getLinePlaneIntersection(CPoint p1, CPoint p2, CPoint p3, CPoint p4, CPoint p5, CPoint& p)
{
double a, b, c, d, t;
// 线段方向向量
CPoint n_line = p4 - p5;
CPoint n1 = p2 - p1;
CPoint n2 = p3 - p1;
CPoint n = n1 ^ n2;
a = n[0];
b = n[1];
c = n[2];
d = 0 - (a * p1[0] + b * p1[1] + c * p1[2]);
//std::cout << "plane: " << a << " " << b << " " << c << " " << d << std::endl;
//std::cout << "line vector: " << n_line[0] << " " << n_line[1] << " " << n_line[2] << std::endl;
// 线段与平面平行且不在面上
if (n_line * n == 0 && a * p4[0] + b * p4[1] + c * p4[2] + d != 0 && a * p5[0] + b * p5[1] + c * p5[2] + d != 0)
{
return 0;
}
// 线段在面上
if (a * p4[0] + b * p4[1] + c * p4[2] + d == 0 && a * p5[0] + b * p5[1] + c * p5[2] + d == 0)
{
return 2;
}
// 有交点
else
{
t = ((p1[0] - p4[0]) * a + (p1[1] - p4[1]) * b + (p1[2] - p4[2]) * c) / (n_line * n);
p[0] = p4[0] + n_line[0] * t;
p[1] = p4[1] + n_line[1] * t;
p[2] = p4[2] + n_line[2] * t;
//cout << "Intersection: " << p[0] << " " << p[1] << " " << p[2] << endl;
return 1;
}
}
- 点p3是否在线段内,p1,p2表示线段端点,p3表示用于检测的点
bool ESLib::pointBetweenEdge(CPoint p1, CPoint p2, CPoint p3)
{
// p3 is on the endpoint
if (p3 == p1 || p3 == p2)
{
return true;
}
CPoint n13 = p3 - p1;
CPoint n32 = p2 - p3;
double alpha = acos((n13 * n32) / (n13.norm() * n32.norm()));
// p3在p1 p2中间,则角度应为0(阈值)
if (alpha <= 0)
{
//std::cout << "between" << std::endl;
return true;
}
else
{
//std::cout << "not between" << std::endl;
return false;
}
}
- 点p是否在面pF内
bool ESLib::insideFace(ESFace* pF, CPoint p)
{
CPoint p1, p2, p3;
ESHalfEdge* he = (ESHalfEdge*)pF->halfedge();
p1 = he->target()->point();
p2 = he->he_next()->target()->point();
p3 = he->he_next()->he_next()->target()->point();
// on the face vertex is not inside
if (p == p1 || p == p2 || p == p3)
{
return false;
}
if (pointBetweenEdge(p1, p2, p) || pointBetweenEdge(p2, p3, p) || pointBetweenEdge(p3, p1, p))
{
return false;
}
CPoint n = ((p2 - p1) ^ (p3 - p1)) / ((p2 - p1).norm() * (p3 - p1).norm());
CPoint n1 = ((p1 - p) ^ (p2 - p)) / ((p1 - p).norm() * (p2 - p).norm());
CPoint n2 = ((p2 - p) ^ (p3 - p)) / ((p2 - p).norm() * (p3 - p).norm());
CPoint n3 = ((p3 - p) ^ (p1 - p)) / ((p3 - p).norm() * (p1 - p).norm());
// 在面内且不包含边界
if ((n * n1) > 0 && (n * n2) > 0 && (n * n3) > 0)
{
return true;
}
// 不在面内
else
{
return false;
}
}
不足之处
作为项目中的原子操作,调用了acos函数以及除法求解方程。会导致原子操作耗时相对较长。因此后续会考虑如何提高运行速度,减少耗时较长的函数调用,替换为耗时低的运算操作。