You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于ARKit与iPhoneX的嘴唇渲染:动作与节点关联问题咨询

嘿,这个问题我之前帮不少做ARKit面部追踪的开发者捋清楚过,咱们一步步来拆解解决~

核心思路:两种关联方式(形状键优先)

ARKit的BlendShapes本质是面部动作的权重值,要和你的嘴唇模型关联,主要有两种方案,优先推荐形状键(Morph Targets)的方式,因为动画更自然;如果你的模型结构简单,也可以用独立子节点来驱动。

一、是否需要拆分嘴唇节点?分两种情况

  • 如果你的DAE模型是一个整体网格:必须先在3D建模软件(Blender、Maya都可以)里给模型添加对应ARKit动作的形状键,或者把嘴唇拆成上唇、下唇、嘴角等独立子节点。
  • 如果DAE里已经把嘴唇的可动部分做成了独立子节点:直接用这些子节点就行,但还是更推荐形状键的方式,动作过渡更平滑。

二、具体关联步骤

方案1:用形状键(Morph Targets)驱动(推荐)

这是和ARKit BlendShapes匹配度最高的方式,步骤如下:

  1. 建模阶段准备:
    在建模软件里给嘴唇模型创建和ARKit BlendShapes对应的形状键。比如:
    • 做一个mouthClose形状键:把嘴唇调整到完全闭合的形态
    • 做一个mouthOpen形状键:把嘴唇调整到张开的形态
    • 对应.mouthSmileLeft、.mouthSmileRight等动作,分别创建对应的形状键
      注意尽量让形状键的名字和ARKit的BlendShapes键名一致,后期代码更省心。
  2. 导入SceneKit后配置:
    导入DAE文件后,在SceneKit编辑器里选中嘴唇节点,查看Morph Targets面板,确认所有形状键都被正确导入。
  3. 代码里绑定权重:
    在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:用独立子节点驱动(适合简单模型)

如果你的嘴唇模型已经拆成了上唇、下唇、左嘴角、右嘴角等独立子节点,可以通过调整节点的位置、旋转来模拟动作:

  1. 记录基准位置:
    先记录每个子节点的初始位置(比如baseUpperLipY = upperLipNode.position.y),作为动作的基准。
  2. 实时驱动节点:
    在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)
        }
    }
    

三、如何确定准确的动作对应关系?

  1. 打印实时BlendShapes值:
    在renderer(_:didUpdate:for:)里遍历所有BlendShapes,打印键和对应的值,这样你做动作的时候就能看到哪个键的数值变化:
    for (key, value) in faceAnchor.blendShapes {
        print("\(key.rawValue): \(value)")
    }
    
    比如你闭嘴时,.mouthClose的值会从0跳到接近1;微笑时,对应侧的.mouthSmileLeft/.mouthSmileRight值会上升。
  2. 逐个调试动作:
    先单独处理一个BlendShape,比如只测试mouthClose,看模型的反应,确认对应正确后再添加其他动作,避免混乱。
  3. 匹配形状键/节点与动作:
    如果用形状键,建模时的形状键形态要和ARKit动作完全对应;如果用独立节点,要根据动作的方向调整节点的平移/旋转方向,比如张嘴时上唇向上,下唇向下,这个逻辑很直观,多试几次就能找到合适的系数。

小提示

  • 确保DAE文件导入时,形状键没有被压缩或丢失,SceneKit编辑器里能看到Morph Targets就没问题。
  • 用独立节点时,要把节点的pivot(轴心)设置在动作的中心点,比如嘴角节点的轴心放在嘴角位置,旋转时才会自然。
  • 实时更新一定要在didUpdate方法里处理,保证每帧都能同步面部动作。

内容的提问来源于stack exchange,提问作者user3490363

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 07:59:12