【Sceneform-EQR】(手势优化)通过手势事件实现在AR/VR等三维场景中的控制模型旋转、平移与缩放

news/2024/10/12 16:46:28/
<article class="baidu_pl">
article_content" class="article_content clearfix">
arkdown_views prism-atom-one-light">arker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);">

在上一篇文档中,我们实现了通过手势控制模型节点的旋转、缩放和平移。现在本文将介绍如何优化上一篇做的手势控制器,从而实现更好的跟手效果。

相关链接:【Sceneform-EQR】(手势控制器实现)通过手势事件实现在AR/VR等三维场景中的控制模型旋转、平移与缩放


手势优化

平移手势优化

当前存在的问题

直接采用安卓提供的onScroll方法,能够获取到distanceX\Y,而不同设备的屏幕密度不一致,会导致distanceX\Y的值都会有差异。
此外,在AR场景中,不同手机平板的相机的内参(相机焦距、感光元件尺寸等)都不同,会导致相机的视场角不同。
总之,这些都会导致无法实现“指哪打哪”的效果。

优化后的效果

“指哪打哪”的效果

请添加图片描述

解决思路

  • 思路是屏幕坐标转空间坐标

需要注意的是,屏幕坐标是二维的,空间坐标是三维的。
这里通过屏幕坐标计算出一条射线,然后通过这条射线取指定距离的空间点或是求射线与指定平面的交点。(两种方式都可以很好的实现平移效果,但是实际上有些区别(与相机的距离不一样))

  • 采用指定距离

Sceneform-EQR提供了屏幕坐标转射线的方法camera.screenPointToRay(x,y),然后我们传入指定距离即可。

        //计算射线,再取得固定距离的空间点坐标.作为新的空间位置Vector3 newPosition = camera.screenPointToRay(screenPoint.x, screenPoint.y).getPoint(/*外部传入*/distance);
  • 采用线面相交

原理是,求射线与前方指定距离的平面的交点。用高中的立体几何知识,就不列举公式了,直接看下面代码。

代码如下:

//向量AB
Vector3 vectorAB = Vector3.subtract(b, a);
//向量AC
Vector3 vectorAC = Vector3.subtract(c, a);
//直线方向向量i
Vector3 i = Vector3.subtract(v1, v0);
//平面法向量n
Vector3 n = Vector3.cross(vectorAB,vectorAC);//点法式平面方程常数K(条件:代入A点坐标计算=>)
float constK = n.x * a.x + n.y * a.y + n.z * a.z;
//点向式直线方程常数M(条件:直线与平面相交=>)
float constM = (constK - n.x * v0.x - n.y * v0.y - n.z * v0.z)/(i.x * n.x + i.y * n.y + i.z * n.z);//D点坐标
Vector3 d=new Vector3(v0.x + i.x * constM,v0.y + i.y * constM,v0.z + i.z * constM);

EQR-源码定位

    private void onDoubleFingerScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {isDoubleFingerScroll = true;//计算当前Node的世界坐标系下的空间位置。Vector3 screenPoint = camera.worldToScreenPoint(target.getWorldPosition());screenPoint.x -= distanceX;screenPoint.y -= distanceY;//计算射线,再取得固定距离的空间点坐标.作为新的空间位置Vector3 newPosition = camera.screenPointToRay(screenPoint.x, screenPoint.y).getPoint(/*外部传入*/distance);target.setWorldPosition(newPosition);}

旋转手势优化

当前存在的问题

在AR/VR等三维场景中,若场景相机的位姿(位置、姿态)发生变化,原旋转轴的计算已不再适用了

旋转手势优化前
由上图,可发现,当前相机视角发生改变后,原来的旋转手势只能实现模型在它自身坐标系下旋转。

优化后的效果

优化后效果如下:

旋转手势优化后

解决思路

步骤

  • 通过屏幕坐标转空间坐标的方法(与平移优化中用到的一样)计算两个MotionEvent事件的屏幕坐标AB对应的空间坐标A、B。
  • 获取当前相机的在世界坐标系下的位置C。
  • 通过余弦定义求∠ACB,这个作为旋转角度。
  • 通过向量CA与向量CB的叉乘求旋转轴。

在这里插入图片描述
叉乘计算:

  /*** 叉乘* @return 垂直于两个矢量的矢量*/public static Vector3 cross(Vector3 lhs, Vector3 rhs) {float lhsX = lhs.x;float lhsY = lhs.y;float lhsZ = lhs.z;float rhsX = rhs.x;float rhsY = rhs.y;float rhsZ = rhs.z;return new Vector3(lhsY * rhsZ - lhsZ * rhsY, lhsZ * rhsX - lhsX * rhsZ, lhsX * rhsY - lhsY * rhsX);}
  • 通过计算得到的旋转角度与旋转轴构造四元数,以此做旋转

EQR-源码定位

Commit记录

  • 优化旋转手势
        //单指实现旋转(计算旋转轴和旋转角度)//原理://上一触摸点转为空间坐标点A,当前触摸点转为空间触摸点B。记当前场景相机的位置为点O//那么向量OA与向量OB的叉积则是旋转轴//转为空间坐标(屏幕坐标->射线->固定距离的点)Vector3 pointA = camera.screenPointToRay(currentEvent.getX() - distanceX,currentEvent.getY() - distanceY).getPoint(/*外部传入*/distance);Vector3 pointB = camera.screenPointToRay(currentEvent.getX(),currentEvent.getY()).getPoint(distance);Vector3 pointO = camera.getWorldPosition();Vector3 oa = Vector3.subtract(pointA, pointO);Vector3 ob = Vector3.subtract(pointB, pointO);float c = Vector3.subtract(pointA, pointB).length();float a = oa.length();float b = ob.length();float cosC = (a * a + b * b - c * c) / (2 * a * b);//旋转轴Vector3 cross = Vector3.cross(oa, ob).normalized();

查看示例demo请转至git。欢迎交流讨论!

Git仓库

  • Sceneform-EQR

http://www.ppmy.cn/news/1537937.html

相关文章

【C++】——继承(下)

【C】——继承&#xff08;下&#xff09; 5 继承与友元6 继承与静态成员7 多继承7.1 继承模型7.2 菱形继承的问题7.3 虚继承7.4 多继承中的指针偏移问题 8 组合与继承 5 继承与友元 友元关系不能被继承。即一个函数是父类的友元函数&#xff0c;但不是子类的友元函数。也就是说…

大数据-158 Apache Kylin 安装配置详解 集群模式启动

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

鼠标右键删除使用Visual Studio 打开(v)以及恢复【超详细】

鼠标右键删除使用Visual Studio 打开&#xff08;v&#xff09; 1. 引言2. 打开注册表3. 进入对应的注册表地址4. 右键删除 AnyCode 项5. 效果6. 备份注册表文件——恢复菜单 1. 引言 安装完 Visual Studio 鼠标右键总有 “使用Visual Studio 打开(v)”&#xff0c;让右键菜单…

在 Ubuntu ECS 实例上部署高性能安全的 Redis 服务指南

在现代 Web 开发中,Redis 作为一款高性能的内存数据库,被广泛应用于缓存、消息队列等场景。本文将详细介绍如何在 Ubuntu 系统的阿里云 ECS 实例上安装和配置 Redis,分配 2GB 内存,并进行全面的性能优化和安全配置,使其功能接近阿里云的商用 Redis 服务。 一、环境准备 1…

影刀RPA:Excel内容填充指令

1.实战目标 本次主要介绍影刀RPA如何操作内容相关的填充与替换指令。主要包含以下 这些指令在数据处理方面有着重要的作用&#xff0c;可以对数据做运算&#xff0c;填充&#xff0c;替换&#xff0c;实现数据格式统一&#xff0c;便于最终的数据分析。在操作的过程中&#xf…

@zabbix监控网站黑链接监控及数据推送

zabbix监控网站黑链接及数据推送 文章目录 zabbix监控网站黑链接及数据推送1.检测脚本1》编写python脚本2》脚本执行 2.数据推送1》方案一2》方案二 3.zabbix web 1.检测脚本 1》编写python脚本 创建脚本check_black_links.py&#xff0c;使用python脚本实现网站黑链接检测&a…

机械设计基本知识,做设计的一定要收藏

掌握扎实的机械设计基础知识&#xff0c;机械工程师能够在面对复杂工程挑战时&#xff0c;展现出卓越的分析能力、创新思维和决策智慧。 以下整理了机械设计基础知识相关知识点&#xff0c;希望对你有帮助。 关于3DSOURCE零件库3DSOURCE零件库由上海新迪数字技术有限公司自主研…

如何不依赖注册中心实现服务的远程调用

​ FeignClient(name "formflowprovider",url "http://ip:port/formflowprovider",configuration FeignConfig.class)​ FeignClient注解的url属性的作用&#xff1a; url 属性&#xff1a;用于指定远程服务的基础 URL 地址。通常情况下&#xff0c;F…