ARCore之路:AugmentedFaceExample例子分析
项目效果
将一个三维物体附着到脸部,随着脸部的移动,三维物体也会跟踪脸部来进行移动。
下图是该项目的 GameObject 节点,下面将从各个节点分析该项目。
1、ARCore Device
该节点的介绍可移步到这里浏览:传送门。
下面分析该项目中的一些需要注意的参数:
该项目使用的是前置摄像头,并且定义了自己的ARCore 会话文件。
会话文件中,Plane Finding Mode
选项禁用了平面查找模式,并且 Augmented Face Mode
选项设为Mesh 模式
。
2、FaceAttachment
2.1、FaceTexture
和FaceOccluder
节点
两个节点都是把相机捕捉到的脸部图像绘制到材质贴图中,如下图红色方框中的 Mesh Filter
2.1.1、ARCoreAugmentedFaceMeshFilter
脚本分析
namespace GoogleARCore.Examples.AugmentedFaces
{using System.Collections.Generic;using GoogleARCore;using UnityEngine;/// <summary>/// Helper component to update face mesh data./// </summary>[RequireComponent(typeof(MeshFilter))]public class ARCoreAugmentedFaceMeshFilter : MonoBehaviour{/// <summary>/// If true, this component will update itself using the first AugmentedFace detected by ARCore./// </summary>public bool AutoBind = false;private AugmentedFace _augmentedFace = null;private List<AugmentedFace> _augmentedFaceList = null;// 保留前一帧的网格多边形以避免每帧更新网格。private List<Vector3> _meshVertices = new List<Vector3>();private List<Vector3> _meshNormals = new List<Vector3>();private List<Vector2> _meshUVs = new List<Vector2>();private List<int> _meshIndices = new List<int>();private Mesh _mesh = null;private bool _meshInitialized = false;/// <summary>/// Gets or sets the ARCore AugmentedFace object that will be used to update the face mesh data./// </summary>public AugmentedFace AumgnetedFace{get{return _augmentedFace;}set{_augmentedFace = value;Update();}}// 初始化数据public void Awake(){_mesh = new Mesh(); // 创建一个 MeshGetComponent<MeshFilter>().mesh = _mesh;// 将创建的 Mesh 赋值当前对象的Mesh Filter 组件,即上图红色方框的组件_augmentedFaceList = new List<AugmentedFace>(); // 创建一个集合,存储相机捕捉到的人脸}/// <summary>/// The Unity Update() method./// </summary>public void Update(){if (AutoBind)// 检测是否自动绑定{// 把存储人脸的集合清空_augmentedFaceList.Clear(); // 重新获取相机捕捉到的人脸Session.GetTrackables<AugmentedFace>(_augmentedFaceList, TrackableQueryFilter.All);// 相机捕捉到人脸,将第一张人脸作为 augmentedFaceif (_augmentedFaceList.Count != 0)_augmentedFace = _augmentedFaceList[0]; }if (_augmentedFace == null) // 如果return;// 更新贴图的位置和旋转,CenterPose 是中心点transform.position = _augmentedFace.CenterPose.position;transform.rotation = _augmentedFace.CenterPose.rotation;UpdateMesh(); // 更新 Mehs}// 通过获得人脸的顶点、法线、UV、三角片数据来更新 Mehsprivate void UpdateMesh(){_augmentedFace.GetVertices(_meshVertices); // 获取人脸的顶点_augmentedFace.GetNormals(_meshNormals); // 获取人脸的法线if (!_meshInitialized){_augmentedFace.GetTextureCoordinates(_meshUVs); // 获取人脸的UV_augmentedFace.GetTriangleIndices(_meshIndices); // 获取人脸的三角片// Only update mesh indices and uvs once as they don't change every frame._meshInitialized = true; // UV 和 三角片只获取一次}_mesh.Clear(); // 清空贴图// 重新设置贴图_mesh.SetVertices(_meshVertices);_mesh.SetNormals(_meshNormals);_mesh.SetTriangles(_meshIndices, 0);_mesh.SetUVs(0, _meshUVs);_mesh.RecalculateBounds(); // 从顶点重新计算网格的包围体。}}
}
2.2、fox_sample
节点
该节点是三维模型的节点,下图就是三维模型,当捕捉到人脸后,该模型会贴合到人脸上
该节点下的root
节点是骨骼节点,分别是左耳、右耳、鼻子的骨骼节点,用来标记模型的位置。
捕捉到人脸后,就是调整骨骼节点的位置,从而导致模型也跟着骨骼节点位置移动。
2.2.1、ARCoreAugmentedFaceRig
脚本分析
namespace GoogleARCore.Examples.AugmentedFaces
{using System.Collections.Generic;using GoogleARCore;using UnityEngine;/// <summary>/// Helper component to update face regions./// </summary>[ExecuteInEditMode]public class ARCoreAugmentedFaceRig : MonoBehaviour{/// <summary>/// If true, this component will update itself using the first AugmentedFace detected by ARCore./// </summary>public bool AutoBind = false;// AugmentedFaceRegion 是一个枚举private static readonly Dictionary<AugmentedFaceRegion, string> _regionTransformNames =new Dictionary<AugmentedFaceRegion, string>(){{ AugmentedFaceRegion.NoseTip, "NOSE_TIP" }, // 枚举 和 root 节点下的骨骼名字对应{ AugmentedFaceRegion.ForeheadLeft, "FOREHEAD_LEFT" },{ AugmentedFaceRegion.ForeheadRight, "FOREHEAD_RIGHT" }};private AugmentedFace _augmentedFace;private List<AugmentedFace> _augmentedFaceList = new List<AugmentedFace>();// 枚举和相应骨骼节点的 Transform 绑定到字典private Dictionary<AugmentedFaceRegion, Transform> _regionGameObjects =new Dictionary<AugmentedFaceRegion, Transform>();/// <summary>/// Gets or sets the ARCore AugmentedFace object that will be used to update the face region./// </summary>public AugmentedFace AumgnetedFace{get{return _augmentedFace;}set{_augmentedFace = value;Update();}}public void Awake(){_augmentedFaceList = new List<AugmentedFace>();InitializeFaceRegions(); // 获得骨骼节点和三维模型各个部件对象}public void Update(){if (!Application.isPlaying) // 程序没有在运行,则退出return;if (AutoBind)// 检测是否自动绑定{// 把存储人脸的集合清空_augmentedFaceList.Clear(); // 重新获取相机捕捉到的人脸Session.GetTrackables<AugmentedFace>(_augmentedFaceList, TrackableQueryFilter.All);// 相机捕捉到人脸,将第一张人脸作为 augmentedFaceif (_augmentedFaceList.Count != 0)_augmentedFace = _augmentedFaceList[0]; }if (_augmentedFace == null)return;UpdateRegions(); // 更新三维模型的位置}// 获得骨骼节点和三维模型各个部件对象private void InitializeFaceRegions(){foreach (AugmentedFaceRegion region in _regionTransformNames.Keys){string name = _regionTransformNames[region]; // 获得骨骼节点的名字// 通过节点名字找到GameObjectTransform regionTransform = FindChildTransformRecursive(transform, name);// 找不到就创建一个 name节点if (regionTransform == null){GameObject newRegionObject = new GameObject(name);newRegionObject.transform.SetParent(transform);regionTransform = newRegionObject.transform;}// 把骨骼节点的 Transform 与相应的枚举绑定,存储到字典_regionGameObjects[region] = regionTransform; }}// 通过节点名字找到GameObjectprivate Transform FindChildTransformRecursive(Transform target, string name){if (target.name == name){return target;// 在 target 节点的子孩子中查找 name 节点foreach (Transform child in target){if (child.name.Contains(name))return child;// 在子节点的孩子节点继续找Transform result = FindChildTransformRecursive(child, name);if (result != null)return result;}return null;}// 更新三维模型的位置private void UpdateRegions(){// 判断当前是否正在进行脸部跟踪bool isTracking = _augmentedFace.TrackingState == TrackingState.Tracking;// 如果正在进行跟踪,那么 3D 模型也得跟随脸部移动,即需要更新当前节点位置if (isTracking){transform.position = _augmentedFace.CenterPose.position;transform.rotation = _augmentedFace.CenterPose.rotation;}foreach (AugmentedFaceRegion region in _regionGameObjects.Keys){// 获得骨骼节点的 Transform Transform regionTransform = _regionGameObjects[region];// 如果没有正在跟踪,那就隐藏 3D 物体regionTransform.gameObject.SetActive(isTracking);// 如果当前处于跟踪状态,更新骨骼节点的位置if (isTracking){Pose regionPose = _augmentedFace.GetRegionPose(region);regionTransform.position = regionPose.position;regionTransform.rotation = regionPose.rotation;}}}}
}
3、AugmentedFaceExampleController
该节点下只有 AugmentedFacesExampleController
脚本。
namespace GoogleARCore.Examples.AugmentedFaces
{using System.Collections.Generic;using GoogleARCore;using UnityEngine;using UnityEngine.UI;/// <summary>/// Controller for the AugmentedFaces sample scene./// </summary>public class AugmentedFacesExampleController : MonoBehaviour{/// <summary>/// The game object that renders the face attachment on an Augmented Face./// </summary>public GameObject FaceAttachment;/// <summary>/// True if the app is in the process of quitting due to an ARCore connection error,/// otherwise false./// </summary>private bool _isQuitting = false;private List<AugmentedFace> _tempAugmentedFaces = new List<AugmentedFace>();public void Awake(){Application.targetFrameRate = 60; // 将程序的帧率设为 60 fps}public void Update(){UpdateApplicationLifecycle(); // 该函数的解析在 3.1小节讲解 // 获得相机中捕捉到的人脸放到 _tempAugmentedFaces 集合中Session.GetTrackables<AugmentedFace>(_tempAugmentedFaces, TrackableQueryFilter.All);// 如果检测不到人脸,就把设备的熄屏时间设置回原样.if (_tempAugmentedFaces.Count == 0){Screen.sleepTimeout = SleepTimeout.SystemSetting; // 把手机的熄屏时间设为手机中设置的熄屏时间FaceAttachment.SetActive(false); // 隐藏 3D 模型}else{Screen.sleepTimeout = SleepTimeout.NeverSleep; // 把手机设置为永不熄屏FaceAttachment.SetActive(true); // 显示 3D 模型}}}
}
3.1、UpdateApplicationLifecycle()
函数分析
该函数是一个检测应用是否退出或是否获得相应权限的函数。
/// <summary>/// Check and update the application lifecycle./// </summary>private void UpdateApplicationLifecycle(){// 检查手机是否按了返回键,按了返回键,程序退出if (Input.GetKey(KeyCode.Escape))Application.Quit();if (_isQuitting)return;// Quit if ARCore was unable to connect and give Unity some time for the toast to// appear.(Session.Status 可以获取 AR 当前的状态)if (Session.Status == SessionStatus.ErrorPermissionNotGranted){// 应用未获取手机相机的权限ShowAndroidToastMessage("Camera permission is needed to run this application.");_isQuitting = true;Invoke("DoQuit", 0.5f); // 退出程序}else if (Session.Status.IsError()){// AR 程序初始化失败ShowAndroidToastMessage("ARCore encountered a problem connecting. Please start the app again.");_isQuitting = true;Invoke("DoQuit", 0.5f); // 退出程序}}// 退出程序private void DoQuit(){Application.Quit();}// 在 Android 设备上显示一条弹窗消息private void ShowAndroidToastMessage(string message){AndroidJavaClass unityPlayer =new AndroidJavaClass("com.unity3d.player.UnityPlayer");AndroidJavaObject unityActivity =unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");if (unityActivity != null){AndroidJavaClass toastClass = new AndroidJavaClass("android.widget.Toast");unityActivity.Call("runOnUiThread", new AndroidJavaRunnable(() =>{AndroidJavaObject toastObject = toastClass.CallStatic<AndroidJavaObject>("makeText", unityActivity, message, 0);toastObject.Call("show");}));}}
ARCore之路:AugmentedFaceExample例子分析相关推荐
- OO系统分析员之路--用例分析系列(2)--用例的类型与粒度 [整理重发]
在正式讨论如何获取用例之前,笔者觉得有两个问题还是先解释清楚为好,这对正确获取用例有很大帮助.这两个问题也是初学者最为困惑,也是最难掌握的.一个是各种用例类型之间的区别和用法,另一个是用例的粒度. ...
- OO系统分析员之路--用例分析系列(4)--业务建模一般步骤和方法[整理重发]
本篇开始之前先扯点闲话,商业应用系统开发经历了三个阶段: 第一个阶段以计算为中心,分析设计围绕程序的运行效率,算法优劣,存贮优化来进行.90年代的大学课程讲的都是这些. 第二阶段以数据为中心,分析设计 ...
- Colly源码解析——结合例子分析底层实现
通过<Colly源码解析--框架>分析,我们可以知道Colly执行的主要流程.本文将结合http://go-colly.org上的例子分析一些高级设置的底层实现.(转载请指明出于break ...
- FFMpeg中apiexample.c例子分析——编码分析
FFMpeg中apiexample.c例子分析--编码分析apiexample.c例子教我们如何去利用ffmpeg库中的api函数来自己编写编解码程序. (1)首先,main函数中一开始会去调用avc ...
- FFMpeg中apiexample.c例子分析——解码分析
FFMpeg中apiexample.c例子分析--解码分析 收藏 我们直接从 video_decode_example() 函数开始讲,该函数实现了如何去解码一个视频文件,以 .mpeg 文 件为例. ...
- python 爬虫 包_python爬虫学习之路-抓包分析
利用浏览器抓包,是爬虫中的很实用的技能.在爬虫编程之前,我们要对抓取的目标页面有所了解,比如浏览器的这个请求这个页面中间都经历了什么,数据是怎么发送和返回的. 抓包的作用 我把抓包分析的作用简单列一下 ...
- arcore之路-unity开发从入门到实践_AR开发之路——准备工作
由于现在支持AR功能的手机没有普及,讲AR开发的教程也少,所以要学习AR开发还是要花点成本的,当然也有好处,花了学费,才会逼着自己坚持学下去. 为了学习AR,Relax今天入手了一部华为Nova 3i ...
- OO系统分析员之路--用例分析系列(2)--用例的类型与粒度
OO系统分析员之路--用例分析系列(2)--用例的类型与粒度 在正式讨论如何获取用例之前,笔者觉得有两个问题还是先解释清楚为好,这对正确获取用例有很大帮助.这两个问题也是初学者最为困惑,也是最难掌握的 ...
- 流媒体学习之路(mediasoup)——拥塞控制分析(6)
流媒体学习之路(mediasoup)--拥塞控制分析(6) 文章目录 流媒体学习之路(mediasoup)--拥塞控制分析(6) 一.TransportCongestionControlClient ...
最新文章
- Merge into 详细介绍
- windows远程桌面管理工具下载_vnc viewer 64位下载,3步实现vnc viewer 64位下载
- STM32开发 -- 4G模块开发详解(3)
- 20211201 二范数的导数小于等于导数的二范数(导数存在情况下)
- uni-app组件之间的传值
- python将元祖写入txt文档_python元祖和文件用法举例
- Linux中安装nc(netcat)常见问题
- Sql Server常用时间段查询汇总
- ubuntu安装hive2.3.7
- CSS失效的问题总结(转:孟子E章)
- DevExpress之TreeList用法
- 国内开发商品基金的一些设想
- cad放大_dwg文件怎么打开?CAD看图,360°精确识别CAD图块,细节见真章
- 10.5 Vue电商后台管理完善--订单详情页面显示商品信息,添加备注
- android3d动画的实现,Android 3D旋转动画库
- 使用Guava-retrying优雅地解决异常重试场景
- 2021年全球网络保险收入大约9593.9百万美元,预计2028年达到68230百万美元,2022至2028期间,年复合增长率CAGR为35.1%
- linux centos 6.0下载,centos 6.0 下载 32/64位 最新高速下载地址 国内迅雷下载BT下载...
- 免费好用的流程图软件yed
- 出现Cannot find module 'xxx' 错误