利用UE4 Ndisplay加optitrack构建三通道桌面CAVE系统

1.内容综述

好记性不如烂笔头,这次记录一下如何使用UE4和optitrack的动捕搭建一个简单的三通道桌面CAVE系统,这个系统通过Ndisplay实现将渲染任务分发给三台渲染节点(电脑)进行处理并分别输出部分场景画面至各自的显示器,最终拼接成一个完整的场景。此外,通过Ndisplay自带的vrpn接入了optitrack的动捕数据和手柄按键数据,实现了一个简单的交互功能,如果有英伟达3Dvision的设备,还可以实现一个主动立体,但是我这边之前简单测试了一下,多个通道同步显示存在点问题,没能解决,暂时还没有搞,有兴趣和条件的同志们可以自行研究一下,整篇文章记录了我从零开始到配置完成中所有的知识点,我会尽量写得详细甚至鸡婆,避免我以后自己看不懂。整体框架和显示效果如图所示。

2.开发环境配置

简单介绍一下开发环境搭建。

2.1渲染节点的三台主机

1.用一个路由器连接到一个网络,设置三台机子的固定IP在同一网段保证设备间互相可以Ping通(如果连不上,多半是防火墙的问题,知道怎么设置就创建几个进出站规则把ue4、ndisplay啥的都放过去,否则就关了)。

2.设置共享文件夹,后期使用的时候需要把UE4工程或者发布的程序拷贝到每台机子的同一目录下,设置共享文件方便手动拷贝或者用脚本搞定。

3*.mouse without board,非必须,三台机子,用三套键鼠控制太麻烦,可以使用微软的无界鼠标使用一套键鼠把三台机子都控制了,具体设置网上很多,请自行百度。

2.2光学追踪设备

我们这边用的是optitrack 的桌面追踪系统,用htc 的vive啥的也都一样,主要用来追踪眼睛和操纵手柄的位置。

2.3交互设备

配合追踪设备使用,我这就用了两个,一个眼镜(挂着追踪刚体,用来追踪眼位)一个手柄(也带着刚体,用来进行交互)

3.Ndisplay

关于Ndisplay的详细介绍大家可以参照这个连接,这是UE4官方的教程,我也是主要参考了这个弄得,但是也有些内容我没有在里面找到或者写的不够详细,我也是在网上找了半天才解决,后边我会按照自己的方式记录整个过程,不会像教程里写的那么面面俱到,但是会针对使用到的内容进行更加详细的描述。

https://docs.unrealengine.com/en-US/Engine/Rendering/nDisplay/QuickStart/index.html

3.1创建并配置Ndisplay工程

我们使用的版本是424.3(424.2有bug,添加相机追踪后没有反应,后边会有说明)。创建Ndisplay工程有两种方式,一种是直接用模板搞,这种非常简单,直接在工程创建窗口选择Film,Television这个类别(其他版本的不一定在这,自己找一下就行),后边就可以找到ndisplay的模板,按照指示点下一步就可以创建,创建的工程就可以直接用了。但是不知道是不是我自己没找到,在424这个版本里,这样创建的工程没法选择C++,只能用蓝图,如果需要C++编程的可能会比较麻烦,下边我再说一下如何在已有的一个工程里配置环境。

现在我们随意创建一个C++的工程什么模板都可以,我这用的第三人称。打开后进行配置。

1.首先进入到Edits->Plugins里,点击左侧的Built-in然后在右侧搜索框中搜索ndisplay,找到后勾选Enabled。勾选完后会提示重启工程(我这还弹出了个Beta版本谨慎使用地提示,如果有的话无视即可)。

2.进入Edit->Project Setting 里在左侧找到Plugins->nDisplay,将Main中的Enabled勾选。

3.同样是Project Setting找到Project->Description在右侧Settings中勾选Use Borderless Window。

然后重启工程。

4.创建ClusterRoot

为了使用ndisplay,需要在场景中创建一个DisplayClusterRootActor,这个在教程里好像被忽略了。我在刚开始弄这个的时候就开在这了,发现用模板创建的工程是可以正常使用的,但是使用自己的工程进行配置的时候就用不了,对比了半天后发现是场景里少了这个actor。

在content里右击选择创建blueprint,在All Class中找到并选择DisplayClusterRootActor类创建对应的BluePrint,名字随便取,创建后拉入到场景中即可。

3.2配置文件

Ndisplay的主要使用基本都在配置文件的编制。具体的配置在官网的教程里都有,我这边就捡着我用到的条目进行一下详细的说明,不会把所有的都说到,但是说到的都是常用的。编制配置文件不需要从零开始,最好是在UE4自己的模板中进行修改,UE4的配置模板存储在C:\Program Files\Epic Games\UE_4.24\Templates\TP_nDisplayBP\Content\ExampleConfigs文件夹下,大家根据自己的安装路径和版本找到对应的文件夹即可。我是在wall_flat_3x2.cfg基础上修改的,Ndisplay的配置文件是逐条目定义的,在每条前面都会有[xxx]来标记当前条目的类别,#后的内容为注释。往下我逐条目对需要用到的内容进行一下说明。

1.[cluster_node]条目

样例:[cluster_node] id="node_1_up" addr="192.168.1.1" window="wnd_1_up" master="true" sound="true"

定义渲染节点信息。

id-节点的id名称,每个节点都需要一个,不能重复,建议不要出现空格,如果出现务必使用 " "将名称括住

addr-节点的ip地址

window-当前节点的使用的窗口id名称,详见下一条目

master-是否是主节点,主节点将处理所有的I/O操作,比如本文搭建的系统需要接收optitrack的动捕数据,这样的话optitrack的motive软件就需要跑在被定义为master的节点上。如果某一节点不是master,直接把这一个参数删除即可

sound-是否开启声音

2.[window] 条目

样例:[window] id="wnd_1_up" viewports="vp_1_up,vp_1_dn" fullscreen="false" WinX="0" WinY="0"    ResX="1920" ResY="1080"

这一条目对应渲染节点的显示窗口,通常对应显示器或者投影。

id-窗口的唯一id名称

viewports-当前窗口对应的视口id,一个窗口可以显示多个视口,只用把id号用逗号隔开,如下图所示,就是一个在一个窗口中创建了四个视口,每个视口显示一个独立视角的内容。具体这些视口如何在窗口显示,详见下一条目

fullscreen-是否为全屏显示

WinX,WinY-窗口左上角在显示器中出现的位置,单位为像素

ResX,ResY-窗口分辨率大小

3.[viewport]条目

样例:[viewport] id="vp_1_up" x="0" y="0" width="1920" height="1080" projection="proj_simple_1_up"

定义视口的相关信息

id-视口的唯一id名称

x,y-用来设置视口左上角相对于所属窗口的出现位置,单位为像素

width,height-视口的分辨率大小

projection-视口使用的投影配置,详见下一条目

4.[projection] 条目

样例:[projection] id="proj_simple_1_up" type="simple" screen="scr_1_up"

用来定义投影方式

id-投影的唯一id名称

type-投影类型,截止到我使用的版本,官方提供了三种可以选择的类型,simple、MPCDI和EasyBlend,正常我们使用平面显示器的话就使用simple就行,剩下两种是用来配置诸如球幕等特殊使用类别的屏幕,我也没用过,不清楚啥情况,有需要的同志可以自行去看一下相关的内容

screen-投影需要匹配的屏幕id,详见下一条目

5.[screen]条目

样例:[screen] id="scr_1_up" loc="X=0,Y=0,Z=0" rot="P=0,Y=0,R=0" size="X=0.5,Y=0.3" parent="display_1_up"

用来定义投影屏幕的参数,这里的屏幕与窗口和视口的概念有所区别。如果将人眼当做是场景中的相机,窗口和视口就像是显示器,对于一个显示器,你不论如何摆放或者你移动到不同的角度去观看,你能够看到的画面仍然是固定的,不会根据你和显示器的位置关系发生变化。但是投影屏幕不一样,投影屏幕像是窗户,你在不同角度透过窗户看到的景色是不一样的,投影屏幕就是用来配合投影关系(projection)生成视口(viewports)需要显示的场景并在窗口(window)上进行显示。上述几个相关条目间的关系为,窗口(Window)包含一到多个视口(Viewports),视口(viewport)对应一个投影关系(projection),投影关系(projection)对应一个投影屏幕(Screen)。

id-投影屏幕的唯一id名称

loc-投影屏幕的空间位置,这个空间位置是以你控制的pawn的相机为参考的一个局部位置,坐标轴定义与UE4的坐标系一致,单位为m(注意UE4的坐标为厘米,动捕设备的追踪数据通常为m或者mm,在进行配置时要考虑单位变换)。假设我们设置X(前),Y(右),Z(上)的值分别为1,0,0则屏幕会固定出现在我们pawn的前方1m处,并跟随我们进行运动,如下图红色透明方块所示(后边会说到如何显示出这个投影屏幕位置)。其中透明方块中的框住图像便是对应视口Viewport上显示的内容。

rot-投影屏幕绕3个轴的旋转,单位为度,下图为设置屏幕绕Z轴旋转45度的结果。

size-投影屏幕的尺寸,在loc介绍的图中,我设置的屏幕大小为1*1,即1m*1m大小。

parent-对应[scene_node]节点,此节点为设置的任意一个空间点(后边介绍),将parent 设置为该节点后投影loc的坐标将为相对于此节点的坐标,主要用于多屏幕布局。

6.[camera]条目

样例:[camera] id="camera_static" loc="X=0,Y=0,Z=0" parent="socket_cam" eye_swap="false" eye_dist="0.064"  tracker_id="VRPNTrack" tracker_ch="0"

ndisplay的相机,可以绑定动捕设备实现场景漫游,在漫游方面这个相机跟上个条目中提到的pawn那个相机有点不一样(我不确定这两个是不是同一个对象,但在漫游控制方面是有所差别的)。pawn的那个相机可以用过在蓝图中获取并设置pawn的位置实现在场景中的运动(利用手柄或者键盘的输入来控制他的位置就可以实现交互式的漫游)。但是,就像我在前面投影屏幕中说的,不管我怎么移动pawn,那个投影屏幕相对于pawn相机的位置是不变的。这种漫游就像是保持头部不动开车,我透过前面的玻璃(Projection Screen)看到了外面的场景,我可以开车前往各个地方,看到不同的景色,但是我因为我的头不能动,所以我不能从车内不同位置观看外面的场景(Ue4的虚拟场景)。[camera]条目的相机则恰恰相反,他像是在一所房子里进行漫游,透过房子的玻璃窗户(Projection Screen)观看外面的场景(Ue4的虚拟场景),房子和窗户是不动的,你是通过走到不同的位置来观看外面的场景,但受限于追踪的范围,只能在局部进行漫游,难以进行大范围漫游。所以,一般的漫游是将这两个结合到一起,构建一个可以移动的房子,用手柄或者键盘移动房子的位置,再让用户通过[camera]在房子里进行漫游。

id-跟其他条目一样

loc-相机的相对位置,相对指的是相对于parent(跟前一个条目一样,可以不写,默认是0,0,0)。如果追踪设备接入,将会自动控制这个位置进行漫游

parent-跟上一个条目一样,后边细说

eye_swap-进行立体显示的时候会用到,用来交换左右眼的图像,一般用不到,如果发现左右眼的图像反了可以加上去

eye_dist-瞳距,用来生成具有视差的立体图像,与人眼保持一致,通常为0.064,单位是米

tracker_id-对应绑定相机的追踪设备id,后边会提到。

tracker_ch-追踪设备的通道,一个追踪设备可能会通过不同通道输出多个追踪目标的数据,可以通过这个参数指定使用哪个通道的数据。

7.[scene_node] 条目

样例:[scene_node] id="eye_level"    loc="X=0,Y=0,Z=0"      rot="P=0,Y=0,R=0"   parent="cave_origin"

场景节点,前面出现了两次了,相当于空间中的定位点,用来设置局部坐标的,比如我要设置一面电视墙,我可以把墙的中心设置为一个[scene_node]然后将所有[screen]的parent设置为这个节点,之后设置屏幕位置时只用设置屏幕相对于[scene_node]的坐标,上下左右各一块屏幕,那么这四块屏幕的loc就是(0,0,1),(0,0,-1),(0,-1,0),(0,1,0),之后如果想旋转平移整个电视墙,那么只需要改变scene_node的值就可以了。

loc-点的位置,单位米

rot-点的朝向,单位度,P,Y,R分别对应俯仰角Pitch,航向角Yaw以及滚转角Roll

parent-场景节点也可以嵌套,可以设置其他场景节点为此节点的父节点

8.[input]条目

样例:[input] id=WandAxis type=analog addr="Mouse0@127.0.0.1"

输入设备设置,Ndisplay使用VRPN协议处理设备输入,可用来实现相机漫游,手柄操控,运动捕捉系统接入等功能,输入设备需要在主节点进行处理。关于VRPN网上有很多介绍,在这就不细说了,后边有机会我会再写一篇博客,专门就VRPN的使用与开发进行说明,但愿有时间吧。简单来说,VRPN相当于是一个数据处理中心,可以读取不同的输入设备(各类运动捕捉设备,手柄,键盘,鼠标等)并将这些设备的输入进行格式化输出。其中Ndisplay支持的数据输出类型主要有三类:一类是Button,就是按钮的触发信息,提供按下,释放的事件;一类是Analog,记录一个或者多个模拟量,如鼠标、摇杆等;还有一类是Tracker,提供追踪设备的动捕信息,通常为空间坐标、欧拉角以及四元数等。VRPN还有记录旋转值的Dial类型以及受力相关的ForceDevice类型,这些我就没用过了,而且ndisplay官方好像也没有支持,有需要可以研究一下。

id-唯一标识,用来在其他条目以及蓝图中获取定义的输入设备

type-输入设备类别,分为三类,analog、button以及tracker,需要跟VRPN输出类别对应

addr-设备地址,地址语法分为两部分用@分开,前面为设备名称,后边为VRPN服务运行主机的Ip地址(我印象中可以在ip后加上冒号和端口号指定输出端口,如127.0.0.1:5000,但是我没试过,如果有必要可以一试)。设备名称需要单独说一下,这个名称是VRPN配置文件中定义的设备名称,需要与VRPN配置文件保持一致,VRPN配置文件如下图所示,#为注释符号,图中vrpn_Mouse Mouse0以及vrpn_Keyboard keyBoard0两个条目分别是鼠标与键盘设备的配置,去掉对应条目前面的#即可接入对应设备。ndisplay配置中的设备名称需与红框中的名称(Mouse0以及Keyboard0)保持一致。

此外,如果使用optitrack则有所不同,因为optitrack的软件Motive已经把VRPN服务集成了而且只是自用,所以根本就没有vrpn的配置文件,也就没有设备名称这个东西,这是我踩的第一个坑,各处打探后得知他的设备名称就是在Motive软件中创建的追踪刚体的名称。进一步,如果你跟我一样使用了optitrack那个索尼的手柄,那么还有第二个坑,那个手柄进入Motive中需要把刚体的名称设置为Controller 1(2、3、4也行,好像支持5个),才能获取按键和摇杆的信息,因为只有把名称设置成这个才能套用他自己的模板,把这个手柄识别成一个具有按键输入的设备。设置完还没有完,因为设备名称只有一个,直接使用Controller 1这个名称去接入设备只能获取到追踪信息,如果需要获取按键和摇杆的信息需要使用Controller 1Analog以及Controller 1Button两个名字,下面是完整的配置。

[input] id=WandAxis type=analog addr="Controller 1Analog@127.0.0.1" reflect=ue4
[input] id=WandButtons type=buttons addr="Controller 1Button@127.0.0.1" reflect=ue4
[input] id="VRPNTrack" type="tracker" addr="Controller 1@127.0.0.1"  loc="X=0,Y=0,Z=0" rot="P=0,Y=0,R=0" front="-Z" right="X" up="Y"

loc-设置设备输出数据坐标的位置偏移量,可通过添加parent条目设置

rot-设置旋转偏移

front,right,up-只有追踪设备有,用来进行坐标轴映射,将追踪设备的坐标轴映射到UE4的坐标轴。

即将追踪设备的坐标XYZ映射到UE4坐标 front (X) ,right (Y) ,up (Z)。

9.[input_setup]条目

样例:[input_setup] id="WandAxis" ch=0 bind="nDisplay Analog 0"

用来设置输入设备与UE4蓝图输入变量的映射。如下图所示,在UE4中,已经预定义了一系列变量,可以绑定vrpn输入的交互信息如按键、模拟量,用来在蓝图中获取输入信息,设置完成后就可以直接使用绑定的变量获取输入设备的交互信息。这个绑定可以通过ndisplay的这个配置进行,也可以调用蓝图的函数实现,蓝图函数在官方的教程里有,我没有用这个也就不说了,我这重点把通过配置绑定的方式记录一下。

id-这回不是自己的标识了,需要写需要绑定的[input]设备的id

ch-需要绑定的数据通道,通常是0,但是像是鼠标、摇杆这种至少有X和Y两个,那么就得分别绑定0和1两个信道的数据到两个变量

bind-ndisplay中同类型变量的名称,也可以自己在project setting的input里定义,我嫌麻烦就用预定义的了

我用到的条目介绍就到此为止了,还有很多其他的条目以及参数,有需要的可以参考官网的介绍,我后边如果用到了我再补充。下边附上我的配置,供大家参考。

[info] version="23"

[cluster_node] id="LEFT-NODE" addr="192.168.0.234" window="wnd_LEFT" master="true"
[cluster_node] id="CENTER-NODE" addr="192.168.0.235" window="wnd_CENTER"    
[cluster_node] id="RIGHT-NODE" addr="192.168.0.236" window="wnd_RIGHT"  

[window] id="wnd_LEFT"   fullscreen="false" WinX="0"   WinY="0"   ResX="2560" ResY="1440" viewports="VP_LEFT"
[window] id="wnd_CENTER" fullscreen="false" WinX="0"   WinY="0"   ResX="2560" ResY="1440" viewports="VP_CENTER"
[window] id="wnd_RIGHT"  fullscreen="false" WinX="0"   WinY="0"   ResX="2560" ResY="1440" viewports="VP_RIGHT"

[viewport] id="VP_LEFT" x="0"  y="0"  width="2560" height="1440" projection="proj_simple_LEFT" 
[viewport] id="VP_CENTER" x="0"  y="0"  width="2560" height="1440" projection="proj_simple_CENTER" 
[viewport] id="VP_RIGHT" x="0"  y="0"  width="2560" height="1440" projection="proj_simple_RIGHT" 

[projection] id="proj_simple_LEFT" type="simple" screen="scr_LEFT"
[projection] id="proj_simple_CENTER" type="simple" screen="scr_CENTER"
[projection] id="proj_simple_RIGHT" type="simple" screen="scr_RIGHT"

#The Cave wall is contructed with three 2k display, arranged in a line, each display is of 60cm in length
[screen] id="scr_LEFT" loc="X=0,Y=-0.6,Z=0"     rot="P=0,Y=0,R=0" size="X=0.6,Y=0.336" parent="cave_origin"
[screen] id="scr_CENTER" loc="X=0,Y=0,Z=0"    rot="P=0,Y=0,R=0" size="X=0.6,Y=0.336" parent="cave_origin"
[screen] id="scr_RIGHT" loc="X=0,Y=0.6,Z=0"      rot="P=0,Y=0,R=0" size="X=0.6,Y=0.336" parent="cave_origin"


[camera] id="camera_dynamic" loc="X=0,Y=0,Z=0"  tracker_id="GlassTracking" tracker_ch="0" 

[scene_node] id="cave_origin" loc="X=0,Y=0,Z=0"   rot="P=0,Y=0,R=0" 

[input] id=GlassTracking type="tracker" addr="Glass@127.0.0.1"  loc="X=0,Y=0,Z=0" rot="P=0,Y=0,R=0" front="-Z" right="X" up="Y" 
[input] id=ControlKeyboard type=keyboard addr=Keyboard0@127.0.0.1 reflect=ue4
[input] id=WandAxis type=analog addr="Controller 1Analog@127.0.0.1" reflect=ue4
[input] id=WandButtons type=buttons addr="Controller 1Button@127.0.0.1" reflect=ue4
[input] id=WandTrack type="tracker" addr="Controller 1@127.0.0.1"  loc="X=0,Y=0,Z=0" rot="P=0,Y=0,R=0" front="-Z" right="X" up="Y" 

#For Wand Axis 
#id: the id of [input]
#ch: the channel of the device, the joystick has more than one buttons, in VRPN they are packaged together and can be accessed through single address like "Controller 1Button@127.0.0.1". To get each of them, we can use channel, each channel refers to one button.
#bind:the UE4 input target that you want to bind the device signal with. All object in the input category like F1,W,A can be binded

[input_setup] id="WandAxis" ch=0 bind="nDisplay Analog 0"
[input_setup] id="WandAxis" ch=1 bind="nDisplay Analog 1"

#For Wand Buttons
#Same as the Wand Axis

[input_setup] id="WandButtons" ch=0 bind="nDisplay Button 0"
[input_setup] id="WandButtons" ch=1 bind="nDisplay Button 1"
[input_setup] id="WandButtons" ch=2 bind="nDisplay Button 2"
[input_setup] id="WandButtons" ch=3 bind="nDisplay Button 3"
[input_setup] id="WandButtons" ch=4 bind="nDisplay Button 4"
[input_setup] id="WandButtons" ch=5 bind="nDisplay Button 5"
[input_setup] id="WandButtons" ch=6 bind="nDisplay Button 6"
[input_setup] id="WandButtons" ch=7 bind="nDisplay Button 7"
[input_setup] id="WandButtons" ch=8 bind="nDisplay Button 8"
[input_setup] id="WandButtons" ch=9 bind="nDisplay Button 9"

[general] swap_sync_policy="1"

3.3运行程序

说了半天都是准备工作,现在我们先运行一下程序看看效果。ndisplay程序运行方式我总结可以分为三种,在官网中就介绍了一个,剩下两个我是通过摸索发现的,我下边逐个介绍一下。

1.官网提供的发布方式

(1)首先我们需要进行工程打包,就直接打包成一个win64的桌面工程就行,然后把打包好的文件夹拷贝到各个节点的相同目录下。

(2)然后,我们需要准备好配置文件,就是上一小结中写的那堆,同样需要拷贝到各个节点的相同目录下。

(3)找到nDisplayListener和nDisplayLauncher,这俩文件在\Epic Games\UE_版本号\Engine\Binaries\DotNET目录下。listener是监听程序,需要拷贝到每个子节点计算机上启动,用于进行同步与通信。launcher只需要在主节点启动,用于启动程序。

(4)启动工程

先把找到的listener复制到各个节点上运行起来,不需要同一路径。然后启动launcher,启动后如下图所示。点击程序下边的Add选择发布程序的.exe文件,则在Applications里会出现一条新的条目,然后点击Config Files后边的Add,选择配置文件,则在配置文件下拉菜单里会出现相应条目,注意,这两个路径在各个节点都需要一样,否则无法运行。选择添加的application和config file然后点Run即可,如果没有问题的话各个节点会按照配置文件定义的内容渲染出相应的场景,如果出现无法发布的问题,请检查网络、配置、以及程序和配置文件路径等是否正确设置。

2.不打包程序进行测试

第一种官网给的方式需要进行程序打包,如果需要debug则在修改后还要从新打包并拷贝到各个节点,非常麻烦。其实可以直接使用工程运行。跟方法1基本都一样,就是在最后添加工程时选择Add Project in Editor -game而不是Add, 选择后先会提示选择Editor,这个要选择使用的引擎路径,比如我是424,就如下图所示。

点击open后,会提示选择UE4工程文件,找到并选择自己的uproject文件就行(注意,这个也得每个节点都放一份,而且要在同一路径,这种方法比较适合先在主节点进行测试,功能都没问题了再进行集群测试,否则来回拷贝也很麻烦),之后就跟1的方法一样了,添加配置文件然后run就行。

3.Editor下直接测试

上述两种都得使用launcher进行发布,其实在Editor下就可以直接进行测试,虽然只能测试一个节点,但是便捷程度上笔前两种肯定要好。这种就比较简单了,首先把配置文件放到工程的Content目录下(子目录也成,我测试的时候发现如果放在工程外边即使给了完整路径也可能找不到,所以还是老实放到Content里吧),随后在Word Outliner中找到我们创建的DisplayClusterRootActor的BluePrint(模板里默认有),然后在Detail里找到Display Cluster(Editor Only)条目,这里就可以设置config file和Node ID两个变量,Config file是我们要使用的配置文件的地址,Node ID就是我们要查看的节点ID。配置完成后,在Editor里点击运行就可以看到所设置ID的节点的显示状态,同时可以获取输入信息等,可以用来进行快速测试。

*额外说一点,在Detail的Display Cluster条目中可以选择Show projection screens,并指定显示的材质。这就是我在前边介绍[screen]条目中说的可以将设置的屏幕显示出来的选项,设置这个选项可以查看屏幕的位置关系,辅助设定屏幕的位置。

4.蓝图编程

蓝图这边其实没啥好说的,就是按照自己的需求写程序就行了,里面比较重要的是获取[input]定义vrpn设备输入数据的方法,在这我挨个说一下,拿到了数据后大家就可以按照自己的需求编制逻辑了。

4.1按钮事件获取

按钮非常简单,使用[input_setup]进行绑定后,直接在蓝图里找到对应的变量就可以了,比如我将手柄的x按键绑定到了nDisplay Button 0这个事件,那么我就可以直接使用这个事件来编制触发逻辑,下图我设置当按下x键的时候打印出"Cross"。

4.2模拟量获取

模拟量跟按钮一样,绑定后直接通过变量获取就行,下图我把手柄摇杆的X 和 Y两个轴的模拟输出分别绑定到了nDisplay Analog 0和nDisplay Analog 1两个变量。然后在蓝图中就可以实时获取这两个值。

4.3追踪数据获取

追踪数据不太一样,好像没法直接绑定到一个数据上,我是通过vrpn的蓝图函数直接获取的,位置和姿态数据需要分别获取。利用Get VRPN tracker location 和Get VRPN tracker rotation来获取,两个函数需要连接一个DisplayCluster Module API变量,直接右击搜索就能找到,Device Id为配置文件[input]的Id.

总算写完了,后续有新增内容我会更新,欢迎大家指正错误。全程手打,原创不易,转载请著名出处。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值