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


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

相关链接:【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.mfbz.cn/a/888970.html

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

相关文章

FiBiNET模型实现推荐算法

1. 项目简介 A031-FiBiNET模型项目是一个基于深度学习的推荐系统算法实现,旨在提升推荐系统的性能和精度。该项目的背景源于当今互联网平台中,推荐算法在电商、社交、内容分发等领域的广泛应用。推荐系统通过分析用户的历史行为和兴趣偏好,预…

Linux平台Kafka高可用集群部署全攻略

🐇明明跟你说过:个人主页 🏅个人专栏:《大数据前沿:技术与应用并进》🏅 🔖行路有良友,便是天堂🔖 目录 一、引言 1、Kafka简介 2、Kafka核心优势 二、环境准备 1…

【学习笔记】SquareLine Studio安装教程(LVGL官方工具)

一.简介与导航: SquareLine Studio是由LVGL官方开发的一款UI设计工具,采用图形化进行界面UI设计,轻易上手。 SquareLine Studio官方网址:https://squareline.io/SquareLine Studio官方文档:https://docs.squareline.io…

上传本地项目到GitHub远程仓库(极简洁操作版)

第一步:在GitHub创建一个空的仓库 第二步:将仓库克隆(下载)到本地 第三步:将你要上传的所有文件放到这个克隆的仓库文件夹中 第四步:通过git add .将待上传文件添加到暂存区 此时,可以通过git …

css3-----2D转换、动画

2D 转换(transform) 转换(transform)是CSS3中具有颠覆性的特征之一,可以实现元素的位移、旋转、缩放等效果 移动:translate旋转:rotate缩放:scale 二维坐标系 2D 转换之移动 trans…

SpringBoot项目打成jar包,在其他项目中引用

1、首先新建一个SpringBoot工程 记得要将Gradle换成Maven 2、新建一个要引用的方法 3、打包的时候要注意: ① 不能使用springboot项目自带的打包插件进行打包,下面是自带的: ②要换成传统项目的maven打包,如下图: 依…

《网络安全自学教程》- Nmap使用及扫描原理分析

《网络安全自学教程》 Nmap(Network Mapper)是一款免费的开源网络扫描器,向目标主机发送特定的数据包,根据返回的流量特征,分析主机信息。主要功能有:「端口扫描」、「主机探测」、「服务识别」和「系统识别…

(接口测试)接口测试理论 http理论 接口测试流程 接口文档解析

一.接口测试理论 1.接口和接口测试 服务器为客户端开了一个验证接口(接口本质:函数方法)客户端向服务器传送的消息可以相当于函数的参数,接口是用来让客户端传递数据的 接口:相当于开了一个通道 当服务器要给客户端响…

笔记整理—linux进程部分(6)进程间通信、alarm和pause

两个进程间通信可能是任何两个进程间的通信(IPC)。同一个进程是在同一块地址空间中的,在不同的函数与文件以变量进程传递,也可通过形参传递。2个不同进程处于不同的地址空间,要互相通信有难度(内存隔离的原…

Awaken Likho恶意组织利用高级网络工具对俄罗斯政府发起“猛攻”

近日,俄罗斯政府机构和工业实体遭遇了一场名为“ Awaken Likho ”的网络活动攻击活动。 卡巴斯基表示,攻击者现在更倾向于使用合法MeshCentral平台的代理,而不是他们之前用来获得系统远程访问权限的UltraVNC模块。这家俄罗斯网络安全公司详细…

Golang | Leetcode Golang题解之第457题环形数组是否存在循环

题目: 题解: func circularArrayLoop(nums []int) bool {n : len(nums)next : func(cur int) int {return ((curnums[cur])%n n) % n // 保证返回值在 [0,n) 中}for i, num : range nums {if num 0 {continue}slow, fast : i, next(i)// 判断非零且方…

docker简述

1.安装dockers,配置docker软件仓库 安装,可能需要开代理,这里我提前使用了下好的包安装 启动docker systemctl enable --now docker查看是否安装成功 2.简单命令 拉取镜像,也可以提前下载使用以下命令上传 docker load -i imag…

大数据毕业设计选题推荐-B站热门视频数据分析-Python数据可视化-Hive-Hadoop-Spark

✨作者主页:IT研究室✨ 个人简介:曾从事计算机专业培训教学,擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

使用C语言获取iostat中的await值的方法和方案

使用C语言获取iostat中的await值的方法和方案 1. 准备工作2. 调用iostat命令并获取输出3. 解析iostat输出4. 完整实现和错误处理5. 注意事项在Linux系统中,iostat命令是sysstat软件包的一部分,用于监控系统的CPU、网卡、tty设备、磁盘、CD-ROM等设备的活动情况和负载信息。其…

鸿蒙OS投票机制

(基于openharmony5.0) 投票机制 param get | grep ohos.boot.time 图 投票机制参数图 只有当所有的投票完成,开机动画才会退出,整理需要投票的系统应用(三方应用不参与投票)如下图所示: 以进程foundation为例&…

基于Kafka2.1解读Producer原理

文章目录 前言一、Kafka Producer是什么?二、主要组件1.Kafka Producer1.1 partitioner1.2 keySerializer1.3 valueSerializer1.4 accumulator1.5 sender 2.Sender2.1 acks2.2 clientinFlightBatches 3. Selector3.1 nioSelector3.2 channels 4. 全局总览 总结 前言…

Arduino UNO R3自学笔记20 之 Arduino如何测定电机速度?

注意:学习和写作过程中,部分资料搜集于互联网,如有侵权请联系删除。 前言:在学习了Arduino的相关基础知识后,现在做个综合应用,给旋转的电机测速。 1.实验目的 测定旋转电机的转速。 2.实验器材-编码器 …

【hot100-java】二叉树的最近公共祖先

二叉树篇 我觉得是比两个节点的深度,取min(一种情况) DFS解题。 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode(int x) { val x; }* }*/ clas…

力扣题11~15

题11(中等): 思路: 这种题目第一眼就是双循环,但是肯定不行滴,o(n^2)这种肯定超时,很难接受。 所以要另辟蹊径,我们先用俩指针(标志位)在最左端和最右端&am…

基于SpringBoot智能垃圾分类系统【附源码】

基于SpringBoot智能垃圾分类系统 效果如下: 系统首页界面 用户注册界面 垃圾站点页面 商品兑换页面 管理员登录界面 垃圾投放界面 物业登录界面 物业功能界图 研究背景 随着城市化进程的加速,生活垃圾的产量急剧增加,传统的垃圾分类方式已…