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


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

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


手势优化

平移手势优化

当前存在的问题

直接采用安卓提供的onScroll方法,能够获取到distanceX\Y,而不同设备的屏幕密度不一致,会导致distanceX\Y的值都会有差异。

此外,在AR场景中,不同手机平板的相机的内参(相机焦距、感光元件尺寸等)都不同,会导致相机的视场角不同。

总之,这些都会导致无法实现"指哪打哪"的效果。

优化后的效果

"指哪打哪"的效果

解决思路

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

需要注意的是,屏幕坐标是二维的,空间坐标是三维的。

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

  • 采用指定距离

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

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

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

代码如下:

java 复制代码
//向量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-源码定位

java 复制代码
    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的叉乘求旋转轴。

叉乘计算:

java 复制代码
  /**
   * 叉乘
   * @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记录

java 复制代码
        //单指实现旋转(计算旋转轴和旋转角度)
        //原理:
        //上一触摸点转为空间坐标点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仓库

相关推荐
xhyyvr4 小时前
VR廉洁教|VR廉政体验馆|VR廉政信息综合平台
vr
xhyyvr1 天前
VR青少年法治教育综合信息平台:青少年法治探险小课堂
vr·vr法治教育·vr法治体验馆·vr法治展厅·vr预防诈骗
2401_858286111 天前
OS39.5.【Linux】分析ar命令生成的归档文件的格式
linux·ar·unix
九影网络1 天前
虚实游戏怎么选?详解AR与VR游戏的核心区别
游戏·ar·vr
ar01231 天前
AR远程协助方案白皮书
ar
BFT白芙堂1 天前
Franka Research 3 进阶应用:基于神经网络的 ORACLE 交互控制策略深度解析
人工智能·深度学习·神经网络·oracle·机器人·人机交互·vr
凯禾瑞华养老实训室2 天前
产教融合新抓手:智慧健康养老服务与管理实训室报价及人才培育路径
大数据·人工智能·物联网·ar·vr·智慧健康养老服务与管理
Axis tech2 天前
MIT沉浸式实验室如何使用MANUS手套实现远程VR神经外科培训
科技·vr
ar01232 天前
什么是AR远程协助?当前AR远程协助的应用场景有哪些?
人工智能·ar
zhz52142 天前
代码之恋(第十四篇:分叉的路径与意外的Push)
ai·重构·机器人·vr·伴侣机器人