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

在Sceneform-EQR中实现旋转平移缩放手势

实现在AR/VR等三维场景,通过手势控制模型节点的缩放、平移和旋转。

实现思路

实现模型旋转

Sceneform-EQR(filament\opengl)中采用右手坐标系。通过欧拉角进行旋转采用Z->Y->X的顺序,在这里,我们不采用欧拉角控制旋转,而是采用四元数进行操作。因为四元数可进行插值,也方便后续对旋转操作做手势惯性。

采用四元数表示旋转
  • 实现思路:采用四元数的定义,通过旋转轴和旋转角度去构建四元数

旋转四元数相关定义可参考下面这个链接

链接:四元数:优雅而高效的空间旋转表示方法

实现思路

采样四元数表示旋转 -> 通过旋转轴与旋转角度构造四元数

现已将问题转化为,通过安卓中的MotionEvent事件,构造旋转轴和旋转角度。

  • 示意图:
  • 说明:

记:当前MotionEvent事件的触摸位置为B,上一MotionEvent事件的触摸位置为A。

那么,旋转轴与AB连线垂直,考虑AB的前进方向和右手坐标系,得出旋转轴的方向向量在AB前进方向的左侧。

因此这里,可以直接将AB向量绕点B逆时针旋转90度,得到的新向量即可表示旋转轴。

平面中,一个点(x,y)绕任意点(dx,dy)逆时针旋转a度后的坐标

xx= (x - dx)*cos(a) - (y - dy)*sin(a) + dx ;

yy= (x - dx)*sin(a) + (y - dy)*cos(a) +dy ;

这里,代入a=PI/2,dx = 向量AB的x,dy = 向量AB的y。x = 点B坐标的X,y = 点B坐标的Y。

此外,由于手机设备采用NDC坐标系,而当前渲染器采用同OpenGL的右手坐标系。

NDC坐标系的X方向 = GL的右手系的X方向

NDC坐标系的Y方向 = GL的右手系的-Y方向

因此,得出旋转轴为

复制代码
//e1为点A对应的MotionEvent事件,e2为点B对应的MotionEvent事件
 x = 0 - (e1.y - e2.y),
 y = -/*取负是由于NDC坐标系和GL坐标系的Y轴正方向相反*/(e1.x - e2.x),
 z = 0f

最后说明,安卓的GestureDetector类提供了onScroll方法,通过重写onScroll方法即可。且由于onScroll方法提供了distanceX和distanceY。

因此,最后我们可以简化成如下的样子。

复制代码
        lastRotationAxis.x = -distanceY;
        lastRotationAxis.y = -distanceX;
        lastRotationAxis.z = 0f;
源码定位

参考源码第137行

Commit记录

java 复制代码
    private void onOneFingerScroll(float distanceX, float distanceY) {
        if (isScaling || isDoubleFingerScroll)return;
        //单指实现旋转
        double distance = Math.sqrt(distanceX * distanceX + distanceY * distanceY);
        //原理:
        // 求AB两点(当前时刻的位置为B,上一时刻的位置为A)的垂线(这里是求旋转平面的法向量),
        // (将垂线的一个方向作为正方向,这里当作旋转平面的法向量)再将这个方向向量作为旋转轴。
        // 根据绕轴旋转定义四元数。
        // 这里将求垂线 => 简化成绕点旋转90度。因为求垂线后仍然需要考虑方向,而绕B点逆时针旋转90度即可得到B2。'AB2'则是目标向量
        //注意:安卓设备坐标系与OpenGL右手坐标系的XY方向。
//            dev.romainguy.kotlin.math.Float3(
//                x = 0 - (e1.y - e2.y)/* + e2.x*/,
//                y = -/*取负是由于NDC坐标系和GL坐标系的Y轴正方向相反*/(e1.x - e2.x)/* + e2.y*/,
//                z = 0f
//            )
        //改成使用distanceX/Y,简化后如下:
        lastRotationAxis.x = -distanceY;
        lastRotationAxis.y = -distanceX;
        lastRotationAxis.z = 0f;
        //todo 这里后续修改,为改用射线检测实现精准控制
        Quaternion rotation = Quaternion.axisAngle(lastRotationAxis, (float) Math.toRadians(distance * 0.168f));
        Quaternion localRotation = target.getLocalRotation();
        target.setLocalRotation(Quaternion.multiply(rotation,localRotation));
    }

实现模型缩放

模型缩放较为简单,借助安卓提供的ScaleGestureDetector即可检测到scale值

源码定位
java 复制代码
    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        isScaling = true;
        if (target == null)return false;
        float scaleFactor = detector.getScaleFactor();
        target.setLocalScale(scaleTmp.scaled(scaleFactor));
        return false;
    }

    @Override
    public boolean onScaleBegin(ScaleGestureDetector detector) {
        if (target == null)return false;
        scaleTmp = target.getLocalScale();
        return true;
    }

    @Override
    public void onScaleEnd(ScaleGestureDetector detector) {
        isScaling = false;
    }

实现模型平移

最容易想到的是,通过手势的Move事件的distanceX\Y,直接去修改所渲染的模型节点的position。

这种实现思路有两个弊端,一个是由于不同设备的屏幕密度不同会导致每台设备的平移效果不一致,二是无法精准控制平移的位置。

下一篇将介绍采用射线检测的方式去做平移。



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

相关推荐
_helen_52020 小时前
unreal engine 虚拟现实 + airsim
游戏引擎·vr·虚幻
Godspeed Zhao21 小时前
跨越天际:从智能汽车到 eVTOL 的适航与系统级开发37——增强现实平显(AR-HUD)与合成视觉系统(SVS)在低能见度进近中的应用
汽车·ar·evtol
云和恩墨2 天前
数据库一体机简史:从ODA到zData X,命名之中的玄机
数据库·vr
图扑软件3 天前
WebGIS 零碳智能港口态势感知与 VR 远控系统
物联网·自动化·vr·webgl·数字孪生·3d 渲染
粤科源兴4 天前
LM5164DDAR是什么芯片?产品特点、应用及采购指南
ar
ar012323 天前
AR远程协助产品:重塑工业服务的未来
人工智能·ar
世优科技虚拟人23 天前
VR大空间成文旅研学新宠:山海经、西游记、吴哥窟三大IP项目实探
vr·mr·虚拟体验·vr大空间
DolitD23 天前
点盾云新增VR加密功能:一机一码,让VR内容分发安全可控
安全·vr·视频
纪纯24 天前
PicoVR Unity Integration SDK 3.4 常用交互API
unity·游戏引擎·vr·pico
世界很奇妙塔24 天前
AR/VR产业去泡沫后:从消费端娱乐,走向产业端全域赋能
ar·vr·娱乐