简体   繁体   中英

How to temporarily freeze a node in front of the camera using ARKit, SceneKit in Swift

I built a complete structure as a node (with its child nodes) and the user will walk through it using ARKit. At some point, if the user cannot continue because of some real obstacle in the real world, I added a "pause" button which should freeze whatever the user currently sees in front of the camera, the user could then move freely to some other open space and when the user will release the pause button he/she will be able to resume where they left off (only someplace else in the real world).

A while ago I asked about it in the Apple Developer forum and an Apple Frameworks Engineer gave the following reply:

For "freezing" the scene, you could transform the anchor's position (in world coordinates) to camera coordinates, and then anchor your content to the camera. This will give you the effect that the scene is "frozen", ie, does not move relative to the camera.

I'm currently not using an anchor because I don't necessarily need to find a flat surface. Rather, my node is placed at a certain position relative to where we start at (0,0,0).

My question is how do I exactly do what the Apple engineer told me to do? I have the following code which I'm still stuck with. When I add the node to the camera (pointOfView, last line of the code below), it does freeze in place, but I can't get it to freeze in the same position and orientation as it was before it was frozen.

    @IBAction func pauseButtonClicked(_ sender: UIButton) {

    let currentPosition = sceneView.pointOfView?.position
    let currentEulerAngles = sceneView.pointOfView?.eulerAngles
    
    var internalNodeTraversal = lastNodeRootPosition - currentPosition!     // for now, lastNodeRootPosition is (0,0,0)         
    internalNodeTraversal.y = lastNodeRootPosition.y + 20       // just so it’s positioned a little higher in front of the camera

    myNode?.removeFromParentNode()      // remove the node from the Real World view.  Looks like this line has no effect and just adding the node as a child to the camera (pointOfView) is enough, but it feels more right to do this anyway.
    
    myNode?.position = internalNodeTraversal        // the whole node is moved respectively in the opposite direction from the root to where I’m standing to reposition the camera in my current position inside the node
    
 //       myNode?.eulerAngles = (currentEulerAngles! * -1)      — this code put the whole node in weird positions so I removed it
        myNode?.eulerAngles.y = currentEulerAngles!.y * -1  // opposite orientation of the node so the camera will be oriented in the same direction
        myNode?.eulerAngles.x = 0.3     // just tilting it up a little bit to have a better view, more similar to the view as before it was locked to the camera
// I don’t think I need to change the eulerAngles.z
        

    myNode!.convertPosition(internalNodeTraversal, to: sceneView.pointOfView)       // I’m not sure I wrote this correctly.  Also, this line doesn’t seem tp change anything
    
    sceneView.pointOfView?.addChildNode(myNode!)        // attaching the node to the camera so it will remain stuck while the user moves around until the button is released
}

So I first calculate where in the node I'm currently standing and then I change the position of the node in the opposite direction so that the camera will now be in that position. That seems to be correct. Now I need to change the orientation of the node so that it will point in the right direction and here things get funky. I've been trying so many things for days now. I use the eulerAngles for the orientation. If I set the whole vector multiplied by -1, it would show weird orientations. I ended up only using the eulerAngles.y which is the left/right orientation and I hardcoded the x orientation (up/down).

Ultimately what I have in the code above is the closest that I was able to get. If I'm pointing straight, the freeze will be correct. If I turn just a little bit, the freeze will be pretty close as well. Almost the same as what the user saw before the freeze. But the more I turn, the more the frozen image is off and more slanted. At some point (say I turn 50 or 60 degrees to the side) the whole node is off the camera and cannot be seen.

Somehow I have a feeling that there must be an easier and more correct way to achieve the above. The Apple engineer wrote to "transform the anchor's position (in world coordinates) to camera coordinates". For that reason I added the "convertPosition" function in my code, but a) I'm not sure I used it correctly and b) it doesn't seem to change anything in my code if I have that line or not.

What am I doing wrong? Any help would be very much appreciated. Thanks!

I found the solution, Actually. the problem I had was not even described as I didn't think it was relevant. I built the AR nodes 2 meters in front of the origin (-2 for the z-coordinate) while the center of my node was still at the origin, So when I changed the rotation or eulerAngles. it rotated around the origin so my nodes moved in a large curve and in fact also changed their position as a result.

The solution was to use a simdPivot. Instead of changing the position and rotation of the node itself, I created a translation matrix and a rotation matrix which was at the point of the camera (where the user is standing) and I then multiplied both matrices. Now when I added the node as a child of the camera (pointOfView) this would freeze the image and in effect show exactly what the user was seeing before it was frozen as the position is the same and the rotation is exactly around the user's standing position.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM