ARKit — Как узнать, находится ли 3D-объект в центре экрана?

#swift #augmented-reality #scenekit #arkit #frustum

#swift #дополненная реальность #scenekit #arkit #усечение

Вопрос:

Я помещаю 3d-объект в мировое пространство. После этого я пытаюсь перемещать камеру случайным образом. Тогда прямо сейчас мне нужно знать, после того, как я узнал, что объект оказался внутри усеченного с помощью isNode метода, находится ли объект в центре, сверху или снизу камеры.

Ответ №1:

Для решения, которое не является взломом, вы можете использовать projectPoint: API. Вероятно, лучше работать с координатами пикселей, потому что этот метод использует фактические настройки камеры, чтобы определить, где объект появляется на экране.

 let projectedPoint = sceneView.projectPoint(self.sphereNode.worldPosition)
let xOffset = projectedPoint.x - screenCenter.x; 
let yOffset = projectedPoint.y - screenCenter.y; 
if xOffset * xOffset   yOffset * yOffset < R_squared {
    // inside a disc of radius 'R' at the center of the screen
}
  

Комментарии:

1. что вы подразумеваете под R_squared?

Ответ №2:

Решение

Для достижения этого вам нужно использовать хитрость. Создайте новую SCNCamera, сделайте ее дочерней pointOfView камерой по умолчанию и установите ее FoV примерно на 10 градусов.

Затем внутри renderer(_:updateAtTime:) метода экземпляра используйте isNode(:insideFrustumOf:) метод.

Вот рабочий код:

 import ARKit

class ViewController: UIViewController,
                      ARSCNViewDelegate,
                      SCNSceneRendererDelegate {

    @IBOutlet var sceneView: ARSCNView!
    @IBOutlet var label: UILabel!
    let cameraNode = SCNNode()
    let sphereNode = SCNNode()
    let config = ARWorldTrackingConfiguration()
    
    public func renderer(_ renderer: SCNSceneRenderer,
                  updateAtTime time: TimeInterval) {
        
        DispatchQueue.main.async {
            if self.sceneView.isNode(self.sphereNode,
                                     insideFrustumOf: self.cameraNode) {
                self.label.text = "In the center..."
            } else {
                self.label.text = "Out OF CENTER"
            }
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()

        sceneView.delegate = self
        sceneView.allowsCameraControl = true
        let scene = SCNScene()
        sceneView.scene = scene
        
        cameraNode.camera = SCNCamera()
        cameraNode.camera?.fieldOfView = 10
        
        DispatchQueue.main.asyncAfter(deadline: .now()   0.5) {
            self.sceneView.pointOfView!.addChildNode(self.cameraNode)
        }
        
        sphereNode.geometry = SCNSphere(radius: 0.05)
        sphereNode.geometry?.firstMaterial?.diffuse.contents = UIColor.red
        sphereNode.position.z = -1.0
        sceneView.scene.rootNode.addChildNode(sphereNode)
        sceneView.session.run(config)
    }
}
  

Кроме того, в этом решении вы можете включить orthographic projection дочернюю камеру вместо perspective одной. Это помогает, когда модель находится далеко от камеры.

 cameraNode.camera?.usesOrthographicProjection = true
  

Вот как может выглядеть ваш экран:

введите описание изображения здесь

Следующие шаги

Таким же образом вы можете добавить две дополнительные SCNCamera, разместить их над и под центральной SCNCamera и протестировать ваш объект с помощью двух дополнительных isNode(:insideFrustumOf:) методов экземпляра.

Комментарии:

1. Большое вам спасибо за ценный ответ!, Я хочу спросить вас: почему вы добавили узел камеры в корневой узел, если он уже находится в поле зрения?

2. Да, вы абсолютно правы., @lady. Нет необходимости подключать эту камеру к корневому узлу сцены. Это просто привычка))

Ответ №3:

Я решил проблему другим способом:

  let results = self.sceneView.hitTest(screenCenter!, options: [SCNHitTestOption.rootNode: parentnode])
  

где parentnode родительский узел целевого узла, потому что у меня несколько узлов.

Комментарии:

1. В этом сценарии я получил именно то, что мне нужно, но проблема: результат возвращается только тогда, когда камера находится над центром объекта. Почему мне нужна каждая точка узла внутри окна bouding????????

2. Я рад, что вы нашли свой сценарий. Но тестирование попадания и преобразование лучей требуют больше ресурсов процессора, особенно trackedRaycast() метод.

Ответ №4:

     func nodeInCenter() -> SCNNode? {
        let x = (Int(sceneView.projectPoint(sceneView.pointOfView!.worldPosition).x - sceneView.projectPoint(sphereNode.worldPosition).x) ^^ 2) < 9
        let y = (Int(sceneView.projectPoint(sceneView.pointOfView!.worldPosition).y - sceneView.projectPoint(sphereNode.worldPosition).y) ^^ 2) < 9
            if x amp;amp; y {
                return node
            }
            return nil
        }