ARFoundation从零开始9-AR锚点(AR Anchor)

项目代码:https://github.com/sueleeyu/ar-localanchor

使用锚点,可以让虚拟对象看起来仿佛留在 AR 场景中。锚点可确保对象在空间中看起来保持不变,并保持在现实世界中的虚拟对象的视觉效果。

锚点的工作原理[1]:

与锚点相关的两个概念是:世界空间和姿态。

世界空间:是指相机和对象所在位置的坐标空间;相机和对象在现实世界空间中的位置会逐帧更新

姿势:表示对象在世界空间中的位置和方向,在 iOS 中也称为“转换”

创建锚点时,您可以使用一个姿势来描述相对于当前帧的世界空间估计的位置和方向。

您向此锚点附加了一个或多个对象。锚点和连接到它的对象看起来会停留在它们在真实世界中的位置。由于锚点姿势在每个帧中适应世界空间更新,因此锚点也会相应地更新对象姿态。

您可以将多个对象附加到同一个锚点,以确保即使在锚点的姿态调整的情况下,这些对象仍能保持它们的相对位置和方向。

锚点的类型:

1.本地锚点:随应用一起存储在本地,并且仅对应用实例有效。用户必须实际位于其放置锚点的位置

2.云锚点:存储在 Google Cloud 中,并可在应用实例之间共享。用户必须实际位于其放置锚点的位置

3. 地理空间锚点:基于大地纬度、经度和海拔,加上 Google 的视觉定位系统 (VPS) 数据,可在世界上几乎任何地方提供精确的位置;这些锚点可能会在应用实例之间共享。只要应用已连接到互联网并且可以使用 VPS,用户就可以从远程位置放置锚点。

下面开始添加本地锚点。

一、添加组件

1.添加AR Anchor Manager和AR Raycast Manager。点击左侧Hierarchy-AR Session Origin,点击右侧Inspector的Add Component按钮,分别搜索添加AR Plane Manager、AR Anchor Manager和AR Raycast Manager:

ARFoundation从零开始9-AR锚点(AR Anchor)_第1张图片

2.新建AR Default Plane:

ARFoundation从零开始9-AR锚点(AR Anchor)_第2张图片

拖到当前工程的Prefabs下,同时删除Hierarchy下新建的AR Default Plane:

ARFoundation从零开始9-AR锚点(AR Anchor)_第3张图片

Hierarchy下选择AR Session Origin,右侧找到AR Plane Manager,将Prefabs下创建的AR Default Plane预制件拖动到AR Plane Manager的Plane Prefab栏:

ARFoundation从零开始9-AR锚点(AR Anchor)_第4张图片

3.添加Button组件,选择该组件,右侧Inspector内增加onclick事件,实现清除锚点功能:

ARFoundation从零开始9-AR锚点(AR Anchor)_第5张图片

二、unity知识点

在可跟踪对象(比如平面)或 ARCore 会话环境中创建锚点,例如:

1.为场景中已有对象添加锚点

    gameObject.AddComponent();

或在Inspector中添加:

ARFoundation从零开始9-AR锚点(AR Anchor)_第6张图片

2.动态实例化预制件并添加锚点

void AnchorContent(Vector3 position, GameObject prefab)

{

    // Create an instance of the prefab

    var instance = Instantiate(prefab, position, Quaternion.identity);

    // Add an ARAnchor component if it doesn't have one already.

    if (instance.GetComponent() == null)

    {

        instance.AddComponent();

    }

}

3.添加附着在平面上的锚点

public ARAnchor AttachAnchor(ARPlane plane, Pose pose);

如果想移除锚点,就销毁游戏对象的ARAnchor组件(或游戏对象自身):

//m_AnchorManager.RemoveAnchor(anchor);已弃用

Destroy(anchor.gameObject);

三、添加代码

新建AnchorCreator.cs脚本:

ARFoundation从零开始9-AR锚点(AR Anchor)_第7张图片

拖动AnchorCreator.js挂载到AR Session Origin上:

ARFoundation从零开始9-AR锚点(AR Anchor)_第8张图片

1.定义预制件,将准备好的锚点预制件拖到cs脚本的Prefab栏:

[SerializeField]

        GameObject m_Prefab;//要放置的预制件

        public GameObject prefab

        {

            get => m_Prefab;

            set => m_Prefab = value;

        }

ARFoundation从零开始9-AR锚点(AR Anchor)_第9张图片

2.update方法中根据类型检测射线碰撞

// Raycast against planes and feature points射线的检测类型包括平面和

            const TrackableType trackableTypes =

                TrackableType.FeaturePoint |

                TrackableType.PlaneWithinPolygon;

            // Perform the raycast

            if (m_RaycastManager.Raycast(touch.position, s_Hits, trackableTypes))

            {

//do createAnchor

}

如果是平面,依附平面添加锚点:

if (hit.trackable is ARPlane plane)

            {

                var planeManager = GetComponent();

                if (planeManager)

                {

                    Logger.Log("Creating anchor attachment.");

                    var oldPrefab = m_AnchorManager.anchorPrefab;

                    m_AnchorManager.anchorPrefab = prefab;//替换AnchorManager原有锚点预制件

                    anchor = m_AnchorManager.AttachAnchor(plane, hit.pose);//将锚点锚定到平面,当前hit.pose创建锚点

                    m_AnchorManager.anchorPrefab = oldPrefab;//还原AnchorManager原有锚点预制件

                    SetAnchorText(anchor, $"Attached to plane {plane.trackableId}");

                    return anchor;

                }

            }

否则,根据碰撞的特征点或其他可追踪对象实例化游戏对象添加锚点:

// Note: the anchor can be anywhere in the scene hierarchy

            var gameObject = Instantiate(prefab, hit.pose.position, hit.pose.rotation);//使用预制件和当前pose创建对象

            // Make sure the new GameObject has an ARAnchor component

            anchor = gameObject.GetComponent();

3.清除锚点

public void RemoveAllAnchors()

        {

            Logger.Log($"Removing all anchors ({m_Anchors.Count})");

            foreach (var anchor in m_Anchors)

            {

                Destroy(anchor.gameObject);//m_AnchorManager.RemoveAnchor(anchor);已弃用

            }

            m_Anchors.Clear();

        }

4.AnchorCreator.cs:

using System.Collections.Generic;

using UnityEngine;

using UnityEngine.XR.ARFoundation;

using UnityEngine.XR.ARSubsystems;

namespace FrameworkDesign.Example

{

    [RequireComponent(typeof(ARAnchorManager))]

    [RequireComponent(typeof(ARRaycastManager))]

    public class AnchorCreator : MonoBehaviour

    {

        [SerializeField]

        GameObject m_Prefab;//要放置的预制件

        public GameObject prefab

        {

            get => m_Prefab;

            set => m_Prefab = value;

        }

        static List s_Hits = new List();

        List m_Anchors = new List();

        ARRaycastManager m_RaycastManager;

        ARAnchorManager m_AnchorManager;

        void Awake()

        {

            m_RaycastManager = GetComponent();

            m_AnchorManager = GetComponent();

        }

        void SetAnchorText(ARAnchor anchor, string text)

        {

            var canvasTextManager = anchor.GetComponent();

            if (canvasTextManager)

            {

                canvasTextManager.text = text;

            }

        }

        ARAnchor CreateAnchor(in ARRaycastHit hit)

        {

            ARAnchor anchor = null;

            // If we hit a plane, try to "attach" the anchor to the plane

            if (hit.trackable is ARPlane plane)

            {

                var planeManager = GetComponent();

                if (planeManager)

                {

                    Logger.Log("Creating anchor attachment.");

                    var oldPrefab = m_AnchorManager.anchorPrefab;                 

                    m_AnchorManager.anchorPrefab = prefab;//替换AnchorManager原有锚点预制件

                    anchor = m_AnchorManager.AttachAnchor(plane, hit.pose);//将锚点锚定到平面,当前hit.pose创建锚点

                    m_AnchorManager.anchorPrefab = oldPrefab;//还原AnchorManager原有锚点预制件

                    SetAnchorText(anchor, $"Attached to plane {plane.trackableId}");                    

                    return anchor;

                }

            }

            // Otherwise, just create a regular anchor at the hit pose

            Logger.Log("Creating regular anchor.");

            // Note: the anchor can be anywhere in the scene hierarchy

            var gameObject = Instantiate(prefab, hit.pose.position, hit.pose.rotation);//使用预制件和当前pose创建对象

            // Make sure the new GameObject has an ARAnchor component

            anchor = gameObject.GetComponent();

            if (anchor == null)

            {

                anchor = gameObject.AddComponent();//为当前对象添加锚点

            }

            SetAnchorText(anchor, $"Anchor (from {hit.hitType})");

            return anchor;

        }

        public void RemoveAllAnchors()

        {

            Logger.Log($"Removing all anchors ({m_Anchors.Count})");

            foreach (var anchor in m_Anchors)

            {

                Destroy(anchor.gameObject);//m_AnchorManager.RemoveAnchor(anchor);已弃用

            }

            m_Anchors.Clear();

        }

        void Update()

        {

            if (Input.touchCount == 0)

                return;

            var touch = Input.GetTouch(0);

            if (touch.phase != TouchPhase.Began)

                return;

            // Raycast against planes and feature points射线的检测类型包括平面和

            const TrackableType trackableTypes =

                TrackableType.FeaturePoint |

                TrackableType.PlaneWithinPolygon;

            // Perform the raycast

            if (m_RaycastManager.Raycast(touch.position, s_Hits, trackableTypes))

            {

                // Raycast hits are sorted by distance, so the first one will be the closest hit.

                var hit = s_Hits[0];

                // Create a new anchor

                var anchor = CreateAnchor(hit);

                if (anchor)

                {

                    // Remember the anchor so we can remove it later.

                    m_Anchors.Add(anchor);//锚定以Component的形式对应于GameObject

                }

                else

                {

                    Logger.Log("Error creating anchor");

                }

            }

        }       

    }

}

三、android打包运行

如未配置,参看《ARFoundation从零开始3》:

ARFoundation从零开始3-创建ARFoundation项目_suelee_hm的博客-CSDN博客

1.安装运行

     ARFoundation从零开始9-AR锚点(AR Anchor)_第10张图片 

     ARFoundation从零开始9-AR锚点(AR Anchor)_第11张图片 

四、常见问题

五、参考文献

1. ARCore文档

使用锚点  |  ARCore  |  Google Developers

2.ARFoundation示例:

GitHub - Unity-Technologies/arfoundation-samples: Example content for Unity projects based on AR Foundation

3.本项目示例源代码:

https://github.com/sueleeyu/ar-localanchor

你可能感兴趣的:(ARFoundation,ar,unity,xr)