Best way to obtain ARKit or RealityKit camera rotation?

Viewed 821

I am writing in Swift and trying to obtain the RealityKit camera's rotation. I've successfully gotten the position using:

xsettings.xcam = arView.session.currentFrame?.camera.transform.columns.3.x ?? 0);
xsettings.ycam = arView.session.currentFrame?.camera.transform.columns.3.y ?? 0);
xsettings.zcam = arView.session.currentFrame?.camera.transform.columns.3.z ?? 0);

this works excellently, but I haven't found a rotation solution that seems to work as well. Currently I am doing this:

xsettings.xcamrot =  arView.session.currentFrame?.camera.eulerAngles[0] ?? 0;
xsettings.ycamrot =  arView.session.currentFrame?.camera.eulerAngles[1] ?? 0;
xsettings.zcamrot =  arView.session.currentFrame?.camera.eulerAngles[2] ?? 0;

but it doesn't seem to work correctly, there is a lot of weirdness on the roll (eulerAngles[2]) and just some inconsistency overall, at least compared to the positioning which is excellent. Just curious if there is a better way to access the camera's rotation?

2 Answers

It's not weird. An orientation of ARKit's or RealityKit's camera is expressed as roll (z), pitch (x), and yaw (y). Thus you can easily get right values with expressions you've mentioned earlier:

arView.session.currentFrame?.camera.eulerAngles.x
arView.session.currentFrame?.camera.eulerAngles.y
arView.session.currentFrame?.camera.eulerAngles.z

However, the order of rotation is ZYX. Read about it here.

And several words about subscript and dot notation. Each two lines are identical:

DispatchQueue.main.asyncAfter(deadline: .now() + 4.0) {
    // Pitch
    print(arView.session.currentFrame?.camera.eulerAngles[0])  // -0.6444593
    print(arView.session.currentFrame?.camera.eulerAngles.x)   // -0.6444593
    // Yaw
    print(arView.session.currentFrame?.camera.eulerAngles[1])  // -0.69380975
    print(arView.session.currentFrame?.camera.eulerAngles.y)   // -0.69380975
    // Roll
    print(arView.session.currentFrame?.camera.eulerAngles[2])  // -1.5064332
    print(arView.session.currentFrame?.camera.eulerAngles.z)   // -1.5064332
}

That's because ARView camera's eulerAngles instance property is SIMD3<Float> (a.k.a. simd_float3) type that supports subscript and dot notation.

On the other hand, ARSCNView pointOfView's eulerAngles instance property is SCNVector3 type that doesn't support subscript but supports dot notation.

P.S.

You don't need to (and can't) assign a rotation order explicitly because it's implicit inner rotation mechanism.

You might be better off taking the quaternion for rotation, depending on what you're wanting to do with the output.

You can also use arView.cameraTransform to get the camera's transform. From there, translation can be taken from arView.cameraTransform.translation.{x,y,z}, and quaternion rotation with arView.cameraTransform.rotation. One of the benefits of a quaternion here is that you will not have a problem with rotation order.

If you still wanted to get Euler rotations, you can always use MDLTransform:
MDLTransform(matrix: self.cameraTransform.matrix).rotation.{x,y,z}

Related