Arkit - Get Current Position of Arcamera in a Scene

ARKit – Get current position of ARCamera in a scene

Set yourself as the ARSession.delegate. Than you can implement session(_:didUpdate:) which will give you an ARFrame for every frame processed in your session. The frame has an camera property that holds information on the cameras transform, rotation and position.

func session(_ session: ARSession, didUpdate frame: ARFrame) {
// Do something with the new transform
let currentTransform = frame.camera.transform
doSomething(with: currentTransform)
}

As rickster pointed out you always can get the current ARFrame and the camera position through it by calling session.currentFrame.
This is useful if you need the position just once, eg to move a node where the camera has been but you should use the delegate method if you want to get updates on the camera's position.

ARKit – Position of the ARCamera

You can use the ARCamera.transform property to get the current transform of your camera.
The following code is from Introducing ARKit at WWDC 2017 which you probably want to watch as an introduction.
We are using the transform to place a node 10 cm in front of the camera.

In Swift:

var translation = matrix_identity_float4x4
translation.columns.3.z = -0.1 // Translate 10 cm in front of the camera
node.simdTransform = matrix_multiply(currentFrame.camera.transform, translation)

In Objective-C

matrix_float4x4 translation = matrix_identity_float4x4;
translation.columns[3][2] = -0.1; // Translate 10 cm in front of the camera
node.simdTransform = matrix_multiply(currentFrame.camera.transform, translation)

Track camera position with RealityKit

Using ARView Camera Transform:

You can access the ARView Camera Transform using the following method:

var cameraTransform: Transform

The transform of the currently active camera.

So assuming your ARView was called arView you could access the Transform like so:

let cameraTransform = arView.cameraTransform

A more useful implementation however would be to enable your ARView to observe SceneEvents.Updateby making use of the following:

subscribe(to:on:_:)

func subscribe<E>(to event: E.Type, on sourceObject: EventSource? = nil, _ handler: @escaping (E) -> Void) -> Cancellable where E : Event

Which means you would have an observer of any:

event triggered once per frame interval that you can use to execute
custom logic for each frame.

To do that you would:
Firstly import the Combine Framework.

You would then create a Cancellable variable:

var sceneObserver: Cancellable!

Then in ViewDidLoad add something like the following:

sceneObserver = arView.scene.subscribe(to: SceneEvents.Update.self) { [unowned self] in self.updateScene(on: $0) }

Whereby each update calls the following:

/// Callback For ARView Update Events
/// - Parameter event: SceneEvents.Update
func updateScene(on event: SceneEvents.Update) {

print(arView.cameraTransform)

}

Using ARSessionDelegate:

Alternatively you can access the ARCamera from within RealityKit by subscribing to the ARSessionDelegate e.g:

arView.session.delegate = self

And then registering for the following callback:

func session(_ session: ARSession, didUpdate frame: ARFrame)

Whereby a working example would look something like this:

extension ViewController: ARSessionDelegate {

func session(_ session: ARSession, didUpdate frame: ARFrame) {

guard let arCamera = session.currentFrame?.camera else { return }
print("""
ARCamera Transform = \(arCamera.transform)
ARCamera ProjectionMatrix = \(arCamera.projectionMatrix)
ARCamera EulerAngles = \(arCamera.eulerAngles)
""")

}

}

Hope it points you in the right direction.

ARkit - Camera Position and 3D model Positions

If you want to place an SCNNode infront of the camera you can do so like this:

   /// Adds An SCNNode 3m Away From The Current Frame Of The Camera
func addNodeInFrontOfCamera(){

guard let currentTransform = augmentedRealitySession.currentFrame?.camera.transform else { return }

let nodeToAdd = SCNNode()
let boxGeometry = SCNBox(width: 0.1, height: 0.1, length: 0.1, chamferRadius: 0)
boxGeometry.firstMaterial?.diffuse.contents = UIColor.red
nodeToAdd.geometry = boxGeometry

var translation = matrix_identity_float4x4

//Change The X Value
translation.columns.3.x = 0

//Change The Y Value
translation.columns.3.y = 0

//Change The Z Value
translation.columns.3.z = -3

nodeToAdd.simdTransform = matrix_multiply(currentTransform, translation)
augmentedRealityView?.scene.rootNode.addChildNode(nodeToAdd)

}

And you can change any of the X,Y,Z values as you need.

Hope it points you in the right direction...

Update:

If you have multiple nodes e.g. in a scene, in order to use this function, it's probably best to create a 'holder' node, and then add all your content as a child.

Which means then you can simply call this function on the holder node.

How to get the screen central position in arkit?

By looking at your constraints, the cross that you have set is not based on the Safe Area but instead on the whole view.

The bullet that you have is currently at the center of the scene, but your cross is not. You can change the property of the second item to for both Center X and Center Y for your cross.

Changing Constraint

Of course, you can do otherwise by changing your sceneView constraint based on the whole View instead of Safe Area.

Sample Image

How to get the SCNVector3 position of the camera in relation to it's direction ARKit Swift

Edited to better answer the question now that it's clarified in a comment

New Answer:

You are using only the position of the camera, so if the camera is rotated, it doesn't affect the ball.

What you can do is get the transform matrix of the ball and multiply it by the transform matrix of the camera, that way the ball position will be relative to the full transformation of the camera, including rotation.

e.g.

let ballShape = SCNSphere(radius: 0.03)
let ballNode = SCNNode(geometry: ballShape)
ballNode.position = SCNVector3Make(0.0, 0.0, -0.4)
let ballMatrix = ballNode.transform
let cameraMatrix = sceneView.pointOfView!.transform
let newBallMatrix = SCNMatrix4Mult(ballMatrix, cameraMatrix)
ballNode.transform = newBallMatrix
sceneView.scene.rootNode.addChildNode(ballNode)

Or if you only want the SCNVector3 position, to answer exactly to your question (this way the ball will not rotate):

...
let newBallMatrix = SCNMatrix4Mult(ballMatrix, cameraMatrix)
let newBallPosition = SCNVector3Make(newBallMatrix.m41, newBallMatrix.m42, newBallMatrix.m43)
ballNode.position = newBallPosition
sceneView.scene.rootNode.addChildNode(ballNode)

Old Answer:

You are using only the position of the camera, so when the camera rotates, it doesn't affect the ball.

SceneKit uses a hierarchy of nodes, so when a node is "child" of another node, it follows the position, rotation and scale of its "parent". The proper way of attaching an object to another object, in this case the camera, is to make it "child" of the camera.

Then, when you set the position, rotation or any other aspect of the transform of the "child" node, you are setting it relative to its parent. So if you set the position to SCNVector3Make(0.0, 0.0, -0.4), it's translated -0.4 units in Z on top of its "parent" translation.

So to make what you want, it should be:

let ballShape = SCNSphere(radius: 0.03)
let ballNode = SCNNode(geometry: ballShape)
ballNode.position = SCNVector3Make(0.0, 0.0, -0.4)
let cameraNode = sceneView.pointOfView
cameraNode?.addChildNode(ballNode)

This way, when the camera rotates, the ball follows exactly its rotation, but separated -0.4 units from the camera.



Related Topics



Leave a reply



Submit