Saki's 研究记录

碰撞检测中函数(线与面,线与多边形,点到线的最近点) _ 转 - [叁]

字数统计: 1k阅读时长: 4 min
2022/04/21
  1. 线和平面碰撞
    调用: IntersectedPlane(vTriangle, vLine);
    传入参数:线(起点,终点),平面(三个顶点)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    bool IntersectedPlane(CVector3 vTriangle[], CVector3 vLine[]) {
    float distance1=0, distance2=0;
    CVector3 vNormal = Normal(vTriangle); // 三角形法向量
    // originDistance 为 Ax+By+Cz+D=0中的D (A,B,C为法向量)
    float originDistance = PlaneDistance(vNormal, vTriangle[0]);
    // 求出线段两端点到平面的距离,若距离相乘为负,相交,否则不相交
    distance1 = ((vNormal.x * vLine[0].x) + // Ax +
    (vNormal.y * vLine[0].y) + // Bx +
    (vNormal.z * vLine[0].z)) + originDistance; // Cz + D
    distance2 = ((vNormal.x * vLine[1].x) + // Ax +
    (vNormal.y * vLine[1].y) + // Bx +
    (vNormal.z * vLine[1].z)) + originDistance; // Cz + D
    if(distance1 * distance2 >= 0)
    return false;

    return true;
    }
  2. 线段和多边形相交
    调用: IntersectedPolygon(vTriangle, vLine, 3);
    参数:多边形顶点数组,线段,多边形顶点个数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    bool IntersectedPolygon(CVector3 vPoly[], CVector3 vLine[], int verticeCount) {
    CVector3 vNormal = {0};
    float originDistance = 0;
    // 如果不和多边形所在平面相交,则不相交
    if (!IntersectedPlane(vPoly, vLine, vNormal, originDistance))
    return false;

    // 求出线和平面的交点
    CVector3 vIntersection = IntersectionPoint(vNormal, vLine, originDistance);
    // 如果交点在多边形内,就相交
    if (InsidePolygon(vIntersection, vPoly, verticeCount))
    return true;

    return false;
    }

    辅助函数 1: IntersectionPoint()返回线和平面的交点
    思路:求出点与点和平面交点的距离1,用起始点+线段方向*距离1,求出交点

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    CVector3 IntersectionPoint(CVector3 vNormal, CVector3 vLine[], double distance) {
    CVector3 vPoint = {0}, vLineDir = {0};
    double Numerator = 0.0, Denominator = 0.0, dist = 0.0;
    vLineDir = Vector(vLine[1], vLine[0]); // 得到线向量
    vLineDir = Normalize(vLineDir); // 标准化
    // 注意1:为什么要加负号,假设线段方向从平面正面指向背面。求线段到线段和平面间交点的距离(为正)
    // 1.点在平面正面,x1:Ax+By+Cz+D>0,线段方向和法向量的夹角x2:cos<0,dist=-x1/x2>0
    // 2.点在平面背面,x1:Ax+By+Cz+D<0,线段方向和法向量的夹角x2:cos<0,dist=-x1/x2<0
    // 在最终计算交点交点时, vPoint.x = vLine[0].x + vLineDir.x * dist;
    // 不管点在哪一面,交点始终指向平面
    // 注意2:线段:起点 vLine[0],方向vLineDir,定义射线 vLine[0]+t*vLineDir
    // Ax+By+Cz为原点到平面的距离,在之前定义了Ax+By+Cz+distance=0
    // 所以原点到已知平面的距离为 -distance
    // 若线段和射线相交( vLine[0]+t*vLineDir)*vNormal=-distance
    // t=(-distance-vLine[0]*vNormal)/(vNormal*vLineDir)
    Numerator = - (vNormal.x * vLine[0].x + vNormal.y * vLine[0].y +
    vNormal.z * vLine[0].z + distance); // 线段起点到平面的距离
    Denominator = Dot(vNormal, vLineDir); // 法线和线段夹角的cos值
    if( Denominator == 0.0) // 如何平面法向量和线段垂直,线段在平面上,返回线段上任意一点
    return vLine[0];

    dist = Numerator / Denominator; // 线段起点到和平面交点的距离
    vPoint.x = (float)(vLine[0].x + (vLineDir.x * dist));
    vPoint.y = (float)(vLine[0].y + (vLineDir.y * dist));
    vPoint.z = (float)(vLine[0].z + (vLineDir.z * dist));

    return vPoint;
    }

    辅助函数2:InsidePolygon()判断点是否在多边形内
    思路: 当点在多边形内时,点和多边形任意两个角形成的角度之和等于360

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    bool InsidePolygon(CVector3 vIntersection, CVector3 Poly[], long verticeCount) {
    const double MATCH_FACTOR = 0.9999;
    double Angle = 0.0;
    CVector3 vA, vB;
    for (int i = 0; i < verticeCount; i++) {
    vA = Vector(Poly[i], vIntersection);
    vB = Vector(Poly[(i + 1) % verticeCount], vIntersection);
    Angle += AngleBetweenVectors(vA, vB); // 返回(vA*vB)/(|vA|*|vB|)的arcos
    }

    if(Angle >= (MATCH_FACTOR * (2.0 * PI)) )
    return TRUE;

    return FALSE;
    }
  3. 求一个球的球心到一条线段上的最近点
    调用: ClosestPointOnLine(g_vLine[0], g_vLine[1], g_vPosition);
    参数:线段点1,线段点2,小球球心
    思路:球心vPoint,线段起点vA,终点vB,求出线段起点到小球球心向量在线段上的投影长度,线段起点+投影长度*线段方向即为所求。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    CVector3 ClosestPointOnLine(CVector3 vA, CVector3 vB, CVector3 vPoint) {
    CVector3 vVector1 = vPoint - vA; // 线段起点到小球球心向量
    CVector3 vVector2 = Normalize(vB - vA); // 线段方向
    float d = Distance(vA, vB); // d为线段长度
    float t = Dot(vVector2, vVector1); // t为投影长度

    if (t <= 0) // 如果投影长度<0,为起点为最近点
    return vA;
    if (t >= d) // 如果投影长度>线段长度,终点为最近点
    return vB;

    CVector3 vVector3 = vVector2 * t;
    CVector3 vClosestPoint = vA + vVector3;

    return vClosestPoint;
    }

以上。

CATALOG