操作系统:Windows8.1

显卡:Nivida GTX965M

开发工具:Android studio 3.0.0 | Cardboard 1.0


使用 Google Cardboard 开发VR应用,会用到其中的几个功能,其中一个便是头部跟踪,即HeadTracker。接下来几个章节将会逐一分析 Cardboard 头部跟踪的具体设计和实现。考虑新版本的SDK已经不再提供源代码的支持,故采用比较老的版本对源代码进行分析说明。

Overview Of HeadTracker


有关头部跟踪的代码均在 com.google.vrtoolkit.cardboard.sensors 包下。SDK通过 HeadTracker 对外提供服务,该类内部通过 DeviceSensorLooper 完成对Android系统的 SensorManager 的绑定、监听工作。通过 OrientationEKF 完成旋转角度的转换工作,其中涉及到应用扩展卡尔曼滤波及罗德里格旋转公式,该部分会在后面章节用到的时候逐一介绍。首先通过一张图,看一下 HeadTrackerDeviceSensorLooper 的关系结构。

从类结构来看 HeadTracker 实现了Android系统的 SensorEventListener 接口,该接口提供了必要的功能定义,用于监听系统对应的传感器的回调。换句话说 HeadTracker 将会作为监听器间接的或者直接的用以注册到系统的 SensorManager 中,获取源源不断的传感器数据。下面分别介绍几个主要的 PublicAPI 。

1. createFromContext(Context) 根据传递的Context创建 HeadTracker Instance:

public static HeadTracker createFromContext(final Context context) {final SensorManager sensorManager = (SensorManager)context.getSystemService("sensor");final Display display = ((WindowManager)context.getSystemService("window")).getDefaultDisplay();return new HeadTracker(new DeviceSensorLooper(sensorManager), new SystemClock(), display);}

该函数获取系统的 SensorManager,如之前所述用于注册监听器获取传感器数据。接下来通过 WindowManager 获取默认的 Display 对象,用于判断当前设备屏幕的旋转朝向。接着创建 DeviceSensorLooper 作为一个中间层内部启用了一个线程维护 SensorEventListenerSensorManager 的交互操作及外部控制逻辑,并将之前获取的 sesorManager 作为构造参数使用。最后实例化的是 SystomClock,该实例用于获得系统精确的 nano time 参与计算旋转角度。

2. onSensorChanged(SensorEvent) 将获取的传感器数据缓存,并传递给 OrientationEKF 进行处理以获得更准确的旋转数据。值得注意的是头部跟踪相关的传感器类型为应加速度计 Acc 和陀螺仪 Gyro。

public void onSensorChanged(final SensorEvent event) {if (event.sensor.getType() == 1) {this.mLatestAcc.set(event.values[0], event.values[1], event.values[2]);this.mTracker.processAcc(this.mLatestAcc, event.timestamp);}else if (event.sensor.getType() == 4) {this.mLatestGyroEventClockTimeNs = this.mClock.nanoTime();this.mLatestGyro.set(event.values[0], event.values[1], event.values[2]);Vector3d.sub(this.mLatestGyro, this.mGyroBias, this.mLatestGyro);this.mTracker.processGyro(this.mLatestGyro, event.timestamp);}}

3. startTracking() 开启头部跟踪功能。

public void startTracking() {if (this.mTracking) {return;}this.mTracker.reset();this.mSensorEventProvider.registerListener((SensorEventListener)this);this.mSensorEventProvider.start();this.mTracking = true;}

重置 OrientationEKF 对象,向 SensorEventProvider 接口的实现类 DeviceSensorLooper 对象注册 HeadTracker 实例本身。除此之外开启 DeviceSensorLooper 线程,最后设置上下文 mIsRunningtrue

4. stopTracking() 关闭头部跟踪功能。

@Overridepublic void stop() {if (!this.mIsRunning) {return;}this.mSensorManager.unregisterListener(this.mSensorEventListener);this.mSensorEventListener = null;this.mSensorLooper.quit();this.mSensorLooper = null;this.mIsRunning = false;}

从传感器管理器移除所有的监听器,停止监听。并结束传感器线程,设置上下文 mIsRunningfalse

5. setGyroBias(float[]) 设置陀螺仪Gyro的偏好数据,对于头部跟踪的最小原型,该函数是非必要的。

public void setGyroBias(final float[] gyroBias) {if (gyroBias == null) {this.mGyroBias.setZero();return;}if (gyroBias.length != 3) {throw new IllegalArgumentException("Gyro bias should be an array of 3 values");}this.mGyroBias.set(gyroBias[0], gyroBias[1], gyroBias[2]);}

6. setNeckModelEnabled(boolean) 开启颈部模型偏移数据。

public void setNeckModelEnabled(final boolean enabled) {this.mNeckModelEnabled = enabled;}

关于该函数没什么好说的,该功能默认是关闭的,如果开启需要留意类中定义了颈部模型的offset补偿的参数:

private static final float DEFAULT_NECK_HORIZONTAL_OFFSET = 0.08f;
private static final float DEFAULT_NECK_VERTICAL_OFFSET = 0.075f;
private static final boolean DEFAULT_NECK_MODEL_ENABLED = false;

Details of DeviceSensorLooper


在介绍 getLastHeadView(float[], int) 函数之前,先看一下 DeviceSensorLooper 的设计与实现,以便更好的理解后续的逻辑。该类的初始化阶段定义了感兴趣的传感器类型:

static {INPUT_SENSORS = new int[] { 1, 4 };}

1. constructor(SensorManager) 现在看一下构造函数的定义实现:保存 HeadTracker 传递进来的系统 SensorManager 实例引用,用于注册监听器所使用。

public DeviceSensorLooper(final SensorManager sensorManager) {super();this.mRegisteredListeners = new ArrayList<SensorEventListener>();this.mSensorManager = sensorManager;}

2. start() 开启独立工作线程,监听传感器传递的数据,并向注册的监听器回调获取到的数据,可以看到在内部类中引用了监听器集合 mRegisteredListeners,会遍历所有的监听器分发数据。

@Overridepublic void start() {if (this.mIsRunning) {return;}this.mSensorEventListener = (SensorEventListener)new SensorEventListener() {public void onSensorChanged(final SensorEvent event) {for (final SensorEventListener listener : DeviceSensorLooper.this.mRegisteredListeners) {synchronized (listener) {listener.onSensorChanged(event);}}}public void onAccuracyChanged(final Sensor sensor, final int accuracy) {for (final SensorEventListener listener : DeviceSensorLooper.this.mRegisteredListeners) {synchronized (listener) {listener.onAccuracyChanged(sensor, accuracy);}}}};final HandlerThread sensorThread = new HandlerThread("sensor") {protected void onLooperPrepared() {final Handler handler = new Handler(Looper.myLooper());for (final int sensorType : DeviceSensorLooper.INPUT_SENSORS) {final Sensor sensor = DeviceSensorLooper.this.mSensorManager.getDefaultSensor(sensorType);DeviceSensorLooper.this.mSensorManager.registerListener(DeviceSensorLooper.this.mSensorEventListener, sensor, 0, handler);}}};sensorThread.start();this.mSensorLooper = sensorThread.getLooper();this.mIsRunning = true;}

函数的后半部分,会实例化线程的Handler,并在Handler初始化的时候获取感兴趣的传感器对象,并最终注册代理 SensorEventListener 对象。完成后启动线程,并修改标志位为已运行状态。

2. stop() 停传感器工作线程,移除代理中间监听器对象,退出线程并修改标志位为停止运行状态。

@Overridepublic void stop() {if (!this.mIsRunning) {return;}this.mSensorManager.unregisterListener(this.mSensorEventListener);this.mSensorEventListener = null;this.mSensorLooper.quit();this.mSensorLooper = null;this.mIsRunning = false;}

3. registerListener(SensorEventListener) 向外部提供接口,用以注册外部Sensor监听器对象。

@Overridepublic void registerListener(final SensorEventListener listener) {synchronized (this.mRegisteredListeners) {this.mRegisteredListeners.add(listener);}}

4. unregisterListener(SensorEventListener) 向外部提供接口,用以注销外部Sensor监听器对象。

@Overridepublic void unregisterListener(final SensorEventListener listener) {synchronized (this.mRegisteredListeners) {this.mRegisteredListeners.remove(listener);}}

Brief summary


Cardboard SDK 为获取系统的传感器数据,定义了 DeviceSensorLooper 对象维护系统内部 SensorManager 与外部 SensorEventListener 监听器的联系,其内部开启了一个独立的worker工作线程专门负责传感器数据的获取与分发。而 HeadTracker 通过注册自身到 DeviceSensorLooper 中最终获得了有效的传感器原始数据,AccGyro

目前位置,拿到传感器原始数据后,如何计算精准的旋转矩阵并未提及。该部分将会在接下来的章节逐一讨论。

转载于:https://www.cnblogs.com/heitao/p/7750553.html

Cardboard Talk01 HeadTracker相关推荐

  1. 如何用Unity和Cardboard做一款VR游戏

    随着Oculus宣布1月6日开启预售,2016年很可能成为VR游戏元年,但很多的调研显示,手游设备才是市场增长的关键,SuperData发布的报告显示,2016年全球VR游戏市场规模预计在51亿美元左 ...

  2. 专题突破之反悔贪心——建筑抢修,Cow Coupons G, Voting (Hard Version),Cardboard Box

    文章目录 [JSOI2007]建筑抢修 [USACO12FEB]Cow Coupons G CF1251E2 Voting (Hard Version) CF436E Cardboard Box [J ...

  3. Cardboard:虚拟现实怎样在国内最容易普及

    Google Cardboard是谷歌在虚拟现实上充满谷歌式意味的反击. Facebook有Oculus Rift,索尼有Morpheus.虚拟现实这种充满未来主义又缺乏大规模应用的技术恰好是谷歌最热 ...

  4. Cardboard开发教程:使用Unity制作Cardboard全景图片浏览器

    这两年,虚拟现实(VR)领域很火,很多人认为这将会是下一个手机般改变人们生活的技术.目前全球最领先的还是Facebook旗下的Oculus,HTC VIVE,以及最流行的Cardboard.国内多家厂 ...

  5. 马云:蚂蚁最应该感谢微信;波音软件修复文件不合格;谷歌开源Cardboard|极客头条...

    整理 | 郭芮 快来收听极客头条音频版吧,智能播报由标贝科技提供技术支持. 「CSDN 极客头条」,是从 CSDN 网站延伸至官方微信公众号的特别栏目,专注于一天业界事报道.风里雨里,我们将每天为朋友 ...

  6. 开发一个最简单的Cardboard虚拟现实应用(四)做一个Cardboard第一人称控制器

    [开源互助-原创文章,转载请说明出处] 第三帖中已经创建了一个cardboard自带的demo应用,但它是不能移动的,玩家只能站在原地,通过头部转动来观察四周,除此之外,玩家并没有更多的手段与游戏场景 ...

  7. Google VR开发-Cardboard VR SDK反畸变实现

    上一篇文章分析了Cardboard SDK的生命周期设计. 这里我们看下畸变部分的实现. Cardboard中将畸变这部分封装成了一个Distortion类和DistortionRenderer类. ...

  8. Cardboard的学习(一)Cardboard的介绍

    cardboard介绍 cardboard是谷歌的两位开发工程师发明的一个廉价的硬件设备,因为价格低廉,SDK的学习门槛较低,出现之后收到了广大开发者的喜爱,很卡成为了主流的移动VR的新技术之一. C ...

  9. cardboard的使用

    本人第一次写文章勿喷!发现好多人开始写博客,我也来写写:用处就是可以方便以后记忆,那里看看就知道了.今天写个cardboard的demo.话不多说步骤如下: 1.去官网下载cardboard,网址:h ...

最新文章

  1. 2022-2028年中国电商物流行业投资分析及前景预测报告
  2. Java 设计模式之外观模式
  3. CSS 文本溢出时显示省略标记
  4. 20150103--SQL连接查询+视图-02
  5. 国内首家!平头哥宣布开源RISC-V内核MCU芯片设计平台;IBM驳斥谷歌量子霸权主张;Facebook将赔偿350亿美元……...
  6. Java线程volatile(二)
  7. 解决谷歌网站Your connection is not private问题
  8. Python关键字和标识符
  9. lstm网络一般训练多少轮_Pytorch的LSTM的理解
  10. hexo matery 相册 视频相册 相册加密 描述备注
  11. 公务员可以做哪些合法正规的兼职
  12. NanoHTTPD服务
  13. 周礼键君:中国福建省建瓯市之《闽郡八音字典》
  14. 【毕业设计】基于人脸登录的大学生快递系统
  15. Service(服务)之 Local Service(本地服务)
  16. Spring Destroying singletons ... root of factory hierarchy 问题【已解决】
  17. C-V2X 与智能车路协同技术 的深度融合
  18. 网上免费平台学习美术
  19. 【大话设计模式-2】UML 类图的绘制(源码案例分析)
  20. python处理pdf文件

热门文章

  1. 计算机系统——信息的表示与处理
  2. CREO:CREO软件之工程图界面的【创建】、【布局】、【表】、【注释】的简介(图文教程)之详细攻略
  3. Android 软键盘的确定键修改
  4. Mysql错误Error writing file ‘/home/tmp/xxxx’ (Errcode: 28)的解决方法
  5. html5怎么查看路由器状态,怎么看路由器是否正常_怎么看路由器是否联网?-192路由网...
  6. 使用反射生成 JDK 动态代理
  7. Linux服务器下载日志到本地
  8. 快应用如何接入微信支付
  9. Android Studio分析工具ApkAnalyzer基本使用
  10. centos怎么读(centos系统怎么读)