How to calculate the angle in between two points with regards to camera orientation in 3-D space

459 Views Asked by At

I am currently facing the problem that I want to calculate the angle in radians from the camera's position to a target position. However, this calculation needs to take into account the heading of the camera as well.

For example, when the camera is facing away from the object the function should return π. So far the function I have written works most of the time. However when the user gets close to the X and Z axis the arrow does not point to the target any more, rather it points slightly to the left or right depending if you are at positive or negative X and z space.

Currently, I'm not sure why my function does not work. The only explanation I would have for this behavior is gimbal lock. However I'm not quite sure how to implement the same function using quaternions.

I also attached some photos to this post that the issue is a little bit more clear.

Here is the function I'm using right now:

 func getAngle() -> Float {
    guard let pointOfView = self.sceneView.session.currentFrame else { return 0.0  }
    let cameraPosition =  pointOfView.camera.transform.columns.3

    let heading =  getUserVector()
    let distance = SCNVector3Make(TargetPosition.x - cameraPosition.x ,TargetPosition.y - cameraPosition.y - TargetPosition.y,TargetPosition.z - cameraPosition.z)

    let heading_scalar = sqrtf(heading.x * heading.x + heading.z * heading.z)
    let distance_scalar = sqrtf(distance.z * distance.z + distance.z * distance.z)

    let x = ((heading.x * distance.x) + (heading.z * distance.z) / (heading_scalar * distance_scalar))

    let theta = acos(max(min(x, 1), -1))

    if theta < 0.35 {
        return 0
    }

    if (heading.x * (distance.z / distance_scalar) - heading.z * (distance.x/distance_scalar)) > 0{
        return theta
    }
    else{
        return -theta
    }

}


func getUserVector() -> (SCNVector3) { // (direction)
    if let frame = self.sceneView.session.currentFrame {
        let mat = SCNMatrix4(frame.camera.transform) // 4x4 transform matrix describing camera in world space
        let dir = SCNVector3(-1 * mat.m31, -1 * mat.m32, -1 * mat.m33) // orientation of camera in world space
         print(mat)
        return dir

    }
    return SCNVector3(0, 0, -1)
}

Consider the following image as an example. The arrow in the top right corner should be pointing straight up to follow the line to the center object but instead it is pointing slightly to the left. As I am aligned with the z-axis the same behavior happens when aligning with the x-axis.

enter image description here

1

There are 1 best solutions below

0
On BEST ANSWER

I figured out the answer to my problem the solution was transforming the object into the prospective of the camera and then simply taking the atan2 to get the angle in between the camera and object hope this post will help future readers!

func getAngle() -> Float {
    guard let pointOfView = self.sceneView.session.currentFrame else { return 0.0  }
    let cameraPosition =  pointOfView.camera.transform
    let targetPosition = simd_float4x4(targetNode.transform)
    let newTransform =  simd_mul(cameraPosition.inverse, targetPosition).columns.3
    let theta = atan2(newTransform.z, newTransform.y)
    return theta + (Float.pi / 2)

}