我正在使用ARKit来创建增强型相机应用。当ARSession初始化时,ARSCNView中会显示一个3d字符。我想要获得这个角色 前往追踪ARCamera的观点,以便他们在用户移动拍照时始终关注相机。
我使用过Apple的变色龙演示,它使用SCNLookAtConstraint添加了一个跟踪摄像机视角的焦点节点但我得到了 奇怪的行为。头部落到侧面并随着ARCamera平移而旋转。如果我添加一个SCNTransformConstraint来限制 头部向上/向下/左右移动,它保持垂直,但然后看起来远离并且不会跟踪。
我尝试分开变色龙演示,看看为什么我的工作不起作用,但几天之后我就被卡住了。
我使用的代码是:
class Daisy: SCNScene, ARCharacter, CAAnimationDelegate {
// Rig for animation
private var contentRootNode: SCNNode! = SCNNode()
private var geometryRoot: SCNNode!
private var head: SCNNode!
private var leftEye: SCNNode!
private var rightEye: SCNNode!
// Head tracking properties
private var focusOfTheHead = SCNNode()
private let focusNodeBasePosition = simd_float3(0, 0.1, 0.25)
// State properties
private var modelLoaded: Bool = false
private var headIsMoving: Bool = false
private var shouldTrackCamera: Bool = false
/*
* MARK: - Init methods
*/
override init() {
super.init()
loadModel()
setupSpecialNodes()
setupConstraints()
}
/*
* MARK: - Setup methods
*/
func loadModel() {
guard let virtualObjectScene = SCNScene(named: "daisy_3.dae", inDirectory: "art.scnassets") else {
print("virtualObjectScene not intialised")
return
}
let wrapper = SCNNode()
for child in virtualObjectScene.rootNode.childNodes {
wrapper.addChildNode(child)
}
self.rootNode.addChildNode(contentRootNode)
contentRootNode.addChildNode(wrapper)
hide()
modelLoaded = true
}
private func setupSpecialNodes() {
// Assign characters rig elements to nodes
geometryRoot = self.rootNode.childNode(withName: "D_Rig", recursively: true)
head = self.rootNode.childNode(withName: "D_RigFBXASC032Head", recursively: true)
leftEye = self.rootNode.childNode(withName: "D_Eye_L", recursively: true)
rightEye = self.rootNode.childNode(withName: "D_Eye_R", recursively: true)
// Set up looking position nodes
focusOfTheHead.simdPosition = focusNodeBasePosition
geometryRoot.addChildNode(focusOfTheHead)
}
/*
* MARK: - Head animations
*/
func updateForScene(_ scene: ARSCNView) {
guard shouldTrackCamera, let pointOfView = scene.pointOfView else {
print("Not going to updateForScene")
return
}
followUserWithHead(to: pointOfView)
}
private func followUserWithHead(to pov: SCNNode) {
guard !headIsMoving else { return }
// Update the focus node to the point of views position
let target = focusOfTheHead.simdConvertPosition(pov.simdWorldPosition, to: nil)
// Slightly delay the head movement and the animate it to the new focus position
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2, execute: {
let moveToTarget = SCNAction.move(to: SCNVector3(target.x, target.y, target.z), duration: 1.5)
self.headIsMoving = true
self.focusOfTheHead.runAction(moveToTarget, completionHandler: {
self.headIsMoving = false
})
})
}
private func setupConstraints() {
let headConstraint = SCNLookAtConstraint(target: focusOfTheHead)
headConstraint.isGimbalLockEnabled = true
let headRotationConstraint = SCNTransformConstraint(inWorldSpace: false) { (node, transform) -> SCNMatrix4 in
// Only track the up/down and side to side movement
var eulerX = node.presentation.eulerAngles.x
var eulerZ = node.presentation.eulerAngles.z
// Restrict the head movement so it doesn't rotate too far
if eulerX < self.rad(-90) { eulerX = self.rad(-90) }
if eulerX > self.rad(90) { eulerX = self.rad(90) }
if eulerZ < self.rad(-30) { eulerZ = self.rad(-30) }
if eulerZ > self.rad(30) { eulerZ = self.rad(30) }
let tempNode = SCNNode()
tempNode.transform = node.presentation.transform
tempNode.eulerAngles = SCNVector3(eulerX, 0, eulerZ)
return tempNode.transform
}
head?.constraints = [headConstraint, headRotationConstraint]
}
// Helper to convert degrees to radians
private func rad(_ deg: Float) -> Float {
return deg * Float.pi / 180
}
}
场景编辑器中的模型是:
答案 0 :(得分:1)
我已经解决了我遇到的问题。有两个问题:
followUserWithHead中的目标应该已经转换了它的父亲的simdWorldPosition并且已经从(而不是)转换为
focusOfTheHead.parent!.simdConvertPosition(pov.simdWorldPosition,from:nil)
头节点的本地坐标不正确。 z轴应该是x轴,因此当我获得焦点时,头部运动跟踪,耳朵始终跟随相机。
我没有意识到Xcode中的 Debug View Hierarchy 将显示SCNScene的详细信息。这有助于我调试场景并找到节点跟踪的位置。您可以将场景导出为dae,然后加载到SceneKit编辑器
修改强> 我使用localFront作为下面评论中建议的mnuages,它使跟踪工作在正确的方向。虽然头部偶尔会移动。我把它放在模型上运行的动画上,试图应用一个转换,然后在下一个更新周期中进行更改。我决定从头部移除跟踪并使用相同的方法仅跟踪眼睛。