#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
}