SteamVR(HTC Vive) Unity插件深度分析(一)

发表于2017-04-06
评论3 7.6k浏览

插件中的readme.txtquickstart.pdf已经有一些描述了,这里对里面的每个重要文件进行详细分析。本文基于SteamVR Unity插件v1.1.0版本。文档最后会更新各最新版本相对于前一个版本的变化

1.        openvr_api.dll

位于Plugins//下面。这个是OpenVR SDK的核心,虽然宣称OpenVR开源,也在github上建了目录,但这个核心的openvr_api.dll是不开源的(但它在github上还是开源了不少东西的,包括valve公司的几个非常有代表性的游戏。参看:https://github.com/ValveSoftware

 

说一下OpenVR SDKUnity插件的关系。OpenVR SDK本质上是一套C++的接口,供传统的用C/C++(比如直接使用OpenGL)来写游戏所使用的。可以看到github上的openvr目录下,SDK是以二进制的动态库(bin目录及lib目录)+头文件的方式提供的。在samples目录下有不少示例演示如何调用这个sdk。而Unity插件的核心或者说基础也是OpenVR SDK,我们可以看到Plugins目录下就是带了openvr_api.dll这个SDK,只不过,由于是给Unity开发者使用的,上面再加了一层C#脚本来封装。我们也可以看到github上供C/C++开发者使用的openvr_api.dll与供Unity开发者使用的openvr_api.dll几乎是一样的,仅仅新增了几个与Unity相关的导出函数:

OpenVRSDKopenvr_api.dll中的导出函数

UnityOpenVR插件中openvr_api.dll中的导出函数

 

openvr_api.dll的大小不大,只有300K,显然它也不是VR的核心实现。也是,VR的核心实现肯定是在SteamVR的运行时当中,OpenVR作为同时支持VIVEOculusSDK,可以猜测它的工作是对这两者的封装,并对外提供接口。而OpenVRUnity插件又是通过C#OpenVR的封装。

 

看一下这些导出函数都是干什么的,C#封装中没有注释,但还好C++封装中有注释和例子,可以参看/headers/openvr.h中的注释。

1
2
3
4
5
/** Returns the interface of the specified version. This method must be called after VR_Init. The
 
* pointer returned is valid until VR_Shutdown is called.
 
*/

获取指定版本的接口,这个接口应该是与OpenVR交互的核心。需要在调用了VR_init后再调用。但VR_init并没有导出,在C++版本的SDK中,在头文件中有此函数,是对导出函数VR_initInternal的封装

1
2
3
4
5
VR_INTERFACE void *VR_CALLTYPE VR_GetGenericInterface( const char *pchInterfaceVersion, EVRInitError *peError );
 
 
 
/** Returns a token that represents whether the VR interface handles need to be reloaded */


返回一个表示VR接口句柄是否需要重新加载的token。根据示例代码,token其实是VR_InitInternal的返回值(VR_init返回的才是上面的VR_GetGenericInterface返回的VR接口句柄),如果VR_GetInitToken的返回值与之前保存的VR_InitInternal或者VR_GetInitToken的返回值不一样了,则表示token有更新,就需要重新调用VR_GetGenericInterface重新获取了。C++ SDK来看,所有重新操作前都会做这个检查

VR_INTERFACEuint32_t VR_CALLTYPE VR_GetInitToken();

 

VR_GetStringForHmdError这个函数在C++SDK中并没有看到导出,在Unity中有,显然就是获取错误代码的描述字符串

 

/** Returnsan english string for an EVRInitError. Applications should callVR_GetVRInitErrorAsSymbol instead and use that as a key to look up their ownlocalized error message. This function may be called outside ofVR_Init()/VR_Shutdown(). */

获取EVRInitErrorEVRInitError是一个枚举,VR_initInternalVR_GetGenericInterface会返回它,代表初始化状态)的英文错误描述字符串。应用不要调用这个函数,而应该调用VR_GetVRInitErrorAsSymbol,用它的返回值去查询本地化的错误消息字符串。这个函数可以在VR_Init/VR_Shutdown之外调用

1
2
3
4
5
VR_INTERFACE const char *VR_CALLTYPE VR_GetVRInitErrorAsEnglishDescription( EVRInitError error );
 
  
 
/** Returns the name of the enum value for an EVRInitError. This function may be called outside of VR_Init()/VR_Shutdown(). */


返回EVRInitError的枚举名称。这个函数可以在VR_Init/VR_Shutdown之外调用

1
2
3
4
5
VR_INTERFACE const char *VR_CALLTYPE VR_GetVRInitErrorAsSymbol( EVRInitError error );
 
  
 
VR_INTERFACE uint32_t VR_CALLTYPE VR_InitInternal( EVRInitError *peError, EVRApplicationType eApplicationType );


这个没有注释。它在openvr_api.dll中有导出,在C++版的SDK中对外提供了一个VR_Init的封装,外部应用不会直接调用VR_InitInternal

我们看一下VR_init的实现:

inlineIVRSystem *VR_Init( EVRInitError *peError, EVRApplicationType eApplicationType)

它里面的做法就是先调用VR_InitInternal做初始化,然后调用VR_GetGenericInterface获取VR接口句柄。

 

1
2
3
4
5
6
7
/** Returns true if there is an HMD attached. This check is as lightweight as possible and
 
* can be called outside of VR_Init/VR_Shutdown. It should be used when an application wants
 
* to know if initializing VR is a possibility but isn't ready to take that step yet.
 
*/

判断有没有HMDhead mounted display,头戴式显示器,头显)连接。这个判断是轻量级的,可以在VR_InitVR_Shutdown之外调用。当应用程序想知道是否可以初始化VR,但不一定就要准备初始化时可以调用它。

1
2
3
4
5
6
7
VR_INTERFACE bool VR_CALLTYPE VR_IsHmdPresent();
 
  
 
/** Returns whether the interface of the specified version exists.
 
*/


返回指定版本的接口是否存在

1
2
3
4
5
VR_INTERFACE bool VR_CALLTYPE VR_IsInterfaceVersionValid( const char *pchInterfaceVersion );
 
  
 
/** Returns true if the OpenVR runtime is installed. */


返回OpenVRSteamVR)运行时是否安装

1
2
3
4
5
VR_INTERFACE bool VR_CALLTYPE VR_IsRuntimeInstalled();
 
  
 
/** Returns where the OpenVR runtime is installed. */


返回OpenVR运行时的安装路径

1
2
3
4
5
VR_INTERFACE const char *VR_CALLTYPE VR_RuntimePath();
 
  
 
VR_INTERFACE void VR_CALLTYPE VR_ShutdownInternal();


同样,VR_ShowdownInternalVR_InitInternal一样,在C++SDK中,对外公开的实际是VR_Showdown。可以看到VR_Shutdown就是简单地调用了VR_ShutdownInternal

 

那显然最重要的是VR_GetGenericInterface返回的VR接口句柄,有一系列的VR接口,根据参数不同VR_GetGenericInterface返回不同的接口指针,这些接口有:IVRSystemIVRApplicationsIVRSettingsIVRChaperoneIVRChaperonSetupIVRCompositorIVRNotificationsIVROverlayIVRRenderModelsIVRExtendedDisplayIVRTrackedCameraIVRScreenshots。每个接口都包含有大量的虚函数以提供完善的功能。

 

以上均是参考C++版的SDK的描述,那么,如果Unity插件版和C++版本所使用的openvr_api.dll是相同的,那么上面说的通过VR_GetGenericInterface获取各实际操作接口,返回的是C++对象的指针,在C#里面怎么使用呢?这个就涉及到C#如何调用DLL的问题,下节将给出解答。

 

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引