詹姆斯·卡梅隆1984年的电影《终结者》引入了我们现在认为理所当然的许多科幻风格。其中最为持久的是热成像平视显示器(HUD),能让观众通过阿诺德斯瓦辛格的T-800角色看到世界。在设计圈里,它是被频繁用于学习借鉴及重制的经典用户界面设计之一。

本文将介绍如何使用Unity在HoloLens中重制这样的界面。您还可以将这个界面连接到微软认知服务,以对房间中的对象进行分析,加入面部识别,甚至是一些光学字符识别(OCR),来让这个教程更加有趣。您可以点击这里下载本教程的资源包。

重制UI
第一步要了解的是如何重制T-800热成像HUD显示器。首先在Unity中新建3D项目命名为“Terminator Vision”。新建“main”场景,添加HoloToolkit Unity包到项目中。您可以从HoloToolkit项目的GitHub仓库下载该资源包。本教程使用HoloToolkit-Unity-v1.5.5.0.unitypackage。将该资源包导入Unity场景,在Unity编辑器菜单上依次点击HoloToolkit -> Configure,设置项目目标平台为HoloLens。

正确配置好Unity项目和场景后,下面为场景添加一个Canvas对象作为显示文本的界面。在层级视图中选中“main”场景点击右键,在弹出菜单中选择GameObject -> UI -> Canvas添加Canvas,重命名为“HUD”。

这个HUD还需要一些文本,所以下一步为HUD添加文本。在层级视图中,右键点击HUD对象,在弹出菜单中依次选择UI -> Text添加4个Text对象,分别命名为BottomCenterText、MiddleRightText、MiddleLeftText和MiddleCenterText。添加一些文本让UI风格与《终结者》电影中的UI更匹配。

设置MiddleRightText文本内容如下:

SCAN MODE 43984
SIZE ASSESSMENT
ASSESSMENT COMPLETE
FIT PROBABILITY 0.99
RESET TO ACQUISITION
MODE SPEECH LEVEL 78
PRIORITY OVERRIDE
DEFENSE SYSTEMS SET
ACTIVE STATUS
LEVEL 2347923 MAX

设置MiddleLeftText文本内容如下:

ANALYSIS:
***************
234654 453 38
654334 450 16
245261 856 26
453665 766 46
382856 863 09
356878 544 04
664217 985 89

BottomCenterText文本设为“MATCH”即可。在场景视图中调整HUD周围的文本对象,直到与《终结者》电影的截图大体一致。MiddleCenterText暂时留空,稍后将用于显示调试信息。

为文本设置正确的字体和颜色也很重要,HUD中的大部分文字字体可能是Helvetica。默认情况下,Unity在Windows平台默认使用Arial字体,很接近Helvetica。将字体颜色设置为灰白色(236, 236, 236, 255),字体样式为粗体,大小为20。

HUD底部显示的“MATCH”字体为Heinlein,这也是电影中使用的字体。也可以使用另一种字体Modern Vision来模拟Heinlein。在Assets文件夹下新建文件夹Fonts,将所有自定义TTF字体文件拖拽到Fonts文件夹中。然后将字体赋给BottomCenterText的Font字段,或单击字体字段旁边的目标符号显示选择界面。此外,将“MATCH”的字体大小增加到32,比HUD中的其他文本稍大一点。

上图中”MATCH“的右侧显示了一个白色方块。要模拟此正方形,需要在HUD对象下新建一个InputFiled(UI -> Input Field),并将其命名为“Square“。删除默认文字,调整其大小和位置,直到与截图相匹配。

固定HUD位置
默认情况下,画布将被锁定在世界空间。但这里我们希望可以像《终结者》电影那样固定在屏幕空间。

在检视面板选择HUD画布查看其属性,来配置摄像机锁定视图。将画布的Render Mode字段值设为Screen Space – Camera。然后将Main Camera从层级视图拖至画布的Render Camera字段中,作为画布被锁定的相机视图。

HUD的Plane Distance初始设为1米。这是混合现实应用中HUD与脸部的距离。因为HoleLens是立体的,调整每个眼睛的视野直到视觉舒适。目前HoleLens的焦距是2米,所以平面距离至少应该设置为焦距大小。

为了方便起见,将平面距离(Plane Distance)设置为100。HUD对象上的所有内容将按照视野大小进行自动缩放。

请注意,将视觉内容锁定到相机视图的做法被称为“头锁”,通常在混合现实设计中不建议采用,因为可能导致视觉不舒适。相反,推荐做法是使用与玩家一起标记的“身体锁定”来创建混合现实中的HUD和菜单。本教程为了实现与电影一致的效果,暂且忽视该规则。

粉红世界
《终结者》视图应该是热成像视图,整个场景色调为热情的红色,下面使用着色器来实现该特效。

着色器是可以用于改变图片效果的高度优化的算法。可以添加一个带有红色畸变透明通道的着色器到场景,来创建这里的热视觉着色效果。

在虚拟现实应用中,虚拟环境是封闭的,所以可以使用RenderWithShader方法将着色器应用到摄像机上。该方法会将着色器应用到所有可见的游戏对象上。然而,在全息体验中不适用该方法,因为现实中的对象也需要进行畸变。

依次点击Unity编辑器菜单Assets -> Create -> Material新建材质。在着色器字段中,单击下拉菜单,找到HoloToolkit - > Lambertian Configurable Transparent。 HoloLens应用首选着色器就是HoloToolkit附带的着色器。 Lambertian Configurable Transparent着色器可选择红色,这里设为(200,43,38)。

为HUD对象新增Plane(3D Object -> Plane)命名为“Thermal”。然后将之前设置的Lambertian着色器拖至“Thermal”对象上。将平面旋转设置为270,并设置缩放为(100,1,100),以填满整个视图。

最后,如果不希望红色的着色影响文本,请将各Text对象的Z坐标设为-10。 这会将文本拉至HUD前方一点,所以它不受热视觉效果影响。

将项目部署到设备或模拟器,以查看Terminator Vision的效果。

让文本动起来
为了将HUD连接到认知服务(Cognitive Services),首先要策划一种使文本动态化的方式。选择HUD对象。 然后在检视面板中,单击Add Component -> New Script新建脚本命名为“Hud”。

双击Hud.cs在Visual Studio中编辑脚本。在脚本的顶部,创建四个公共变量,将用于保存对项目中的Text对象的引用。

[C#] 纯文本查看 复制代码
?
public Text InfoPanel;
public Text AnalysisPanel;
public Text ThreatAssessmentPanel;
public Text DiagnosticPanel;

查看检视面板中的Hud组件,可以看到四个可以设置的新字段。 将HUD文本对象拖到这些字段中,如下图:

在Start方法中,添加一些默认文本,以检测动态文本是否正常运行。

[C#] 纯文本查看 复制代码
?
void Start()
{
    AnalysisPanel.text = "ANALYSIS:\n**************\ntest\ntest\ntest";
    ThreatAssessmentPanel.text = "SCAN MODE XXXXX\nINITIALIZE";
    InfoPanel.text = "CONNECTING";
//...
}

部署和运行Terminator Vision应用程序时,应该在Start函数中指定的新文本替代默认文本。现在设置System.Threading.Timer来确定扫描房间进行分析的频率。 Timer类测量时间(以毫秒为单位)。 它的第一个参数是回调方法。下面的代码将每30秒钟调用一次Tick方法。Tick方法又将调用AnalyzeScene,AnalyzeScene将使用内置的彩色摄像机(称为可定位摄像头)来拍摄终结者眼前的景象,并将其发送到认知服务进一步分析。

[C#] 纯文本查看 复制代码
?
System.Threading.Timer _timer;
void Start()
{
    //...
    int secondsInterval = 30;
    _timer = new System.Threading.Timer(Tick, null, 0, secondsInterval * 1000);
}
  
private void Tick(object state)
{
    AnalyzeScene();
}

Unity访问定位摄像机的方式与访问任意网络摄像机相同。这涉及到创建照片捕捉实例的一系列调用,配置摄像机,拍照并将其保存到设备。在该过程中还可以加入终结者风格的消息,发送到HUD以指示进度。

[C#] 纯文本查看 复制代码
?
void AnalyzeScene()
{
    InfoPanel.text = "CALCULATION PENDING";
    PhotoCapture.CreateAsync(false, OnPhotoCaptureCreated);
}
  
PhotoCapture _photoCaptureObject = null;
void OnPhotoCaptureCreated(PhotoCapture captureObject)
{
    _photoCaptureObject = captureObject;
  
Resolution cameraResolution =
                             PhotoCapture.SupportedResolutions.OrderByDescending((res) =>
res.width * res.height).First();
  
    CameraParameters c = new CameraParameters();
    c.hologramOpacity = 0.0f;
    c.cameraResolutionWidth = cameraResolution.width;
    c.cameraResolutionHeight = cameraResolution.height;
    c.pixelFormat = CapturePixelFormat.BGRA32;
  
    captureObject.StartPhotoModeAsync(c, OnPhotoModeStarted);
}
  
private void OnPhotoModeStarted(PhotoCapture.PhotoCaptureResult result)
{
    if (result.success)
    {
        string filename = string.Format(@"terminator_analysis.jpg");
        string filePath = System.IO.Path.Combine(Application.persistentDataPath,
                                          filename);
_photoCaptureObject.TakePhotoAsync(filePath, PhotoCaptureFileOutputFormat.JPG,OnCapturedPhotoToDisk);
    }
    else
    {
        DiagnosticPanel.text = "DIAGNOSTIC\n**************\n\nUnable to start photo mode.";
        InfoPanel.text = "ABORT";
    }
}

如果成功拍摄并保存了照片,就可以获取该照片,将其序列化为字节数组,并发送到Cognitive Services以检索描述房间的标签数组。最后,处理照片捕捉对象。

[C#] 纯文本查看 复制代码
?
void OnCapturedPhotoToDisk(PhotoCapture.PhotoCaptureResult result)
{
    if (result.success)
    {
        string filename = string.Format(@"terminator_analysis.jpg");
        string filePath = System.IO.Path.Combine(Application.persistentDataPath, filename);
  
        byte[] image = File.ReadAllBytes(filePath);
        GetTagsAndFaces(image);
        ReadWords(image);
    }
    else
    {
        DiagnosticPanel.text = "DIAGNOSTIC\n**************\n\nFailed to save Photo to disk.";
        InfoPanel.text = "ABORT";
    }
    _photoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode);
}
  
void OnStoppedPhotoMode(PhotoCapture.PhotoCaptureResult result)
{
    _photoCaptureObject.Dispose();
    _photoCaptureObject = null;
}

为了进行REST调用,要使用Unity WWW对象。还需通过Unity协同程序进行调用,以免阻塞调用。 可以通过注册获得免费的订阅密钥来使用Microsoft Cognitive Services API。

[C#] 纯文本查看 复制代码
?
string _subscriptionKey = "b1e514eYourKeyGoesHere718c5";
string _computerVisionEndpoint =
"https://westus.api.cognitive.microsoft.com/vision/v1.0/analyze?visualFeatures=Tags,Faces";
public void GetTagsAndFaces(byte[] image)
{
        coroutine = RunComputerVision(image);
        StartCoroutine(coroutine);
}
  
IEnumerator RunComputerVision(byte[] image)
{
    var headers = new Dictionary<string, string>() {
        { "Ocp-Apim-Subscription-Key", _subscriptionKey },
        { "Content-Type", "application/octet-stream" }
    };
  
    WWW www = new WWW(_computerVisionEndpoint, image, headers);
    yield return www;
  
    List<string> tags = new List<string>();
    var jsonResults = www.text;
var myObject =
JsonUtility.FromJson<AnalysisResult>(jsonResults);
    foreach (var tag in myObject.tags)
    {
        tags.Add(tag.name);
    }
    AnalysisPanel.text = "ANALYSIS:\n***************\n\n" + string.Join("\n", tags.ToArray());
  
    List<string> faces = new List<string>();
    foreach (var face in myObject.faces)
    {
        faces.Add(string.Format("{0} scanned: age {1}.", face.gender, face.age));
    }
    if (faces.Count > 0)
    {
        InfoPanel.text = "MATCH";
    }
    else
    {
        InfoPanel.text = "ACTIVE SPATIAL MAPPING";
    }
    ThreatAssessmentPanel.text = "SCAN MODE 43984\nTHREAT ASSESSMENT\n\n" + string.Join("\n", faces.ToArray());
}

计算机视觉标记功能是检测照片中对象的一种方法。 它也可以用在像这样的应用程序中进行即时对象识别。

当调用认知服务返回JSON数据时,可以使用JsonUtility将数据反序列化为名为AnalysisResult的对象,如下所示。

[C#] 纯文本查看 复制代码
?
public class AnalysisResult
{
    public Tag[] tags;
    public Face[] faces;
  
}
  
[Serializable]
public class Tag
{
    public double confidence;
    public string hint;
    public string name;
}
  
[Serializable]
public class Face
{
    public int age;
    public FaceRectangle facerectangle;
    public string gender;
}
  
[Serializable]
public class FaceRectangle
{
    public int height;
    public int left;
    public int top;
    public int width
}

使用JsonUtility时需要注意,它只适用于字段,而不适用于属性。 如果对象类有getter和setter,JsonUtility将不会对其进行处理。

现在运行应用程序,它会每30秒更新一次HUD,并显示关于您房间的信息。

为了让应用程序更加功能化,还可以添加OCR功能。

[C#] 纯文本查看 复制代码
?
string _ocrEndpoint = "https://westus.api.cognitive.microsoft.com/vision/v1.0/ocr";
public void ReadWords(byte[] image)
{
    coroutine = Read(image);
    StartCoroutine(coroutine);
}
  
  
IEnumerator Read(byte[] image)
{
var headers = new Dictionary<string, string>() {
    { "Ocp-Apim-Subscription-Key", _subscriptionKey },
    { "Content-Type", "application/octet-stream" }
};
  
WWW www = new WWW(_ocrEndpoint, image, headers);
yield return www;
  
List<string> words = new List<string>();
var jsonResults = www.text;
var myObject = JsonUtility.FromJson<OcrResults>(jsonResults);
foreach (var region in myObject.regions)
foreach (var line in region.lines)
foreach (var word in line.words)
{
    words.Add(word.text);
}
  
string textToRead = string.Join(" ", words.ToArray());
if (myObject.language != "unk")
{
    DiagnosticPanel.text = "(language=" + myObject.language + ")\n" + textToRead;
}
}

该服务将挑选它所发现的任何单词,并为终结者更新显示。它还会尝试确定其找到的任何单词的原文,以用于进一步分析。

总结
这篇文章讲解了如何利用Unity在HoloLens中重现标志性科幻电影中的炫酷视觉效果。并介绍了如何从Unity调用Microsoft Cognitive Services,以进一步接近电影效果。

您可以使用通过OCR找到的文本以及Translator API,调用Cognitive Services将其翻译为另一种语言,进一步扩展Terminator Vision应用的功能。还可以使用Bing Speech API以原始语言和翻译语言两种方式朗读文本。

请访问Github上查看Terminator Vision的源代码。

使用Unity在HoloLens中实现终结者视觉HUD相关推荐

  1. 【HoloLens2】HoloLens中如何操控自制的3D模型(移动、缩放、旋转)

    使用了官方的火星车,也操作了自己在Maya里头建的一个场景 取消空间网格的问题,不取消就会如下图: 那如何取消空间网格?请翻看之前的记录 如何操作3D对象 1. 先创建一个新的项目 或者在现有的项目上 ...

  2. Unite 2017 | 使用Unity开发HoloLens应用实战技巧(下)

    昨天我们分享了<运用Unity开发HoloLens应用实战技巧(上)>,今天下篇将讲解Unity开发HoloLens应用的全息模拟器与交互设计. 附演讲的现场视频(时长30分钟,流量党随意 ...

  3. Unity制作游戏中的场景

    Unity制作游戏中的场景 1.2.3  场景 在Unity中,场景(Scene)就是游戏开发者制作游戏时,所使用的游戏场景.它是一个三维空间,对应的三维坐标轴分别是X轴.Y轴和Z轴本文选自Unity ...

  4. Draw Call未被批处理?在Unity 5.6中如何查找原因

    unity在5.6之前的版本中并未提供很直接的方式来查找Draw Call未被批处理的原因,但Unity 5.6在Frame Debugger中新增了一项功能,帮助开发者查找相关信息.今天这篇文章就为 ...

  5. unity 线程断点时卡机_Compute Shader在Unity和UE4中的应用

    该文档为学习文档,如有错误欢迎指正. 1. D3D11 Compute Shader概述 Compute Shader 是一个通用计算 Stage.它利用了GPU的并行处理器,实现大量线程并发执行.它 ...

  6. unity三维地图的经纬度如何在二维地图上表示_接入C++版本recastnavigation寻路库到Unity/服务端中...

    前言 因为Unity版本的更新迭代,老版本的A*插件在新版本Unity已经无法正常使用,包括一些运行时代码也已经过时,重新接入要花费很多时间,干脆接入一个新的寻路方案吧. 这里选择的是久负盛名的htt ...

  7. Unity游戏开发中的向量运算-点乘和叉乘

    Unity游戏开发中的向量运算-点乘和叉乘 1.点乘: 定义: 又称点积.数量积.标量积.既可以由向量坐标的代数运算得出,也可以通过引入两个向量的长度和角度等几何概念来求解. 公式一: 公式二: 点积 ...

  8. 使用Unity实现VR中在黑板上写字(升级篇)(一)-----解决画笔穿透画板的问题

    一.概述: 在使用Unity实现VR中在黑板上写字(初级篇)中的最后留下了一些有待完善的地方,首先完善画笔穿透画板的问题: 在之前使用画笔会出现这种情况: 可以看到画笔是穿透了画板,这样在VR中会给用 ...

  9. Unity实现游戏中坦克运动控制(一)

    Unity实现游戏中坦克运动控制(一) 导入模型 控制上下左右移动 导入模型 导入坦克模型资源文件,一般包含fbx格式模型文件和tga格式的贴图文件.一般导入的模型由3dmax制作,与unity中单位 ...

最新文章

  1. C语言程序练习-L1-011 A-B (20分)
  2. why we need createBindingContext in SAP UI5
  3. ButterKnife的简单使用
  4. 微信公众号开发--微信JS-SDK扫一扫功能
  5. 【转载】关于RabbitMQ的高可用性
  6. Android 动态权限申请 BaseActivity 封装 拨打电话
  7. Unreal、CryEngine、Gamebryo引擎介绍
  8. 最新 IntelliJ Idea 2017 激活方法(转)
  9. Bailian2717 基本数据类型【字符串匹配】
  10. JavaScript DOM基础2
  11. 十大旗舰基金是怎样炼成的(主动权益)
  12. 乖离性暗机器人_乖离性百万亚瑟王超弩级暗机器人复刻攻略分享
  13. Android Handler机制详解
  14. CMMB手机电视自毁长城?
  15. 解压缩文件常用命令——linux
  16. 多线段几何图形—— 简单几何图形(布尔运算)
  17. 遛狗牵绳AI识别实时监测助力智慧城市
  18. 计算机组成原理sp接口,计算机组成原理2008年4月真题试题(02318)
  19. 嵌入式应用开发在线图书网址
  20. VIM如何将全部内容复制并粘贴到外部

热门文章

  1. 一分钟带你了解全民拼团模式解析
  2. docker入门,镜像,容器,数据卷,dockerfile,docker网络,springboot微服务打包docker镜像[狂神yyds]
  3. Python教程: while循环20例
  4. 十三五:深圳将打造智慧城市等十张名片
  5. OTA系列小问答:汽车OTA技术架构主要有哪些?域控制器作为OTA master 有什么要求?
  6. 前端Web网页设计的项目实现
  7. 虹科案例 | 医学研究中的DAYSlab
  8. 10分钟讲清int 和 Integer 的区别
  9. 一图看懂C语言%d、%nd、%0nd、%-nd用法
  10. 基于yolo点击的吊舱跟踪