理解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,目前是解決了問題,暫時沒有發現副作用。
若有問題,還請多多指教!!
這一篇,真是不容易,過山車一般。把思路和走過的錯誤道路記錄下來,希望大家別在走彎路。
希望你能點贊支援,手留餘香!!