如何在使用ARFaceTrackingConfiguration时将SceneNode始终定位在sceneView的中心

时间:2019-04-29 15:10:31

标签: swift xcode arkit

我已经使用ARFaceTrackingConfiguration会话设置了SceneView,并在Scene资源中创建了一个虚拟3D模型。
该模型已导入SceneView中,但随着摄像机的移动而相应地移动,我需要将模型位置固定在SceneView的中心。
下图演示了我要实现的目标。
因此3D模型不应基于相机的运动而运动。

enter image description here

@IBOutlet var sceneView: ARSCNView!
let scene = SCNScene(named: "art.scnassets/TEST.scn")!
var contentNode: SCNNode?

override func viewDidLoad() {
    super.viewDidLoad()

    // Set the view's delegate
    sceneView.delegate = self

    // Show statistics such as fps and timing information
    sceneView.showsStatistics = true

    sceneView.scene = self.scene
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    // Create a session configuration
    let configuration = ARFaceTrackingConfiguration()
    configuration.isLightEstimationEnabled = true

    // Run the view's session
    sceneView.session.run(configuration)
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    // Pause the view's session
    sceneView.session.pause()
}
// MARK: - ARSCNViewDelegate
func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
    guard let faceAnchor = anchor as? ARFaceAnchor else { return nil }

    let faceGeometry = ARSCNFaceGeometry(device: sceneView.device!)!

    self.contentNode = SCNNode(geometry: faceGeometry)

    return self.contentNode
}

1 个答案:

答案 0 :(得分:0)

这是我自己的问题的答案。

首先将对象静态地放置在sceneView的中间,这意味着您不希望在摄像机移动时移动该对象,需要将其位置设置为摄像机的当前朝向位置。 要获取相机的当前位置,我们需要访问pointOfView值。 pointOfView包含cameraView的当前位置和方向 一种用例是当我们需要在摄像机前面添加节点时。 位置和方向在pointOfView内编码成transformMatrices SCNMatrix4 。 从Apple文档中:

  

下图显示了一些更常见的矩阵配置   您可以进行的转换。将任何坐标乘以   标识变换返回完全相同的坐标。对于其他   变换,如何修改坐标完全取决于   您更改哪些矩阵组件。例如,沿   仅x轴,您将为tx组件提供非零值   转换矩阵的值,并将ty和tz值保留为0。   旋转,您将提供适当的正弦和余弦值   目标旋转角度。

的矩阵配置

enter image description here

P ...! 现在,如果您使用pointOfView方法而不是ARSCNViewDelegate方法,则可以在类主体中获取renderer(_nodeFor:),然后对象将遵循面的几何形状:

guard let pointOfView = self.sceneView.pointOfView else { return }

然后可以通过以下方式将对象放置在屏幕中间:

DispatchQueue.main.async {
    pointOfView.addChildNode(yourNode)
}

因此,现在该对象位于摄像头的中央,并且不会随摄像头的移动而相应地移动。

然后通过上下倾斜头部移动对象,我们需要实现ARSCNViewDelegate并调用:

func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode?

此方法要求委托人提供一个与新添加的锚点相对应的SceneKit节点,在我的场景中,我对FaceAnchor感兴趣。 我已为透明度分配给faceAnchor的脸部几何图形设置为0,因为我不需要看到它只是为了通过该节点跟踪脸部位置,当脸部移动时,将调用didUpdate方法,然后我们可以使用didUpdate方法更新节点或更新节点,因为只要目标节点移动,它就会被调用。

func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
    guard let faceAnchor = anchor as? ARFaceAnchor else { return }

    let faceTransform = faceAnchor.transform
    let positionY = faceLocation.columns.2.y
    let positionX = faceLocation.columns.2.x
    let horizentalLine = Int(positionY * 100)

    self.horizentalNode.eulerAngles = SCNVector3(horizentalLine.degreeToRadians, 0, 0)

因此,正如我之前说过的,在每个节点/锚点内部都有一个称为transform的属性,该属性是一个矩阵,该矩阵编码锚点相对于放置该锚点的AR会话的世界坐标空间的位置,方向和比例。 简而言之:这与3维指标有关。 如果发现第2列中的y和x值是头部的水平和垂直移动,则那里的小数学运算符+ degreeToRadians的扩展名只是用来转换eulerAngles旋转矩阵的值。