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:

2.新建AR Default Plane:

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

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

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

二、unity知识点

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

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

    gameObject.AddComponent<ARAnchor>();

或在Inspector中添加:

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<ARAnchor>() == null)

    {

        instance.AddComponent<ARAnchor>();

    }

}

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

public ARAnchor AttachAnchor(ARPlane plane, Pose pose);

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

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

Destroy(anchor.gameObject);

三、添加代码

新建AnchorCreator.cs脚本:

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

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

[SerializeField]

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

        public GameObject prefab

        {

            get => m_Prefab;

            set => m_Prefab = value;

        }

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<ARPlaneManager>();

                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<ARAnchor>();

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<ARRaycastHit> s_Hits = new List<ARRaycastHit>();

        List<ARAnchor> m_Anchors = new List<ARAnchor>();

        ARRaycastManager m_RaycastManager;

        ARAnchorManager m_AnchorManager;

        void Awake()

        {

            m_RaycastManager = GetComponent<ARRaycastManager>();

            m_AnchorManager = GetComponent<ARAnchorManager>();

        }

        void SetAnchorText(ARAnchor anchor, string text)

        {

            var canvasTextManager = anchor.GetComponent<CanvasTextManager>();

            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<ARPlaneManager>();

                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<ARAnchor>();

            if (anchor == null)

            {

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

            }

            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.安装运行

      

      

四、常见问题

五、参考文献

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) 应用程序开发中,Anchor Points)用于定义虚拟对象相对于物理世界的位置和方向。通过创建,可以确保虚拟物体稳定地附着于检测到的真实环境中特定位置。 #### 的作用 不仅能够帮助固定虚拟内容,还支持后续算法处理图像特征[^1]。例如,在物体识别场景下,可以通过提取关键来定位目标物中心与其他参照之间的关系;而在图像配准过程中,则利用这些特征找到匹配项的具体坐标。 对于基于 UnityAR Foundation 构建的应用来说,`Trackable` 类提供了追踪功能的基础接口,而 `ARAnchorManager` 及其管理下的 `ARAnchor` 则负责维护由不同类型的可跟踪实体所生成的实际空间标记。 #### 创建与管理实例 下面是一个简单的 C# 脚本片段展示如何在一个新发现平面时自动放置一个带有立方体模型作为视觉反馈的: ```csharp using UnityEngine; using UnityEngine.XR.ARFoundation; public class PlaceOnPlane : MonoBehaviour { private GameObject m_CubePrefab; // 需要预先指定预制件资源路径 void Start() { var session = FindObjectOfType<ARSession>(); if (!session.subsystemDescriptors.Any()) Debug.LogError("No AR subsystem available."); m_CubePrefab = Resources.Load<GameObject>("Cube"); // 假设有一个名为 "Cube" 的预制件位于 Resources 文件夹内 } public void OnTap(ARRaycastHit hitResult) { Pose pose = new(hitResult.pose.position, Quaternion.identity); ARRaycastHit arRaycastHit = hitResult; ARAnchor anchor = arRaycastHit.trackable.CreateAnchor(pose); Instantiate(m_CubePrefab, anchor.transform); // 将立方体贴合至触碰处并关联给新建好的 Anchor 组件上 } } ``` 此脚本监听触摸事件,并尝试将射线投向当前可见平面上。如果成功命中,则会在该位置创建一个新的并将预加载的游戏对象附加上去显示出来。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值