AR FaceTrackingで鼻を伸ばしてみる
はじめに
ARKitでFaceTrackingができるようだったので試してみました。
ARFaceTrackingConfigurationが使用できない端末は以下のようなエラーログが表示されました。
ARFaceTracking[70313:5827507] [Sensor] ARFaceTrackingImageSensor <0x117f355b0>:
(AVCaptureDeviceTypeBuiltInTrueDepthCamera - Front): Does not support camera
calibration delivery
できたもの
コード
class ViewController: UIViewController {
@IBOutlet var sceneView: ARSCNView! {
didSet {
sceneView.delegate = self
}
}
private var faceNode = SCNNode()
private var virtualFaceNode = SCNNode()
private let serialQueue = DispatchQueue(label: "queue")
override func viewDidLoad() {
super.viewDidLoad()
let device = sceneView.device!
let glassesGeometry = ARSCNFaceGeometry(device: device)!
glassesGeometry.firstMaterial!.colorBufferWriteMask = []
virtualFaceNode.geometry = glassesGeometry
//顔に表示する鼻用のオブジェクト
let box = SCNBox(width: 0.03, height: 0.03, length: 0.1,chamferRadius: 0)
let material = SCNMaterial()
material.diffuse.contents = UIColor.red
let boxNode = SCNNode(geometry: box)
boxNode.geometry?.materials = [material]
boxNode.position = SCNVector3(0, 0, 0.1)
virtualFaceNode.addChildNode(boxNode)
let configuration = ARFaceTrackingConfiguration()
configuration.isLightEstimationEnabled = true
sceneView.session.run(configuration, options: [.resetTracking, .removeExistingAnchors])
}
}
extension ViewController: ARSCNViewDelegate {
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
faceNode = node
serialQueue.async {
self.faceNode.addChildNode(self.virtualFaceNode)
}
}
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
guard let faceAnchor = anchor as? ARFaceAnchor else { return }
let geometry = virtualFaceNode.geometry as! ARSCNFaceGeometry
geometry.update(from: faceAnchor.geometry)
}
}
個別の顔パーツを弄りたい時
ARFaceAnchorにblendShapesというプロパティを保持しているのでその中から引っ張ればいけそうです。
let blendShapes = faceAnchor.blendShapes
guard let eyeBlinkLeft = blendShapes[.eyeBlinkLeft] as? Float,
let eyeBlinkRight = blendShapes[.eyeBlinkRight] as? Float,
let jawOpen = blendShapes[.jawOpen] as? Float
else { return }
eyeLeftNode.scale.z = 1 - eyeBlinkLeft
eyeRightNode.scale.z = 1 - eyeBlinkRight
jawNode.position.y = originalJawY - jawHeight * jawOpen
50個以上あるっぽいのでだいぶ細かいところまで弄れそうな感じでした。