基于ARKit与iPhoneX的嘴唇渲染:动作与节点关联问题咨询
嘿,这个问题我之前帮不少做ARKit面部追踪的开发者捋清楚过,咱们一步步来拆解解决~
核心思路:两种关联方式(形状键优先)
ARKit的BlendShapes本质是面部动作的权重值,要和你的嘴唇模型关联,主要有两种方案,优先推荐形状键(Morph Targets)的方式,因为动画更自然;如果你的模型结构简单,也可以用独立子节点来驱动。
一、是否需要拆分嘴唇节点?分两种情况
- 如果你的DAE模型是一个整体网格:必须先在3D建模软件(Blender、Maya都可以)里给模型添加对应ARKit动作的形状键,或者把嘴唇拆成上唇、下唇、嘴角等独立子节点。
- 如果DAE里已经把嘴唇的可动部分做成了独立子节点:直接用这些子节点就行,但还是更推荐形状键的方式,动作过渡更平滑。
二、具体关联步骤
方案1:用形状键(Morph Targets)驱动(推荐)
这是和ARKit BlendShapes匹配度最高的方式,步骤如下:
- 建模阶段准备:
在建模软件里给嘴唇模型创建和ARKit BlendShapes对应的形状键。比如:- 做一个
mouthClose形状键:把嘴唇调整到完全闭合的形态 - 做一个
mouthOpen形状键:把嘴唇调整到张开的形态 - 对应
.mouthSmileLeft、.mouthSmileRight等动作,分别创建对应的形状键
注意尽量让形状键的名字和ARKit的BlendShapes键名一致,后期代码更省心。
- 做一个
- 导入SceneKit后配置:
导入DAE文件后,在SceneKit编辑器里选中嘴唇节点,查看Morph Targets面板,确认所有形状键都被正确导入。 - 代码里绑定权重:
在renderer(_:didUpdate:for:)方法里,实时获取ARFaceAnchor的BlendShapes值,赋值给对应形状键的权重:func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) { guard let faceAnchor = anchor as? ARFaceAnchor else { return } // 获取闭嘴动作的权重值(范围0-1) if let mouthCloseValue = faceAnchor.blendShapes[.mouthClose] as? CGFloat { // 给对应形状键设置权重 lipNode.geometry?.morphTargetsSet.setValue(mouthCloseValue, forKey: "mouthClose") } // 同理处理其他动作,比如张嘴、微笑 if let mouthOpenValue = faceAnchor.blendShapes[.mouthOpen] as? CGFloat { lipNode.geometry?.morphTargetsSet.setValue(mouthOpenValue, forKey: "mouthOpen") } }
方案2:用独立子节点驱动(适合简单模型)
如果你的嘴唇模型已经拆成了上唇、下唇、左嘴角、右嘴角等独立子节点,可以通过调整节点的位置、旋转来模拟动作:
- 记录基准位置:
先记录每个子节点的初始位置(比如baseUpperLipY = upperLipNode.position.y),作为动作的基准。 - 实时驱动节点:
在renderer(_:didUpdate:for:)方法里,根据BlendShapes的值调整节点属性:func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) { guard let faceAnchor = anchor as? ARFaceAnchor else { return } // 处理张嘴动作:上唇上移,下唇下移 if let mouthOpenValue = faceAnchor.blendShapes[.mouthOpen] as? CGFloat { upperLipNode.position.y = baseUpperLipY + (mouthOpenValue * 0.4) // 0.4是调试出来的系数,根据模型大小调整 lowerLipNode.position.y = baseLowerLipY - (mouthOpenValue * 0.4) } // 处理左侧微笑:左嘴角左移+上移 if let smileLeftValue = faceAnchor.blendShapes[.mouthSmileLeft] as? CGFloat { leftMouthCornerNode.position.x = baseLeftCornerX - (smileLeftValue * 0.3) leftMouthCornerNode.position.y = baseLeftCornerY + (smileLeftValue * 0.2) } }
三、如何确定准确的动作对应关系?
- 打印实时BlendShapes值:
在renderer(_:didUpdate:for:)里遍历所有BlendShapes,打印键和对应的值,这样你做动作的时候就能看到哪个键的数值变化:
比如你闭嘴时,for (key, value) in faceAnchor.blendShapes { print("\(key.rawValue): \(value)") }.mouthClose的值会从0跳到接近1;微笑时,对应侧的.mouthSmileLeft/.mouthSmileRight值会上升。 - 逐个调试动作:
先单独处理一个BlendShape,比如只测试mouthClose,看模型的反应,确认对应正确后再添加其他动作,避免混乱。 - 匹配形状键/节点与动作:
如果用形状键,建模时的形状键形态要和ARKit动作完全对应;如果用独立节点,要根据动作的方向调整节点的平移/旋转方向,比如张嘴时上唇向上,下唇向下,这个逻辑很直观,多试几次就能找到合适的系数。
小提示
- 确保DAE文件导入时,形状键没有被压缩或丢失,SceneKit编辑器里能看到Morph Targets就没问题。
- 用独立节点时,要把节点的
pivot(轴心)设置在动作的中心点,比如嘴角节点的轴心放在嘴角位置,旋转时才会自然。 - 实时更新一定要在
didUpdate方法里处理,保证每帧都能同步面部动作。
内容的提问来源于stack exchange,提问作者user3490363
相关产品推荐
相关产品推荐

