ARKit – 如何知道 3d 对象是否在屏幕中央?
ARKit – How to know if 3d object is in the center of a screen?
我在世界中放置 3d 对象 space。之后我尝试随机移动相机。然后现在我需要知道在我知道对象通过 isNode
方法进入平截头体内后,对象是在相机视图的中心、顶部还是底部。
解决方案
要做到这一点,你需要使用一个技巧。创建新的 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
您的屏幕可能如下所示:
后续步骤
您可以使用相同的方法附加两个额外的 SCNCameras,将它们放在中央 SCNCamera 的上方和下方,并使用两个额外的 isNode(:insideFrustumOf:)
实例方法测试您的对象。
对于不是 hack 的解决方案,您可以使用 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
}
我用另一种方法解决了问题:
let results = self.sceneView.hitTest(screenCenter!, options: [SCNHitTestOption.rootNode: parentnode])
其中parentnode
是目标节点的父节点,因为我有多个节点。
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 && y {
return node
}
return nil
}
我在世界中放置 3d 对象 space。之后我尝试随机移动相机。然后现在我需要知道在我知道对象通过 isNode
方法进入平截头体内后,对象是在相机视图的中心、顶部还是底部。
解决方案
要做到这一点,你需要使用一个技巧。创建新的 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
您的屏幕可能如下所示:
后续步骤
您可以使用相同的方法附加两个额外的 SCNCameras,将它们放在中央 SCNCamera 的上方和下方,并使用两个额外的 isNode(:insideFrustumOf:)
实例方法测试您的对象。
对于不是 hack 的解决方案,您可以使用 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
}
我用另一种方法解决了问题:
let results = self.sceneView.hitTest(screenCenter!, options: [SCNHitTestOption.rootNode: parentnode])
其中parentnode
是目标节点的父节点,因为我有多个节点。
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 && y {
return node
}
return nil
}