ARCore之路:HelloAR项目例子分析

本文详细介绍了基于ARCore的项目,包括ARCoreDevice节点的深度图生成、HelloARController节点的用户交互逻辑,如触摸生成模型,以及PlaneGenerator节点的平面网格生成。此外,还提到了SettingsMenu脚本对UI界面的管理。项目利用点云、深度图和平面检测技术,实现三维模型在现实世界中的即时放置和交互。
摘要由CSDN通过智能技术生成

项目效果

在这里插入图片描述
通过相机扫描环境中的平面,它会生成上图中的白色网格。可通过点击白色网格来生成三维模型。
下图是项目中的节点,下面将从挑选一些节点来分析,其中 Environmental Light 节点是灯光、EventSystem节点是控制输入输出事件的、PlaneDiscoveryCanvas 节点 是UI界面,这些就不讲解了。

在这里插入图片描述

1、ARCore Device

该节点的介绍可去这里(传送门)浏览。
该节点下的 First Person Camera 节点还有一个 DepthPreview 节点,主要是用来生成深度图的(如下图所示)。
在这里插入图片描述

该项目中的 Session Config 文件配置如下图,灯光估计是 HDR 带有反射的模式、Instant Placement Mode 是 Local Y Up 模式。
在这里插入图片描述

2、HelloAR Controller

该节点只有两个脚本,其中一个是 HelloARController ,它是用来检测用户是否触摸了设备,并在触摸点处生成相应的模型;另一个是 SettingsMenu ,它是控制用户与UI界面的交互,如显示和隐藏UI界面。
在这里插入图片描述

2.1、HelloARController 脚本

该脚本中的配置从上到下分别是:

  • 深度功能的菜单界面的控制脚本
  • 放置模型的设置界面的控制脚本
  • 放置模型功能的生成模型
  • 第一人称相机
  • 点击垂直平面时的生成模型
  • 点击水平平面时的生成模型
  • 点击点云时的生成模型
  • 点击深度点云时的模型
namespace GoogleARCore.Examples.HelloAR
{
    using System.Collections.Generic;
    using GoogleARCore;
    using GoogleARCore.Examples.Common;
    using UnityEngine;
    using UnityEngine.EventSystems;

#if UNITY_EDITOR
    // Set up touch input propagation while using Instant Preview in the editor.
    using Input = InstantPreviewInput;
#endif

    public class HelloARController : MonoBehaviour
    {
        public DepthMenu DepthMenu;
        public InstantPlacementMenu InstantPlacementMenu;
        public GameObject InstantPlacementPrefab;
        public Camera FirstPersonCamera;
        public GameObject GameObjectVerticalPlanePrefab;
        public GameObject GameObjectHorizontalPlanePrefab;
        public GameObject GameObjectPointPrefab;
        public GameObject GameObjectDepthPointPrefab;

        // 生成模型时设置的旋转参数
        private const float _prefabRotation = 180.0f;

        // 标记程序是否退出
        private bool _isQuitting = false;

        public void Awake()
        {
            // 注意:当 QualitySettings.vSyncCount != 0 时,Application.targetFrameRate 会被忽略
            Application.targetFrameRate = 60;   // 帧率设为 60 帧
        }
    }
}

Update() 函数
该函数中主要是检测用户是否触摸了设备,并根据点击到的GameObject 对象在点击位置处生成相应的模型。
UpdateApplicationLifecycle() 函数讲解

public void Update()
{
    UpdateApplicationLifecycle();	// 可点击上方的链接浏览该函数的功能

    // 检测是否点击了设备的屏幕.
    // Input.touchCount 获取当前帧触摸屏幕的次数,没有触摸就返回
    // Input.GetTouch(0)).phase 获取手指触摸屏幕的状态,Began 是 手指触摸了屏幕
    Touch touch;
    if (Input.touchCount < 1 || (touch = Input.GetTouch(0)).phase != TouchPhase.Began)
        return;

    // 如果触摸了 UI,那么也返回,因为触摸UI不会放置模型
    if (EventSystem.current.IsPointerOverGameObject(touch.fingerId)){
        return;

    // 对玩家触摸的位置进行射线投射以搜索平面。
    TrackableHit hit;
    bool foundHit = false;

    // raycastFilter 是射线的过滤器
    // PlaneWithinPolygon:碰撞发生在 DetectedPlane 的凸边界多边形内。
    // FeaturePointWithSurfaceNormal:碰撞发生在当前帧的点云中具有表面法线估计(方向)的特征点上
    // Depth:碰撞发生在当前帧的深度图上。
    TrackableHitFlags raycastFilter = TrackableHitFlags.PlaneWithinPolygon |
        TrackableHitFlags.FeaturePointWithSurfaceNormal;

    // Allows the depth image to be queried for hit tests.
    raycastFilter |= TrackableHitFlags.Depth;
    // 检测碰撞点,如果检测到就返回 true
    foundHit = Frame.Raycast(touch.position.x, touch.position.y, raycastFilter, out hit);
    
    if (!foundHit && InstantPlacementMenu.IsInstantPlacementEnabled())
    {
        foundHit = Frame.RaycastInstantPlacement(
            touch.position.x, touch.position.y, 1.0f, out hit);
    }

    if (foundHit)
    {
        // 检测触摸点中的物体是不是平面且使用触摸点和相机的位置做一个点乘运算,
        // 检测是否点击到平面的背面,如果是,不需要创建锚点
        if ((hit.Trackable is DetectedPlane) &&
            Vector3.Dot(FirstPersonCamera.transform.position - hit.Pose.position,
                hit.Pose.rotation * Vector3.up) < 0)
        {
            Debug.Log("Hit at back of the current DetectedPlane");
        }
        else
        {
            if (DepthMenu != null)
                DepthMenu.ConfigureDepthBeforePlacingFirstAsset();// 显示深度图菜单

            // 根据点击到的物体生成相应的三维模型
            GameObject prefab;
            if (hit.Trackable is InstantPlacementPoint)
            {
                prefab = InstantPlacementPrefab;
            }
            else if (hit.Trackable is FeaturePoint)
            {
                prefab = GameObjectPointPrefab;
            }
            else if (hit.Trackable is DepthPoint)
            {
                prefab = GameObjectDepthPointPrefab;
            }
            else if (hit.Trackable is DetectedPlane)
            {
                // 如果是平面,还需要根据平面是垂直还是水平来选择生成的模型
                DetectedPlane detectedPlane = hit.Trackable as DetectedPlane;
                
                if (detectedPlane.PlaneType == DetectedPlaneType.Vertical)
                    prefab = GameObjectVerticalPlanePrefab;
                else
                    prefab = GameObjectHorizontalPlanePrefab;
            }
            else
            {
                prefab = GameObjectHorizontalPlanePrefab;
            }

            // 在点击的位置实例化一个前面选择的预制体
            var gameObject = Instantiate(prefab, hit.Pose.position, hit.Pose.rotation);

            // 设置预制体的选择参数
            gameObject.transform.Rotate(0, _prefabRotation, 0, Space.Self);

            // 在点击的位置创建一个锚点
            var anchor = hit.Trackable.CreateAnchor(hit.Pose);

            // 设置锚点为生成预制体的父节点
            gameObject.transform.parent = anchor.transform;

            // 点击到的对象是InstantPlacementPoint, 生成一个放置物体时的是假的效果
            if (hit.Trackable is InstantPlacementPoint)
            {
                gameObject.GetComponentInChildren<InstantPlacementEffect>()
                    .InitializeWithTrackable(hit.Trackable);
            }
        }
    }
}

2.2、SettingsMenu 脚本

该脚本主要是用来管理项目中的 UI 界面,其中总共有三种功能类型的脚本,分别是:

  • 公共的设置界面
  • 是否启用深度功能的设置界面
  • 放置模型的设置界面

3、Plane Generator

该节点主要是用来生成白色的网格的,并可以在这些网格中生成模型。其中 Detected Plane Prefab 就是网格的预制体。
在这里插入图片描述

namespace GoogleARCore.Examples.Common
{
    using System.Collections.Generic;
    using GoogleARCore;
    using UnityEngine;

    public class DetectedPlaneGenerator : MonoBehaviour
    {
        
        public GameObject DetectedPlanePrefab;

        // 在 ARCore开始追踪起到当前帧,生成的平面保存在下面的集合中
        private List<DetectedPlane> _newPlanes = new List<DetectedPlane>();

        public void Update()
        {
            // 检查运动跟踪是否正在跟踪。
            if (Session.Status != SessionStatus.Tracking)
                return;

            // 遍历在此帧中找到的平面并实例化相应的游戏对象以可视化它们
            // 过滤器中实现了新检测的平面才放进去,即在集合中的平面就不用放进去了
            Session.GetTrackables<DetectedPlane>(_newPlanes, TrackableQueryFilter.New);
            for (int i = 0; i < _newPlanes.Count; i++)
            {
                // 实例化一个平面可视化预制体并将其设置为跟踪新平面。
                // 由于我们的预制体的网格在 Unity World 坐标中更新,因此变换设置为具有标识旋转的原点
                GameObject planeObject =
                    Instantiate(DetectedPlanePrefab, Vector3.zero, Quaternion.identity, transform);
                planeObject.GetComponent<DetectedPlaneVisualizer>().Initialize(_newPlanes[i]);
            }
        }
    }
}

4、Point Cloud

该节点主要是生成点云的,点云就是下图中那些青蓝色的点,如何生成这些点的脚本就不分析了。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值