1. 程式人生 > >理解HTC Vive更新——控制相機旋轉和位移

理解HTC Vive更新——控制相機旋轉和位移

一、寫在前面


在HTC的vive 頭盔中,
一旦Vive頭盔連線都unity遊戲中,就會控制所有Camera的旋轉和位置。

這對於有需要的控制非頭盔相機帶來了煩惱。

比方說,上篇部落格中,在VR中,對某個特點位置截圖,就會由於頭盔控制所有相機的旋轉,
造成截圖不精確和出現偏移。


現在,經過測試發現,其實是可以控制的。

在Win10系統下測試,unity版本為5.4.4f1,Steam VR 版本v1.1.1。

二、怎麼控制

這裡寫圖片描述
圖0

頭盔在正常情況下,被頭盔具體位置和旋轉賦值控制。

這裡寫圖片描述
圖1

同時頭盔的引數也控制其他,比如UI相機的位置和旋轉。


加上我們的指令碼:

這裡寫圖片描述
圖2

遊戲編輯器下執行,頭盔給Ui相機的賦值,被強制給變成了零。

三、控制指令碼程式碼


接下來看看指令碼程式碼:

//@cartzhang
using UnityEngine;
using System.Collections;

public class StickRotate : MonoBehaviour {

    // Use this for initialization
    void Start () {

    }


    // NO ,不行。
    //private void FixedUpdate()
    //{
    //    transform.rotation = Quaternion.identity;
//} // yes ,可以限定。 //private void OnPreCull() //{ // transform.rotation = Quaternion.identity; //} // Update is called once per frame void OnPostRender () { transform.rotation = Quaternion.identity; transform.position = Vector3.zero; } }


經過測試,發現在Update和fixedUpdate中處理上不可行的。
在OnPreCull和OnPostRender進行重新賦值都可以的。

四、重點來了,都是幻覺

1. 沒有那麼簡單


當時第六感就懷疑,應該沒有這麼簡單。要不就不會有成群結隊的人要求unity和steamVR外掛做介面和選項了,讓可以控制旋轉和移動了。

當時是這樣想的:
結果我還是有一定的懷疑的。是不是由於指令碼執行順序不同,在不同的電腦和不同時候結果也不一樣的。
若有的話,試試看通過調整指令碼執行順序看能不能解決問題。

2.轉折了


結果,出去遛一圈回來,就不行了。鬱悶啊!!
截圖都在,電腦不認了。
都是幻覺啊!!

3. 然而,並不氣


氣也沒有用。考慮對策,

第一,我試圖把VR5.3的VR外掛直接拷貝到我的5.4.4f1版本里來做替換,結果可想而知,失敗,替換後根本就沒有了VR效果,因為VR的外掛相當於沒有載入。

第二、使用相對旋轉和相對位置實時做矯正。


也參看來其他的只需要旋轉的一個問題。有需要的可以參考:

這裡寫圖片描述
圖4

4. 給出程式碼

using UnityEngine;
using System.Collections;

public class StickRotate : MonoBehaviour
{   
    private Vector3 InitialPos;
    private Vector3 hmdPos;
    public GameObject HMD;
    private Transform CameraPos;

    void Start()
    {
        CameraPos = transform;
        InitialPos = transform.position;
    }

    // Update is called once 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 void FixedUpdate()
    //{
    //    transform.rotation = Quaternion.identity;
    //}


    // NO ,不能限制旋轉。
    //private void Update()
    //{
    //    transform.rotation = Quaternion.identity;
    //}

    // yes ,可以限定。這個原來是可以的,後來莫名就不行了。
 //   private void OnPreCull()
 //   {
 //       transform.rotation = Quaternion.identity;
 //   }

 //   // 這個跟上面一下,當時還有點沾沾自喜...
 //   void OnPostRender ()
 //   {
 //       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<SteamVR_UpdatePoses>();
        }
#else
        if (cameras.Length == 0)
        {
            enabled = false;
            return;
        }

        // If our FixedUpdate rate doesn't match our render framerate, then catch the handoff here.
        SteamVR_Utils.QueueEventOnRenderThread(SteamVR.Unity.k_nRenderEventID_PostPresentHandoff);
#endif
        // Force controller update in case no one else called this frame to ensure prevState 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 input focus
                        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 相關指令碼並不會影響頭盔的旋轉和位置。

我理解的的是,這是Unity在VR的底層程式碼進行的處理。或者在OnEnable或Awake中進行了繫結,所有以後是否執行指令碼都沒有關係了。

2. SteamVR_UpdatePoses姿態更新


單獨說下這個更新,是由於若在Vive相機上直接對相機下的模型貼上UI會造成在編輯器下正常,在遊戲中UI的煽動,也就是在移動過程中,並不是實時的更隨物體移動,而是有類似於彈簧似的移動,就像是使用了DoTween。

解決方法就在這裡。

//void OnPreCull() 
    //fixed change for ui follow controller at leaset one frame delay.@zpj
    void LateUpdate()


把原來的OnPreCull修改為LateUpdate,目前是解決了問題,暫時沒有發現副作用。

若有問題,還請多多指教!!

這一篇,真是不容易,過山車一般。把思路和走過的錯誤道路記錄下來,希望大家別在走彎路。


希望你能點贊支援,手留餘香!!

六、參考