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

How to position SceneNode always at the center of the sceneView while using ARFaceTrackingConfiguration

我已经使用 ARFaceTrackingConfiguration 会话设置了 SceneView,并在场景资产中制作了一个虚拟 3D 模型。
模型导入到场景视图中,但它会随着相机的移动而相应移动,我需要的是将模型位置固定在场景视图的中心。
下图展示了我正在努力实现的目标。
因此 3D 模型不应根据相机移动而移动。

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

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

首先静态的在sceneView中间放置一个物体,也就是说你不希望物体随着相机的移动而移动,你需要将它的位置设置为相机当前的朝向位置。 要获取相机的当前位置,我们需要访问 pointOfView 值。 pointOfView 包含cameraView的当前位置和方向 一个用例是当我们需要在相机前面添加节点时。 位置和方向在 pointOfView 内部编码为 transformMatrices SCNMatrix4 . 来自 Apple 文档:

Image below shows the matrix configurations for some of the more common transformations you can make. Multiplying any coordinate by the identity transform returns the exact same coordinate. For other transformations, how the coordinate is modified depends entirely on which matrix components you change. For example, to translate along the x-axis only, you would supply a nonzero value for the tx component of the translation matrix and leave the ty and tz values to 0. For rotations, you would provide the appropriate sine and cosine values of the target rotation angle. Matrix configurations for

呜呜……! 现在在 class 主体中获取 pointOfView,而不是在 ARSCNViewDelegate 方法中,如果您在 renderer(_nodeFor:) 方法中执行此操作,那么您的对象将遵循面部几何形状:

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

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

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

所以现在对象在相机前面居中,它不会随着相机移动而相应移动。

然后为了通过倾斜头部移动对象 up/down 我们需要实现 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)

所以正如我之前所说,在每个 node/anchor 中有一个 属性 称为变换,它是一个矩阵,编码锚点相对于世界坐标的位置、方向和比例 space 放置锚点的 AR 会话。 简而言之:这与 3 维指标有关。 如果发现第 2 列中的 y 和 x 值是头部的水平和垂直运动,那里的小数学 + degreeToRadians 扩展只是将值转换为 eulerAngles 旋转矩阵。