[OpenGL]从零开始写一个Android平台下的全景视频播放器——5.5 响应用户的多指触控事件(缩放)

Github项目地址

为了方便没有准备好梯子的同学,我把项目在CSDN上打包下载,不过不会继续更新(保留在初始版本)

回到目录

先贴代码再讲原理吧

缩放事件(Scale)监听

和Scroll一样,Scale也有一个辅助类帮我们来记录用户的缩放操作(代码是缩放和平移的整合版)


private GestureDetector gestureDetector;
private ScaleGestureDetector scaleGestureDetector;
private static final float sDensity =  Resources.getSystem().getDisplayMetrics().density;
private static final float sDamping = 0.2f;
private float mDeltaX;
private float mDeltaY;
private float mScale;


private void initGestureHandler(){
    mDeltaX=mDeltaY=0;
    mScale=1;
    gestureDetector=new GestureDetector(context,new GestureDetector.SimpleOnGestureListener(){

        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {
            return super.onSingleTapConfirmed(e);
        }

        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            mDeltaX+= distanceX / sDensity * sDamping;
            mDeltaY+= distanceY / sDensity * sDamping;
            return super.onScroll(e1, e2, distanceX, distanceY);
        }
    });

    scaleGestureDetector=new ScaleGestureDetector(context, new ScaleGestureDetector.OnScaleGestureListener() {
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            float scaleFactor=detector.getScaleFactor();
            updateScale(scaleFactor);
            return true;
        }

        @Override
        public boolean onScaleBegin(ScaleGestureDetector detector) {
            //return true to enter onScale()
            return true;
        }

        @Override
        public void onScaleEnd(ScaleGestureDetector detector) {

        }
    });
}

public boolean handleTouchEvent(MotionEvent event) {
    boolean ret=scaleGestureDetector.onTouchEvent(event);
    if (!scaleGestureDetector.isInProgress()){
        ret=gestureDetector.onTouchEvent(event);
    }
    return  ret;
}

public void updateScale(float scaleFactor){
    mScale=mScale+(1.0f-scaleFactor);
    mScale=Math.max(0.122f,Math.min(1.0f,mScale));
}

我们只需要在onScale中获取scaleFactor,再在onDrawFrame里面更新projectionMatrix即可

//视角从90度到14度
float currentDegree= (float) (Math.toDegrees(Math.atan(mScale))*2);
Matrix.perspectiveM(projectionMatrix, 0, currentDegree,(float)screenWidth/screenHeight/2, 1f, 500f);

效果预览

这次我决定要来一张效果好一点的GIF(1.7MB)。。
这里写图片描述

缩放原理

其实原理就一句话:改变透视投影矩阵,即projectionMatrix,通过改变透视投影矩阵来实现只显示画面中更小或更大一部分的内容

那么要怎么改变投影矩阵呢?我们来看Matrix.perspectiveM的定义

/**
 * Defines a projection matrix in terms of a field of view angle, an
 * aspect ratio, and z clip planes.
 *
 * @param m the float array that holds the perspective matrix
 * @param offset the offset into float array m where the perspective
 *        matrix data is written
 * @param fovy field of view in y direction, in degrees
 * @param aspect width to height aspect ratio of the viewport
 * @param zNear
 * @param zFar
 */
public static void perspectiveM(float[] m, int offset,
      float fovy, float aspect, float zNear, float zFar)

关键的参数是fovy,这个是y方向的可视角度,结合aspect以及近平面,远平面就可以推出整个视锥体
另外,我们的两个监听器要注意处理顺序,缩放的判断应该在平移之前,不然缩放也变成平移了
结合这张图,应该更容易理解:
这里写图片描述
细心的朋友应该注意到了0.122f这个神奇的数字,其实这个就是 tan(7) 的结果,表示我们的最小视角是14度——所以如果要改变最大视角和最小视角的话,知道该怎么办了吧(不要问我为什么最小是14度。。)?

另一种略不完美的解决方案

此方案来自MD360Player4Android

if(statusHelper.getPanoDisPlayMode()==PanoMode.DUAL_SCREEN)
 Matrix.frustumM(projectionMatrix, 0, -ratio/4, ratio/4, -0.5f, 0.5f, 0.7f, 500);
 else Matrix.frustumM(projectionMatrix, 0, -ratio/2, ratio/2, -0.5f, 0.5f, 0.7f, 500);

frustumM也是透视投影,而且比较直观,看定义就知道了

/**
 * Defines a projection matrix in terms of six clip planes.
 *
 * @param m the float array that holds the output perspective matrix
 * @param offset the offset into float array m where the perspective
 *        matrix data is written
 * @param left
 * @param right
 * @param bottom
 * @param top
 * @param near
 * @param far
 */
public static void frustumM(float[] m, int offset,
        float left, float right, float bottom, float top,
        float near, float far) 

直接指定可视区域的范围——上下左右(这里的上下左右是指近平面的上下左右)前后,够直观的吧~

但是这样做存在一个小问题(个人观点,也许不算问题),由于视角和放大比例其实并不是线性关系,在放大时会出现放大速度越来越快的情况(唰的一下就放到最大了)。因此如果能先计算反正切(atan/atan2),那么就能够实现接近平滑的缩放(Matrix.perspectiveM)

好了,到这里我们已经几乎实现了所有的功能,剩下的就是UI(侧重点主要是实现UI时会碰到的问题)以及扩展功能了~(更多的细节以及功能请去项目中探索)

Github项目地址
回到目录

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值