zoukankan      html  css  js  c++  java
  • 《VR入门系列教程》之18---Oculus代码剖析

    代码剖析

    原文作者:Tony Parisi

        那么,Unity究竟是如何支持Oculus VR运行的?首先,我们来看看Unity场景是如何构建的。在Unity集成开发包中有一个相机预设体,这个预设体提供了最基本的VR技术,包括:Oculus的立体渲染和头动追踪,下面我们来具体操作一下。

        在Hierarchy面板中定位到OVRCameraRig物体,然后我们点击它左边的向下箭头展开它的子物体,Camera Rig中包含一个叫TrackingSpace的子物体,TrackingSpace下面包含:LeftEyeAnchor,CenterEyeAnchor,RightEyeAnchor和TrackerAnchor四个子物体。其中,Left和Right Anchor 是关键所在,它们分别带有一个相机,用来分别渲染左右眼视图。这两个相机在Inspector中的参数都是默认值,它们的参数会在程序运行的时候有所改变。

        我们再次定位到OVRCameraRig物体,我们双击它上面带的一个叫OVRCameraRig的脚本组件,Unity的编辑器会用MonoDevelop为我们打开一个叫OVRCameraRig.cs的脚本源代码。

        在Monodevelop中搜索源代码,找到LateUpdate函数,如下:
    1. #if !UNITY_ANDROID || UNITY_EDITOR  
    2. privatevoid LateUpdate()  
    3. #else  
    4. privatevoid Update()  
    5. #endif  
    6. {  
    7.     EnsureGameObjectIntegrity();  
    8.     if(!Application.isPlaying)  
    9.         return;  
    10.     UpdateCameras();  
    11.     UpdateAnchors();  
    12. }  
    #if !UNITY_ANDROID || UNITY_EDITOR
    privatevoid LateUpdate()
    #else
    privatevoid Update()
    #endif
    {
        EnsureGameObjectIntegrity();
        if(!Application.isPlaying)
            return;
        UpdateCameras();
        UpdateAnchors();
    }


        我们没有在安卓上面构建,所以#if语句为真,我们使用的是LateUpdate这个函数,Unity脚本在运行的时候会不停的调用多个函数,其中就包括Update和LateUpdate函数。其中,LateUpdate函数更适合用作相机的更新,因为引擎可以确保所有更新操作执行完以后才调用LateUpdate函数,这点非常有必要,比如我们需要在更新相机前获取头动信息。

        LateUpdate函数中我们首先调用了EnsureGameObjectIntegrity这个函数,目的是为了确保场景中有我们必须的物体(即OVRCameraRig预设实例),这样可以防止脚本包含进场景但没有实例化OVRCameraRig物体。

        检查完应用是否正在运行之后,我们就开始干正事了。首先,我们调用UpdateCameras更新两个相机的参数,如下代码:
    1. privatevoid UpdateCameras()  
    2. {  
    3.     if(needsCameraConfigure)  
    4.     {  
    5.         leftEyeCamera = ConfigureCamera(OVREye.Left);  
    6.         rightEyeCamera = ConfigureCamera(OVREye.Right);  
    7. #if !UNITY_ANDROID || UNITY_EDITOR  
    8.         needsCameraConfigure = false;  
    9. #endif  
    10.     }  
    11. }  
    privatevoid UpdateCameras()
    {
        if(needsCameraConfigure)
        {
            leftEyeCamera = ConfigureCamera(OVREye.Left);
            rightEyeCamera = ConfigureCamera(OVREye.Right);
    #if !UNITY_ANDROID || UNITY_EDITOR
            needsCameraConfigure = false;
    #endif
        }
    }

         这个函数在桌面端只会调用一次,它会通过Oculus的配置资源中获取配置参数,然后通过一个标识变量告诉下一次执行的程序已经配置过了。

        下面就是ConfigureCamera函数,用来配置每个相机的参数:
    1. privateCamera ConfigureCamera(OVREye eye)  
    2. {  
    3.     Transform anchor = (eye == OVREye.Left) ? leftEyeAnchor : rightEyeAnchor;  
    4.     Camera cam = anchor.GetComponent<Camera>();  
    5.     OVRDisplay.EyeRenderDesc eyeDesc = OVRManager.display.GetEyeRenderDesc(eye);  
    6.     cam.fieldOfView = eyeDesc.fov.y;  
    7.     cam.aspect = eyeDesc.resolution.x / eyeDesc.resolution.y;  
    8.     cam.rect = newRect(0f, 0f, OVRManager.instance.virtualTextureScale, OVRManager.instance.virtualTextureScale);  
    9.     cam.targetTexture = OVRManager.display.GetEyeTexture(eye);  
    10.     cam.hdr = OVRManager.instance.hdr;  
    11.     ...  
    12.     returncam;  
    13. }  
    privateCamera ConfigureCamera(OVREye eye)
    {
        Transform anchor = (eye == OVREye.Left) ? leftEyeAnchor : rightEyeAnchor;
        Camera cam = anchor.GetComponent<Camera>();
        OVRDisplay.EyeRenderDesc eyeDesc = OVRManager.display.GetEyeRenderDesc(eye);
        cam.fieldOfView = eyeDesc.fov.y;
        cam.aspect = eyeDesc.resolution.x / eyeDesc.resolution.y;
        cam.rect = newRect(0f, 0f, OVRManager.instance.virtualTextureScale, OVRManager.instance.virtualTextureScale);
        cam.targetTexture = OVRManager.display.GetEyeTexture(eye);
        cam.hdr = OVRManager.instance.hdr;
        ...
        returncam;
    }

        其中,OVRManager类是与Oculus Mobile SDK的主要接口,它负责许多东西,包括与本地的Oculus SDK接口。如果你好奇这个脚本,你可以回到Unity编辑器中找到OVRCameraRig物体,然后在它的组件中就可以找到OVRManager这个脚本。目前为止,我们通过黑盒的方式给两个相机赋予了参数,包含:FOV、屏幕长宽比、视口、渲染目标、是否支持HDR。

        相机的基本参数已经设置好了,但是我们还是得根据HMD的信息实时调整相机位置和朝向,通过下面UpdateAnchors函数可以实现:
    1. privatevoid UpdateAnchors()  
    2. {  
    3.     boolmonoscopic = OVRManager.instance.monoscopic;  
    4.     OVRPose tracker = OVRManager.tracker.GetPose();  
    5.     OVRPose hmdLeftEye = OVRManager.display.GetEyePose(OVREye.Left);  
    6.     OVRPose hmdRightEye = OVRManager.display.GetEyePose(OVREye.Right);  
    7.     trackerAnchor.localRotation = tracker.orientation;  
    8.     centerEyeAnchor.localRotation = hmdLeftEye.orientation; // using left eye for now  
    9.     leftEyeAnchor.localRotation = monoscopic ? centerEyeAnchor.localRotation : hmdLeftEye.orientation;  
    10.     rightEyeAnchor.localRotation = monoscopic ? centerEyeAnchor.localRotation : hmdRightEye.orientation;  
    11.     trackerAnchor.localPosition = tracker.position;  
    12.     centerEyeAnchor.localPosition = 0.5f * (hmdLeftEye.position + hmdRightEye.position);  
    13.     leftEyeAnchor.localPosition = monoscopic ? centerEyeAnchor.localPosition : hmdLeftEye.position;  
    14.     rightEyeAnchor.localPosition = monoscopic ? centerEyeAnchor.localPosition : hmdRightEye.position;  
    15.     if(UpdatedAnchors != null)  
    16.     {  
    17.         UpdatedAnchors(this);  
    18.     }  
    19. }  
    privatevoid UpdateAnchors()
    {
        boolmonoscopic = OVRManager.instance.monoscopic;
        OVRPose tracker = OVRManager.tracker.GetPose();
        OVRPose hmdLeftEye = OVRManager.display.GetEyePose(OVREye.Left);
        OVRPose hmdRightEye = OVRManager.display.GetEyePose(OVREye.Right);
        trackerAnchor.localRotation = tracker.orientation;
        centerEyeAnchor.localRotation = hmdLeftEye.orientation; // using left eye for now
        leftEyeAnchor.localRotation = monoscopic ? centerEyeAnchor.localRotation : hmdLeftEye.orientation;
        rightEyeAnchor.localRotation = monoscopic ? centerEyeAnchor.localRotation : hmdRightEye.orientation;
        trackerAnchor.localPosition = tracker.position;
        centerEyeAnchor.localPosition = 0.5f * (hmdLeftEye.position + hmdRightEye.position);
        leftEyeAnchor.localPosition = monoscopic ? centerEyeAnchor.localPosition : hmdLeftEye.position;
        rightEyeAnchor.localPosition = monoscopic ? centerEyeAnchor.localPosition : hmdRightEye.position;
        if(UpdatedAnchors != null)
        {
            UpdatedAnchors(this);
        }
    }

        这个函数通过OVRTracker和OVRDisplay获取到了HMD当前的位置和朝向,之后又赋值给了相应的组件。左右眼Anchor负责真正的渲染,中心Anchor作为一个标记存在,这样方便应用查找中心位置而不是重新计算,tracker变量负责保存位置追踪的信息。
        至此,我们仅仅是添加了一个预设的实例,就已经实现了Oculus Rift 立体渲染和位置追踪功能。虽然这个预设有点负责,但是,我们仔细深入研究还是会找到奥秘所在。
  • 相关阅读:
    Reaper自定义模板
    c#3.0 特性
    C#中下载文件出现410错误。
    使用Create task with ContentType创建任务的时候,必须先在task list中加上该ContentType
    tsmmc.msc 远程桌面
    工作流的ReplicatorActivity
    关于Windows2003的远程桌面链接数量。
    【手绘】A old painting ,drawed in middle school ,grade 8
    【Notepad++】Notepad ++ plugin Compare
    【资讯】Fight for this goal ,and better than this~
  • 原文地址:https://www.cnblogs.com/haxianhe/p/9271163.html
Copyright © 2011-2022 走看看