詹姆斯·卡梅隆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”。

101324jccblxnocpmc7rxp.jpg

这个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中的其他文本稍大一点。

101424zlbl9bxhmnw85181.jpg

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

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

101524r1p8pw0f5vf8o1f0.png

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

101546pd9oz40odvd49o4m.jpg

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

101618lybmdxabbheba4md.jpg

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

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

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

101742al00llae7nez17ln.jpg

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

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

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

101818w1oikmgkkhizd8ho.jpg

为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文本对象拖到这些字段中,如下图:

101853cz6en42yh16xv8pe.png

在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()); }

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

102117kj6b1bj9zjx61zyf.jpg

当调用认知服务返回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将不会对其进行处理。

102150tc55q1b6q9z8ewuf.jpg

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

102150a0hhrfzb3mkml2r0.jpg

为了让应用程序更加功能化,还可以添加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; } }

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

102151f1oe0mze0lvmmlte.jpg

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

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

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


Unite 2017 Shanghai倒计时10天

Unite 2017 Shanghai仅剩10天拉开帷幕。5月12日,Keynote主题演讲作为Unity全球技术精英芸集的开场秀,免费开放给热爱Unity的你!

111318qhp4sfpehy0ytrrb.png

原文链接:https://blogs.windows.com/buildi ... oaeGxlj6PtUsFEhp.97
原作者:Windows Apps Team
感谢unity官方翻译组成员“nbstar”对本文的贡献!(如何加入翻译组?)
转载请注明:来自Unity官方中文社区(forum.china.unity3d.com),未经允许请勿更改版权说明! HoloLens, HUD, Unity, HoloLens锐亚教育

锐亚教育 锐亚科技 unity unity教程