Ios 场景中的ARKit 3D头部跟踪
我正在使用ARKit创建一个增强相机应用程序。当ARSession初始化时,ARSCNView中会显示一个3d角色。我正试图得到角色的 头部跟踪ARCamera的视角,以便在用户移动拍照时,他们始终注视着相机 我使用了苹果的变色龙演示,它添加了一个焦点节点,使用SCNLookAtConstraint跟踪相机的视角,但我得到了一些帮助 奇怪的行为。随着ARCamera平移,头部下降到一侧并旋转。如果我添加SCNTransformConstraint来限制 头部向上/向下/左右移动,它保持垂直,但随后会向外看,不跟踪 我试着把变色龙的演示分开,看看为什么我的不起作用,但几天后我就卡住了 我使用的代码是:Ios 场景中的ARKit 3D头部跟踪,ios,scenekit,arkit,Ios,Scenekit,Arkit,我正在使用ARKit创建一个增强相机应用程序。当ARSession初始化时,ARSCNView中会显示一个3d角色。我正试图得到角色的 头部跟踪ARCamera的视角,以便在用户移动拍照时,他们始终注视着相机 我使用了苹果的变色龙演示,它添加了一个焦点节点,使用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
}
}
class-Daisy:SCNScene、ARCharacter、CAAnimationDelegate{
//动画装备
私有变量contentRootNode:SCNNode!=SCNNode()
私人var geometryRoot:SCNNode!
私有变量头:SCNNode!
列兵var leftEye:SCNNode!
私人var右眼:SCNNode!
//头部跟踪特性
私有变量focusofhead=SCNNode()
私有let focusNodeBasePosition=simd_float3(0,0.1,0.25)
//状态属性
private var modelLoaded:Bool=false
private var headIsMoving:Bool=false
私有变量shouldTrackCamera:Bool=false
/*
*MARK:-Init方法
*/
重写init(){
super.init()
loadModel()
setupSpecialNodes()
setupConstraints()
}
/*
*标记:-设置方法
*/
func loadModel(){
guard let virtualObjectScene=SCNScene(名为:“daisy_3.dae”,目录:“art.scnassets”)else{
打印(“virtualObjectScene未初始化”)
返回
}
let wrapper=SCNNode()
对于virtualObjectScene.rootNode.childNodes中的子节点{
wrapper.addChildNode(子节点)
}
self.rootNode.addChildNode(contentRootNode)
contentRootNode.addChildNode(包装器)
隐藏()
modelLoaded=true
}
专用函数设置专用节点(){
//将角色装配元素指定给节点
geometryRoot=self.rootNode.childNode(名称为“D_Rig”,递归:true)
head=self.rootNode.childNode(名称为“D_RigFBXASC032Head”,递归:true)
leftEye=self.rootNode.childNode(名称为“D_Eye_L”,递归:true)
rightEye=self.rootNode.childNode(名称为“D_Eye_R”,递归:true)
//设置查找位置节点
focusofhead.simdPosition=focusNodeBasePosition
geometryRoot.addChildNode(头部焦点)
}
/*
*马克:-头部动画
*/
func updateForScene(u场景:ARSCNView){
警卫应该跟踪摄像机,让pointOfView=scene.pointOfView-else{
打印(“不去updateForScene”)
返回
}
followUserWithHead(到:视点)
}
private func followUserWithHead(到pov:SCNNode){
守卫!头正在移动其他{返回}
//将焦点节点更新到“视点”位置
让target=focusofhead.simdConvertPosition(pov.simdWorldPosition,to:nil)
//稍微延迟头部移动,并将其设置为新焦点位置的动画
DispatchQueue.main.asyncAfter(截止日期:.now()+0.2,执行:{
让moveToTarget=SCNAction.move(到:scinvector3(target.x,target.y,target.z),持续时间:1.5)
self.headIsMoving=真
self.focusof head.runAction(moveToTarget,completionHandler:{
self.headIsMoving=false
})
})
}
专用函数setupConstraints(){
让头部约束=SCNLookAtConstraint(目标:头部焦点)
headConstraint.IsGimBallocked=true
让headRotationConstraint=SCNTransformConstraint(inWorldSpace:false){(节点,变换)->中的SCNMatrix4
//仅跟踪向上/向下和侧向移动
var eulerX=node.presentation.eulerAngles.x
var eulerZ=node.presentation.eulerAngles.z
//限制头部移动,使其不会旋转太远
如果eulerXself.rad(90){eulerX=self.rad(90)}
如果eulerZself.rad(30){eulerZ=self.rad(30)}
设tempNode=SCNNode()
tempNode.transform=node.presentation.transform
tempNode.eulerAngles=SCNVector3(eulerX,0,eulerZ)
返回tempNode.transform
}
头?.constraints=[头约束,头旋转约束]
}
//将度转换为弧度的辅助对象
专用函数rad(deg:Float)->Float{
返回度*浮点数/180
}
}
场景编辑器中的模型是:
我已经解决了我遇到的问题。有两个问题: