HoloLens開發手記 - Unity之Gaze凝視射線


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

 

 

實現Gaze Implementing Gaze


 

概念上來說,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的時候,盡量避免每個物體都發出凝視射線,而是使用單例對象來管理凝視射線和其結果。

 

可視化凝視 Visualizing Gaze


 

 

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

 

可視化凝視的例子

 

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

 

// 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; }

        [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 GameObject focusedObject;

        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>
        /// Updates the focus point for every frame.
        /// </summary>
        private void UpdateStabilizationPlane()
        {
            if (SetStabilizationPlane)
            {
                // Calculate the delta between camera's position and current hit position.
                float focusPointDistance = (gazeOrigin - Position).magnitude;
                float lerpPower = focusPointDistance > lastHitDistance
                    ? LerpStabilizationPlanePowerFarther
                    : LerpStabilizationPlanePowerCloser;

                // Smoothly move the focus point from previous hit position to new position.
                lastHitDistance = Mathf.Lerp(lastHitDistance, focusPointDistance, lerpPower * Time.deltaTime);

                Vector3 newFocusPointPosition = gazeOrigin + (gazeDirection * lastHitDistance);

                HolographicSettings.SetFocusPointForFrame(newFocusPointPosition, -gazeDirection);
            }
        }
    }
}

 


免責聲明!

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



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