Hololens的使用如果类比到计算机的使用,在输入操作方面,Hololens了解用户的操作意图的第一个步骤是凝视,用户的凝视射线呈现在场景中的点为凝视点,就好像是电脑中的鼠标光标点,凝视是第一步,是人与hololens操作的开始。
涉及凝视相关的知识点如下:
1 当用户看着一个全息图时,光标点会有反馈表现—表明用户看到了全息图,当用户凝视视线离开全息图时,光标点也要有反馈-表明用户没有在看全息图。
2 当用户注视到全息图时,给于用户更多反馈,例如声音,全息图当变化等。
3 使用定位技术使得用户可以选中更小的全息图。
4 添加方向指示图标指引用户找到全息图
5全息指示面板追随用户移动,一直保持在用户可见视角之内。
前提条件:
1 一台已经安装好开发工具的windows10 pc
2 一些C#编程能力
3 已经完成了教程 Holograms-101
4 一个开发者模式的Hololens 设备
项目文件:
下载该教程所需文件files
章节1-Unity 设置
步骤:
发布到Hololens设备前的一些Unity必要设置
导入美术资源
场景设置
发布项目
章节2 光标点与目标反馈
光标设计遵循以下原则:
1 光标要一直出现在视角中
2 光标不能太小也不能太大
3 光标不能遮挡内容
步骤:
接下来你需要编辑GazeManager.cs 代码,实现以下几点:
1 执行一个物理射线physics raycast,
2 存储射线交叉点的位置和法线position and normal
3 如果射线没有击中任何对象,将位置与法线设为默认值
GazeManager.cs
using HoloToolkit; using UnityEngine; /// <summary> /// GazeManager determines the location of the user‘s gaze, hit position and normals. /// </summary> public class GazeManager : Singleton<GazeManager> { [Tooltip("Maximum gaze distance for calculating a hit.")] public float MaxGazeDistance = 5.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 user‘s gaze. /// </summary> public Vector3 Position { get; private set; } /// <summary> /// RaycastHit Normal direction. /// </summary> public Vector3 Normal { get; private set; } private GazeStabilizer gazeStabilizer; private Vector3 gazeOrigin; private Vector3 gazeDirection; void Awake() { /* TODO: DEVELOPER CODING EXERCISE 3.a */ // 3.a: GetComponent GazeStabilizer and assign it to gazeStabilizer. } private void Update() { // 2.a: Assign Camera‘s main transform position to gazeOrigin. gazeOrigin = Camera.main.transform.position; // 2.a: Assign Camera‘s main transform forward to gazeDirection. gazeDirection = Camera.main.transform.forward; // 3.a: Using gazeStabilizer, call function UpdateHeadStability. // Pass in gazeOrigin and Camera‘s main transform rotation. // 3.a: Using gazeStabilizer, get the StableHeadPosition and // assign it to gazeOrigin. UpdateRaycast(); } /// <summary> /// Calculates the Raycast hit position and normal. /// </summary> private void UpdateRaycast() { /* TODO: DEVELOPER CODING EXERCISE 2.a */ // 2.a: Create a variable hitInfo of type RaycastHit. RaycastHit hitInfo; // 2.a: Perform a Unity Physics Raycast. // Collect return value in public property Hit. // Pass in origin as gazeOrigin and direction as gazeDirection. // Collect the information in hitInfo. // Pass in MaxGazeDistance and RaycastLayerMask. Hit = Physics.Raycast(gazeOrigin, gazeDirection, out hitInfo, MaxGazeDistance, RaycastLayerMask); // 2.a: Assign hitInfo variable to the HitInfo public property // so other classes can access it. HitInfo = hitInfo; if (Hit) { // If raycast hit a hologram... // 2.a: Assign property Position to be the hitInfo point. Position = hitInfo.point; // 2.a: Assign property Normal to be the hitInfo normal. Normal = hitInfo.normal; } else { // If raycast did not hit a hologram... // Save defaults ... // 2.a: Assign Position to be gazeOrigin plus MaxGazeDistance times gazeDirection. Position = gazeOrigin + (gazeDirection * MaxGazeDistance); // 2.a: Assign Normal to be the user‘s gazeDirection. Normal = gazeDirection; } } }
接下来你要编辑 CusorManager.cs 代码实现以下目标:
1 判断哪个光标状态应该被激活,
2 根据光标是否在全息图上来不断更新光标状态
3 始终将光标放在用户正在注视的位置。
using HoloToolkit; using UnityEngine; /// <summary> /// CursorManager class takes Cursor GameObjects. /// One that is on Holograms and another off Holograms. /// Shows the appropriate Cursor when a Hologram is hit. /// Places the appropriate Cursor at the hit position. /// Matches the Cursor normal to the hit surface. /// </summary> public 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; void Awake() { if (CursorOnHolograms == null || CursorOffHolograms == null) { return; } // Hide the Cursors to begin with. CursorOnHolograms.SetActive(false); CursorOffHolograms.SetActive(false); } void Update() { /* TODO: DEVELOPER CODING EXERCISE 2.b */ if (GazeManager.Instance == null || CursorOnHolograms == null || CursorOffHolograms == null) { return; } if (GazeManager.Instance.Hit) { // 2.b: SetActive true the CursorOnHolograms to show cursor. CursorOnHolograms.SetActive(true); // 2.b: SetActive false the CursorOffHolograms hide cursor. CursorOffHolograms.SetActive(false); } else { // 2.b: SetActive true CursorOffHolograms to show cursor. CursorOffHolograms.SetActive(true); // 2.b: SetActive false CursorOnHolograms to hide cursor. CursorOnHolograms.SetActive(false); } // 2.b: Assign gameObject‘s transform position equals GazeManager‘s instance Position. gameObject.transform.position = GazeManager.Instance.Position; // 2.b: Assign gameObject‘s transform up vector equals GazeManager‘s instance Normal. gameObject.transform.up = GazeManager.Instance.Normal; } }
接下来可以再次发布部署一次APP,看一下当光标移动到全息图上时光标的变化。
全息图的凝视反馈:
步骤:
接下来你需要编辑 InteractibleManager.cs 和 Interactible.cs 两个代码文件来实现以下功能 :
using HoloToolkit; using UnityEngine; /// <summary> /// InteractibleManager keeps tracks of which GameObject /// is currently in focus. /// </summary> public class InteractibleManager : Singleton<InteractibleManager> { public GameObject FocusedGameObject { get; private set; } private GameObject oldFocusedGameObject = null; void Start() { FocusedGameObject = null; } void Update() { /* TODO: DEVELOPER CODING EXERCISE 2.c */ oldFocusedGameObject = FocusedGameObject; if (GazeManager.Instance.Hit) { RaycastHit hitInfo = GazeManager.Instance.HitInfo; if (hitInfo.collider != null) { // 2.c: Assign the hitInfo‘s collider gameObject to the FocusedGameObject. FocusedGameObject = hitInfo.collider.gameObject; } else { FocusedGameObject = null; } } else { FocusedGameObject = null; } if (FocusedGameObject != oldFocusedGameObject) { ResetFocusedInteractible(); if (FocusedGameObject != null) { if (FocusedGameObject.GetComponent<Interactible>() != null) { // 2.c: Send a GazeEntered message to the FocusedGameObject. FocusedGameObject.SendMessage("GazeEntered"); } } } } private void ResetFocusedInteractible() { if (oldFocusedGameObject != null) { if (oldFocusedGameObject.GetComponent<Interactible>() != null) { // 2.c: Send a GazeExited message to the oldFocusedGameObject. oldFocusedGameObject.SendMessage("GazeExited"); } } } }
using UnityEngine; /// <summary> /// The Interactible class flags a Game Object as being "Interactible". /// Determines what happens when an Interactible is being gazed at. /// </summary> public class Interactible : MonoBehaviour { [Tooltip("Audio clip to play when interacting with this hologram.")] public AudioClip TargetFeedbackSound; private AudioSource audioSource; private Material[] defaultMaterials; void Start() { defaultMaterials = GetComponent<Renderer>().materials; // Add a BoxCollider if the interactible does not contain one. Collider collider = GetComponentInChildren<Collider>(); if (collider == null) { gameObject.AddComponent<BoxCollider>(); } EnableAudioHapticFeedback(); } private void EnableAudioHapticFeedback() { // If this hologram has an audio clip, add an AudioSource with this clip. if (TargetFeedbackSound != null) { audioSource = GetComponent<AudioSource>(); if (audioSource == null) { audioSource = gameObject.AddComponent<AudioSource>(); } audioSource.clip = TargetFeedbackSound; audioSource.playOnAwake = false; audioSource.spatialBlend = 1; audioSource.dopplerLevel = 0; } } /* TODO: DEVELOPER CODING EXERCISE 2.d */ void GazeEntered() { for (int i = 0; i < defaultMaterials.Length; i++) { // 2.d: Uncomment the below line to highlight the material when gaze enters. defaultMaterials[i].SetFloat("_Highlight", .25f); } } void GazeExited() { for (int i = 0; i < defaultMaterials.Length; i++) { // 2.d: Uncomment the below line to remove highlight on material when gaze exits. defaultMaterials[i].SetFloat("_Highlight", 0f); } } void OnSelect() { for (int i = 0; i < defaultMaterials.Length; i++) { defaultMaterials[i].SetFloat("_Highlight", .5f); } // Play the audioSource feedback when we gaze and select a hologram. if (audioSource != null && !audioSource.isPlaying) { audioSource.Play(); } /* TODO: DEVELOPER CODING EXERCISE 6.a */ // 6.a: Handle the OnSelect by sending a PerformTagAlong message. } }
接下来你可以再一次发布部署到Hololens上查看当凝视点击中全息图时,全息图的反馈状态。
章节3 定位技术
目标:更容易定位到全息对象,稳定且自然到头部移动
步骤:
接下来需要更新GazeManager 脚本
using HoloToolkit; using UnityEngine; /// <summary> /// GazeManager determines the location of the user‘s gaze, hit position and normals. /// </summary> public class GazeManager : Singleton<GazeManager> { [Tooltip("Maximum gaze distance for calculating a hit.")] public float MaxGazeDistance = 5.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 user‘s gaze. /// </summary> public Vector3 Position { get; private set; } /// <summary> /// RaycastHit Normal direction. /// </summary> public Vector3 Normal { get; private set; } private GazeStabilizer gazeStabilizer; private Vector3 gazeOrigin; private Vector3 gazeDirection; void Awake() { /* TODO: DEVELOPER CODING EXERCISE 3.a */ // 3.a: GetComponent GazeStabilizer and assign it to gazeStabilizer. gazeStabilizer = GetComponent<GazeStabilizer>(); } private void Update() { // 2.a: Assign Camera‘s main transform position to gazeOrigin. gazeOrigin = Camera.main.transform.position; // 2.a: Assign Camera‘s main transform forward to gazeDirection. gazeDirection = Camera.main.transform.forward; // 3.a: Using gazeStabilizer, call function UpdateHeadStability. // Pass in gazeOrigin and Camera‘s main transform rotation. gazeStabilizer.UpdateHeadStability(gazeOrigin, Camera.main.transform.rotation); // 3.a: Using gazeStabilizer, get the StableHeadPosition and // assign it to gazeOrigin. gazeOrigin = gazeStabilizer.StableHeadPosition; UpdateRaycast(); } /// <summary> /// Calculates the Raycast hit position and normal. /// </summary> private void UpdateRaycast() { /* TODO: DEVELOPER CODING EXERCISE 2.a */ // 2.a: Create a variable hitInfo of type RaycastHit. RaycastHit hitInfo; // 2.a: Perform a Unity Physics Raycast. // Collect return value in public property Hit. // Pass in origin as gazeOrigin and direction as gazeDirection. // Collect the information in hitInfo. // Pass in MaxGazeDistance and RaycastLayerMask. Hit = Physics.Raycast(gazeOrigin, gazeDirection, out hitInfo, MaxGazeDistance, RaycastLayerMask); // 2.a: Assign hitInfo variable to the HitInfo public property // so other classes can access it. HitInfo = hitInfo; if (Hit) { // If raycast hit a hologram... // 2.a: Assign property Position to be the hitInfo point. Position = hitInfo.point; // 2.a: Assign property Normal to be the hitInfo normal. Normal = hitInfo.normal; } else { // If raycast did not hit a hologram... // Save defaults ... // 2.a: Assign Position to be gazeOrigin plus MaxGazeDistance times gazeDirection. Position = gazeOrigin + (gazeDirection * MaxGazeDistance); // 2.a: Assign Normal to be the user‘s gazeDirection. Normal = gazeDirection; } } }
章节4 方向指示器
给光标添加一个方向箭头使得用户更容易找到全息对象。
步骤:
章节5 广告牌
我们使用一个广告牌来使得全息对象始终面向用户
在做接下来的教程时先把 AstroMan 中的Billboard 脚本删掉
章节6 追随标签
使用追随标签可以使得我们的全息对象追随我们到房间的任何位置。
追随全息对象的设计应遵循以下原则:
1 全息内容应该在视角范围内一眼就可以看到
2全息内容不能显示在用户前进路上
3 头部锁定内容是不舒服的
我们可以想象这个Tag-along全息对象一直保持在我们视角的边缘,无论我们走到哪里,我们都可以一眼就看到它。
步骤:
接下来将使用SimpleTagalong.cs 文件,它会做到:
接下来要做:
using UnityEngine; /// <summary> /// The Interactible class flags a Game Object as being "Interactible". /// Determines what happens when an Interactible is being gazed at. /// </summary> public class Interactible : MonoBehaviour { [Tooltip("Audio clip to play when interacting with this hologram.")] public AudioClip TargetFeedbackSound; private AudioSource audioSource; private Material[] defaultMaterials; void Start() { defaultMaterials = GetComponent<Renderer>().materials; // Add a BoxCollider if the interactible does not contain one. Collider collider = GetComponentInChildren<Collider>(); if (collider == null) { gameObject.AddComponent<BoxCollider>(); } EnableAudioHapticFeedback(); } private void EnableAudioHapticFeedback() { // If this hologram has an audio clip, add an AudioSource with this clip. if (TargetFeedbackSound != null) { audioSource = GetComponent<AudioSource>(); if (audioSource == null) { audioSource = gameObject.AddComponent<AudioSource>(); } audioSource.clip = TargetFeedbackSound; audioSource.playOnAwake = false; audioSource.spatialBlend = 1; audioSource.dopplerLevel = 0; } } /* TODO: DEVELOPER CODING EXERCISE 2.d */ void GazeEntered() { for (int i = 0; i < defaultMaterials.Length; i++) { // 2.d: Uncomment the below line to highlight the material when gaze enters. defaultMaterials[i].SetFloat("_Highlight", .25f); } } void GazeExited() { for (int i = 0; i < defaultMaterials.Length; i++) { // 2.d: Uncomment the below line to remove highlight on material when gaze exits. defaultMaterials[i].SetFloat("_Highlight", 0f); } } void OnSelect() { for (int i = 0; i < defaultMaterials.Length; i++) { defaultMaterials[i].SetFloat("_Highlight", .5f); } // Play the audioSource feedback when we gaze and select a hologram. if (audioSource != null && !audioSource.isPlaying) { audioSource.Play(); } /* TODO: DEVELOPER CODING EXERCISE 6.a */ // 6.a: Handle the OnSelect by sending a PerformTagAlong message. SendMessage("PerformTagAlong"); } }
InteractibleAction.cs脚本在您注视全息图时执行自定义操作。 让我们更新它与标签一起使用。
using HoloToolkit; using UnityEngine; /// <summary> /// InteractibleAction performs custom actions when you gaze at the holograms. /// </summary> public class InteractibleAction : MonoBehaviour { [Tooltip("Drag the Tagalong prefab asset you want to display.")] public GameObject ObjectToTagAlong; void PerformTagAlong() { if (ObjectToTagAlong == null) { return; } // Recommend having only one tagalong. GameObject existingTagAlong = GameObject.FindGameObjectWithTag("TagAlong"); if (existingTagAlong != null) { return; } GameObject instantiatedObjectToTagAlong = GameObject.Instantiate(ObjectToTagAlong); instantiatedObjectToTagAlong.SetActive(true); /* TODO: DEVELOPER CODING EXERCISE 6.b */ // 6.b: AddComponent Billboard to instantiatedObjectToTagAlong. // So it‘s always facing the user as they move. instantiatedObjectToTagAlong.AddComponent<Billboard>(); // 6.b: AddComponent SimpleTagalong to instantiatedObjectToTagAlong. // So it‘s always following the user as they move. instantiatedObjectToTagAlong.AddComponent<SimpleTagalong>(); // 6.b: Set any public properties you wish to experiment with. } }
发布部署到设备上查看
原文链接https://developer.microsoft.com/en-us/windows/holographic/holograms_210
如有翻译上的错误请指正。谢谢
微软Hololens学院教程-Hologram Gaze(凝视)
原文:http://www.cnblogs.com/qichun/p/6055350.html