版本记录
版本号 | 时间 |
---|---|
V1.0 | 2017.09.27 |
前言
苹果最近新出的一个API就是ARKit,是在2017年6月6日,苹果发布iOS11系统所新增框架,它能够帮助我们以最简单快捷的方式实现AR技术功能。接下来几篇我们就详细的对ARKit框架进行详细的解析。AR相关代码已经上传到Github - 刀客传奇,感兴趣的可以看上面几篇。
1. ARKit框架详细解析(一)—— 基本概览
2. ARKit框架详细解析(二)—— 关于增强现实和ARKit
3. ARKit框架详细解析(三)—— 开启你的第一个AR体验之旅
4. ARKit框架详细解析(四)—— 处理增强现实中的3D交互和UI控件
创建基于面部的AR体验
放置和动画3D用户脸部的内容,并匹配面部表情(在具有兼容的前置摄像头的设备上)。
概要
此示例应用程序提供了一个简单的界面,允许您使用兼容设备上的前置摄像头在四个增强现实(AR)可视化之间进行选择(请iOS Device Compatibility Reference)。
- 单独的相机视图,没有任何AR内容。
- ARKit提供的面部网格,可以自动估计真实的定向照明环境。
- 虚拟3D内容似乎附加到用户的真实面孔(并被部分遮蔽)。
- 一个简单的机器人角色,其表情符合用户的动画。
使用示例应用程序中的“+”按钮在这些模式之间切换,如下所示。
Start a Face Tracking Session in a SceneKit View - 在SceneKit视图中启动面部跟踪会话
像ARKit的其他用途一样,脸部跟踪需要配置和运行会话(ARSession对象),并将视频图像与虚拟内容一起渲染。 有关会话和视图设置的更详细说明,请参阅About Augmented Reality and ARKit 和 Building Your First AR Experience。 此示例使用SceneKit显示AR体验,但也可以使用SpriteKit或使用Metal构建您自己的渲染器(请参阅ARSKView 和 Displaying an AR Experience with Metal)。
面部跟踪与用于配置会话的类中的ARKit的其他用途不同。 要启用脸部跟踪,请创建一个实例,配置其属性,并将其传递给与视图关联的AR会话的方法,如下所示。
guard ARFaceTrackingConfiguration.isSupported else { return }
let configuration = ARFaceTrackingConfiguration()
configuration.isLightEstimationEnabled = true
session.run(configuration, options: [.resetTracking, .removeExistingAnchors])
在提供需要面部跟踪AR会话的用户功能之前,请先检查.
属性来确定当前设备是否支持ARKit的脸部跟踪。
Track the Position and Orientation of a Face - 跟踪面部的位置和方向
当脸部跟踪处于活动状态时,ARKit会自动将对象添加到正在运行的AR会话中,其中包含有关用户脸部的信息,包括其位置和方向。
注意:ARKit会检测并提供有关用户脸部信息的信息。 如果摄像机图像中存在多个面部,则ARKit会选择最大或最清晰可识别的脸部。
在基于SceneKit的AR体验中,您可以在方法(从协议)中添加与面锚相对应的3D内容。 ARKit为锚点添加了一个SceneKit节点,并更新了每一帧上节点的位置和方向,因此您添加到该节点的任何SceneKit
内容都会自动跟随用户脸部的位置和方向。
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
// Hold onto the `faceNode` so that the session does not need to be restarted when switching masks.
faceNode = node
serialQueue.async {
self.setupFaceNodeContent()
}
}
在此示例中,该方法调用setupFaceNodeContent
方法将SceneKit内容添加到faceNode
。 例如,如果您更改示例代码中的showsCoordinateOrigin
变量,则应用程序将x / y / z轴的可视化添加到该节点,指示面部锚点坐标系的起点。
Use Face Geometry to Model the User’s Face - 使用面几何来建模用户脸部
ARKit提供与用户脸部的大小,形状,拓扑和当前面部表情相匹配的粗略3D网格几何。 ARKit还提供了该类,提供了一种在SceneKit中可视化此网格的简单方法。
您的AR体验可以使用此网格放置或绘制似乎附加到脸部的内容。 例如,通过将半透明纹理应用于此几何体,您可以将虚拟纹身或化妆画到用户的皮肤上。
要创建一个SceneKit面几何,使用SceneKit视图用于呈现的Metal设备初始化一个对象:
// This relies on the earlier check of `ARFaceTrackingConfiguration.isSupported`.
let device = sceneView.device!
let maskGeometry = ARSCNFaceGeometry(device: device)!
示例代码的setupFaceNodeContent
方法(如上所述)将包含面几何的节点添加到场景中。 通过使该节点成为由脸部锚点提供的节点的子节点,脸部模型自动跟踪用户脸部的位置和方向。
为了使屏幕上的脸部模型符合用户脸部的形状,即使用户眨眼,说话并进行各种面部表情,您需要在委托回调中检索更新的脸部网格。
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
guard let faceAnchor = anchor as? ARFaceAnchor else { return }
virtualFaceNode?.update(withFaceAnchor: faceAnchor)
}
然后,通过将新的面网格传递给其方法来更新场景中的对象进行匹配:
func update(withFaceAnchor anchor: ARFaceAnchor) {
let faceGeometry = geometry as! ARSCNFaceGeometry
faceGeometry.update(from: anchor.geometry)
}
Place 3D Content on the User’s Face - 将3D内容放在用户脸上
ARKit提供的面部网格的另一个用途是在场景中创建遮挡几何。 遮挡几何是不会呈现任何可见内容(允许相机图像显示)的3D模型,但会阻碍相机对场景中其他虚拟内容的视图。
这种技术创造出真实面对与虚拟对象交互的错觉,即使脸部是2D摄像机图像,虚拟内容是渲染的3D对象。 例如,如果您将遮挡几何图形和虚拟眼镜放置在用户的脸部上,则脸部可能会遮挡眼镜框架。
要创建面部的遮挡几何,请先创建一个对象,如上例所示。 但是,不要使用可见的外观来配置该对象的SceneKit材质,而是在渲染期间将材质设置为渲染深度而不是颜色:
geometry.firstMaterial!.colorBufferWriteMask = []
occlusionNode = SCNNode(geometry: geometry)
occlusionNode.renderingOrder = -1
因为材料渲染深度,所以SceneKit渲染的其他对象正确地出现在它的前面或后面。 但是由于材质不会呈现颜色,相机图像会出现在其位置。 示例应用程序将此技术与位于用户眼睛前方的SceneKit对象相结合,创建一个效果,其中对象被用户的鼻子实际遮蔽。
Animate a Character with Blend Shapes - 用混合形状动画化一个人物
除了上述示例中所示的面部网格之外,ARKit还提供了一种以词典形式的用户面部表情的更抽象的模型。 您可以使用该字典中的命名系数值来控制自己的2D或3D资产的动画参数,创建遵循用户真实面部动作和表达的角色(如头像或木偶)。
作为混合形状动画的基本演示,此示例包含使用SceneKit原始形状创建的机器人角色头部的简单模型。 (请参阅源代码中的robotHead.scn
文件。)
要获取用户当前的面部表情,请从代理回调中的面部锚点中读取字典:
func update(withFaceAnchor faceAnchor: ARFaceAnchor) {
blendShapes = faceAnchor.blendShapes
}
然后,检查该字典中的键值对以计算模型的动画参数。 有52个独特系数。 您的应用程序可以使用尽可能少的或很多的必要条件来创建您想要的艺术效果。 在该示例中,RobotHead
类执行此计算,将参数映射到机器人眼睛因子的一个轴,以及偏移机器人下颚位置的参数。
var blendShapes: [ARFaceAnchor.BlendShapeLocation: Any] = [:] {
didSet {
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
}
}
后记
未完,待续~~~~