vuforia android 教程(4) 添加手势识别 手势操作 手势触控 vofuria模型 旋转 缩放 放大 缩小

系列教程:

vuforia android 教程(1)
https://www.jianshu.com/p/37b158175b04
vuforia android 教程(2)
https://www.jianshu.com/p/2d70974857b8
vuforia android 教程(3)
https://www.jianshu.com/p/ff33aa2ffde5

在上一篇文章中,我们了解了如何替换替换vofuria模型

那么接下来,我们来给 vuforia 模型 添加 手势操作 旋转 缩放

1. Android OpenGL ES 原理

1.1 Coordinate System坐标系

OpenGL 使用了右手坐标系统,右手坐标系判断方法:在空间直角坐标系中,让右手拇指指向x轴的正方向,食指指向y轴的正方向,如果中指能指向z轴的正方向,则称这个坐标系为右手直角坐标系。

image

1.2 Translate 平移变换

方法 public abstract void glTranslatef (float x, float y, float z) 用于坐标平移变换。

image

1.3 Rotate 旋转

方法 public abstract void glRotatef(float angle, float x, float y, float z) 用来实现选择坐标变换,单位为角度。(x,y,z)定义旋转的参照矢量方向。多次旋转的顺序非常重要。

image

1.4 Scale(缩放)

方法 public abstract void glScalef (float x, float y, float z)用于缩放变换。

下图为使用 gl.glScalef(2f, 2f, 2f) 变换后的基本,相当于把每个坐标值都乘以2.

image

1.5 组合

Translate & Rotate (平移和旋转组合变换)

在对 Mesh(网格,构成三维形体的基本单位)同时进行平移和选择变换时,坐标变换的顺序也直接影响最终的结果。

比如:先平移后旋转,旋转的中心为平移后的坐标。

image

先选择后平移: 平移在则相对于旋转后的坐标系:

image

一个基本原则是,坐标变换都是相对于变换的 Mesh 本身的坐标系而进行的。

Translate & Scale(平移和缩放组合变换)

同样当需要平移和缩放时,变换的顺序也会影响最终结果。

比如先平移后缩放:

gl.glTranslatef(2, 0, 0);
gl.glScalef(0.5f, 0.5f, 0.5f);  
image

如果调换一下顺序:

gl.glScalef(0.5f, 0.5f, 0.5f);
gl.glTranslatef(2, 0, 0);  

结果就有所不同:

image

2.实际操作

2.1 单点旋转

首先我们在以前的项目下 ,打开 ImageTargets.java ,在activity 中重写onTouchEvent(MotionEvent event)方法.
千万记住, 在opengl es 里面 ,是 xyz 坐标系 ,所以我们需要通过x轴 和 y轴进行旋转 , 当我们需要模型左右旋转的时候,就需要沿y轴进行旋转,当我们需要模型上下旋转的时候就需要沿x轴进行旋转.

 int pointerCount = event.getPointerCount();

        int action = event.getAction();

        y = event.getY();
        x = event.getX();

        // 单点触控的情况主要控制模型的旋转

        if (pointerCount == 1) {

            switch (action) {

                case MotionEvent.ACTION_DOWN:

                    System.out.println("ACTION_DOWN pointerCount=" + pointerCount);

                    break;

                case MotionEvent.ACTION_UP:

                    System.out.println("ACTION_UP pointerCount=" + pointerCount);

                    break;

                case MotionEvent.ACTION_MOVE:

                    System.out.println("ACTION_MOVE pointerCount=" + pointerCount);

                    float dy = y - mPreviousY;//计算触控笔Y位移
                    float dx = x - mPreviousX;//计算触控笔X位移
                    mRenderer.setmAngleX(mRenderer.getmAngleX() + dy * TOUCH_SCALE_FACTOR);//设置沿x轴旋转角度
                    mRenderer.setmAngleY(mRenderer.getmAngleY() + dx * TOUCH_SCALE_FACTOR);//设置沿y轴旋转角度
                    break;

            }

        }

2.2 双点缩放

当有两个点的时候,就是缩放,判断两个点之间距离越大, 就放大, 距离越小就缩小.

  if (pointerCount == 2) {

            switch (action & MotionEvent.ACTION_MASK) {

                case MotionEvent.ACTION_DOWN:

                    System.out.println("ACTION_DOWN pointerCount=" + pointerCount);

                    break;

                case MotionEvent.ACTION_POINTER_DOWN:

                    oldDist = (float) Math.sqrt((event.getX(0) - event.getX(1)) * (event.getX(0) - event.getX(1)) + (event.getY(0) - event.getY(1)) * (event.getY(0) - event.getY(1)));

                    System.out.println("ACTION_UP pointerCount=" + pointerCount);

                    break;

                case MotionEvent.ACTION_MOVE:

                    System.out.println("ACTION_MOVE pointerCount=" + pointerCount);

                    float newDist = (float) Math.sqrt((event.getX(0) - event.getX(1)) * (event.getX(0) - event.getX(1)) + (event.getY(0) - event.getY(1)) * (event.getY(0) - event.getY(1)));

                    float scale = newDist / oldDist;

                    if (scale >= 1.5f) {

                        scale = 1.5f;

                    } else if (scale <= 0.2f) {

                        scale = 0.2f;

                    }

                    mRenderer.setScale(scale);//调用本地方法传值

                    break;

            }

        }

2.3 通过onTouchEvent()计算的值在Renderer里面进行实际旋转和缩放

在 public void renderFrame(State state, float[] projectionMatrix) 方法里进行

2.3.1 旋转

rotateM(float[] m, int mOffset, float a, float x, float y, float z)
m为模型, moffse 为偏移量 , a 为旋转角度 , x 轴, y 轴,z 轴

旋转 X轴如下
Matrix.rotateM(modelViewMatrix,0,mAngleY, 1, 0, 0);//旋转

旋转 Y轴如下
Matrix.rotateM(modelViewMatrix,0,mAngleX, 0, 1, 0);//旋转

2.3.2 缩放

scaleM(float[] m, int mOffset,float x, float y, float z)
m为模型, moffse 为偏移量 , x 轴, y 轴,z 轴

按照手指距离缩放如下
Matrix.scaleM(modelViewMatrix, 0, kBuildingScale, kBuildingScale, kBuildingScale);

下面贴出详细代码,供大家参考

ImageTargetRenderer.java

    // The render function called from SampleAppRendering by using RenderingPrimitives views.
    // The state is owned by SampleAppRenderer which is controlling it's lifecycle.
    // State should not be cached outside this method.
    public void renderFrame(State state, float[] projectionMatrix)
    {
        // Renders video background replacing Renderer.DrawVideoBackground()
        mSampleAppRenderer.renderVideoBackground();

        GLES20.glEnable(GLES20.GL_DEPTH_TEST);

        // handle face culling, we need to detect if we are using reflection
        // to determine the direction of the culling
        GLES20.glEnable(GLES20.GL_CULL_FACE);
        GLES20.glCullFace(GLES20.GL_BACK);

        // Did we find any trackables this frame?
        for (int tIdx = 0; tIdx < state.getNumTrackableResults(); tIdx++) {
            TrackableResult result = state.getTrackableResult(tIdx);
            Trackable trackable = result.getTrackable();
            printUserData(trackable);
            Matrix44F modelViewMatrix_Vuforia = Tool
                    .convertPose2GLMatrix(result.getPose());
            float[] modelViewMatrix = modelViewMatrix_Vuforia.getData();

            int textureIndex = trackable.getName().equalsIgnoreCase("kfc") ? 0
                    : 1;
            //stones

            textureIndex = trackable.getName().equalsIgnoreCase("tarmac") ? 2
                    : textureIndex;



            // deal with the modelview and projection matrices
            float[] modelViewProjection = new float[16];

            Matrix.rotateM(modelViewMatrix,0,mAngleY, 1, 0, 0);//旋转
            Matrix.rotateM(modelViewMatrix,0,mAngleX, 0, 1, 0);//旋转

            //Matrix.rotateM(modelViewMatrix, 0, 90.0f, 1.0f, 0, 0);
            Matrix.scaleM(modelViewMatrix, 0, kBuildingScale,
                    kBuildingScale, kBuildingScale);

            Matrix.multiplyMM(modelViewProjection, 0, projectionMatrix, 0, modelViewMatrix, 0);

            // activate the shader program and bind the vertex/normal/tex coords
            GLES20.glUseProgram(shaderProgramID);

            if (!mActivity.isExtendedTrackingActive()) {
                GLES20.glVertexAttribPointer(vertexHandle, 3, GLES20.GL_FLOAT,
                        false, 0, testModel.getVertices());
                        //false, 0, mTeapot.getVertices());
                GLES20.glVertexAttribPointer(textureCoordHandle, 2,
                        GLES20.GL_FLOAT, false, 0, testModel.getTexCoords());
                        //GLES20.GL_FLOAT, false, 0, mTeapot.getTexCoords());


                GLES20.glEnableVertexAttribArray(vertexHandle);
                GLES20.glEnableVertexAttribArray(textureCoordHandle);

                // activate texture 0, bind it, and pass to shader
                GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
                GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,
                        mTextures.get(textureIndex).mTextureID[0]);
                GLES20.glUniform1i(texSampler2DHandle, 0);

                // pass the model view matrix to the shader
                GLES20.glUniformMatrix4fv(mvpMatrixHandle, 1, false,
                        modelViewProjection, 0);

                // finally draw the teapot
                GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0,
                        testModel.getNumObjectVertex());
                //GLES20.glDrawElements(GLES20.GL_TRIANGLES,
                        //mTeapot.getNumObjectIndex(), GLES20.GL_UNSIGNED_SHORT,
                        //mTeapot.getIndices());

                // disable the enabled arrays
                GLES20.glDisableVertexAttribArray(vertexHandle);
                GLES20.glDisableVertexAttribArray(textureCoordHandle);
            } else {
                GLES20.glDisable(GLES20.GL_CULL_FACE);
                GLES20.glVertexAttribPointer(vertexHandle, 3, GLES20.GL_FLOAT,
                        false, 0, testModel.getVertices());
                        //false, 0, mBuildingsModel.getVertices());
                GLES20.glVertexAttribPointer(textureCoordHandle, 2,
                        GLES20.GL_FLOAT, false, 0, testModel.getTexCoords());
                        //GLES20.GL_FLOAT, false, 0, mBuildingsModel.getTexCoords());

                GLES20.glEnableVertexAttribArray(vertexHandle);
                GLES20.glEnableVertexAttribArray(textureCoordHandle);

                GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
                GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,
                        mTextures.get(3).mTextureID[0]);
                GLES20.glUniformMatrix4fv(mvpMatrixHandle, 1, false,
                        modelViewProjection, 0);
                GLES20.glUniform1i(texSampler2DHandle, 0);
                GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0,
                        testModel.getNumObjectVertex());
                        //mBuildingsModel.getNumObjectVertex());

                SampleUtils.checkGLError("Renderer DrawBuildings");
            }

            SampleUtils.checkGLError("Render Frame");

        }

        GLES20.glDisable(GLES20.GL_DEPTH_TEST);

    }

ImageTargets.java

    public float  oldDist = 90f;
    private final float TOUCH_SCALE_FACTOR = 180.0f / 320;
    private float mPreviousX;
    private float mPreviousY;
    private float x, y;

    public boolean onTouchEvent(MotionEvent event)

    {

        int pointerCount = event.getPointerCount();

        int action = event.getAction();

        y = event.getY();
        x = event.getX();

        // 单点触控的情况主要控制模型的旋转

        if (pointerCount == 1) {

            switch (action) {

                case MotionEvent.ACTION_DOWN:

                    System.out.println("ACTION_DOWN pointerCount=" + pointerCount);

                    break;

                case MotionEvent.ACTION_UP:

                    System.out.println("ACTION_UP pointerCount=" + pointerCount);

                    break;

                case MotionEvent.ACTION_MOVE:

                    System.out.println("ACTION_MOVE pointerCount=" + pointerCount);

                    float dy = y - mPreviousY;//计算触控笔Y位移
                    float dx = x - mPreviousX;//计算触控笔X位移
                    mRenderer.setmAngleX(mRenderer.getmAngleX() + dy * TOUCH_SCALE_FACTOR);//设置沿x轴旋转角度
                    mRenderer.setmAngleY(mRenderer.getmAngleY() + dx * TOUCH_SCALE_FACTOR);//设置沿y轴旋转角度
                    break;

            }

        }

        // 两点触控的情况主要控制模型的缩放

        if (pointerCount == 2) {

            switch (action & MotionEvent.ACTION_MASK) {

                case MotionEvent.ACTION_DOWN:

                    System.out.println("ACTION_DOWN pointerCount=" + pointerCount);

                    break;

                case MotionEvent.ACTION_POINTER_DOWN:

                    oldDist = (float) Math.sqrt((event.getX(0) - event.getX(1)) * (event.getX(0) - event.getX(1)) + (event.getY(0) - event.getY(1)) * (event.getY(0) - event.getY(1)));

                    System.out.println("ACTION_UP pointerCount=" + pointerCount);

                    break;

                case MotionEvent.ACTION_MOVE:

                    System.out.println("ACTION_MOVE pointerCount=" + pointerCount);

                    float newDist = (float) Math.sqrt((event.getX(0) - event.getX(1)) * (event.getX(0) - event.getX(1)) + (event.getY(0) - event.getY(1)) * (event.getY(0) - event.getY(1)));

                    float scale = newDist / oldDist;

                    if (scale >= 1.5f) {

                        scale = 1.5f;

                    } else if (scale <= 0.2f) {

                        scale = 0.2f;

                    }

                    mRenderer.setScale(scale);//调用本地方法传值

                    break;

            }

        }
        mPreviousY = y;//记录触控笔位置
        mPreviousX = x;//记录触控笔位置
        return true;

    }

参考:

https://blog.jayway.com/2009/12/03/opengl-es-tutorial-for-android-part-i/

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260

推荐阅读更多精彩内容