项目效果

将一个三维物体附着到脸部,随着脸部的移动,三维物体也会跟踪脸部来进行移动。

下图是该项目的 GameObject 节点,下面将从各个节点分析该项目。

1、ARCore Device

该节点的介绍可移步到这里浏览:传送门。
下面分析该项目中的一些需要注意的参数:
该项目使用的是前置摄像头,并且定义了自己的ARCore 会话文件。

会话文件中,Plane Finding Mode 选项禁用了平面查找模式,并且 Augmented Face Mode 选项设为Mesh 模式

2、FaceAttachment

2.1、FaceTextureFaceOccluder节点

两个节点都是把相机捕捉到的脸部图像绘制到材质贴图中,如下图红色方框中的 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例子分析相关推荐

  1. OO系统分析员之路--用例分析系列(2)--用例的类型与粒度 [整理重发]

    在正式讨论如何获取用例之前,笔者觉得有两个问题还是先解释清楚为好,这对正确获取用例有很大帮助.这两个问题也是初学者最为困惑,也是最难掌握的.一个是各种用例类型之间的区别和用法,另一个是用例的粒度.  ...

  2. OO系统分析员之路--用例分析系列(4)--业务建模一般步骤和方法[整理重发]

    本篇开始之前先扯点闲话,商业应用系统开发经历了三个阶段: 第一个阶段以计算为中心,分析设计围绕程序的运行效率,算法优劣,存贮优化来进行.90年代的大学课程讲的都是这些. 第二阶段以数据为中心,分析设计 ...

  3. Colly源码解析——结合例子分析底层实现

    通过<Colly源码解析--框架>分析,我们可以知道Colly执行的主要流程.本文将结合http://go-colly.org上的例子分析一些高级设置的底层实现.(转载请指明出于break ...

  4. FFMpeg中apiexample.c例子分析——编码分析

    FFMpeg中apiexample.c例子分析--编码分析apiexample.c例子教我们如何去利用ffmpeg库中的api函数来自己编写编解码程序. (1)首先,main函数中一开始会去调用avc ...

  5. FFMpeg中apiexample.c例子分析——解码分析

    FFMpeg中apiexample.c例子分析--解码分析 收藏 我们直接从 video_decode_example() 函数开始讲,该函数实现了如何去解码一个视频文件,以 .mpeg 文 件为例. ...

  6. python 爬虫 包_python爬虫学习之路-抓包分析

    利用浏览器抓包,是爬虫中的很实用的技能.在爬虫编程之前,我们要对抓取的目标页面有所了解,比如浏览器的这个请求这个页面中间都经历了什么,数据是怎么发送和返回的. 抓包的作用 我把抓包分析的作用简单列一下 ...

  7. arcore之路-unity开发从入门到实践_AR开发之路——准备工作

    由于现在支持AR功能的手机没有普及,讲AR开发的教程也少,所以要学习AR开发还是要花点成本的,当然也有好处,花了学费,才会逼着自己坚持学下去. 为了学习AR,Relax今天入手了一部华为Nova 3i ...

  8. OO系统分析员之路--用例分析系列(2)--用例的类型与粒度

    OO系统分析员之路--用例分析系列(2)--用例的类型与粒度 在正式讨论如何获取用例之前,笔者觉得有两个问题还是先解释清楚为好,这对正确获取用例有很大帮助.这两个问题也是初学者最为困惑,也是最难掌握的 ...

  9. 流媒体学习之路(mediasoup)——拥塞控制分析(6)

    流媒体学习之路(mediasoup)--拥塞控制分析(6) 文章目录 流媒体学习之路(mediasoup)--拥塞控制分析(6) 一.TransportCongestionControlClient ...

最新文章

  1. Merge into 详细介绍
  2. windows远程桌面管理工具下载_vnc viewer 64位下载,3步实现vnc viewer 64位下载
  3. STM32开发 -- 4G模块开发详解(3)
  4. 20211201 二范数的导数小于等于导数的二范数(导数存在情况下)
  5. uni-app组件之间的传值
  6. python将元祖写入txt文档_python元祖和文件用法举例
  7. Linux中安装nc(netcat)常见问题
  8. Sql Server常用时间段查询汇总
  9. ubuntu安装hive2.3.7
  10. CSS失效的问题总结(转:孟子E章)
  11. DevExpress之TreeList用法
  12. 国内开发商品基金的一些设想
  13. cad放大_dwg文件怎么打开?CAD看图,360°精确识别CAD图块,细节见真章
  14. 10.5 Vue电商后台管理完善--订单详情页面显示商品信息,添加备注
  15. android3d动画的实现,Android 3D旋转动画库
  16. 使用Guava-retrying优雅地解决异常重试场景
  17. 2021年全球网络保险收入大约9593.9百万美元,预计2028年达到68230百万美元,2022至2028期间,年复合增长率CAGR为35.1%
  18. linux centos 6.0下载,centos 6.0 下载 32/64位 最新高速下载地址 国内迅雷下载BT下载...
  19. 免费好用的流程图软件yed
  20. 出现Cannot find module 'xxx' 错误

热门文章

  1. 几何图形计算器的设计与实现----代码实现
  2. OSG学习笔记-基本几何体的绘制(3-2)
  3. 华为防火墙策略路由旁路部署
  4. Flink - 批量、流式计算和离线、实时计算
  5. ROS从入门到精通0-1:教程导读
  6. 智能制造体系架构分析与工业互联网应用
  7. udp发送的数据存入MySQL_怎么把收到的UDP数据批量存储到数据库
  8. 【解决方案】TSINGSEE客流统计系统技术架构与使用场景解析
  9. 机械制图计算机识图,二、机械制图与识读基础
  10. 《社会心理学》第二章学习笔记