zoukankan      html  css  js  c++  java
  • 【Holograms 101D】一步步用Unity 开发 Hologram

    转载请注明出处:

    Holograms 101


    该教程将带领你走完 Hologram 创建 的全过程。整个开发分成如下几个部分: 聚焦输入 gaze手势输入gesture , 声音输入voice input映射声音spatial sound and 映射地图spatial mapping.

    整个教程大概耗时1个小时.

    开始前的要求:

    工程文件

    • 解压下载的 开发文件,将该文件夹命名为 Origami


    Chapter 1 - "Holo" world

    在这一章节,我们将要配置 我们的 第一个 Unity 工程,并走过 整个Build (编译)和 deploy(部署)过程

    目标

    • 设置Unity环境,以适应Hologram开发
    • 创建一个Hologram
    • 看到创建出来的Hologram工程效果

    步骤

    • 打开Unity
    • 点击 Open.
    • 找到之前解压并重命名为 Origami 文件夹
    • 选择 Origami 并点击 Select Folder.
    • 因为新工程 Origami project 并没有包含任何 scene, 所以需要保存当前的默认 scene (default scene)为一个新的scene: File / Save Scene As.
    • 将新的scene命名为 Origami 并点击 Save 按钮.

    配置主虚拟镜头(main virtual camera)

    • 在 Hierarchy Panel , 选中 Main Camera.
    • 在右侧的 Inspector 选项栏中,将 position 配置为 0,0,0.
    • 在当前的 Clear Flags 属性中,将下拉框中的设置从Skybox 改为 Solid color
    • 将 Background 属性点开
    • 将 R, G, B, 和 A 设置为0
        

    设置场景scene

    • 在 Hierarchy Panel , 点击 Create 并 Create Empty
    • 新创建的文件夹名字是 GameObject,重命名该文件夹为 OrigamiCollection
    • 从 Project Panel 的 Holograms 文件夹中:
      • 拖拽 Stage 到 Hierarchy Panel 中,作为 OrigamiCollection 的子项
      • 拖拽 Sphere1 到 Hierarchy Panel 中,作为 OrigamiCollection 的子项
      • 拖拽 Sphere2 到 Hierarchy Panel 中,作为 OrigamiCollection 的子项
    • 删除 Hierarchy Panel 中的  Directional Light 项
    • 从 Holograms 文件夹中,拖拽 Lights 项到 Hierarchy Panel 的根目录
    • 选中 Hierarchy Panel  OrigamiCollection 目录
    • 在右侧的 Inspector 栏,设置 tranform的position值为0, -0.5, 2.0.
    • 点击 项目 正上方的 “播放 按钮,可以预览效果

    • 再次点击 “播放”按钮,关闭预览

    从Unity导出工程到Visual Studio

    • 选择 File > Build Settings.
    • 选择 Windows Store 
    • SDK 选择 Universal 10 并选择 Build Type 为 D3D.
    • 选中 Unity C# Projects.
    • 点击 Add Open Scenes 按钮,添加当前的视图到Scenes In Build 栏中
    • 点击 Build.
    • 接下来会弹出一个windows窗口,在该窗口创建文件夹 App
    • 单击 App 文件夹
    • 然后点击 选择文件夹. 就会开始编译

    • 当编译结束,就会自动弹出编译好的文件目录
    • 打开 App 文件夹
    • 双击 Origami.sln.
    • 在VS顶部工具栏中,修改Debug 为 Release ,并修改 ARM 为 X86 架构
    • 点击设备旁的 三角形按钮,选择远程计算机( Remote Device
      • 将地址(Address) 设置为Hololens的 IP 或者 Hololens的名称
      • 设置身份验证模式(Authentication Mode)通用(Universal)
      • 点击选择( Select)
    • 如果是 使用Hololens模拟器,则直接选择HoloLens Emulator 即可。
    • 紧接着开始调试
    • Origami 项目将会被部署在你的Hololens上(或者Hololens 模拟器上),并运行
    • 带上你的Hololens开始体验吧!(译者表示完全体验不了,因为没设备啊(┬_┬),只能仿真玩玩)
        
        启动场景。。。。好兴奋!!!
        
        移动视角看到的效果!!!
        a5989049-9a35-4ff4-8838-01078f0dd8f7.gif 

    Chapter 2 - Gaze


    在本节中,将会描述Hololens三种交互方式之一的  -- 凝视输入(gaze).

    目标

    • 让我们的凝视输入可视化(视线所指会出现一个圆圈).

    介绍

    • 返回 Unity 工程
    • 选择 Holograms 文件夹
    • 将 Cursor 组建拖入 Hierarchy panel 的根目录中
    • 右击 Scripts 文件夹,进入Create 目录,并选择C# Script.

    • 将新创建的脚本命名为 WorldCursor
    • 选中 Cursor 组件
    • 拖拽 WorldCursor 脚本到Inspector panel 中的 Cursor 组件上
    • jdfw.gif
    • 这时候,再双击 WorldCursor 脚本文件,会自动打开 Visual Studio 
    • 复制下面的代码到 WorldCursor.cs 文件中,保存
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    using UnityEngine;
    public class WorldCursor : MonoBehaviour
    {
        private MeshRenderer meshRenderer;
        // Use this for initialization 初始化时候调用
        void Start()
        {
            // Grab the mesh renderer that's on the same object as this script.
            // 获取 meshRenderer
            meshRenderer = this.gameObject.GetComponentInChildren<MeshRenderer>();
        }
        // Update is called once per frame 每一帧都会自动更新
        void Update()
        {
            // Do a raycast into the world based on the user's
            // head position and orientation.
            var headPosition = Camera.main.transform.position;
            var gazeDirection = Camera.main.transform.forward;
            RaycastHit hitInfo;
            if (Physics.Raycast(headPosition, gazeDirection, out hitInfo))
            {
                // If the raycast hit a hologram...
                // Display the cursor mesh.
                meshRenderer.enabled = true;
                // Move the cursor to the point where the raycast hit.
                this.transform.position = hitInfo.point;
                // Rotate the cursor to hug the surface of the hologram.
                this.transform.rotation = Quaternion.FromToRotation(Vector3.up, hitInfo.normal);
            }
            else
            {
                // If the raycast did not hit a hologram, hide the cursor mesh.
                meshRenderer.enabled = false;
            }
        }
    }

    • 进入目录 File > Build Settings 重新生成工程
    • 返回Visual Studio 解决方案中
    • 这时会提示 是否需要重新加载 ,选择是。
    • 然后继续点击调试

    • 现在可以看到视线聚焦之处,有一个红色圆环。

    Chapter 3 - Gestures


    在这一章节中,我们将学习使用 手势输入gestures通过使能 Unity 的物理引擎,打开重力模拟, 当用户选择了一个纸球,就会让该纸球下落。

    目标

    • 使用选择手势控制Hologram

    步骤

    接下来创建一个脚本,使得程序能够检测到 选择手势 

    • 在 Scripts 目录中,创建一个名为 GazeGestureManager 的脚本
    • 将 GazeGestureManager 脚本拖入 OrigamiCollection 目录中

    • 打开 GazeGestureManager 脚本,并复制如下code:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    using UnityEngine;
    using UnityEngine.VR.WSA.Input;
     
    public class GazeGestureManager : MonoBehaviour
    {
        public static GazeGestureManager Instance { getprivate set; }
     
        // Represents the hologram that is currently being gazed at.
        public GameObject FocusedObject { getprivate set; }
     
        GestureRecognizer recognizer;
     
        // Use this for initialization
        void Start()
        {
            Instance = this;
     
            // Set up a GestureRecognizer to detect Select gestures.
            recognizer = new GestureRecognizer();
            recognizer.TappedEvent += (source, tapCount, ray) =>
            {
                // Send an OnSelect message to the focused object and its ancestors.
                if (FocusedObject != null)
                {
                    FocusedObject.SendMessageUpwards("OnSelect");
                }
            };
            recognizer.StartCapturingGestures();
        }
     
        // Update is called once per frame
        void Update()
        {
            // Figure out which hologram is focused this frame.
            GameObject oldFocusObject = FocusedObject;
     
            // Do a raycast into the world based on the user's
            // head position and orientation.
            var headPosition = Camera.main.transform.position;
            var gazeDirection = Camera.main.transform.forward;
     
            RaycastHit hitInfo;
            if (Physics.Raycast(headPosition, gazeDirection, out hitInfo))
            {
                // If the raycast hit a hologram, use that as the focused object.
                FocusedObject = hitInfo.collider.gameObject;
            }
            else
            {
                // If the raycast did not hit a hologram, clear the focused object.
                FocusedObject = null;
            }
     
            // If the focused object changed this frame,
            // start detecting fresh gestures again.
            if (FocusedObject != oldFocusObject)
            {
                recognizer.CancelGestures();
                recognizer.StartCapturingGestures();
            }
        }
    }

    • 创建另外一个脚本 SphereCommands.
    • 占看 OrigamiCollection 目录
    • 拖拽 SphereCommands 脚本到 Sphere1 模型上
    • 拖拽 SphereCommands 脚本到 Sphere2 模型上
    • 打开 visual studio 编辑,复制如下代码到 SphereCommands 脚本中:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    using UnityEngine;
     
    public class SphereCommands : MonoBehaviour
    {
        // Called by GazeGestureManager when the user performs a Select gesture
        void OnSelect()
        {
            // If the sphere has no Rigidbody component, add one to enable physics.
            if (!this.GetComponent<Rigidbody>())
            {
                var rigidbody = this.gameObject.AddComponent<Rigidbody>();
                rigidbody.collisionDetectionMode = CollisionDetectionMode.Continuous;
            }
        }
    }

    • 重新生成Hologram
    • 注视纸球
    • 采用选择手势,查看纸球下落过程
    jdfw.gif

    Chapter 4 - Voice


    这一章节,我们将要添加两个语音输入命令( voice commands ):

    "Reset world": 将掉落的小球,初始化到原始位置

    "Drop sphere":令小球掉落

    目标

    • 添加常驻后台的声音识别命令.
    • 创建一个能对声音产生反应的应用

    步骤

    • 在 Scripts 目录中,创建一个名为 SpeechManager 的脚本
    • 拖拽 SpeechManager 脚本到 OrigamiCollection 目录中
    • 双击打开 SpeechManager 脚本
    • 复制如下代码到脚本 SpeechManager.cs 中:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    using System.Collections.Generic;
    using System.Linq;
    using UnityEngine;
    using UnityEngine.Windows.Speech;
     
    public class SpeechManager : MonoBehaviour
    {
        KeywordRecognizer keywordRecognizer = null;
        Dictionary<string, System.Action> keywords = new Dictionary<string, System.Action>();
     
        // Use this for initialization
        void Start()
        {
            keywords.Add("Reset world", () =>
            {
                // Call the OnReset method on every descendant object.
                this.BroadcastMessage("OnReset");
            });
     
            keywords.Add("Drop Sphere", () =>
            {
                var focusObject = GazeGestureManager.Instance.FocusedObject;
                if (focusObject != null)
                {
                    // Call the OnDrop method on just the focused object.
                    focusObject.SendMessage("OnDrop");
                }
            });
     
            // Tell the KeywordRecognizer about our keywords.
            keywordRecognizer = new KeywordRecognizer(keywords.Keys.ToArray());
     
            // Register a callback for the KeywordRecognizer and start recognizing!
            keywordRecognizer.OnPhraseRecognized += KeywordRecognizer_OnPhraseRecognized;
            keywordRecognizer.Start();
        }
     
        private void KeywordRecognizer_OnPhraseRecognized(PhraseRecognizedEventArgs args)
        {
            System.Action keywordAction;
            if (keywords.TryGetValue(args.text, out keywordAction))
            {
                keywordAction.Invoke();
            }
        }
    }

    • 打开 SphereCommands脚本
    • 更新其代码如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    using UnityEngine;
     
    public class SphereCommands : MonoBehaviour
    {
        Vector3 originalPosition;
     
        // Use this for initialization
        void Start()
        {
            // Grab the original local position of the sphere when the app starts.
            originalPosition = this.transform.localPosition;
        }
     
        // Called by GazeGestureManager when the user performs a Select gesture
        void OnSelect()
        {
            // If the sphere has no Rigidbody component, add one to enable physics.
            if (!this.GetComponent<Rigidbody>())
            {
                var rigidbody = this.gameObject.AddComponent<Rigidbody>();
                rigidbody.collisionDetectionMode = CollisionDetectionMode.Continuous;
            }
        }
     
        // Called by SpeechManager when the user says the "Reset world" command
        void OnReset()
        {
            // If the sphere has a Rigidbody component, remove it to disable physics.
            var rigidbody = this.GetComponent<Rigidbody>();
            if (rigidbody != null)
            {
                DestroyImmediate(rigidbody);
            }
     
            // Put the sphere back into its original local position.
            this.transform.localPosition = originalPosition;
        }
     
        // Called by SpeechManager when the user says the "Drop sphere" command
        void OnDrop()
        {
            // Just do the same logic as a Select gesture.
            OnSelect();
        }
    }

    • 重新build整个工程
    • 注视某一个球体,说出命令 "Drop Sphere".
    • 说出命令"Reset World",让球体返回原来位置。
    • (译者表示Emulator中也是可以使用声音来控制的,Follow me, say " Drop sphere~")

    Chapter 5 - Spatial sound


    在这一章节中,我们将要添加一段音乐到应用app中,然后在特定动作下,触发音乐。我们将要使用 声音映射spatial sound 来 将插入的音乐定位到指定的位置上。

    目标

    • 在我们的世界中,听到Hologram

    步骤

    • 进入选项 Edit > Project Settings > Audio
    2016-04-26 (1).png
    • 在右边的 Inspector Panel 中,, 找到 Spatializer Plugin 并选择 MS HRTF Spatializer.

    • 将 Holograms 文件夹中的 Ambience 模型,拖拽到 OrigamiCollection 目录中
    • 选中 OrigamiCollection 目录,并在右边Inspector panel找到 Audio Source ,修改如下属性:
      • 选中 Spatialize 
      • 选中 Play On Awake.
      • 修改 Spatial Blend 为 3D
      • 选中 Loop
      • 展开 3D Sound Settings,并在Doppler Level 输入 0.1 
      • 设置 Volume Rolloff   Custom Rolloff.

    • 在 Scripts 目录中,创建一个 SphereSounds 脚本
    • 将脚本 SphereSounds 拖拽到 Sphere1 和 Sphere2 模型上
    • 打开 SphereSounds 脚本,并更新如下代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    using UnityEngine;
     
    public class SphereSounds : MonoBehaviour
    {
        AudioSource audioSource = null;
        AudioClip impactClip = null;
        AudioClip rollingClip = null;
     
        bool rolling = false;
     
        void Start()
        {
            // Add an AudioSource component and set up some defaults
            audioSource = gameObject.AddComponent<AudioSource>();
            audioSource.playOnAwake = false;
            audioSource.spatialize = true;
            audioSource.spatialBlend = 1.0f;
            audioSource.dopplerLevel = 0.0f;
            audioSource.rolloffMode = AudioRolloffMode.Custom;
     
            // Load the Sphere sounds from the Resources folder
            impactClip = Resources.Load<AudioClip>("Impact");
            rollingClip = Resources.Load<AudioClip>("Rolling");
        }
     
        // Occurs when this object starts colliding with another object
        void OnCollisionEnter(Collision collision)
        {
            // Play an impact sound if the sphere impacts strongly enough.
            if (collision.relativeVelocity.magnitude >= 0.1f)
            {
                audioSource.clip = impactClip;
                audioSource.Play();
            }
        }
     
        // Occurs each frame that this object continues to collide with another object
        void OnCollisionStay(Collision collision)
        {
            Rigidbody rigid = this.gameObject.GetComponent<Rigidbody>();
     
            // Play a rolling sound if the sphere is rolling fast enough.
            if (!rolling && rigid.velocity.magnitude >= 0.01f)
            {
                rolling = true;
                audioSource.clip = rollingClip;
                audioSource.Play();
            }
            // Stop the rolling sound if rolling slows down.
            else if (rolling && rigid.velocity.magnitude < 0.01f)
            {
                rolling = false;
                audioSource.Stop();
            }
        }
     
        // Occurs when this object stops colliding with another object
        void OnCollisionExit(Collision collision)
        {
            // Stop the rolling sound if the object falls off and stops colliding.
            if (rolling)
            {
                rolling = false;
                audioSource.Stop();
            }
        }
    }

    • 保存代码,重新build工程
    • 这时候两个小球,相当于一个声源,当移动视角,带上耳机体验的话,是能够感觉到双通道声音的赶脚的!(棒)

    Chapter 6 - Spatial mapping


    现在我们要使用 spatial mapping ,将 我们的应用放置到物理世界中的实物上。

    目标

    • 将真实世界代入到虚拟世界中
    • 随意放置我们的Hologram

    步骤

    • 在Unity 中,选中Holograms 目录
    • 拖拽 Spatial Mapping 到 Hierarchy 的根目录下
    • 选中 Spatial Mapping 
    • 在右边的 Inspector panel 中,修改如下属性:
      • 选中 Draw Visual Meshes 选项
      • Draw Material 选项选为 "wireframe
    • 重新编译build工程
    • 当应用运行,可以看到 (网格模型)wireframe mesh 将在物理世界中显示
    • 观察小球是怎么在当前场景下落的

    下面将指导你如何将 OrigamiCollection 移动到一个新的位置:

    • 在 Scripts 文件夹中,创建一个脚本名叫TapToPlaceParent.
    • Hierarchy 中,展开 OrigamiCollection 目录,并选中Stage 模型
    • 将脚本 TapToPlaceParent 拖拽到 Stage 模型上
    • 更新代码如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    using UnityEngine;
     
    public class TapToPlaceParent : MonoBehaviour
    {
        bool placing = false;
     
        // Called by GazeGestureManager when the user performs a Select gesture
        void OnSelect()
        {
            // On each Select gesture, toggle whether the user is in placing mode.
            placing = !placing;
     
            // If the user is in placing mode, display the spatial mapping mesh.
            if (placing)
            {
                SpatialMapping.Instance.DrawVisualMeshes = true;
            }
            // If the user is not in placing mode, hide the spatial mapping mesh.
            else
            {
                SpatialMapping.Instance.DrawVisualMeshes = false;
            }
        }
     
        // Update is called once per frame
        void Update()
        {
            // If the user is in placing mode,
            // update the placement to match the user's gaze.
     
            if (placing)
            {
                // Do a raycast into the world that will only hit the Spatial Mapping mesh.
                var headPosition = Camera.main.transform.position;
                var gazeDirection = Camera.main.transform.forward;
     
                RaycastHit hitInfo;
                if (Physics.Raycast(headPosition, gazeDirection, out hitInfo,
                    30.0f, SpatialMapping.PhysicsRaycastMask))
                {
                    // Move this object's parent object to
                    // where the raycast hit the Spatial Mapping mesh.
                    this.transform.parent.position = hitInfo.point;
     
                    // Rotate this object's parent object to face the user.
                    Quaternion toQuat = Camera.main.transform.localRotation;
                    toQuat.x = 0;
                    toQuat.z = 0;
                    this.transform.parent.rotation = toQuat;
                }
            }
        }
    }

    • 重新编译build工程
    • 现在我们应该可以通过凝视(gazing)将我们的目标重新定位。使用选择手势(Select gesture)就可以移动位置
    jdfw.gif

    Chapter 7 - Holographic fun

    Objectives

    • Reveal the entrance to a holographic underworld.

    Instructions

    Now we'll show you how to uncover the holographic underworld:

    • From the Holograms folder in the Project Panel:
      • Drag Underworld into the Hierarchy to be a child of OrigamiCollection.
    • In the Scripts folder, create a script named HitTarget.
    • In the Hierarchy, expand the OrigamiCollection.
    • Expand the Stage object and select the Target object (blue fan).
    • Drag the HitTarget script onto the Target object.
    • Open the HitTarget script in Visual Studio, and update it to be the following:

    HitTarget.cs[hide]
    using UnityEngine;public class HitTarget : MonoBehaviour{// These public fields become settable properties in the Unity editor.public GameObject underworld;public GameObject objectToHide;// Occurs when this object starts colliding with another objectvoid OnCollisionEnter(Collision collision){// Hide the stage and show the underworld.
            objectToHide.SetActive(false);
            underworld.SetActive(true);// Disable Spatial Mapping to let the spheres enter the underworld.SpatialMapping.Instance.SetMappingEnabled(false);}}

    • In Unity, select the Target object.
    • Two public properties are now visible on the Hit Target component and need to reference objects in our scene:
      • Drag Underworld from the Hierarchy panel to the Underworld property on the Hit Target component.
      • Drag Stage from the Hierarchy panel to the Object to Hide property on the Hit Target component.
    • Export, build and deploy the app.
    • Place the Origami Collection on the floor, and then use the Select gesture to make a sphere drop.
    • When the sphere hits the target (blue fan), an explosion will occur. The collection will be hidden and a hole to the underworld will appear.

    The end

    And that's the end of this tutorial!

    You learned:

    • How to create a holographic app in Unity.
    • How to make use of gaze, gesture, voice, sounds, and spatial mapping.
    • How to build and deploy an app using Visual Studio.

    You are now ready to start creating your own holographic apps!


  • 相关阅读:
    Python网络编程 —— 粘包问题及解决方法
    Python网络编程 —— socket(套接字)及通信
    Python网络编程 —— 网络基础知识
    Python
    MySQL 之 数据的导出与导入
    MySQL 之 慢查询优化及慢日志管理
    MySQL 之 索引进阶
    MySQL 之 索引
    MySQL 之 事务
    MySQL 之 表的存储引擎
  • 原文地址:https://www.cnblogs.com/zhxshseu/p/f8fc662ea409250b136d584729ad7c53.html
Copyright © 2011-2022 走看看