使用Unity在HoloLens中实现终结者视觉HUD

詹姆斯·卡梅隆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 =
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#]  纯文本查看  复制代码
?
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的源代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值