Hololens開發筆記之Gaze凝視射線


凝視是HoloLens首要輸入方式,形式功能類似於桌面系統的光標,用於選擇操作全息對象。然而在Unity中並沒有明確的Gaze API或者組件。

 

概念上來說,Gaze是通過用戶頭部兩眼之間發出一條向前方的射線來實現的,射線可以識別它所碰撞的物體。在Unity中,使用Main Camera來表示用戶頭部的位置和朝向。准確的說,是指UnityEngine.Camera.main.transform.forward 和 UnityEngine.Camera.main.transform.position.調用Physics.RayCast 發出射線后可以得到RaycastHit結果,該結果包含了碰撞點的3D位置參數和碰撞對象。

 

實現Gaze的例子


 

void Update()
{
       RaycastHit hitInfo;
       if (Physics.Raycast(
               Camera.main.transform.position,
               Camera.main.transform.forward,
               out hitInfo,
               20.0f,
               Physics.DefaultRaycastLayers))
       {
           // 如果射線成功擊中物體
           // hitInfo.point代表了射線碰撞的位置
           // hitInfo.collider.gameObject代表了射線注視的全息對象
       }
}

在使用Gaze的時候,盡量避免每個物體都發出凝視射線,而是使用單例對象來管理凝視射線和其結果。

 

可視化凝視的具體例子


 

可以直接使用HoloToolkit中的GazeManager.cs腳本來實現凝視射線。

可以參考或直接使用HoloToolkit-Unity項目中的GazeManager.cs和預制的各種指針資源,包括Cursor.prefab 和 CursorWithFeedback.prefab 等。

 

1、添加GazeManager.cs

點擊“ Create Empty” 創建一個空游戲對象,並將其命名為 Manager,為 Manager對象添加核心腳本組件GazeManager.cs

// Copyright (c) Microsoft Corporation. All rights reserved.  
// Licensed under the MIT License. See LICENSE in the project root for license information.  
  
using UnityEngine;  
using UnityEngine.VR.WSA;  
  
namespace HoloToolkit.Unity  
{  
    /// <summary>  
    /// GazeManager determines the location of the user's gaze, hit position and normals.  
    /// </summary>  
    public partial class GazeManager : Singleton<GazeManager>  
    {  
        [Tooltip("Maximum gaze distance, in meters, for calculating a hit.")]  
        public float MaxGazeDistance = 15.0f;  
  
        [Tooltip("Select the layers raycast should target.")]  
        public LayerMask RaycastLayerMask = Physics.DefaultRaycastLayers;  
  
        /// <summary>  
        /// Physics.Raycast result is true if it hits a hologram.  
        /// </summary>  
        public bool Hit { get; private set; }  
  
        /// <summary>  
        /// HitInfo property gives access  
        /// to RaycastHit public members.  
        /// </summary>  
        public RaycastHit HitInfo { get; private set; }  
  
        /// <summary>  
        /// Position of the intersection of the user's gaze and the holograms in the scene.  
        /// </summary>  
        public Vector3 Position { get; private set; }  
  
        /// <summary>  
        /// RaycastHit Normal direction.  
        /// </summary>  
        public Vector3 Normal { get; private set; }  
  
        /// <summary>  
        /// Object currently being focused on.  
        /// </summary>  
        public GameObject FocusedObject { get; private set; }  
  
        [Tooltip("Checking enables SetFocusPointForFrame to set the stabilization plane.")]  
        public bool SetStabilizationPlane = true;  
        [Tooltip("Lerp speed when moving focus point closer.")]  
        public float LerpStabilizationPlanePowerCloser = 4.0f;  
        [Tooltip("Lerp speed when moving focus point farther away.")]  
        public float LerpStabilizationPlanePowerFarther = 7.0f;  
  
        private Vector3 gazeOrigin;  
        private Vector3 gazeDirection;  
        private float lastHitDistance = 15.0f;  
  
        private void Update()  
        {  
            gazeOrigin = Camera.main.transform.position;  
            gazeDirection = Camera.main.transform.forward;  
  
            UpdateRaycast();  
            UpdateStabilizationPlane();  
        }  
  
        /// <summary>  
        /// Calculates the Raycast hit position and normal.  
        /// </summary>  
        private void UpdateRaycast()  
        {  
            // Get the raycast hit information from Unity's physics system.  
            RaycastHit hitInfo;  
            Hit = Physics.Raycast(gazeOrigin,  
                           gazeDirection,  
                           out hitInfo,  
                           MaxGazeDistance,  
                           RaycastLayerMask);  
  
            GameObject oldFocusedObject = FocusedObject;  
            // Update the HitInfo property so other classes can use this hit information.  
            HitInfo = hitInfo;  
  
            if (Hit)  
            {  
                // If the raycast hits a hologram, set the position and normal to match the intersection point.  
                Position = hitInfo.point;  
                Normal = hitInfo.normal;  
                lastHitDistance = hitInfo.distance;  
                FocusedObject = hitInfo.collider.gameObject;  
            }  
            else  
            {  
                // If the raycast does not hit a hologram, default the position to last hit distance in front of the user,  
                // and the normal to face the user.  
                Position = gazeOrigin + (gazeDirection * lastHitDistance);  
                Normal = -gazeDirection;  
                FocusedObject = null;  
            }  
  
            // Check if the currently hit object has changed  
            if (oldFocusedObject != FocusedObject)  
            {  
                if (oldFocusedObject != null)  
                {  
                    oldFocusedObject.SendMessage("OnGazeLeave", SendMessageOptions.DontRequireReceiver);  
                }  
                if (FocusedObject != null)  
                {  
                    FocusedObject.SendMessage("OnGazeEnter", SendMessageOptions.DontRequireReceiver);  
                }  
            }  
        }  
  
        /// <summary>  
        /// Adds the stabilization plane modifier if it's enabled and if it doesn't exist yet.  
        /// </summary>  
        private void UpdateStabilizationPlane()  
        {  
            // We want to use the stabilization logic.  
            if (SetStabilizationPlane)  
            {  
                // Check if it exists in the scene.  
                if (StabilizationPlaneModifier.Instance == null)  
                {  
                    // If not, add it to us.  
                    gameObject.AddComponent<StabilizationPlaneModifier>();  
                }  
            }  
  
            if (StabilizationPlaneModifier.Instance)  
            {  
                StabilizationPlaneModifier.Instance.SetStabilizationPlane = SetStabilizationPlane;  
            }  
        }  
    }  
}  

2、創建一個新的游戲對象Cube,用來測試凝視效果

3、添加Cursor

就像PC使用鼠標來選中和交互圖標一樣,你可以為凝視也實現一個指針來更好的代表用戶的凝視。

從 HoloToolkit/Input/Prefabs/ 目錄下拖拽 Cursor Prefab 組件到場景中。這樣當凝視在全息對象時,其表面會出現一個藍色的光圈,表示當前凝視該對象,當射線離開該游戲對象時,Cursor變成一個點光源,以此來區分是否凝視游戲對象。

可以查看到Cursor中存在兩個光標對象,分別是凝視在對象上及離開光息對象時分別顯示的光標

CursorManager.cs

// Copyright (c) Microsoft Corporation. All rights reserved.  
// Licensed under the MIT License. See LICENSE in the project root for license information.  
  
using HoloToolkit.Unity;  
using UnityEngine;  
  
/// <summary>  
/// CursorManager class takes Cursor GameObjects.  
/// One that is on Holograms and another off Holograms.  
/// 1. Shows the appropriate Cursor when a Hologram is hit.  
/// 2. Places the appropriate Cursor at the hit position.  
/// 3. Matches the Cursor normal to the hit surface.  
/// </summary>  
public partial class CursorManager : Singleton<CursorManager>  
{  
    //凝視射線在全息對象上時顯示的光標對象  
    [Tooltip("Drag the Cursor object to show when it hits a hologram.")]  
    public GameObject CursorOnHolograms;  
    //凝視射線離開全息對象時顯示的光標對象  
    [Tooltip("Drag the Cursor object to show when it does not hit a hologram.")]  
    public GameObject CursorOffHolograms;  
  
    [Tooltip("Distance, in meters, to offset the cursor from the collision point.")]  
    public float DistanceFromCollision = 0.01f;  
  
    void Awake()  
    {  
        //當未設定光標對象時直接返回  
        if (CursorOnHolograms == null || CursorOffHolograms == null)  
        {  
            return;  
        }  
          
        // Hide the Cursors to begin with.  
        CursorOnHolograms.SetActive(false);  
        CursorOffHolograms.SetActive(false);  
    }  
  
    void LateUpdate()  
    {  
        if (GazeManager.Instance == null || CursorOnHolograms == null || CursorOffHolograms == null)  
        {  
            return;  
        }  
        //當凝視射線在全息對象上及離開全息對象時,分別顯示不同的光標對象,以此來進行區分  
        if (GazeManager.Instance.Hit)  
        {  
            CursorOnHolograms.SetActive(true);  
            CursorOffHolograms.SetActive(false);  
        }  
        else  
        {  
            CursorOffHolograms.SetActive(true);  
            CursorOnHolograms.SetActive(false);  
        }  
  
        //計算並安置光標  
        // Place the cursor at the calculated position.  
        this.gameObject.transform.position = GazeManager.Instance.Position + GazeManager.Instance.Normal * DistanceFromCollision;  
  
        // Orient the cursor to match the surface being gazed at.  
        gameObject.transform.up = GazeManager.Instance.Normal;  
    }  
}  

4、運行測試

當凝視射線在Cube上時,出現藍色的光圈,表示當前凝視的點在該位置

當凝視射線離開Cube時,光標顯示為一個點光源


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM