S01E18:直线与球体相交

1,865 阅读1分钟

说明

三维空间中,直线可能与球体相交,也可能不相交。如果相交的话,会有两个交点,恰好相切则是一个。

几何

最简单的方法,就是将球心当成一个点,计算球心到直线的距离。然后与球体半径进行比较。当然,如果只需要比较大小的话,可以用距离的平方来代替距离进行计算。

不过,如果你还记得如何计算点到直线距离的话,我们实际用到了点到直线的垂足(投影点),计算过程用到了向量归一化和点乘。

那么如果要求相交点的坐标呢?其实这只是个简单的二维几何问题,我们已经知道了球心A到直线CD的垂足坐标G,也知道了距离AG(的平方),只要用球体半径的平方 - 距离平方,再开方就得到了交点距离垂足的距离EG、FG。 最后得到了交点 E、F 的坐标。

代码

static func isIntersection2(line:Line, sphere:Sphere) -> Bool {
    let distanceSquared = Line.distanceSquaredBetween(point: sphere.position, line: line)
    return distanceSquared <= sphere.radius * sphere.radius
}

static func intersectionPoint2(line:Line, sphere:Sphere) -> (simd_float3, simd_float3)? {
    let vector = sphere.position - line.position
    let normalizedDirection = normalize(line.direction)
    let dotValue = dot(vector, normalizedDirection)
    let nearPoint = line.position + dotValue * normalizedDirection
    
    let distanceSquared = distance_squared(nearPoint, sphere.position)
    let radiusSquared = sphere.radius * sphere.radius
    
    if distanceSquared > radiusSquared {
        return nil
    }
    let x = sqrtf(radiusSquared - distanceSquared)
    let point1 = nearPoint + x * normalizedDirection
    let point2 = nearPoint - x * normalizedDirection
    return (point1, point2)
}