200字范文,内容丰富有趣,生活中的好帮手!
200字范文 > #HTC VIVE #进行VR开发的环境

#HTC VIVE #进行VR开发的环境

时间:2020-09-02 11:33:07

相关推荐

#HTC VIVE #进行VR开发的环境

因为一些原因,要求上周的博客缺了一篇,于是在这周补上。好了,让我们开始吧。

HTC VIVE 开发中最重要的基石是steamVR,只有充分理解steamVR,才能在开发中得心应手,创造最佳的虚拟现实体验。

知名游戏公司Valve将OpenVR和Steam平台结合在一起开发了SteamVR,这是一个非常有远见的战略,未来的VR内容平台必然是炙手可热的。让我们看一下SteamVR是如何结合HTC Vive工作的。

首先,HTC Vive提供了两个基站,我们将他们分别架设在一个空间的两端互为犄角,从而建立起一个定位空间。我们将这个空间称为Light House,因为在这个空间中布满了我们看不到的激光。SteamVR会根据激光数据精确地定位头盔和两个手柄的位置,让玩家更精确地在游戏中进行交互,从而得到更棒的VR体验。

然后,Valve为了让Unity的开发者更好地开发出VR游戏,在SteamVR的基础上为Unity开发出了SteamVR Plugin。可以在Unity的Asset Store里找到它。下载后就可以看到

在Scripts文件夹里,我们可以找到SteamVR各脚本的功能

SteamVR/Scripts/下脚本各功能的实现

1、SteamVR.cs 单例管理类,管理SteamVR程序的运行和终止。 2、SteamVR_Camera.cs 给场景添加一个最基本可运行的SteamVR组。 3、SteamVR_CameraFlip.cs 使用Shader将屏幕图像反转得到最终图像。 4、SteamVR_CameraMask.cs 将头盔中看不到的屏幕像素遮盖。 5、SteamVR_Controller.cs 管理类,管理所有设备的输入控制 6、SteamVR_ControllerManager.cs 管理类,管理场景中的设备活动 7、SteamVR_Ears.cs 控制Audio Listener的方向 8、SteamVR_ExternalCamera.cs 用于渲染外部摄像机 9、SteamVR_Fade.cs 屏幕渐变功能 10、SteamVR_Frustum.cs 生成用于渲染的面片 11、SteamVR_GameView.cs 处理除眼图像之外的渲染 12、SteamVR_IK.cs 手柄IK的控制 13、SteamVR_LoadLevel.cs 用于场景之间的平滑切换 14、SteamVR_Menu.cs 给出一个范例菜单 15、SteamVR_Overlay.cs 提供和控制2D图像的绘制 16、SteamVR_PlayArea.cs 对移动空间的设置 17、SteamVR_Render.cs 控制眼图像的渲染 18、SteamVR_RenderModel.cs 渲染手柄模型 19、SteamVR_Skybox.cs 设置天空盒 20、SteamVR_SphericalProjection.cs 应该是应用畸变投影矩阵 21、SteamVR_Stats.cs 通过GUI Text显示头盔状态 22、SteamVR_Status.cs 由事件控制的渐变效果的基类 23、SteamVR_StatusText.cs 继承22的文字渐变 24、SteamVR_TestController.cs 测试手柄每个按钮的输入 25、SteamVR_TrackedCamera.cs 提供记录相机的位置的功能 26、SteamVR_TrackedObject.cs 使场景中的物体和控制器的Pose保持一致 27、SteamVR_UpdatePose.cs 当使用OpenVR接口时用此更新Pose 28、SteamVR_Utils.cs 一些公共方法和数据结构 SteamVR/Extras/脚本下功能的实现

详细脚本解析:

SteamVR_GazeTracker.cs脚本解析

这个脚本的作用是判断当前物体是否被用户(头显)所注视,进入注视和离开注视都会有回调。处于注视状态的物体与实际注视点的距离范围定义为小于0.15米,而离开注视状态的距离范围为大于0.4米。之所以有一个大概的范围,并且使用了一个平面来相交,是因为注视这个动作是比较粗略的,玩家比较难能精确注视。

Gaze回调的事件结构体,只有一个参数,即距离,表示凝视点与物体(中心)的距离

public struct GazeEventArgs{public float distance;}public delegate void GazeEventHandler(object sender, GazeEventArgs e);public class SteamVR_GazeTracker : MonoBehaviour{//当前是否处于gaze状态public bool isInGaze = false;//入gaze状态回调,使用者可以通过代码添加自己的事件处理方法(在Inspector中不会出现)public event GazeEventHandler GazeOn;//离开gaze状态回调public event GazeEventHandler GazeOff;//定义的进入gaze与离开gaze的距离范围public float gazeInCutoff = 0.15f;public float gazeOutCutoff = 0.4f;// Contains a HMD tracked object that we can use to find the user's gaze//头显的transform对象Transform hmdTrackedObject = null;// Use this for initializationvoid Start (){}public virtual void OnGazeOn(GazeEventArgs e){//如果有注册GazeOff回调,调用它if (GazeOn != null)GazeOn(this, e);}public virtual void OnGazeOff(GazeEventArgs e){//如果有注册GazeOff回调,调用它if (GazeOff != null)GazeOff(this, e);}// Update is called once per framevoid Update (){// If we haven't set up hmdTrackedObject find what the user is looking atif (hmdTrackedObject == null){//首次调用会去查找头显,方法是查找所有SteamVR_TrackedObject对象。所有的跟踪对象(比如头显、手柄、基站)都是SteamVR_TrackedObject对象(相应的对象上附加了SteamVR_TrackedObject脚本)SteamVR_TrackedObject[] trackedObjects = FindObjectsOfType<SteamVR_TrackedObject>();foreach (SteamVR_TrackedObject tracked in trackedObjects){if (tracked.index == SteamVR_TrackedObject.EIndex.Hmd){//找到头显设备,取其transform对象。头显设备的索引是0号索引hmdTrackedObject = tracked.transform;break;}}}if (hmdTrackedObject){//构造一条从头显正方向的射线Ray r = new Ray(hmdTrackedObject.position, hmdTrackedObject.forward);//构造一个头显正方向、在当前物体位置的平面Plane p = new Plane(hmdTrackedObject.forward, transform.position);float enter = 0.0f;//射线与物体平面正向相交,返回的enter为沿射线的距离。如果不相交,或者反向相交,则下面的Raycast返回falseif (p.Raycast(r, out enter)){//intersect为射线与物体平面在三维空间的交点Vector3 intersect = hmdTrackedObject.position + hmdTrackedObject.forward * enter;//计算空间两点的距离,即物体当前位置与交点的距离float dist = Vector3.Distance(intersect, transform.position);//Debug.Log("Gaze dist = " + dist);if (dist < gazeInCutoff && !isInGaze){//当前物体与凝视点的距离小于0.15米,则认为进入gaze状态isInGaze = true;GazeEventArgs e;e.distance = dist;OnGazeOn(e);}else if (dist >= gazeOutCutoff && isInGaze){//当前物体与凝视点的距离超过0.4米,则认为离开gaze状态isInGaze = false;GazeEventArgs e;e.distance = dist;OnGazeOff(e);}}}}}

这个脚本的作用与上面的SteamVR_GazeTracker相关及类似。GazeTracker是通过头显的正视方向与物体相交来计算交点的。而这里是通过所谓的激光束来与物体相交的。激光束就是手柄指向的方向,可以在游戏里面把这个方向渲染出一条激光束出来,特别是在通过手柄进行菜单的UI操作的时候。在github openvr的sample目录下的unity_teleport_sample示例有使用,它被加到右手柄上

同上面的GazeTracker一样,触发的事件所带的参数

public struct PointerEventArgs{//控制器(手柄)索引public uint controllerIndex;//目前好像并没有用到public uint flags;//激光原点到命中点(交点)的距离public float distance;//命中物体的transform对象public Transform target;}public delegate void PointerEventHandler(object sender, PointerEventArgs e);public class SteamVR_LaserPointer : MonoBehaviour{//这个变量并未使用public bool active = true;//激光的颜色public Color color;//激光束的粗细(创建了一个立方体,按下面的scale,x、y是0.002,z是100,就能看 到是一条很长的细线了)public float thickness = 0.002f;//一个空的GameObject,用于作激光束的parentpublic GameObject holder;//激光束本身,是用一个立方体拉长来模拟的(为啥不用圆柱体?显然立方体要比圆柱体渲染简单得多,在很细的情况下,用立方体是明智的选择)public GameObject pointer;//用来判断是否为第一次调用bool isActive = false;//这个是暴露在inspector中的属性,用于控制是否给激光束(长方体)添加刚体。本身光是没有重量的,没有必要添加刚体吧。所以这里缺省是falsepublic bool addRigidBody = false;//这个变量并未使用public Transform reference;//同上面的GazeTracker一样,用于触发激光命中和离开事件public event PointerEventHandler PointerIn;public event PointerEventHandler PointerOut;//上次激光命中的物体的transform对象,用于判断是否命中同一个物体Transform previousContact = null;// Use this for initializationvoid Start (){//在脚本被加载的时候,做一些初始化//首先创建一个holder(即激光束的父物体)holder = new GameObject();//holder的transform的parent设为当前脚本所在的物体(通常这个脚本会加到控制器手柄上面)holder.transform.parent = this.transform;//位置设在0点(本地坐标系,相对于父亲)holder.transform.localPosition = Vector3.zero;holder.transform.localRotation = Quaternion.identity;//创建激光束,用长方体模拟pointer = GameObject.CreatePrimitive(PrimitiveType.Cube);//将父亲设为上面的holderpointer.transform.parent = holder.transform;//设置locale为(0.002,0.002,100),看起来就是一条很长的线pointer.transform.localScale = new Vector3(thickness, thickness, 100f);//位置设在父亲的(0,0,50)位置,因为对于立方体(长方体),其中心在立方体中心,因为上面被放大到了100倍,那移动位置到(0,0,50)可以让激光束的起点为父亲pointer.transform.localPosition = new Vector3(0f, 0f, 50f);pointer.transform.localRotation = Quaternion.identity;// 如果指定了addRigidBody为true,则为激光束添加一个刚体,对应的collider 则只设为触发器(不会执行碰撞,但会进入代码)。否则,会把collider销毁掉,也就是不需要colliderBoxCollider collider = pointer.GetComponent<BoxCollider>();if (addRigidBody){if (collider){collider.isTrigger = true;}Rigidbody rigidBody = pointer.AddComponent<Rigidbody>();rigidBody.isKinematic = true;}else{if(collider){Object.Destroy(collider);}}//新建纯色材质并添加到MeshRender中。Color值通过inspector设置Material newMaterial = new Material(Shader.Find("Unlit/Color"));newMaterial.SetColor("_Color", color);pointer.GetComponent<MeshRenderer>().material = newMaterial;}public virtual void OnPointerIn(PointerEventArgs e){//回调激光命中委托if (PointerIn != null)PointerIn(this, e);}public virtual void OnPointerOut(PointerEventArgs e){//回调激光不再命中委托if (PointerOut != null)PointerOut(this, e);}// Update is called once per framevoid Update(){if (!isActive){//第一次调用时将holder设为active(当前物体transform的第一个child就是holder)isActive = true;this.transform.GetChild(0).gameObject.SetActive(true);}//命中物体(或者说激光束)的最远距离记为100米float dist = 100f;//当前物体(手柄上)上还要挂一个SteamVR_TrackedController脚本SteamVR_TrackedController controller = GetComponent<SteamVR_TrackedController>();// 构造一条射线Ray raycast = new Ray(transform.position, transform.forward);RaycastHit hit;//计算射线命中的场景中的物体bool bHit = Physics.Raycast(raycast, out hit);if(previousContact && previousContact != hit.transform){// 如果之前已经有一个命中的物体,而当前命中的物体发生了变化,那么说明前一个命中的物体就要收到一个不再命中的通知PointerEventArgs args = new PointerEventArgs();if (controller != null){args.controllerIndex = controller.controllerIndex;}args.distance = 0f;args.flags = 0;args.target = previousContact;OnPointerOut(args);previousContact = null;}if(bHit && previousContact != hit.transform){//通知命中新的物体PointerEventArgs argsIn = new PointerEventArgs();if (controller != null){argsIn.controllerIndex = controller.controllerIndex;}// hit.distance为射线原点到命中点的距离argsIn.distance = hit.distance;argsIn.flags = 0;//target记录的是命中物体的transformargsIn.target = hit.transform;OnPointerIn(argsIn);// 记录上一次命中的物体的transformpreviousContact = hit.transform;}if(!bHit){previousContact = null;}if (bHit && hit.distance < 100f){//如果命中物体距离小于100,则记录下来,否则最远就是100米dist = hit.distance;}if (controller != null && controller.triggerPressed){//当按下扳机键时,将光束的粗细增大5倍,同时长度会设为dist,这样看起来光束就会到命中点截止,不会穿透物体pointer.transform.localScale = new Vector3(thickness * 5f, thickness * 5f, dist);}else{//按下扳机或者当前控制器没有添加SteamVR_TrackedController时,显示原始粗细的光束pointer.transform.localScale = new Vector3(thickness, thickness, dist);}//光束的位置总是设在光束长度的一半的位置,使得光束看起来总是从手柄发出来的pointer.transform.localPosition = new Vector3(0f, 0f, dist/2f);}}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。