建筑网站设计大全,酒店网站开发,唐山cms模板建站,励志做的很好的网站在上一节中已经了解了 iOS ARkit 进行BlendShapes的基本操作#xff0c;这一小节继续实践同时开启前后摄像头进行人脸捕捉和世界追踪。 iOS设备配备了前后两个摄像头#xff0c;在运行AR 应用时#xff0c;需要选择使用哪个摄像头作为图像输人。最常见的AR 体验使用设备后置… 在上一节中已经了解了 iOS ARkit 进行BlendShapes的基本操作这一小节继续实践同时开启前后摄像头进行人脸捕捉和世界追踪。 iOS设备配备了前后两个摄像头在运行AR 应用时需要选择使用哪个摄像头作为图像输人。最常见的AR 体验使用设备后置摄像头进行世界跟踪、虚实融合通常使用 ARWorldTrackingConfiguration 配置跟踪使用者的真实环境。除了进行虚实融合我们通常还利用后置摄像头采集的图像信息评估真实世界中的光照情况、对真实环境中的2D图像或者3D物体进行检测等。 对具备前置深度相机TrueDepth Camera或者A12及以上处理器的设备使用 ARFaceTrackingConfiguration配置可以实时进行人脸检测跟踪实现人脸姿态和表情的捕捉。拥有前置深度相机或 A12及以上处理器硬件的iPhone/iPad在运行iOS 13及以上系统时还可以同时开启设备前后摄像头即同时进行人脸检测和世界跟踪。这是一项非常有意义且实用的功能意味着使用者可以使用表情控制场景中的虚拟物体实现除手势与语音之外的另一种交互方式。 在 RealityKit 中同时开启前后摄像头需要使用 ARFaceTrackingConfiguration 配置或者ARWorldTrackingConfiguration 配置之一。使用 ARFaceTracking Configuration 配置时将其 supportsWorldTracking属性设置为 true使用 ARWorldTrackingConfiguration 配置时将其 userFaceTrackingEnabled 属性设置为true 都可以在支持人脸检测的设备上同时开启前后摄像头。 同时开启前后摄像头后RealityKit 会使用后置摄像头跟踪现实世界同时也会通过前置摄像头实时检测人脸信息包括人脸表情信息。 需要注意的是并不是所有设备都支持同时开启前后摄像头只有符合前文所描述的设备才支持该功能因此在使用之前也应当对该功能的支持情况进行检查。在不支持同时开启前后摄像头的设备上应当执行另外的策略如提示用户进行只使用单个摄像头的操作。 在下面的演示中我们会利用后置摄像头的平面检测功能在检测到的水平平面上放置机器头像模型然后利用从前置摄像头中捕获的人脸表情信息驱动头像模型。核心代码如代码如下所示。
//
// BlendShapeRobot.swift
// ARKitDeamo
//
// Created by zhaoquan du on 2024/1/25.
//import SwiftUI
import ARKit
import RealityKitstruct BlendShapeRobot: View {var body: some View {BlendShapeRobotContainer().edgesIgnoringSafeArea(.all)}
}struct BlendShapeRobotContainer :UIViewRepresentable{func makeUIView(context: Context) - ARView {let arView ARView(frame: .zero)return arView}func updateUIView(_ uiView: UIViewType, context: Context) {guard ARFaceTrackingConfiguration.isSupported else {return}let config ARWorldTrackingConfiguration()config.userFaceTrackingEnabled trueconfig.isLightEstimationEnabled trueconfig.worldAlignment .gravityconfig.planeDetection .horizontaluiView.session.delegate context.coordinatoruiView.automaticallyConfigureSession falseuiView.session.run(config, options: [])let planeAnchor AnchorEntity(plane:.horizontal)planeAnchor.addChild(context.coordinator.robotHead)uiView.scene.addAnchor(planeAnchor)}func makeCoordinator() - Coordinator {Coordinator()}class Coordinator: NSObject, ARSessionDelegate{var robotHead RobotHead()func session(_ session: ARSession, didUpdate anchors: [ARAnchor]) {for anchor in anchors {guard let anchor anchor as? ARFaceAnchor else {continue}robotHead.update(with: anchor)}}}}在代码中我们首先对设备支持情况进行检查在确保设备支持同时开启前后摄像头功能时使用 ARWorldTrackingConfiguration 配置并运行 AR进程然后在检测到平面时将机器头像模型放置于平面上最后利用 sessiondidUpdate frame 代理方法使用实时捕获到的人脸表情数据更新机器头像模型从而达到了使用人脸表情驱动场景中模型的目的。需要注意的是代码中 userFaceTrackingEnabled 必须设置为true并且开启平面检测功能另外为更好地组织代码我们将与模型及表情驱动相关的代码放到了RobotHead类中。RobotHead类用于管理机器头像模型加载及使用表情数据驱动模型的工作关键代码如下所示。
//
// RobotHead.swift
// ARKitDeamo
//
// Created by zhaoquan du on 2024/1/25.
//import RealityKit
import ARKitclass RobotHead: Entity, HasModel {// Default color valuesprivate let eyeColor: SimpleMaterial.Color .blueprivate let eyebrowColor: SimpleMaterial.Color .brownprivate let headColor: SimpleMaterial.Color .greenprivate let lipColor: SimpleMaterial.Color .lightGrayprivate let mouthColor: SimpleMaterial.Color .grayprivate let tongueColor: SimpleMaterial.Color .redprivate let clearColor: SimpleMaterial.Color .clearprivate var originalJawY: Float 0private var originalUpperLipY: Float 0private var originalEyebrowY: Float 0private lazy var eyeLeftEntity findEntity(named: eyeLeft)!private lazy var eyeRightEntity findEntity(named: eyeRight)!private lazy var eyebrowLeftEntity findEntity(named: eyebrowLeft)!private lazy var eyebrowRightEntity findEntity(named: eyebrowRight)!private lazy var jawEntity findEntity(named: jaw)!private lazy var upperLipEntity findEntity(named: upperLip)!private lazy var headEntity findEntity(named: head)!private lazy var tongueEntity findEntity(named: tongue)!private lazy var mouthEntity findEntity(named: mouth)!private lazy var jawHeight: Float {let bounds jawEntity.visualBounds(relativeTo: jawEntity)return (bounds.max.y - bounds.min.y)}()private lazy var height: Float {let bounds headEntity.visualBounds(relativeTo: nil)return (bounds.max.y - bounds.min.y)}()required init() {super.init()if let robotHead try? Entity.load(named: robotHead) {robotHead.position.y 0.05addChild(robotHead)} else {fatalError(无法加载模型.)}originalJawY jawEntity.position.yoriginalUpperLipY upperLipEntity.position.yoriginalEyebrowY eyebrowLeftEntity.position.ysetColor()}func setColor(){headEntity.color headColoreyeLeftEntity.color eyeColoreyeRightEntity.color eyeColoreyebrowLeftEntity.color eyebrowColoreyebrowRightEntity.color eyebrowColorupperLipEntity.color lipColorjawEntity.color lipColormouthEntity.color mouthColortongueEntity.color tongueColor}// MARK: - Animations/// - Tag: InterpretBlendShapesfunc update(with faceAnchor: ARFaceAnchor) {// Update eyes and jaw transforms based on blend shapes.let blendShapes faceAnchor.blendShapesguard let eyeBlinkLeft blendShapes[.eyeBlinkLeft] as? Float,let eyeBlinkRight blendShapes[.eyeBlinkRight] as? Float,let eyeBrowLeft blendShapes[.browOuterUpLeft] as? Float,let eyeBrowRight blendShapes[.browOuterUpRight] as? Float,let jawOpen blendShapes[.jawOpen] as? Float,let upperLip blendShapes[.mouthUpperUpLeft] as? Float,let tongueOut blendShapes[.tongueOut] as? Floatelse { return }eyebrowLeftEntity.position.y originalEyebrowY 0.03 * eyeBrowLefteyebrowRightEntity.position.y originalEyebrowY 0.03 * eyeBrowRighttongueEntity.position.z 0.1 * tongueOutjawEntity.position.y originalJawY - jawHeight * jawOpenupperLipEntity.position.y originalUpperLipY 0.05 * upperLipeyeLeftEntity.scale.z 1 - eyeBlinkLefteyeRightEntity.scale.z 1 - eyeBlinkRightlet cameraTransform self.parent?.transformMatrix(relativeTo: nil)let faceTransformFromCamera simd_mul(simd_inverse(cameraTransform!), faceAnchor.transform)let rotationEulers faceTransformFromCamera.eulerAngleslet mirroredRotation Transform(pitch: rotationEulers.x, yaw: -rotationEulers.y .pi, roll: rotationEulers.z)self.orientation mirroredRotation.rotation}
}extension Entity {var color: SimpleMaterial.Color? {get {if let model components[ModelComponent.self] as? ModelComponent,let color (model.materials.first as? SimpleMaterial)?.color.tint {return color}return nil}set {if var model components[ModelComponent.self] as? ModelComponent {if let color newValue {model.materials [SimpleMaterial(color: color, isMetallic: false)]} else {model.materials []}components[ModelComponent.self] model}}}
}extension simd_float4x4 {// Note to ourselves: This is the implementation from AREulerAnglesFromMatrix.// Ideally, this would be RealityKit API when this sample gets published.var eulerAngles: SIMD3Float {var angles: SIMD3Float .zeroif columns.2.y 1.0 - .ulpOfOne * 10 {angles.x -.pi / 2angles.y 0angles.z atan2(-columns.0.z, -columns.1.z)} else if columns.2.y -1.0 .ulpOfOne * 10 {angles.x -.pi / 2angles.y 0angles.z atan2(columns.0.z, columns.1.z)} else {angles.x asin(-columns.2.y)angles.y atan2(columns.2.x, columns.2.z)angles.z atan2(columns.0.y, columns.1.y)}return angles}
}在代码中我们首先从 ARFaceAnchor 中获取 BendShapes 表情运动因子集合并从中取出感兴趣的运动因子然后利用这些表情因子对机器头像模型中的子实体对象相关属性进行调整最后处理了人脸与模型旋转关系的对应问题。 在支持同时开启前置与后置摄像头的设备上编译运行当移动设备在检测到的水平平面时放置好机器头像模型将前置摄像头对准人脸可以使用人脸表情驱动机器头像模型当人体头部旋转时机器头像模理也会相应地进行旋转实现效果如图 所示。 以上演示的是一个简单的实例完整实现了利用前置摄像头采集的人脸表情信息控制后置摄像头模型的功能。在使用前置摄像头时后置摄像头可以进行世界追踪。 由于Realiy Kit 目前沒有控制网格变形的函数要实现利用人脸表情控制驱动模型的功能需要手动进行人脸表情与模型状态变化的绑定人工计算模型中各因子对应的位置与方问这是一个比较容易出错的过程。经过测试发现ARKit 对人脸表情的捕捉还是比较准确的在使用配备深度相机的设备时捕捉精度较高可以应付一般应用需求。
具体代码地址https://github.com/duzhaoquan/ARkitDemo.git