寫給VR手游開發(fā)小白的教程:(三)UnityVR插件CardboardSDKForUnity解析(二)

原文地址:http://blog.csdn.net/mao_xiao_feng/article/details/52074384
上一章講了Cardboard這個類,做一個總結码倦,這個類總就是給一些私有的變量去提供了共有的接口,以供用戶去修改他們的值捕发。這些量大多都是用戶級別的變量庙楚,多是控制一些功能的使能,例如是否開啟VR模式等哎壳。還有一個重要作用毅待,類實現了自身的單例化,如下:

public class Singleton{    private static Singleton _instance = null;    //將構造函數設為private归榕,防止客戶代碼通過new實例化對象    private Singleton()    {    }    public static Singleton CreateInstance()    {            if(_instance == null)            _instance = new Singleton();        return _instance;    }}

這就是單件模式一個重要的例子尸红,用戶永遠只能通過自定義的公有方法去實例化或者返回已經實例化的對象。
單件模式蹲坷,有兩個好處:
第一個是資源共享驶乾,例如在這個demo中,我們只希望VR Mode Enabled這個變量只有一個循签,外部共享的調用它级乐。如果它有兩個,這樣就會導致難以處理一些請求和資源的損耗县匠。
第二個是信息交互會更加簡便风科,就像為什么Unity里面永遠只有一個Main Camera,而我們去find它的時候不需要遍歷的去搜索判斷它的名字乞旦,道理是一樣的贼穆。
我們還簡單介紹了以下幾項屬性的功能(其中Editor Mock Settings是Unity編輯器下運行的設置,一般不會做改動的兰粉,也對手機上運行沒什么影響)

Paste_Image.png

/************************************************************分割線****************************************************************/

好了故痊,接下來進入這次的主題,先上圖
Paste_Image.png](http://upload-images.jianshu.io/upload_images/1312008-c2e8a1d51f5f8d10.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
這是VRDevice文件夾下所有類的繼承關系玖姑,可以看到所有類都繼承于同一個基類愕秫,BaseVRDevice

BaseVRDevice的介紹
// Represents a vr device that this plugin interacts with. 代表了與這個插件交互的VR設備
這個類里面應該定義了作為VR設備必須要實現的一些方法慨菱,但我們不必關注是怎樣實現的,這些方法會在子類里面根據不同的平臺(iOS戴甩,Android)重寫符喝,給出詳細的實現步驟。
我們把重要的和不重要的歸個類
重要:
public virtual RenderTexture CreateStereoScreen()
//Creating new default cardboard screen texture.為Cardboard屏幕生成一個新的默認RenderTexture甜孤,RenderTexture是一個實時更新的紋理协饲,動態(tài)的,上節(jié)說過

public Pose3D GetHeadPose()
protected MutablePose3D headPose = new MutablePose3D();
//兩個一起講缴川,headPose是變量茉稠,記錄了頭部的位置信息,注意是protected的二跋,GetHeadPose()得到頭部的位置战惊,返回的是headPose,這個方法是public的

public Pose3D GetEyePose(Cardboard.Eye eye)
protected MutablePose3D leftEyePose = new MutablePose3D();
protected MutablePose3D rightEyePose = new MutablePose3D();
//同理扎即,眼睛位置

public Matrix4x4 GetProjection(Cardboard.Eye eye,Cardboard.Distortion distortion = Cardboard.Distortion.Distorted)
protected Matrix4x4 leftEyeDistortedProjection;
protected Matrix4x4 rightEyeDistortedProjection;
protected Matrix4x4 leftEyeUndistortedProjection;
protected Matrix4x4 rightEyeUndistortedProjection;
//根據眼睛類型(左右眼)和是否應用透鏡彎曲效果來返回一個Projection(投影)吞获,這個投影是Matrix4x4類型的,Unity官方對投影矩陣的解釋是 如果你改變這個矩陣谚鄙,相機的渲染不再基于它的fieldOfView更新各拷,直到調用ResetProjectionMatrix.只有當真正需要一個非標準的投影時,才使用自定義投影闷营。

public Rect GetViewport(Cardboard.Eye eye,Cardboard.Distortion distortion = Cardboard.Distortion.Distorted)
protected Rect leftEyeDistortedViewport;
protected Rect rightEyeDistortedViewport;
protected Rect leftEyeUndistortedViewport;
protected Rect rightEyeUndistortedViewport;
//根據眼睛類型(左右眼)和是否應用透鏡彎曲效果來返回一個Viewport(視口)烤黍,這個視口是Rect 類型的,Rect類型很簡單傻盟,里面存著(x,y,w,h)四個量速蕊,而每個量分別代表的是如下圖,而一個camera呈現的畫面娘赴,它在屏幕上占多大的地规哲,就是以視口決定的。

Paste_Image.png

public abstract void UpdateState();
public abstract void UpdateScreenData();
public abstract void Recenter();
public abstract void PostRender(bool vrMode);
//又來四個抽象方法诽表,從字面意思上看唉锌,是狀態(tài)更新,屏幕數據更新竿奏,重新定位中心袄简,完成渲染后

public virtual void OnPause(bool pause)
//當停止了,就不再UpdateScreenData()
public virtual void OnFocus(bool focus)
public virtual void Reset()
public virtual void OnApplicationQuit()
public virtual void Destroy()
//由字面意思就可以理解
protected void ComputeEyesFromProfile()//這個函數很關鍵7盒ァB逃铩!官方打的注釋是// Compute left eye matrices from screen and device params,意思就是根據設備的參數計算左眼的觀測信息

/****************************************************分割線************************************************************/

這一部分對ComputeEyesFromProfile()函數做一個詳細介紹

protected void ComputeEyesFromProfile() {
// Compute left eye matrices from screen and device params
Matrix4x4 leftEyeView = Matrix4x4.identity;
leftEyeView[0, 3] = -Profile.device.lenses.separation / 2;
leftEyePose.Set(leftEyeView);

float[] rect = new float[4];
Profile.GetLeftEyeVisibleTanAngles(ref rect);
leftEyeDistortedProjection = MakeProjection(rect[0], rect[1], rect[2], rect[3], 1, 1000);
Profile.GetLeftEyeNoLensTanAngles(ref rect);
leftEyeUndistortedProjection = MakeProjection(rect[0], rect[1], rect[2], rect[3], 1, 1000);

leftEyeUndistortedViewport = Profile.GetLeftEyeVisibleScreenRect(rect);
leftEyeDistortedViewport = leftEyeUndistortedViewport;

// Right eye matrices same as left ones but for some sign flippage.
Matrix4x4 rightEyeView = leftEyeView;
rightEyeView[0, 3] *= -1;
rightEyePose.Set(rightEyeView);

rightEyeDistortedProjection = leftEyeDistortedProjection;
rightEyeDistortedProjection[0, 2] *= -1;
rightEyeUndistortedProjection = leftEyeUndistortedProjection;
rightEyeUndistortedProjection[0, 2] *= -1;

rightEyeUndistortedViewport = leftEyeUndistortedViewport;
rightEyeUndistortedViewport.x = 1 - rightEyeUndistortedViewport.xMax;
rightEyeDistortedViewport = rightEyeUndistortedViewport;
}

來自CODE的代碼片BaseVRDevice.cs

首先這個方法中用到的數據都是來源于CardboardProfile這個類汞舱,這個類中記錄了Cardboard設備的各項參數
第一句話建立了Matrix4x4 leftEyeView這個量伍纫,然后把一個4*4的單位矩陣賦給他
第二句話涉及到了CardboardProfile里的Lenses,想要解釋清楚這部分昂芜,需要畫三張圖


Paste_Image.png

Paste_Image.png](http://upload-images.jianshu.io/upload_images/1312008-2edda55bebedac71.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
Lenses里面一些量標注如上圖。
還有MaxFOV這個量
public struct MaxFOV { public float outer; // Towards the side of the screen. public float inner; // Towards the center line of the screen. public float upper; // Towards the top of the screen. public float lower; // Towards the bottom of the screen. }
這個量表示我們視野的最大值赔蒲,是用角度來定義的泌神,如outer這個量就是俯視圖中,∠ABC的大小舞虱,inner就是∠CBD的大小了
接著第二句話leftEyeView[0, 3] = -Profile.device.lenses.separation / 2欢际,這就是一個賦值語句,給第4列的第一個元素賦了值
第三句話leftEyePose.Set(leftEyeView)矾兜,從這句話可以看出了leftEyeView這個矩陣里面是存儲了眼睛的位置信息的损趋,后續(xù)看了Set()函數,矩陣的第四列存了Position椅寺,第二第三列存了Orientation朝向信息浑槽。
再看后面 float[] rect = new float[4];Profile.GetLeftEyeVisibleTanAngles(ref rect);這兩句話,新建了一個數組返帕,在一個方法GetLeftEyeVisibleTanAngles中修改了數組里的元素的值

[csharp] view plain copy

public void GetLeftEyeVisibleTanAngles(ref float[] result) {  
    // Tan-angles from the max FOV.  
    float fovLeft = (float) Math.Tan(-device.maxFOV.outer * Math.PI / 180);  
    float fovTop = (float) Math.Tan(device.maxFOV.upper * Math.PI / 180);  
    float fovRight = (float) Math.Tan(device.maxFOV.inner * Math.PI / 180);  
    float fovBottom = (float) Math.Tan(-device.maxFOV.lower * Math.PI / 180);  
    // Viewport size.  
    float halfWidth = screen.width / 4;  
    float halfHeight = screen.height / 2;  
    // Viewport center, measured from left lens position.  
    float centerX = device.lenses.separation / 2 - halfWidth;  
    float centerY = -VerticalLensOffset;  
    float centerZ = device.lenses.screenDistance;  
    // Tan-angles of the viewport edges, as seen through the lens.  
    float screenLeft = device.distortion.distort((centerX - halfWidth) / centerZ);  
    float screenTop = device.distortion.distort((centerY + halfHeight) / centerZ);  
    float screenRight = device.distortion.distort((centerX + halfWidth) / centerZ);  
    float screenBottom = device.distortion.distort((centerY - halfWidth) / centerZ);  
    // Compare the two sets of tan-angles and take the value closer to zero on each side.  
    result[0] = Math.Max(fovLeft, screenLeft);  
    result[1] = Math.Min(fovTop, screenTop);  
    result[2] = Math.Min(fovRight, screenRight);  
    result[3] = Math.Max(fovBottom, screenBottom);  
  }  

看一下GetLeftEyeVisibleTanAngles這個函數桐玻,先是把FOV角度轉換成了正切值,再取到了屏幕相對于左眼的正切角荆萤,相比取了一個最小值镊靴,可以這樣理解,如果手機屏幕比正常視野大了链韭,那么我們看不全整個屏幕偏竟,為了有更好的體驗,需要以視野作為標準敞峭,當視野比屏幕大了踊谋,當然是以手機屏幕作為標準更好一些。

以上函數完成了以后儡陨,rect的值產生了變化褪子,接下來就需要應用這些值,leftEyeDistortedProjection = MakeProjection(rect[0], rect[1], rect[2], rect[3], 1, 1000);計算投影矩陣骗村,因為上一步嫌褪,我們做了distort變換,可以把distort理解為經過了透鏡的光線折射胚股,所以投影矩陣的結果應該也是distort的笼痛,camera呈現的畫面就會是這樣
[圖片上傳中。。缨伊。(7)]
當無透鏡的時候Profile.GetLeftEyeNoLensTanAngles(ref rect);leftEyeUndistortedProjection = MakeProjection(rect[0], rect[1], rect[2], rect[3], 1, 1000);會做一個inverse distort摘刑,即distort的逆變換,得到的自然是無透鏡的情況
[圖片上傳中刻坊。枷恕。。(8)]
直到這里谭胚,我們說的都是Camera的Projection徐块,他跟viewport是有區(qū)別的,后面執(zhí)行的
leftEyeUndistortedViewport = Profile.GetLeftEyeVisibleScreenRect(rect);leftEyeDistortedViewport = leftEyeUndistortedViewport;
處理的是camera的viewport灾而,即在屏幕上劃定一個矩形胡控,camera捕捉到的畫面都將在矩形中顯示,這在游戲分屏技術中用的很多(比如兩個玩家對抗旁趟,在一個屏幕中顯示兩個畫面)昼激,當然viewport的劃定與是否需要distort無關,這樣leftEyeDistortedViewport與leftEyeUndistortedViewport 應該是一個值锡搜。

/****************************************************分割線************************************************************/

不重要:
public virtual bool SupportsNativeDistortionCorrection(List<string> diagnostics)
//一個用來判斷是否支持NativeDistortionCorrection的方法橙困,需要滿足兩個條件,支持RenderTexture和Unity4.5版本以上余爆,才能支持透鏡扭曲的效果

public virtual bool SupportsNativeUILayer(List<string> diagnostics)
//同理纷宇,但是這個NativeUILayer僅需要Unity4.5以上版本一個條件

public abstract void SetDistortionCorrectionEnabled(bool enabled);
public abstract void SetVRModeEnabled(bool enabled);
public abstract void SetAlignmentMarkerEnabled(bool enabled);
public abstract void SetSettingsButtonEnabled(bool enabled);
public abstract void SetNeckModelScale(float scale);
public abstract void SetAutoDriftCorrectionEnabled(bool enabled);
public abstract void SetStereoScreen(RenderTexture stereoScreen);
//一堆抽象方法,后面會重寫的蛾方,現在先放著吧
public abstract void UpdateState();
public abstract void UpdateScreenData();
public abstract void Recenter();
public abstract void PostRender(bool vrMode);
//又來四個抽象方法像捶,從字面意思上看,是狀態(tài)更新桩砰,屏幕數據更新拓春,重新定位中心,完成渲染后

public virtual void OnPause(bool pause)
//當停止了亚隅,就不再UpdateScreenData()
public virtual void OnFocus(bool focus)
public virtual void Reset()
public virtual void OnApplicationQuit()
public virtual void Destroy()
//由字面意思就可以理解

OKE鹈А!BaseVRDevice就看到這里煮纵,這個類中還是有比較關鍵的函數的懂鸵,后面會繼續(xù)看子類的代碼,有些地方比如怎樣去計算Projection的44的矩陣行疏,投影為什么要用44矩陣可能涉及到數學的東西了匆光,我也不太明白,只能是看懂了以后再作介紹酿联。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末终息,一起剝皮案震驚了整個濱河市夺巩,隨后出現的幾起案子,更是在濱河造成了極大的恐慌周崭,老刑警劉巖柳譬,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異续镇,居然都是意外死亡美澳,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進店門磨取,熙熙樓的掌柜王于貴愁眉苦臉地迎上來人柿,“玉大人,你說我怎么就攤上這事忙厌。” “怎么了江咳?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵逢净,是天一觀的道長。 經常有香客問我歼指,道長爹土,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任踩身,我火速辦了婚禮胀茵,結果婚禮上,老公的妹妹穿的比我還像新娘挟阻。我一直安慰自己琼娘,他們只是感情好,可當我...
    茶點故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布附鸽。 她就那樣靜靜地躺著脱拼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪坷备。 梳的紋絲不亂的頭發(fā)上熄浓,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天,我揣著相機與錄音省撑,去河邊找鬼赌蔑。 笑死,一個胖子當著我的面吹牛竟秫,可吹牛的內容都是我干的娃惯。 我是一名探鬼主播,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼鸿摇,長吁一口氣:“原來是場噩夢啊……” “哼石景!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤潮孽,失蹤者是張志新(化名)和其女友劉穎揪荣,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體往史,經...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡仗颈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了椎例。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挨决。...
    茶點故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖订歪,靈堂內的尸體忽然破棺而出脖祈,到底是詐尸還是另有隱情,我是刑警寧澤刷晋,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布盖高,位于F島的核電站,受9級特大地震影響眼虱,放射性物質發(fā)生泄漏喻奥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一捏悬、第九天 我趴在偏房一處隱蔽的房頂上張望撞蚕。 院中可真熱鬧,春花似錦过牙、人聲如沸甥厦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽矫渔。三九已至,卻和暖如春摧莽,著一層夾襖步出監(jiān)牢的瞬間庙洼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工镊辕, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留油够,地道東北人。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓征懈,卻偏偏與公主長得像石咬,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子卖哎,可洞房花燭夜當晚...
    茶點故事閱讀 43,543評論 2 349

推薦閱讀更多精彩內容