写给VR手游开发小白的教程:(三)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设备必须要实现的一些方法,但我们不必关注是怎样实现的,这些方法会在子类里面根据不同的平台(iOSAndroid)重写,给出详细的实现步骤。
我们把重要的和不重要的归个类
重要:
public virtual RenderTexture CreateStereoScreen()
//Creating new default cardboard screen texture.为Cardboard屏幕生成一个新的默认RenderTexture,RenderTexture是一个实时更新的纹理,动态的,上节说过

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);
//又来四个抽象方法,从字面意思上看,是状态更新,屏幕数据更新,重新定位中心,完成渲染后

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()//这个函数很关键!!!官方打的注释是// 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这个矩阵里面是存储了眼睛的位置信息的,后续看了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是有区别的,后面执行的
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);
//又来四个抽象方法,从字面意思上看,是状态更新,屏幕数据更新,重新定位中心,完成渲染后

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()
//由字面意思就可以理解

OK!!BaseVRDevice就看到这里,这个类中还是有比较关键的函数的,后面会继续看子类的代码,有些地方比如怎样去计算Projection的44的矩阵,投影为什么要用44矩阵可能涉及到数学的东西了,我也不太明白,只能是看懂了以后再作介绍。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容