instructions

In AR, we sometimes need to place virtual objects in front of or to the right (left) of the user. But this can be difficult: the user’s orientation and that of the camera (i.e., the phone) can sometimes be inconsistent. For example, we developed a portrait version of our AR app, but when the user turns the phone sideways during AR, the right side of the phone faces up or down.

The geometric

In AR, the phone is the camera, and the Y-axis of the world coordinates is always up, opposite to gravity. We can use this to look ahead and to the right.

In front of the

In AR applications, the phone is directly in front of the cameraNode’s -Z direction. We just need to know the direction of this direction in world coordinates.

Camera Node workssceneView.pointOfViewTo get, and-ZDirection issceneView.pointOfView? .simdWorldFront. Of course, we can use itsceneView.pointOfView? .simdLocalFront, and then manually invoke the transformation method simdConvertVector(_:to:) or simdConvertVector(_:from:)To convert to world coordinates.

However, there are other times when we don’t want to be directly in front of the phone, we want to be horizontally in front of the camera. In this case, we need to discard the y-coordinate, and for better distance control, we usually normalize the new vector so that the object is placed one meter in front of the horizontal.

guard let front = sceneView.pointOfView?.simdWorldFront else {return}
let horizontalFront = normalize(simd_float3(front.x, 0, front.z))
Copy the code

To the right

So once we have the front and the top, we can use the cross product to get the right of the horizontal.

guard let front = sceneView.pointOfView?.simdWorldFront else {return}
let horazontalRight = cross(front, simd_float3(0.1.0))
Copy the code

So, if we want the right side to be on the same horizontal plane as the phone, we need to change the calculated y value to the phone’s Y value, or the y value 1 meter in front of the phone:

code

@IBAction func frontBtnClick(_ sender: UIButton) {
    guard let front = sceneView.pointOfView?.simdWorldFront else {return}
// let horizontalFront = normalize(simd_float3(front.x, 0, front.z))
    addChildNode(position: front, color: .red)
}
@IBAction func rightBtnClick(_ sender: UIButton) {
    guard let front = sceneView.pointOfView?.simdWorldFront else {return}
    var horazontalRight = cross(front, simd_float3(0.1.0))
    horazontalRight.y = front.y
    addChildNode(position: horazontalRight, color: .green)
}

private func addChildNode(position:simd_float3, color:UIColor) {
    let cube = SCNBox(width: 0.1, height: 0.1, length: 0.1, chamferRadius: 0)
    cube.firstMaterial?.diffuse.contents = color
    let cubeNode = SCNNode(geometry: cube)
    cubeNode.simdPosition = position
    sceneView.scene.rootNode.addChildNode(cubeNode)
}
Copy the code