SharpNow Vidoo SDK  2.02
微动开发指南

VR支持概述

微动可以连接在VR(Virtual Reality)头盔或Android手机上以提供VR操控体验。开发者进行VR应用开发时, 可同时获取手部的定位信息与手部的增强影像,与VR场景的交互。

VR模式的安装

访问 http://www.sharpnow.com/dev 可下载微动VR开发者配件的3D打印模型文件,自制微动托架。 目前可下载适配Oculus DK2的托架方案,与较通用的VR眼镜托架方案。您也可以使用其他方法,将微动固定 于头盔或智能眼镜产品的前方。然后使用USB数据线与头盔连接,或使用OTG USB数据线与手机连接。

vrsetup.png
VR模式的安装
注解
佩戴时,设备的USB接口方向应为您的右侧。使用OTG数据线时,请确认手机端与设备端接口,请勿混淆使用。

启用并配置VR模式

VR 模式下的坐标系与空间模式不同,所以需调用 sharpnow::ConfigDeviceMode 改变后设备工作状态。 此外,为了与VR场景的渲染坐标保持一致,还需要使用 sharpnow::ConfigVR 接口让 VidooSDK 了解您的VR渲染参数,包括:
左右眼渲染的分辨率与各自的裁剪体(参数定义与OpenGL glFrustum 函数接口的含义相同):

各参数图示如下:

clip.png
裁剪参数

此外还包括:渲染设置中双眼的距离 eye_distance 与微动安装位置与投影屏幕的距离 vidoo_distance

以下范例为 Oculus DK2 默认的渲染参数设置流程:

// 设置设备工作模式
sharpnow::VRConfig config;
dc.eye_distance = 0.064f;
dc.vidoo_distance = 0.07f;
// 设置左眼裁剪参数
dc.eye_left.resolution = sharpnow::Vector2(1182.0f, 1462.0f);
dc.eye_left.clipping_near = 0.1f;
dc.eye_left.clipping_far = 1000.0f;
dc.eye_left.clipping_left = -0.105865765f;
dc.eye_left.clipping_right = 0.109236801f;
dc.eye_left.clipping_top = 0.133160317f;
dc.eye_left.clipping_bottom = -0.133160317f;
// 设置右眼裁剪参数
dc.eye_right.resolution = sharpnow::Vector2(1182.0f, 1462.0f);
dc.eye_right.clipping_near = 0.1f;
dc.eye_right.clipping_far = 1000.0f;
dc.eye_right.clipping_left = -0.109236801f;
dc.eye_right.clipping_right = 0.105865765f;
dc.eye_right.clipping_top = 0.133160317f;
dc.eye_right.clipping_bottom = -0.133160317f;

使用增强影像

使用SDK可以获取手部的增强影像,将其叠加于VR场景中,以获得更直观的交互效果。 Frame 中分别存有双眼的增强影像纹理数据指针。根据用户设置的渲染参数,SDK将 自动计算得到两组纹理坐标,开发者可以将纹理按坐标覆盖于两眼的渲染图层上。
以下的范例可用于获取并绑定两眼的增强影像,详细范例请见SampleOculus:

const sharpnow::Frame* frame = vid_sdk.GetFrameInfo();
if (frame->ar_image_left && frame->ar_image_right)
{
// 写入左眼的增强影像纹理
glBindTexture(GL_TEXTURE_2D, handTex0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, frame->ar_image_width, frame->ar_image_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, frame->ar_image_left);
// ...
// 使用左眼增强影像的纹理坐标,叠加纹理
sharpnow::Vector2 d1 = frame->tex_coord1_left;
sharpnow::Vector2 d2 = frame->tex_coord2_left;
glBegin(GL_QUADS);
glNormal3f(0, 0, 1);
glMultiTexCoord2d(GL_TEXTURE0, 0, 0);
glMultiTexCoord2d(GL_TEXTURE1, d1.x, d2.y);
glVertex2d(-1, -1);
glMultiTexCoord2d(GL_TEXTURE0, 0, 1);
glMultiTexCoord2d(GL_TEXTURE1, d1.x, d1.y);
glVertex2d(-1, 1);
glMultiTexCoord2d(GL_TEXTURE0, 1, 1);
glMultiTexCoord2d(GL_TEXTURE1, d2.x, d1.y);
glVertex2d(1, 1);
glMultiTexCoord2d(GL_TEXTURE0, 1, 0);
glMultiTexCoord2d(GL_TEXTURE1, d2.x, d2.y);
glVertex2d(1, -1);
glEnd();
// 写入右眼的增强影像纹理
glBindTexture(GL_TEXTURE_2D, handTex1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, frame->ar_image_width, frame->ar_image_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, frame->ar_image_right);
// ...
// 使用右眼增强影像的纹理坐标,叠加纹理
d1 = frame->tex_coord1_right;
d2 = frame->tex_coord2_right;
glBegin(GL_QUADS);
glNormal3f(0, 0, 1);
glMultiTexCoord2d(GL_TEXTURE0, 0, 0);
glMultiTexCoord2d(GL_TEXTURE1, d1.x, d2.y);
glVertex2d(-1, -1);
glMultiTexCoord2d(GL_TEXTURE0, 0, 1);
glMultiTexCoord2d(GL_TEXTURE1, d1.x, d1.y);
glVertex2d(-1, 1);
glMultiTexCoord2d(GL_TEXTURE0, 1, 1);
glMultiTexCoord2d(GL_TEXTURE1, d2.x, d1.y);
glVertex2d(1, 1);
glMultiTexCoord2d(GL_TEXTURE0, 1, 0);
glMultiTexCoord2d(GL_TEXTURE1, d2.x, d2.y);
glVertex2d(1, -1);
glEnd();
}

增强影像与渲染场景融合后的效果如下图所示:

arimg.png
增强影像
注解
若VR渲染参数被正确设置,所有关节点的位置应与图像相匹配。如发现错位等情况,请检查VR参数,或检查渲染坐标系是否统一。

移动端VR支持

微动可以通过OTG数据线直接与基于android系统的移动设备(手机、VR一体机、机顶盒等)连接, 并为其提供交互支持。在移动设备上使用微动,需安装微动OTG后台程序的apk安装包。其运行机制与PC上略有不同:

otg.png
移动端运行机制

使用SDK OTG的支持,请先安装androidOTG目录下的vidoo.otg.apk。微动通过OTG数据线与手机连接后,OTG后台会自动启动 (无需手动启动)。后台程序负责接收设备的数据,并通过本机的UDP端口转发给其他应用。
SDK提供了android系统下使用Java解析微动消息字串的范例工程 SampleAndroidDecode,供您参考。

此外,为了方便您的开发,SDK可以对无线传输、广播的行为在PC上进行模拟。选择后台服务的GUI模式,可以看到各网络模拟选项:

VR控件

微动SDK目前提供以下几种手势交互的控件支持:点击按钮;悬停按钮;手势按钮;拖拽式控件;滚动条。
使用时请先填写Widget结构体,并使用 sharpnow::RegisterWidget 接口注册按钮。 SDK会根据当前手势与位姿自动更新 Widget 的状态,通过 sharpnow::GetWidget 接口可获取最新状态。
若有控件不再使用,可通过 sharpnow::DeleteWidget 接口删除。

此外,使用按钮控件时,在通过接口 sharpnow::RetrieveFrame 获取每帧最新数据之前,请使用 sharpnow::UpdateHeadPose 接口更新头部当前姿态。
矩阵为 4 * 4 齐次矩阵的首指针。 数据排列如下:
m00 m01 m02 m03
m10 m11 m12 m13
m20 m21 m22 m23
m30 m31 m32 m33

其中旋转姿态为:
m00 m01 m02
m10 m11 m12
m20 m21 m22

位置的空间坐标为:
m03 m13 m23

点击按钮

注册方法

sharpnow::Widget widget;
widget.type = sharpnow::WT_Click; // 按钮类型
widget.width = 0.08f; // 按钮宽度
widget.height = 0.08f; // 按钮高度
widget.widget_on = false; // 按钮状态
widget.position = sharpnow::Vector3(-0.15f, 1.7f, -4.65f); // 初始位置
widget.rotation.Identity(); // 初始旋转
int widget_id = vid_sdk.RegisterWidget(&widget); // 注册按钮
注解
按钮的宽度与高度用于检测是否进入焦点状态,一般不小于绘制的图片尺寸,单位为米;注册返回的id可用于sharpnow::GetWidget获取按键状态。

状态转换

注解
Contact状态下,widget.pass_through 成员为按钮的穿透距离。开发者可以使用该距离绘制一定的视觉反馈,如:将按钮向前推动相同的距离;或改变按钮颜色等。

悬停按钮

注册方法

sharpnow::Widget widget;
widget.type = sharpnow::WT_Stop; // 按钮类型
widget.width = 0.08f; // 按钮宽度
widget.height = 0.08f; // 按钮高度
widget.widget_on = false; // 按钮状态
widget.position = sharpnow::Vector3(-0.15f, 1.7f, -4.65f); // 初始位置
widget.rotation.Identity(); // 初始旋转
widget.countdown = 30; // 倒计时
int widget_id = vid_sdk.RegisterWidget(&widget); // 注册按钮

状态转换

注解
Contact状态下,widget.confirm_cnt 为当前确认计数器,其数值从0 增加到100,可使用该数值绘制进度条等视觉反馈。

手势按钮

注册方法

sharpnow::Widget widget;
widget.type = sharpnow::WT_Gesture; // 按钮类型
widget.gesture = sharpnow::HG_Five; // 按钮手势
widget.widget_on = false; // 按钮状态
widget.position = sharpnow::Vector3(-0.15f, 1.7f, -4.65f); // 初始位置
widget.rotation.Identity(); // 初始旋转
widget.countdown = 30; // 倒计时
int widget_id = vid_sdk.RegisterWidget(&widget); // 注册按钮

状态转换

注解
Contact状态下,widget.confirm_cnt 为当前确认计数器,其数值从0 增加到100,可使用该数值绘制进度条等视觉反馈。

拖拽式控件

注册方法

sharpnow::Widget widget;
widget.type = sharpnow::WT_Drag; // 按钮类型
widget.width = 0.08f; // 按钮宽度
widget.height = 0.08f; // 按钮高度
widget.position = sharpnow::Vector3(-0.15f, 1.7f, -4.65f); // 初始位置
widget.rotation.Identity(); // 初始旋转
widget.countdown = 30; // 倒计时
int widget_id = vid_sdk.RegisterWidget(&widget); // 注册按钮

状态转换

注解
Contact状态下,widget.position 与 widget.rotation 将随手部移动改变。拖拽过程中,允许手部短暂离开视野,时间不得超过 widget.countdown* 20ms。

滚动条

注册方法

sharpnow::Widget widget;
widget.type = sharpnow::WT_Scrollbar; // 按钮类型
widget.width = 0.08f; // 按钮宽度
widget.height = 0.08f; // 按钮高度
widget.scrollbar_head = sharpnow::Vector3(-0.25f, 1.65f, -4.65f); // 滚动条首端位置
widget.scrollbar_tail = sharpnow::Vector3(0.05f, 1.65f, -4.65f); // 滚动条尾端位置
widget.position = widget.scrollbar_head; // 初始位置
widget.rotation.Identity(); // 初始旋转
int widget_id = vid_sdk.RegisterWidget(&widget); // 注册按钮
注解
滚动条的光标在scrollbar_head与scrollbar_tail之间移动,且始终位于首尾端点的连线之上。
光标的宽度与高度用于检测是否进入焦点状态,一般不小于绘制的图片尺寸,单位为米;注册返回的id可用于sharpnow::GetWidget获取控件状态。

状态转换

注解
Contact状态下,widget.pass_through 成员为按钮的穿透距离。开发者可以使用该距离绘制一定的视觉反馈,如:将按钮向前推动相同的距离;或改变按钮颜色等。
光标位置widget.position将随手指移动改变,并始终处于滚动条之上。
widget.scrollbar_rate为当前进度条的比例,该数值可用于进度控制(改变音量,更改视频播放进度等)。

上一章: 手部识别
下一章: 虚拟外设