OpenXR手部追踪实现详解

在虚拟现实(VR)和增强现实(AR)应用中,手部追踪技术是提高用户交互自然性的关键技术之一。本文将详细介绍如何使用OpenXR API实现手部追踪功能,包括系统属性的查询、手部追踪器的创建和手部关节的定位。

开始之前

在深入手部追踪实现之前,确保您已经有一个正确配置的OpenXR环境,包括有效的XrInstanceXrSystemIdXrSession,以及一个参考空间XrSpace。这些对象通常在应用程序初始化阶段创建,并在整个应用程序生命周期中使用。

检查手部追踪支持

首先,我们需要检查当前XR系统是否支持手部追踪。这一步骤是通过查询XrSystemProperties来完成的,该结构中嵌入了XrSystemHandTrackingPropertiesEXT以获取手部追踪相关的支持信息:

XrSystemHandTrackingPropertiesEXT handTrackingSystemProperties{
    XR_TYPE_SYSTEM_HAND_TRACKING_PROPERTIES_EXT};
XrSystemProperties systemProperties{XR_TYPE_SYSTEM_PROPERTIES,
                                    &handTrackingSystemProperties};

CHK_XR(xrGetSystemProperties(instance, systemId, &systemProperties));

if (!handTrackingSystemProperties.supportsHandTracking) {
    return;  // 系统不支持手部追踪
}

获取创建手部追踪器的函数指针

OpenXR利用扩展机制提供了手部追踪功能,我们需要通过xrGetInstanceProcAddr获取xrCreateHandTrackerEXT函数的指针,以便创建手部追踪器:

PFN_xrCreateHandTrackerEXT pfnCreateHandTrackerEXT;
CHK_XR(xrGetInstanceProcAddr(instance, "xrCreateHandTrackerEXT",
                             reinterpret_cast<PFN_xrVoidFunction*>(&pfnCreateHandTrackerEXT)));

创建手部追踪器

有了函数指针后,我们可以为左手创建一个追踪器,配置为追踪默认的手部关节集合:

XrHandTrackerEXT leftHandTracker{};
XrHandTrackerCreateInfoEXT createInfo{XR_TYPE_HAND_TRACKER_CREATE_INFO_EXT};
createInfo.hand = XR_HAND_LEFT_EXT;
createInfo.handJointSet = XR_HAND_JOINT_SET_DEFAULT_EXT;
CHK_XR(pfnCreateHandTrackerEXT(session, &createInfo, &leftHandTracker));

配置关节数据结构

在开始帧循环之前,初始化用于存储关节位置和速度数据的结构:

XrHandJointLocationEXT jointLocations[XR_HAND_JOINT_COUNT_EXT];
XrHandJointVelocityEXT jointVelocities[XR_HAND_JOINT_COUNT_EXT];

XrHandJointVelocitiesEXT velocities{XR_TYPE_HAND_JOINT_VELOCITIES_EXT};
velocities.jointCount = XR_HAND_JOINT_COUNT_EXT;
velocities.jointVelocities = jointVelocities;

XrHandJointLocationsEXT locations{XR_TYPE_HAND_JOINT_LOCATIONS_EXT};
locations.next = &velocities;
locations.jointCount = XR_HAND_JOINT_COUNT_EXT;
locations.jointLocations = jointLocations;

获取定位手部关节的函数指针

与创建手部追踪器相似,我们需要获取xrLocateHandJointsEXT函数指针:

PFN_xrLocateHandJointsEXT pfnLocateHandJointsEXT;
CHK_XR(xrGetInstanceProcAddr(instance, "xrLocateHandJointsEXT",
                             reinterpret_cast<PFN_xrVoidFunction*>(&pfnLocateHandJointsEXT)));

执行帧循环中的手部追踪

在每一帧中,使用xrLocateHandJointsEXT函数来更新手部关节的位置和速度:

while (1) {
    XrFrameState frameState; // 预先从xrWaitFrame获取
    const XrTime time = frameState.predictedDisplayTime;

    XrHandJointsLocateInfoEXT

 locateInfo{XR_TYPE_HAND_JOINTS_LOCATE_INFO_EXT};
    locateInfo.baseSpace = worldSpace;
    locateInfo.time = time;

    CHK_XR(pfnLocateHandJointsEXT(leftHandTracker, &locateInfo, &locations));

    if (locations.isActive) {
        const XrPosef &indexTipInWorld = jointLocations[XR_HAND_JOINT_INDEX_TIP_EXT].pose;
        const XrPosef &thumbTipInWorld = jointLocations[XR_HAND_JOINT_THUMB_TIP_EXT].pose;
        // 可以进一步处理关节位置和速度数据...
    }
}

完整代码

以下是完整的代码示例,展示了如何在OpenXR环境下实现手部追踪功能,包括检查手部追踪支持、创建手部追踪器、定位手部关节等:

#include <openxr/openxr.h>
#include <openxr/openxr_ext.h>

// 已经初始化的XR实例,系统ID,会话,以及一个参考空间
XrInstance instance; // XR实例
XrSystemId systemId; // 系统ID
XrSession session;   // XR会话
XrSpace worldSpace;  // 参考空间,例如从XR_REFERENCE_SPACE_TYPE_LOCAL创建

// 检查手部追踪系统属性
XrSystemHandTrackingPropertiesEXT handTrackingSystemProperties{
    XR_TYPE_SYSTEM_HAND_TRACKING_PROPERTIES_EXT}; // 手部追踪属性结构
XrSystemProperties systemProperties{XR_TYPE_SYSTEM_PROPERTIES,
                                    &handTrackingSystemProperties}; // 系统属性结构
// 获取系统属性
CHK_XR(xrGetSystemProperties(instance, systemId, &systemProperties));
// 如果系统不支持手部追踪,则不继续执行
if (!handTrackingSystemProperties.supportsHandTracking) {
    return;
}

// 获取xrCreateHandTrackerEXT函数指针
PFN_xrCreateHandTrackerEXT pfnCreateHandTrackerEXT;
CHK_XR(xrGetInstanceProcAddr(instance, "xrCreateHandTrackerEXT",
                             reinterpret_cast<PFN_xrVoidFunction*>(&pfnCreateHandTrackerEXT)));

// 创建左手的手部追踪器,追踪默认设置的手部关节
XrHandTrackerEXT leftHandTracker{};
{
    XrHandTrackerCreateInfoEXT createInfo{XR_TYPE_HAND_TRACKER_CREATE_INFO_EXT};
    createInfo.hand = XR_HAND_LEFT_EXT; // 设置为左手
    createInfo.handJointSet = XR_HAND_JOINT_SET_DEFAULT_EXT; // 关节集设置为默认
    CHK_XR(pfnCreateHandTrackerEXT(session, &createInfo, &leftHandTracker));
}

// 在帧循环开始前分配缓冲区以接收关节位置和速度数据
XrHandJointLocationEXT jointLocations[XR_HAND_JOINT_COUNT_EXT]; // 关节位置数组
XrHandJointVelocityEXT jointVelocities[XR_HAND_JOINT_COUNT_EXT]; // 关节速度数组

XrHandJointVelocitiesEXT velocities{XR_TYPE_HAND_JOINT_VELOCITIES_EXT};
velocities.jointCount = XR_HAND_JOINT_COUNT_EXT;
velocities.jointVelocities = jointVelocities;

XrHandJointLocationsEXT locations{XR_TYPE_HAND_JOINT_LOCATIONS_EXT};
locations.next = &velocities; // 连接速度和位置结构
locations.jointCount = XR_HAND_JOINT_COUNT_EXT;
locations.jointLocations = jointLocations;

// 获取xrLocateHandJointsEXT函数指针
PFN_xrLocateHandJointsEXT pfnLocateHandJointsEXT;
CHK_XR(xrGetInstanceProcAddr(instance, "xrLocateHandJointsEXT",
                             reinterpret_cast<PFN_xrVoidFunction*>(&pfnLocateHandJointsEXT)));

// 开始帧循环
while (1) {
    XrFrameState frameState; // 从xrWaitFrame获取的帧状态
    const XrTime time = frameState.predictedDisplayTime; // 预测的显示时间

    XrHandJointsLocateInfoEXT locateInfo{XR_TYPE_HAND_JOINTS_LOCATE_INFO_EXT};
    locateInfo.baseSpace = worldSpace; // 设置基础空间为世界空间
    locateInfo.time = time; // 设置时间

    // 定位左手的关节
    CHK_XR(pfnLocateHandJointsEXT(leftHandTracker, &locateInfo, &locations));

    if (locations.isActive) {
        // 如果关节位置有效,则可以使用关节位置
        const XrPosef &indexTipInWorld = jointLocations[XR_HAND_JOINT_INDEX_TIP_EXT].pose; // 食指尖端位置
        const XrPosef &thumbTipInWorld = jointLocations[XR_HAND_JOINT_THUMB_TIP_EXT].pose; // 拇指尖端位置

        // 进一步处理关节位置和速度信息...
        const float indexTipRadius = jointLocations[XR_HAND_JOINT_INDEX_TIP_EXT].radius; // 食指尖端半径
        const XrHandJointVelocityEXT &indexTipVelocity = jointVelocities[XR_HAND_JOINT_INDEX_TIP_EXT]; // 食指尖端速度
    }
}

这段代码完整地演示了如何在OpenXR框架下通过扩展接口实现手部追踪。从检查系统属性是否支持手部追踪开始,到获取必要的函数指针,再到创建手部追踪器和关节的实时位置与速度更新,都是建立高交互性VR/AR应用的基础。希望这个示例能帮助开发者更好地理解和使用OpenXR进行手部追踪开发。

结语

通过以上步骤,您可以在支持OpenXR的平台上实现精确的手部追踪功能,进一步丰富您的AR/VR应用的交互体验。这种实现方式提供了高度的灵活性和扩展性,是现代XR应用开发的基石之一。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/556141.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

source map 开发优化工具

什么是 Source map 简单来说 Source map 就是一个存储信息的文件&#xff0c;里面储存着位置信息。 Source map 英文释义&#xff1a;源程序映射。 位置信息&#xff1a;转换后的代码 对应的 转换前的代码 位置映射关系。 有了 Source map&#xff0c;就算线上运行的是转换…

大型网站系统架构演化实例_2.使用缓存改善网站性能

1.使用缓存改善网站性能 网站访问的特点和现实世界的财富分配一样遵循二八定律&#xff1a;80%的业务访问集中在20%的数据上。既然大部分业务访问集中在一小部分数据上&#xff0c;那么如果把这一小部分数据缓存在内存中&#xff0c;就可以减少数据库的访问压力&#xf…

深入剖析Tomcat(三) 实现一个简易连接器

Tomcat中的servlet容器叫做Catalina&#xff0c;Catalina有两个主要模块&#xff1a;连接器与容器。在本章&#xff0c;将会建立一个连接器来增强第二章中应用程序的功能&#xff0c;用一种更好的方式来创建request与response对象。 截止文章编写日期&#xff0c;servlet规范已…

docker 启动时报错

docker 启动时报如下错误 Job for docker.service failed because the control process exited with error code. See "systemctl status docker.service" and "journalctl -xe" for details 因为安装docker时添加了镜像源 解决方案&#xff1a; mv /etc/…

学习部分排序,插入排序,冒泡排序以及希尔排序

1.插入排序 <1>.首先我们举个例子 我们要把6进行前面的插入&#xff0c;那我们要进行比较&#xff0c;首先确定一个end的指针&#xff0c;然后他指向的数字就是我们需要比较的&#xff0c;如果end指向的数比我们end1 的大的话&#xff0c;那我们就往前挪一个&#xff0c…

ElasticSearch虚拟机安装(单机版)

1.下载7.10.2 下载链接&#xff0c;选择LINUX X86_64下载 2.创建用户 useradd es也可以使用系统默认用户&#xff08;非root&#xff09;,root用户会报错 3.解压 tar xvf elasticsearch-7.10.2-linux-x86_64.tar.gz假定目录在/home/es/elasticsearch-7.10.2-linux-x86_64 …

读所罗门的密码笔记21_读后总结与感想兼导读

1. 基本信息 所罗门的密码&#xff1a;AI时代的价值、权力与信任 Solomons Code 奥拉夫格罗思 马克尼兹伯格 著 中信出版社,2022年5月出版 1.1. 读薄率 书籍总字数257千字&#xff0c;笔记总字数37780字。 读薄率37780257000≈14.7% 1.2. 读厚方向 千脑智能 脑机穿越 …

Java垃圾回收1

1.对象什么时候可以被垃圾器回收 1.垃圾回收的概念 为了让程序员更专注于代码的实现&#xff0c;而不用过多的考虑内存释放的问题&#xff0c;所以&#xff0c; 在Java语言中&#xff0c;有了自动的垃圾回收机制&#xff0c;也就是我们熟悉的GC(Garbage Collection)。 有了垃圾…

python中的守护进程、僵尸进程、孤儿进程

继续上一篇文章的探讨&#xff1a;https://blog.csdn.net/weixin_39743356/article/details/137885419 守护进程 守护进程&#xff08;Daemon Process&#xff09;是一种在后台运行的特殊类型的进程&#xff0c;它独立于控制终端&#xff0c;并且周期性地执行某种任务或等待处…

本地部署 Meta Llama3-8b 和 Llama3-70b

本地部署 Meta Llama3-8b 和 Llama3-70b 0. 引言1. Meta对Llama 3的目标2. Llama 3的性能3. 下载和安装 Ollama4. 使用 Ollama 运行 Llama3 0. 引言 今天&#xff0c;Meta 正式介绍Meta Llama 3&#xff0c;Meta 开源大型语言模型的下一代产品。 这次发布包括具有80亿&#xf…

数据可视化(四):Pandas技术的高级操作案例,豆瓣电影数据也能轻松分析!

Tips&#xff1a;"分享是快乐的源泉&#x1f4a7;&#xff0c;在我的博客里&#xff0c;不仅有知识的海洋&#x1f30a;&#xff0c;还有满满的正能量加持&#x1f4aa;&#xff0c;快来和我一起分享这份快乐吧&#x1f60a;&#xff01; 喜欢我的博客的话&#xff0c;记得…

typecho博客的相对地址实现

typecho其中的博客地址,必须写上绝对地址,否则在迁移网址的时候会出现问题,例如页面记载异常 修改其中的 typecho\var\Widget\Options\General.php 中的165行左右, /** 站点地址 */if (!defined(__TYPECHO_SITE_URL__)) {$siteUrl new Form\Element\Text(siteUrl,null,$this-…

Tomcat和Spring Boot配置https

生成测试证书 生成证书前&#xff0c;先验证本地是否正确配置jdk环境变量&#xff0c;如果jdk环境变量配置正确&#xff0c;在命令行程序输入生成证书的命令。 keytool -genkey -alias tomcat -keyalg RSA -keystore "F:\job\apache-tomcat-8.5.29\key\freeHttps.keysto…

MySQL模糊查询

一、MySQL通配符模糊查询(%&#xff0c;_) 1.1.通配符的分类 1.“%”百分号通配符&#xff1a;表示任何字符出现任意次数&#xff08;可以是0次&#xff09; 2.“_”下划线通配符&#xff1a;表示只能匹配单个字符&#xff0c;不能多也不能少&#xff0c;就是一个字符。当然…

Yoshua Bengio独家专访:我不想把大模型未来押注在Scaling Law上,AGI路上要“注意安全”...

导读 漫长的30年间&#xff0c;数度从主流方向的超然出走&#xff0c;是Bengio的制胜秘诀。这种不盲从主流的风格体现在他研究生涯的方方面面。 90年代末期&#xff0c;神经网络被打入冷宫&#xff0c;Bengio的论文多次遭拒&#xff0c;连学生们也开始担心&#xff0c;和他一起…

EPSON晶振应用到汽车电子产品上的型号有哪些?

EPSON品牌应用在汽车电子产品上的晶振.&#xff0c;当然也少不了晶振可能最熟悉的就是32.768K系列和26MHZGPS晶振用的多。 在汽车里每一个部件都应有的不一样,甚至多次使用到同一尺寸,不同频率的晶振.爱普生品牌晶振型号就有几百种,很容易混淆,要想记住汽车里所应用到的不是件…

⑥【Shiro】使多个自定义Realm规则生效。

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ ⑥【Shiro】Shiro中&#xff0c;如何使多个自定…

DevOps(七)Jenkins发布第一个流水线任务

Jenkins的流水线&#xff08;Pipeline&#xff09;是一种强大的工具&#xff0c;用于定义和管理持续集成和持续交付&#xff08;CI/CD&#xff09;过程。它允许你以代码的形式&#xff08;即"Pipeline as Code"&#xff09;定义整个构建、测试和部署流程&#xff0c;…

UE4 拍摄、保存并浏览相册

效果&#xff1a; 1.新建CameraActor类 2.修改截图保存路径 3.编写BP_Camera蓝图 注意路径 Save Image函数要在执行拍照和BeginPlay事件执行一次 按钮执行拍摄事件 3.编写UMG蓝图 技巧&#xff1a;让Index加1、减1循环赋值 4.把BP_Camera挂在玩家上

《QT实用小工具·三十》基于QT开发的访客管理平台demo

1、概述 源码放在文章末尾 该项目为访客管理平台demo&#xff0c;包含主界面、系统设置、警情查询、调试帮助、用户退出功能。 项目部分代码如下&#xff1a; #ifndef QTHELPER_H #define QTHELPER_H#include "head.h"class QtHelper { public://获取所有屏幕区域…
最新文章