代码剖析


原文作者: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 立体渲染和位置追踪功能。虽然这个预设有点负责,但是,我们仔细深入研究还是会找到奥秘所在。

《VR入门系列教程》之18---Oculus代码剖析的更多相关文章

  1. 《VR入门系列教程》之22---GearVR SDK代码剖析

    GearVR SDK代码剖析     接下来我们来了解一下GearVR开发包的底层代码,它底层的代码和之前在第三章中讲的桌面SDK代码非常类似,当然,也有许多不同的地方.     首先,我们看看如何构 ...

  2. 《VR入门系列教程》之19---GearVR开发初识

    本章我们来介绍一下如何在移动VR设备的佼佼者GearVR上进行开发,之前我们在桌面端的VR开发想法可以直接应用在移动端,但是仍然会有些不一样的技术需要注意.这次,我们仍然采用Unity3D引擎构建示例 ...

  3. 《VR入门系列教程》之16---第一个OculusVR应用

    第一个VR应用    之前我们已经将Oculus的开发包导入到空工程中了,现在我们来构建第一个桌面VR的示例.开发包中已经有一个示例场景,只需要几步就可以让这个场景运行起来.我们将要构建的这个Demo ...

  4. 《VR入门系列教程》之20---使用Oculus移动端SDK

    使用Oculus移动端SDK     在基于安卓系统的GearVR上开发应用需要用到Oculus的移动端SDK,下面的网址可以下载SDK:http://developer.oculus.com     ...

  5. 《VR入门系列教程》之15---配置Oculus的开发环境

    安装Oculus SDK     在使用类似Unity3D之类的引擎开发Oculus Rift应用之前,你必须先安装Oculus的SDK,在Oculus的官网上可以下载:http://develope ...

  6. 《VR入门系列教程》之14---面向大众的Unity3D

    大众化的游戏引擎--Unity3D     并不是所有VR应用都是游戏,然而现在做VR开发的几乎都会用专业游戏引擎来做,因为游戏引擎既满足了一个引擎的要求又可以方便地制作出高品质的VR应用.一个游戏引 ...

  7. 《VR入门系列教程》之10---3D图形学初识

    第三章 基于Oculus Rift开发桌面端VR应用     接下来的几个章节中我们会进行VR开发的实际操练,本章就从Oculus Rift开发开始,我们会介绍如何开发一个桌面端的VR应用.虽然只是介 ...

  8. 《VR入门系列教程》之4---运行平台

    运行平台     大多数的VR应用都可以在目前多数的PC和手机上运行,基本上一个不太旧的PC或者配置好点的笔记本电脑都可以正常运行Oculus Rift,如果手机的CPU和显卡不错的话也可以有很好的V ...

  9. 《VR入门系列教程》之3---运动追踪与输入设备

    运动追踪设备    第二种可以使人脑相信它真实处于虚拟世界的关键技术就是运动追踪技术,它可以通过追踪头部的运动状态实时更新渲染的场景.这与我们在真实世界中观看周围非常类似.    高速的惯性测量单元( ...

随机推荐

  1. 35+个实用jQuery菜单插件

    应用jQuery菜单插件可以让浏览者在浏览你的网页时获得最好的动态导航.jQuery是一个轻量级.跨浏览器的JavaScript框架(库),效果非常给力,它强调并简化了JavaScript.CSS和H ...

  2. 带你手写基于 Spring 的可插拔式 RPC 框架(一)介绍

    概述 首先这篇文章是要带大家来实现一个框架,听到框架大家可能会觉得非常高大上,其实这和我们平时写业务员代码没什么区别,但是框架是要给别人使用的,所以我们要换位思考,怎么才能让别人用着舒服,怎么样才能让 ...

  3. react 项目全家桶构件流程

    资源:create-react-app.react.react-dom.redux.react-redux.redux-thunk.react-router-dom.antd-mobile/antd. ...

  4. JavaScript非构造函数的继承( object()方法、浅拷贝与深拷贝 )

    一.什么是"非构造函数"的继承? 比如,现在有一个对象,叫做"中国人". var Chinese = { nation:'中国' }; 还有一个对象,叫做&qu ...

  5. 如何配置selinux

    参考命令:   一.开启/关闭selinux   getenforce:查看selinux运行状态 setenforce 0 :关闭selinux setenforce 1 :开启selinux 系统 ...

  6. HashMap原理(二) 扩容机制及存取原理

    我们在上一个章节<HashMap原理(一) 概念和底层架构>中讲解了HashMap的存储数据结构以及常用的概念及变量,包括capacity容量,threshold变量和loadFactor ...

  7. Codeforces 777D:Cloud of Hashtags(水题)

    http://codeforces.com/problemset/problem/777/D 题意:给出n道字符串,删除最少的字符使得s[i] <= s[i+1]. 思路:感觉比C水好多啊,大概 ...

  8. RedisDesktopManager远程连接Linux系统的Redis服务

    linux下安装redis :https://www.runoob.com/redis/redis-install.html 进入 src 运行redis   : ./redis-server 打开另 ...

  9. django基础知识之定义模板:

    定义模板 模板语言包括 变量 标签 { % 代码块 % } 过滤器 注释{# 代码或html #} 变量 语法: {{ variable }} 当模版引擎遇到一个变量,将计算这个变量,然后将结果输出 ...

  10. golang开发:类库篇(三)命令行工具cli的使用

    为什么要使用命令行 觉得这个问题不应该列出来,又觉得如果初次进行WEB开发的话,可能会觉得所有的东西都可以使用API去做,会觉得命令行没有必要. 其实,一个生产的项目命令行是绕不过去的.比如运营需要导 ...