你能获得什么
- ARKit的基本使用方法
- 人脸滤镜
- 举一反三学会其他内容
原理
通过人脸追踪技术,将自己绘制的贴图贴到人脸上。
结果
开始
需要导入的库
import UIKit
import SceneKit
import ARKit
设备检测以及摄像头权限
AR的应用毋庸置疑需要摄像头的权限,所以在调用的时候首先要在Info.plist文件中添加字段CameraUsageDescription,在Value处填写应用调用摄像头的目的,例如"该应用需要调用您摄像头来进行AR操作".
人脸追踪的技术需要前置的TrueDepth摄像头(iPhone X及以上),对手机设备有要求,调用人脸追踪技术的时候需要先检查当前设备是否支持。如下代码
//检测当前设备是否支持AR
private lazy var isSupproted:Bool = {
guard ARConfiguration.isSupported else{
return false
}
guard ARFaceTrackingConfiguration.isSupported else{
return false
}
return true
}()
前置内容显示
增加现实,目的就是将虚拟的物体与现实世界相结合起来,首先需要显示内容。虚拟内容通过ARSCNView显示。
private lazy var sceneView:ARSCNView = {
let view = ARSCNView(frame: self.view.frame)
view.session.delegate = self
view.delegate = self
return view
}()
再加sceneView添加到主视图中
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(sceneView)
}
将视图添加后便需要让数据流展现在View上,为了性能我们可以在ViewController隐藏的时候让sceneView的数据流停止,在ViewController显示的时候重新运行。
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
sceneView.session.pause()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if isSupproted{
//开始捕获视频流
sceneView.session.run(faceConfiguration, options: [.removeExistingAnchors,.resetTracking])
}
}
到现在为止,没有什么意外的话,你的应用应该可以显示前置摄像头的内容了,虽然看上去好像跟普通的前置没有区别,实际上前置摄像头一直在追踪着你的人脸,只是没有内容输出。
虚拟节点的显示
我们在上面操作后便获得了现实场景,现在尝试着将虚拟节点添加上去吧!那么虚拟节点的渲染是通过ARSCNViewDelegate回调实现的,在这里主要使用到的方法是
///处理数据流出现问题时的毁掉
func session(_ session: ARSession, didFailWithError error: Error)
///当人脸开始移动的时候,追踪人脸并且将贴图贴到指定地方
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor)
///相当于初始化,将虚拟节点贴到现实场景中
func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor)
SCNNode
这个便是虚拟节点类,包含着节点的构成信息(geomerty),构成信息中的材料(firstMaterial),包含着虚拟节点的由什么图像组成。
ARSCNFaceGeometry
这个便是苹果自带的人脸的构成信息,可以直接生成,它的模型成如下所示,你可以想象它检测到你人脸的位置,并且将这些虚拟的模型添加在人脸中,实现滤镜贴图,但是光光这样贴图是没有东西的,它缺少了“皮”,你需要给这个模型皮肤。
那么我们先准备好如下皮肤,这个是展开图,你可以把它当作3D的面具贴合在你的脸上
开始渲染
func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
//保证device以及anchor为脸部anchor
guard let device = sceneView.device,let _ = anchor as? ARFaceAnchor else{
return nil
}
//1.
let faceGeometry = ARSCNFaceGeometry(device: device)
//2.
let rootNode = SCNNode(geometry: faceGeometry)
//imageName指的是你存储上述人脸皮肤时候的文件名
//3.
faceGeometry?.firstMaterial?.diffuse.contents = UIImage(named: imageName)
//4.
rootNode.name = "root"
return rootNode
}
1.生成一个人脸的构成信息
2.生成一个虚拟节点,并且这个节点的构成信息是人脸,相当于创造了一个人脸模型的虚拟节点,而且模型的每一个点位置一一对应到现实中
3.给这个人脸模型穿上皮肤,contents可以是颜色、数字、图像、字符串...
4.节点打上标志,为了以后方便查找这个虚拟节点
没有错就是上述这几行代码便可以将皮肤贴到你的人脸中,是不是很简单,但是真的要去理解起来却需要花费一定的时间。
效果图
渲染更新
那么在转动摄像头,张开嘴巴或者是动动眉毛的时候也希望可以很好的贴合,所以我们需要去更新面部的锚点。
//动态更新根据人脸
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
guard let faceAnchor = anchor as? ARFaceAnchor,let faceGeometry = node.geometry as? ARSCNFaceGeometry else{
return
}
faceGeometry.update(from: faceAnchor.geometry)
}
滤镜效果
如何制作自己需要的滤镜效果呢,那么你可以根据上面面具作为定位图,在定位图中去绘制自己希望的滤镜,如下图
这里要注意图片需要png格式,可以保证没有白底,如果有白底的话效果会是这样的
最后
翻阅了很多的资料,一篇讲的是如何将Emoji表情添加到指定的位置,那篇文章如果没有看懂,可以这样理解,将虚拟的节点添加到主节点(虚拟人脸节点),获取到了人脸的点位anchor.geometry.vertices[9](对应的是鼻子的中心),将虚拟节点放置到指定位置。
那么人脸的滤镜大致就是这样实现的,为什么你摄像头如何移动它都能跟踪到是因为,它是根据人脸的锚点去进行定位的,所以我们在贴滤镜时可以简单根据上述操作进行。