上一篇文章介绍完了点、线、面的基本函数,链接如下。
三维空间常用函数(一) c++ Qt_嘟嘟噜噜噜啦啦的博客-CSDN博客
接下来介绍一下投影以及求交的方法。
1、点投影到平面与直线
点投影到平面
求点到平面的投影点,可以看成以这个点为起点,以平面法向量为方向的直线,与平面的交点,直线可以表示为:p + t * dir,只需求出平面上的点的t值大小即可,如图:
从图中可以看出,投影点 P = P0 + t * nor,而 t = cenP0 * cosθ,cenP0为以cen和P0组成线段的长度,而θ为cenP0与nor的夹角,从之前点乘的定义,很容易得到 t = cenP0 * nor,所以点投影到三维平面的函数如下
// 点投影到平面
QVector3D ProjectionToPlane(const QVector3D& plane_center, const QVector3D& plane_normal, const QVector3D& point)
{
QVector3D nor = plane_normal.normalized();
QVector3D temp_point;
// 求点到平面的距离
double t = QVector3D::dotProduct(nor, (plane_center - point));
temp_point = t * nor + point;// 点到平面的距离乘法向量
return temp_point;
}
点投影到直线
有了点投影到平面之后,点投影到直线就十分简单了,如图:
点P0投影到以cen为起点,dir为方向向量的直线上的投影点P,可以看成是点cen投影到以P0为中心点,以dir为法向量的平面的投影点,所以点投影到三维直线的函数如下:
QVector3D ProjectionToLine(const QVector3D& line_center, const QVector3D& line_dir, const QVector3D& point)
{
return ProjectionToPlane(point, line_dir, line_center);
}
2.线与面相交
直线与平面相交
直线与平面相交,需要先对直线的方向向量进行判断,当直线与平面不平行的时候,那就一定会有一个交点,平行根据直线方向向量与法向量是否垂直判断即可。有交点的时候如图:
图中平面的中心点为cen,法向量为nor,直线上一点为P0,方向向量为dir,直线与平面的交点假设为P,则根据之前点投影到平面的公式,有 t = cenP0 * nor = PP0 * nor,而线段PP0 = P - P0, 因为P在直线上,所以P = P0 + x * dir,其中x为线段PP0的长度。所以可以得到
cenP0 * nor = (P - P0) * nor = (P0 + x * dir - P0) * nor = x * dir * nor
→ x = cenP0 * nor / (dir * nor)
得到了x之后,交点P的坐标就可以用P0 + x * dir得到。所以计算直线和平面的交点的函数如下:
bool LineIntersectPlane(const QVector3D& line_p, const QVector3D& line_dir, const QVector3D& plane_cen, const QVector3D& plane_nor, QVector3D& out_point)
{
if (qAbs(QVector3D::dotProduct(line_dir, plane_nor)) < 0.0001) // 平行
return false;
double t = QVector3D::dotProduct(plane_cen - line_p, plane_nor) / QVector3D::dotProduct(line_dir, plane_nor);
out_point = line_p + t * line_dir;
return true;
}
如果判断是射线的话,只要加个t大于等于0的判断即可。如果为线段,则判断t的值是否在线段的范围内即可。
参考链接:【寒江雪】计算直线与平面的交点坐标_平面img_285与直线img_286的交点坐标为__寒江雪_的博客-CSDN博客
射线与三角形相交
原理和代码参考:
https://www.cnblogs.com/graphics/archive/2010/08/09/1795348.html
这里给出改成Qt使用的函数:
// 求射线与三角形的交点,射线起点,方向向量,三角形三个顶点坐标,交点方向分量,有交点返回真,无交点返回假
bool IntersectTriangle(QVector3D orig, QVector3D dir, QVector3D v0, QVector3D v1, QVector3D v2, double& t)
{
// 射线上的点表示为 起点 + t * 方向向量,三角形内部所以点的参数方程为(1-u-v)*v0+u*v1+v*v2,他们相等就是交点
// 三未知量u,v,t使用三阶齐次矩阵来求解
// 三向量的行列式等于x1点乘x2叉乘x3
double u, v;
QVector3D E1 = v1 - v0;
QVector3D E2 = v2 - v0;
QVector3D P = QVector3D::crossProduct(dir, E2);
double det = QVector3D::dotProduct(E1, P);
QVector3D T;
if (det > 0)
{
T = orig - v0;
}
else
{
T = v0 - orig;
det = -det;
}
if (det < 0.0001f)
{
return false;
}
u = QVector3D::dotProduct(T, P);
if (u<0.0f || u>det)// 没有交点,返回假
return false;
QVector3D Q = QVector3D::crossProduct(T, E1);
v = QVector3D::dotProduct(dir, Q);
if (v<0.0f || u + v>det)// 没有交点,返回假
return false;
t = QVector3D::dotProduct(E2, Q);
t = t / det;
return true;// 计算完毕之后,返回真
}