Vuforia demo 简析

Vuforia 是高通提供的一款 AR SDK。

简要介绍 UserDefinedTargets 用户自定义目标,实现自定义选择场景并投放模型的功能

gif

1. UserDefinedTargets demo 解析

1.1 初始化

vuforiaAppSession = new SampleApplicationSession(this);
vuforiaAppSession.initAR(this, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

其中 SampleApplicationSession.initAR(Activity activity, int screenOrientation) 方法逻辑如下:

  1. 监听屏幕转向,设置竖屏

  2. 设置 Activity 可见时维持亮屏

  3. 后台线程初始化 Vuforia

    Vuforia.setInitParameters(mActivity, mVuforiaFlags, ${VuforiaKey});
    

    mVuforiaFlags: GL_20,表示使用 OpenGL 初始化

    do {
        mProgressValue = Vuforia.init();
        // Publish the progress value:
        publishProgress(mProgressValue);
    } while (!isCancelled() && mProgressValue >= 0 && mProgressValue < 100);
    

    反复调用 Vuforia.init() 直至返回值为 100,表示初始化完成

  4. 主线程初始化跟踪器

    // Initialize the image tracker:
    TrackerManager trackerManager = TrackerManager.getInstance();
    Tracker tracker = trackerManager.initTracker(ObjectTracker
            .getClassType());
    
  5. 加载跟踪器数据(暂时这么描述)

    // Get the image tracker:
    TrackerManager trackerManager = TrackerManager.getInstance();
    ObjectTracker objectTracker = (ObjectTracker) trackerManager
            .getTracker(ObjectTracker.getClassType());
    ...
    
    // Create the data set:
    dataSetUserDef = objectTracker.createDataSet();
    ...
    
    // 激活数据集(没理解)
    objectTracker.activateDataSet(dataSetUserDef)
    
  6. 注册 Vuforia 回调

    Vuforia.registerCallback(Vuforia.UpdateCallbackInterface object)
    

    真正实现逻辑如下(后续分析)

    public class UserDefinedTargets extends Activity implements
        SampleApplicationControl, SampleAppMenuInterface {
        ...
        
        @Override
        public void onVuforiaUpdate(State state) {
            ...
        }
        ...
    }
    

1.2 设置相机、场景背景等

@Override
public void onInitARDone(SampleApplicationException exception) {
    // 初始化 refFreeFrame,mGlView,mRenderer
    initApplicationAR();

    // 最终调用 Renderer.getInstance().setVideoBackgroundConfig(config); 初始化场景的视频背景
    mRenderer.setActive(true);

    // 添加 GlView 至场景中
    addContentView(mGlView, new LayoutParams(LayoutParams.MATCH_PARENT,
            LayoutParams.MATCH_PARENT));

    ...

    try {
        // 设置 CameraDevice 的相关参数,并开启场景帧的跟踪
        vuforiaAppSession.startAR(CameraDevice.CAMERA_DIRECTION.CAMERA_DIRECTION_DEFAULT);
    } catch (SampleApplicationException e) {
        Log.e(LOGTAG, e.getString());
    }

    // 设置为连续对焦模式
    boolean result = CameraDevice.getInstance().setFocusMode(
            CameraDevice.FOCUS_MODE.FOCUS_MODE_CONTINUOUSAUTO);

    ...
}
  1. initApplicationAR 方法

    private void initApplicationAR() {
        
        refFreeFrame = new RefFreeFrame(this, vuforiaAppSession);
        // 加载场景中,相机框框的纹理图
        refFreeFrame.init();
    
        // 创建 GL 视图,并设置 OpenGL 是否支持透明格式,深度缓存区大小,和模板缓冲区大小
        mGlView = new SampleApplicationGLView(this);
        mGlView.init(translucent, depthSize, stencilSize);
    
        // 创建自定义渲染器(封装OpenGL渲染相关的代码)
        mRenderer = new UserDefinedTargetRenderer(this, vuforiaAppSession);
        // 添加之前的初始化绑定的纹理贴图 viewfinder_crop_marks_portrait.png 和 viewfinder_crop_marks_landscape.png
        mRenderer.setTextures(mTextures);
        // 设置自定义的渲染器
        mGlView.setRenderer(mRenderer);
        
        ...
    }
    
  2. vuforiaAppSession.startAR(CameraDevice.CAMERA_DIRECTION.CAMERA_DIRECTION_DEFAULT);

    public class SampleApplicationSession implements UpdateCallbackInterface {
        ...
        
        public void startAR(int camera) throws SampleApplicationException {
            ...
    
            mCamera = camera;
            ...
                // 初始化 Vuforia 相机设备,参数指定使用正面相机
                CameraDevice.getInstance().init(camera);
            ...
                // 设置相机的视频模式为默认,其他参数有高清、流程
                CameraDevice.getInstance().selectVideoMode(
                    CameraDevice.MODE.MODE_DEFAULT);
            ...
                // 开启相机设备
                CameraDevice.getInstance().start();
            ...
    
            // 开启 AR 场景跟踪器
            mSessionControl.doStartTrackers();
            ...
        }
        ...
    }
    
    @Override
    public boolean doStartTrackers() {
        ...
        Tracker objectTracker = TrackerManager.getInstance().getTracker(
                ObjectTracker.getClassType());
        if (objectTracker != null) {
            result = objectTracker.start();
        }
        ...
    }
    

1.3 处理页面获取和失去焦点

  1. 获取焦点

    @Override
    protected void onResume() {
        super.onResume();
    
        // 1. 针对 android 设备,设置请求屏幕转向为横向
    
        ...
        // 2. 内部调用 Vuforia.onResume(),并尝试开启 Tracker
        vuforiaAppSession.resumeAR();
        ...
    
        // 3. 调用 GLSurfaceView onResume 方法
        if (mGlView != null) {
            mGlView.setVisibility(View.VISIBLE);
            mGlView.onResume();
        }
    }
    
  2. 失去焦点

    public void pauseAR() throws SampleApplicationException {
        if (mStarted) {
             // 1. 停止跟踪器,停止 CameraDevice 运行,并销毁相机对象
            stopCamera();
        }
    
        // 
        Vuforia.onPause();
    }
    

1.4 用户点击拍照按钮,构建目标帧

  1. 点击,开始构建目标帧对象

    public void onCameraClick(View v) {
        // 1. 通过目标跟踪器获取的帧的质量设置是否为 ImageTargetBuilder.FRAME_QUALITY.FRAME_QUALITY_NONE 来判断跟踪器是否在运行
        if (isUserDefinedTargetsRunning()) {
            ...
            // 2. 开始目标帧
            startBuild();
        }
    }
    
    void startBuild() {
        // 1.得到目标跟踪器
    
        if (objectTracker != null) {
            // 2. 得到目标构建器
            if (targetBuilder != null) {
                
                // 3. 若 targetBuilder.getFrameQuality() 为 ImageTargetBuilder.FRAME_QUALITY.FRAME_QUALITY_LOW,显示出错
    
                ...
                // 4. 循环遍历构建目标图像数据
                do {
                    ...
                    targetBuilderCounter++;
                } while (!targetBuilder.build(name, 320.0f));
    
                // 5. 标记当前 refFreeFrame 当前的状态是创建状态。
                // 后续 Vuforia_onUpdate() 回调,会根据这个标记从跟踪器中取出目标跟踪图像,结合当前的帧数据,计算模型的显示位置和角度
                refFreeFrame.setCreating();
            }
        }
    }
    
  2. 绑定目标帧对象至目标跟踪器,并激活

    @Override
    public void Vuforia_onUpdate(State s) {
        mSessionControl.onVuforiaUpdate(s);
    }
    
    @Override
    public void onVuforiaUpdate(State state) {
        // 1. 获取跟踪器对象
        ObjectTracker objectTracker = ...;
    
        // 2. 判断是否有新的目标帧对象
        if (refFreeFrame.hasNewTrackableSource()) {
            ...
    
            // 3. 取消激活当前的跟踪数据
            objectTracker.deactivateDataSet(objectTracker.getActiveDataSet(0));
    
            // 4. 添加新的目标跟踪对象
            Trackable trackable = dataSetUserDef
                    .createTrackable(refFreeFrame.getNewTrackableSource());
    
            // 5. 重新激活跟踪数据集
            objectTracker.activateDataSet(dataSetUserDef);
    
            if (mExtendedTracking) {
                trackable.startExtendedTracking();
            }
        }
    }
    

1.5 GLSurfaceView.Renderer 回调函数处理

1.5.1 onSurfaceCreated

@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
    // 最终调用 Vuforia.onSurfaceCreated(),用来确保在初次使用或者 onResume() 之后初始化渲染
    vuforiaAppSession.onSurfaceCreated();

    // 最终调用 void initRendering(),创建 shader 程序对象,加载顶点处理器和片元处理器
    // 创建纹理引用,坐标引用,投影矩阵引用等,同 EasyAR 简单调研文档 1.3.1 初始化 OpenGL
    mSampleAppRenderer.onSurfaceCreated();
}

1.5.2 onSurfaceChanged

@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
    // 1.
    mActivity.updateRendering();

    // Call Vuforia function to handle render surface size changes:
    vuforiaAppSession.onSurfaceChanged(width, height);

    // RenderingPrimitives to be updated when some rendering change is done
    mSampleAppRenderer.onConfigurationChanged(mIsActive);

    // Call function to initialize rendering:
    initRendering();
}
  1. mActivity.updateRendering()

    最终会调用 RefFreeFrame.initGL 方法

    void initGL(int screenWidth, int screenHeight) {
        // 1. 初始化模型矩阵,初始化顶点着色器、片元作色器、颜色等句柄
        frameGL.init(screenWidth, screenHeight);
    }
    
  2. 调用 Vuforia.onSurfaceChanged(width, height)

  3. 重新计算设置 Render 的 VideoBackgroundConfig

    VideoBackgroundConfig config = new VideoBackgroundConfig();
    config.setEnabled(true);
    config.setPosition(new Vec2I(0, 0));
    ...
    config.setSize(new Vec2I(xSize, ySize));
    Renderer.getInstance().setVideoBackgroundConfig(config);
    
  4. 获取渲染基元

    mRenderingPrimitives = Device.getInstance().getRenderingPrimitives();
    
  5. 初始化渲染

    private void initRendering() {
        // 1. 创建茶几对象
        // 2. 设置清空颜色
        // 3. 绑定需要的纹理对象
        // 4. 创建顶点、纹理、矩阵等句柄
    }
    

1.5.3 onDrawFrame

public void render() {
    // 1. 清除颜色和深度缓存区
    // 2. 针对前置摄像头和后置摄像头,设置面片正面方面
    // 3. 从 mRenderingPrimitives 取出 viewList
    ViewList viewList = mRenderingPrimitives.getRenderingViews();
    // 4. 从每个图元中取出当前的视口大小,模型矩阵,用于绘制当前界面
}

1.6 Vuforia SDK 简单小结

  1. 提供接口设置背景相机视频
  2. 提供相机对象,支持设置相机对象的清晰度、对焦模式等
  3. 提供跟踪器
    • 跟踪器支持获取当前的一帧作为目标图像,后续帧参考该帧计算
    • 跟踪器支持同时跟踪多个对象
  4. 提供 RenderingPrimitives 类,提供创建场景时的视口大小和模型矩阵
  5. 暂未看到 SLAM 相关的功能 demo
  6. 最新的免费版本(6.2.10)有水印,早期版本 QCAR(2.6.10)无水印。但最新版本 SDK 效果也并未达到预期要求,早期版本可期望性并不高
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容