理解HTC Vive更新——控制相机旋转和位移

发表于2017-06-01
评论1 4.1k浏览

本文章由cartzhang编写,转载请注明出处。 所有权利保留。 
文章链接:http://blog.csdn.net/cartzhang/article/details/72188658 
作者:cartzhang

一、写在前面


HTCvive 头盔中, 
一旦Vive头盔连接都unity游戏中,就会控制所有Camera的旋转和位置。 

这对于有需要的控制非头盔相机带来了烦恼。 

比方说,上篇博客中,在VR中,对某个特点位置截图,就会由于头盔控制所有相机的旋转, 
造成截图不精确和出现偏移。

地址: 传送门


现在,经过测试发现,其实是可以控制的。 

Win10系统下测试,unity版本为5.4.4f1,Steam VR 版本v1.1.1

二、怎么控制

说明: 这里写图片描述 


头盔在正常情况下,被头盔具体位置和旋转赋值控制。

说明: 这里写图片描述 


同时头盔的参数也控制其他,比如UI相机的位置和旋转。


加上我们的脚本:

说明: 这里写图片描述 


游戏编辑器下运行,头盔给Ui相机的赋值,被强制给变成了零。

三、控制脚本代码


接下来看看脚本代码:

//@cartzhang

using UnityEngine;

using System.Collections;

 

public class StickRotate : MonoBehaviour {

 

    // Use this forinitialization

    void Start () {

 

    }

 

 

    // NO ,不行。

    //private voidFixedUpdate()

    //{

    //   transform.rotation = Quaternion.identity;

    //}

 

 

    // yes ,可以限定。

    //private voidOnPreCull()

    //{

    //   transform.rotation = Quaternion.identity;

    //}

 

    // Update is calledonce per frame

    void OnPostRender ()

    {

       transform.rotation = Quaternion.identity;

       transform.position = Vector3.zero;

    }

}


经过测试,发现在UpdatefixedUpdate中处理上不可行的。 
OnPreCullOnPostRender进行重新赋值都可以的。

四、重点来了,都是幻觉

1. 没有那么简单


当时第六感就怀疑,应该没有这么简单。要不就不会有成群结队的人要求unitysteamVR插件做接口和选项了,让可以控制旋转和移动了。 

当时是这样想的: 
结果我还是有一定的怀疑的。是不是由于脚本执行顺序不同,在不同的电脑和不同时候结果也不一样的。 
若有的话,试试看通过调整脚本执行顺序看能不能解决问题。 

2.转折了


结果,出去遛一圈回来,就不行了。郁闷啊!! 
截图都在,电脑不认了。 
都是幻觉啊!!

说明: 这里写图片描述 


找到了方法 
https://steamcommunity.com/app/358720/discussions/0/365163686052028359/ 
说在5.3 可用,5.4不可用。天啊… 

(说明下,之前想的调整渲染顺序,然并卵。理想与现实的差距啊

3. 然而,并不气


气也没有用。考虑对策, 

第一,我试图把VR5.3VR插件直接拷贝到我的5.4.4f1版本里来做替换,结果可想而知,失败,替换后根本就没有了VR效果,因为VR的插件相当于没有加载。 

第二、使用相对旋转和相对位置实时做矫正。


也参看来其他的只需要旋转的一个问题。有需要的可以参考:

http://answers.unity3d.com/questions/1209337/vrvive-allow-rotation-only.html 

这个方案与我之前的小场地扩展到大场地的策略类似,可以参考: 
HTC Vive
小场地与大场景空间的解决方案———— 
http://blog.csdn.net/cartzhang/article/details/52780621 

这个结果是成功了。

说明: 这里写图片描述 


4. 给出代码

using UnityEngine;

using System.Collections;

 

public class StickRotate : MonoBehaviour

{  

private Vector3 InitialPos;

private Vector3 hmdPos;

public GameObject HMD;

private TransformCameraPos;

 

void Start()

{

CameraPos =transform;

InitialPos =transform.position;

}

 

// Update is calledonce per frame

void LateUpdate()

{

hmdPos =HMD.transform.localPosition;

transform.position =CameraPos.position - hmdPos;

transform.rotation =Quaternion.Euler(CameraPos.rotation.eulerAngles -HMD.transform.rotation.eulerAngles);

}

 

// NO ,不行。

//private voidFixedUpdate()

//{

//   transform.rotation = Quaternion.identity;

//}

 

 

// NO ,不能限制旋转。

//private voidUpdate()

//{

//   transform.rotation = Quaternion.identity;

//}

 

// yes ,可以限定。这个原来是可以的,后来莫名就不行了。

//   private voidOnPreCull()

//   {

//      transform.rotation = Quaternion.identity;

//   }

 

//   // 这个跟上面一下,当时还有点沾沾自喜...

//   voidOnPostRender ()

//   {

//      transform.rotation = Quaternion.identity;

//      transform.position = Vector3.zero;

//}

 

}

五、试着了解 Vive头盔的更新

1. 头盔的渲染更新是在SteamVR_Render中进行的。

// 注释 @cartzhang

void Update()

    {

#if !(UNITY_5_3 || UNITY_5_2 ||UNITY_5_1 || UNITY_5_0)

        // 添加PoseUpdate。在SteamVR_UpdatePoses中实现了位置更新。

        //

        if (poseUpdater == null)

       {

            var go = new GameObject("poseUpdater");

           go.transform.parent = transform;

           poseUpdater = go.AddComponent();

       }

#else

        if (cameras.Length == 0)

       {

           enabled = false;

            return;

       }

 

        // Ifour FixedUpdate rate doesn't match our render framerate, then catch the handoffhere.

       SteamVR_Utils.QueueEventOnRenderThread(SteamVR.Unity.k_nRenderEventID_PostPresentHandoff);

#endif

        //Force controller update in case no one else called this frame to ensureprevState gets updated.

        // 强制调用手柄更新,以防止本帧没有调用。

       SteamVR_Controller.Update();

 

        //Dispatch any OpenVR events.

        // 事件分发。

        var system =OpenVR.System;

        if (system != null)

       {

            var vrEvent = new VREvent_t();

            var size = (uint)System.Runtime.InteropServices.Marshal.SizeOf(typeof(VREvent_t));

            for (int i = 0; i < 64; i++)

           {

                if (!system.PollNextEvent(ref vrEvent, size))

                    break;

 

                switch ((EVREventType)vrEvent.eventType)

               {

                    case EVREventType.VREvent_InputFocusCaptured: // another app has taken focus(likely dashboard)

                        if (vrEvent.data.process.oldPid == 0)

                       {

                            SteamVR_Utils.Event.Send("input_focus", false);

                       }

                        break;

                    case EVREventType.VREvent_InputFocusReleased: // that app has released inputfocus

                        if (vrEvent.data.process.pid == 0)

                       {

                           SteamVR_Utils.Event.Send("input_focus", true);

                       }

                        break;

                    case EVREventType.VREvent_ShowRenderModels:

                       SteamVR_Utils.Event.Send("hide_render_models", false);

                        break;

                    case EVREventType.VREvent_HideRenderModels:

                       SteamVR_Utils.Event.Send("hide_render_models", true);

                        break;

                    default:

                        var name =System.Enum.GetName(typeof(EVREventType), vrEvent.eventType);

                        if (name != null)

                           SteamVR_Utils.Event.Send(name.Substring(8) /*strip VREvent_*/, vrEvent);

                        break;

               }

           }

       }

 

        //Ensure various settings to minimize latency.

        // 不限制最高帧率

       Application.targetFrameRate = -1;

        // 可以在后台运行,不需要强制窗口焦点。

       Application.runInBackground = true; // don't require companion window focus

        // 不限制驱动程序的最大队列值。这个只有DX有,OpenGL中被忽略。

       QualitySettings.maxQueuedFrames = -1;

        // 关闭垂直同步。

       QualitySettings.vSyncCount = 0; // this applies to the companion window

 

        // 是否锁定刷新速率与物理同步。

        if (lockPhysicsUpdateRateToRenderFrequency && Time.timeScale > 0.0f)

       {

            var vr =SteamVR.instance;

            if (vr != null)

           {

                var timing = new Compositor_FrameTiming();

               timing.m_nSize = (uint)System.Runtime.InteropServices.Marshal.SizeOf(typeof(Compositor_FrameTiming));

               vr.compositor.GetFrameTiming(ref timing, 0);

                // 设置新的物理更新间隔。

               Time.fixedDeltaTime = Time.timeScale / vr.hmd_DisplayFrequency;

           }

       }

   }
在代码中加了中文注释。 
在编辑器模式下运行过程中,关闭与steamVR 相关脚本并不会影响头盔的旋转和位置。 

我理解的的是,这是UnityVR的底层代码进行的处理。或者在OnEnableAwake中进行了绑定,所有以后是否运行脚本都没有关系了。

2. SteamVR_UpdatePoses姿态更新


单独说下这个更新,是由于若在Vive相机上直接对相机下的模型贴上UI会造成在编辑器下正常,在游戏中UI的煽动,也就是在移动过程中,并不是实时的更随物体移动,而是有类似于弹簧似的移动,就像是使用了DoTween 

解决方法就在这里。

//void OnPreCull()

    //fixed change for ui followcontroller at leaset one frame delay.@zpj

void LateUpdate()


把原来的OnPreCull修改为LateUpdate,目前是解决了问题,暂时没有发现副作用。 

若有问题,还请多多指教!! 

这一篇,真是不容易,过山车一般。把思路和走过的错误道路记录下来,希望大家别在走弯路。


希望你能点赞支持,手留余香!!

 

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