2

I need to convert a point in the 2d coordinate space of my ARSCNView to a coordinate in 3d space. Basically a ray from the point of view to the touched location (up to a set distance away).

I wanted to use arView.unprojectPoint(vec2d) for that, but the point returned always seems to be located in the center of the view

vec2d is a SCNVector3 created from a 2d coordinate like this

SCNVector3(x, y, 0) // 0 specifies camera near plane

What am I doing wrong? How do I get the desired result?

Andy Jazz
  • 49,178
  • 17
  • 136
  • 220
  • Have a look at Crashalots answer here https://stackoverflow.com/questions/25150737/how-to-use-ios-swift-scenekit-scnscenerenderer-unprojectpoint-properly :) – BlackMirrorz Mar 30 '18 at 12:22

2 Answers2

2

I think you have at least 2 possible solutions:

First

Use hitTest(_:types:) instance method:

This method searches for real-world objects or AR anchors in the captured camera image corresponding to a point in the SceneKit view.

let sceneView = ARSCNView()

func calculateVector(point: CGPoint) -> SCNVector3? {

    let hitTestResults = sceneView.hitTest(point,
                                           types: [.existingPlane])

    if let result = hitTestResults.first {

        return SCNVector3.init(SIMD3(result.worldTransform.columns.3.x,
                                     result.worldTransform.columns.3.y,
                                     result.worldTransform.columns.3.z))
    }
    return nil
}

calculateVector(point: yourPoint)

Second

Use unprojectPoint(_:ontoPlane:) instance method:

This method returns the projection of a point from 2D view onto a plane in the 3D world space detected by ARKit.

@nonobjc func unprojectPoint(_ point: CGPoint, 
            ontoPlane planeTransform: simd_float4x4) -> simd_float3?

or:

let point = CGPoint()
var planeTransform = simd_float4x4()

sceneView.unprojectPoint(point,
                     ontoPlane: planeTransform)
Andy Jazz
  • 49,178
  • 17
  • 136
  • 220
0

Add a empty node infront of camera at 'x' cm offset and making it the child of camera.

    //Add a node in front of camera just after creating scene
    hitNode = SCNNode()
    hitNode!.position = SCNVector3Make(0, 0, -0.25)  //25 cm offset
    sceneView.pointOfView?.addChildNode(hitNode!)


func unprojectedPosition(touch: CGPoint) -> SCNVector3 {
    guard let hitNode = self.hitNode else {
        return SCNVector3Zero
    }

    let projectedOrigin = sceneView.projectPoint(hitNode.worldPosition)
    let offset = sceneView.unprojectPoint(SCNVector3Make(Float(touch.x), Float(touch.y), projectedOrigin.z))

    return offset
}

See the Justaline GitHub implementation of the code here

Atul Vasudev A
  • 463
  • 3
  • 22