ARAnchor is an important type and concept in ARKit. However, there are many problems in use: what is ARAnchor? Why does it always go wrong? I don’t see any obvious benefits. Why don’t we?

What is ARAnchor

According to Apple:

ARAnchor: The real-world location and orientation that can be used to place an object in an AR scene. To track the location and orientation of a physical or virtual object relative to the camera, create an anchor object and add it to the AR session using the add(anchor:) method.Copy the code

Apple engineers have also said: Anchor is the connection point between virtual and reality.

However, this statement is still unclear. Therefore, what is the Anchor point?

I’m thinking

I also thought about this question during development: is there any difference between manually adding an Anchor and binding a SCNNode object to it, and directly adding Node to scene.rootNode?

Coincidentally, Apple also addressed this issue when they talked about multiplayer AR at WWDC2019. Combine me again in the development process of some experience, finally formed a little shallow understanding.

First, to do AR, you must first observe the real world and use math/computer vision to find feature points. Secondly, 3D objects should be placed in the correct position, while Anchor is a virtual position associated with surrounding feature points for placing 3D objects correctly.

What about using the world origin directly? Where rootNode is located? It’s pretty much the same, but a little different, because the origin of the world coordinates is determined and adjusted by combining all the visual features and the phone’s gyroscope data. And ARAnchor will automatically associate with nearby feature points, determine its position according to the nearby feature points and the origin of world coordinates, and make slight adjustments continuously. As you can see below (the World may be shaking as well) :

The ARAnchor class is only associated with nearby feature points, while ARImageAnchor, ARPlaneAnchor and ARObjectAnchor should consider shape and texture in addition to distance factors.

Therefore, theoretically, even if the origin of world coordinates wobble and adjust, these anchors will be more stable because they have their own related feature points.

The actual effect

So in real development, who is more stable when placing virtual objects? After my initial practice, I think stability (the degree to which it is tightly integrated with reality) from high to low is: ARImageAnchor(static) > ARPlaneAnchor >> ARAnchor ≈ WorldOrigin (rootNode origin) >> ARObjectAnchor. >> means much higher. ARFaceAnchor has not been tested because it is not easy to compare.

During development, I found that manually creating ARAnchor and then binding Node objects to it made little difference from adding them directly to rootNode. When the recognition was stable, they did very well, pretty close; When unstable, not so good, ARAnchor a little better.

The static ARImageAnchor identified is the most stable, the ARPlaneAnchor is also very stable when the plane has patterns and is flat, and the ARObjectAnchor is the most unstable, even after recognition, it will keep twisting or shaking, so that in order to be more stable, after recognizing 3D objects, Stop object detection so it’s smoother.

How to use

In actual development, I found that in the process of using ARAnchor, when binding Node to it, adjusting the scale and position of Node was invalid, and only transform or simdTransform could be adjusted.

// Load SCNScene and SCNNode
self.sceneView.scene = [SCNScene sceneNamed:@"art.scnassets/ship.scn"];
self.shipNode = [self.sceneView.scene.rootNode childNodeWithName:@"ship" recursively:YES];

// Set the position and scale. Note that setting scale and position in advance does not work, so transfrom/simdTramsfrom can only be set for unknown reasons
simd_float4x4 trans = simd_diagonal_matrix(simd_make_float4(1.1.1.1));// All the diagonal lines are 1 and the rest are 0, indicating no scaling (1 times scaling), no translation (translation 0)
trans.columns[3] [2] - =0.5;// The fourth column of the matrix is the translation vector, and the third element is the z component, which moves 0.5 m in the opposite direction of the z axis
self.shipNode.simdTransform = trans;

// Create an Anchor and add it to session
ARAnchor *anchor = [[ARAnchor alloc] initWithName:@"ship" transform:trans];
[self.sceneView.session addAnchor:anchor];
Copy the code
// Associate Anchor with Node in the proxy method
- (SCNNode *)renderer:(id<SCNSceneRenderer>)renderer nodeForAnchor:(ARAnchor *)anchor {
    if ([anchor.name isEqualToString:@"ship"]) {
        // Setting scale and position here is also invalid
        // Note: This node is forced to be added under rootNode, regardless of which node it was previously under
        return self.shipNode;
    }
    return nil; } - (void)renderer:(id<SCNSceneRenderer>)renderer didUpdateNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor {
    // This method indicates that the location of anchor has been adjusted relative to the origin of world coordinates. It is found in practice that it is not called often and there is generally no obvious change
    // There are actually two possibilities: one is that the world coordinate wobbles, but the Anchor is closely combined with surrounding objects, resulting in relative movement; Second, the world coordinate has not moved, but the anchor position has been adjusted, which is also relative movement.
    if ([anchor.name isEqualToString:@"ship"]) {
        NSLog(@" Anchor has been adjusted");
    }
    
    //PS. Set the scale and position of node in this method
}
Copy the code

To obtain the mapping between ARAnchor and SCNNode objects, use the following ARSCNView method:

// Get the anchor corresponding to node
- (nullable ARAnchor *)anchorForNode:(SCNNode *)node;

// Get the node corresponding to anchor
- (nullable SCNNode *)nodeForAnchor:(ARAnchor *)anchor;
Copy the code

conclusion

After a round of practice, I found that the most appropriate statement is: Anchor is the connection point between virtual and reality.

Following Apple’s recommendation, when placing any virtual object, we should first create an Anchor and then associate Node with it. However, in actual development, except that ARImageAnchor and ARPlaneAnchor are obviously more stable, other feelings are not much different.

Another trouble is that after Node is associated with Anchor, it will be forced to be placed under rootNode, that is, directly in the world coordinate system (i.e. Node’s transform == worldTransform). This makes it very difficult to pan or rotate several nodes as a whole. Because Apple requires that, unless there are special requirements, it is best to associate each Node with Anchor separately and not place it too far away from Anchor.

I think this is one of the reasons Apple recommends the use of Entity Component System in AR development. Of course, this is just a bit of a guess on my part, as I haven’t used entity component systems much.