1.0 免費(fèi)版本
以下 Target Image 識別部分按照 2.0.0 基礎(chǔ)版本解析
平面監(jiān)測 Demo 按 2.1.0 Pro 版本解析
1.1 初始化
public class MainActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
...
EasyAR.initialize(this, key);
nativeInit();
GLView glView = new GLView(this);
glView.setRenderer(new Renderer());
glView.setZOrderMediaOverlay(true);
((ViewGroup) findViewById(R.id.preview)).addView(glView,
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
nativeRotationChange(getWindowManager().getDefaultDisplay().getRotation() == android.view.Surface.ROTATION_0 ||
getWindowManager().getDefaultDisplay().getRotation() == Surface.ROTATION_180);
}
}
1.1.1 EasyAR 初始化
EasyAR.initialize(this, key)
其中 key 在 EasyAR 應(yīng)用創(chuàng)建 里面申請獲取
1.1.2 相機(jī)、跟蹤器、渲染器初始化
EasyAR::samples::HelloAR ar;
JNIEXPORT jboolean JNICALL JNIFUNCTION_NATIVE(nativeInit(JNIEnv*, jobject)) {
bool status = ar.initCamera();
ar.loadFromJsonFile("targets.json", "argame");
ar.loadFromJsonFile("targets.json", "idback");
ar.loadAllFromJsonFile("targets2.json");
ar.loadFromImage("namecard.jpg");
status &= ar.start();
return status;
}
-
初始化相機(jī)流程:
- 打開相機(jī)
camera - 設(shè)置相機(jī)分辨率大小
- 將相機(jī)綁定到場景跟蹤器上
tracker_ - 設(shè)置
tracker_可同時(shí)識別的目標(biāo)數(shù)目為 4 - 綁定相機(jī)至
augmenter_(渲染器)。從tracker_獲取圖像幀,然后將 camera 的圖像作為 AR 場景的背景渲染出來。通常在渲染線程中使用
bool AR::initCamera() { bool status = true; status &= camera_.open(); camera_.setSize(Vec2I(1280, 720)); status &= tracker_.attachCamera(camera_); tracker_.setSimultaneousNum(4); status &= augmenter_.attachCamera(camera_); return status; } - 打開相機(jī)
-
加載識別目標(biāo)圖片
ar.loadFromJsonFile("targets.json", "argame"); ar.loadFromJsonFile("targets.json", "idback"); ar.loadAllFromJsonFile("targets2.json"); ar.loadFromImage("namecard.jpg");最終調(diào)用的是
EasyARImageTarget target; target.load(path.c_str(), EasyAR::kStorageAssets, targetname.c_str()); tracker_.loadTarget(target, new HelloCallBack()); // HelloCallBack 用于處理識別目標(biāo)加載成功或失敗virtual bool load(const char* path, int storageType, const char* name = 0);其中
storageType的枚舉使用查看 StorageType目標(biāo)對象的
json內(nèi)容{ "images" : [ { "image" : "sightplus/argame00.jpg", "name" : "argame" }, { "image" : "idback.jpg", "name" : "idback", "size" : [8.56, 5.4], "uid" : "uid-string" } ] } -
啟動(dòng)相機(jī)和場景跟蹤器
bool AR::start() { bool status = true; status &= camera_.start(); // 設(shè)置相機(jī)為連續(xù)對焦 camera_.setFocusMode(CameraDevice::kFocusModeContinousauto); status &= tracker_.start(); return status; }
1.1.3 添加視圖
GLView glView = new GLView(this);
glView.setRenderer(new Renderer());
glView.setZOrderMediaOverlay(true);
((ViewGroup) findViewById(R.id.preview)).addView(glView,
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
1.1.4 標(biāo)記屏幕方向
nativeRotationChange(getWindowManager().getDefaultDisplay().getRotation() == Surface.ROTATION_0 ||
getWindowManager().getDefaultDisplay().getRotation() == Surface.ROTATION_180);
void AR::setPortrait(bool portrait) {
portrait_ = portrait;
}
在 C++ 層做好標(biāo)記,用于后續(xù) OpenGL 中渲染過程中計(jì)算視口大小 viewport_
void AR::resizeGL(int width, int height) {
Vec2I size = Vec2I(1, 1);
if(camera_.isOpened())
size = camera_.size();
if (size[0] == 0 || size[1] == 0)
return;
if (portrait_)
std::swap(size[0], size[1]);
float scaleRatio = std::max((float)width / (float)size[0], (float)height / (float)size[1]);
Vec2I viewport_size = Vec2I((int)(size[0] * scaleRatio), (int)(size[1] * scaleRatio));
viewport_ = Vec4I(0, height - viewport_size[1], viewport_size[0], viewport_size[1]);
}
1.2 處理頁面生命周期
@Override
protected void onResume() {
super.onResume();
EasyAR.onResume();
}
@Override
protected void onPause() {
super.onPause();
EasyAR.onPause();
}
EasyAR: onResume 和 onPause 方法的內(nèi)部實(shí)現(xiàn)如下:
public class EasyAR {
public static void onResume() {
EasyARNative.onResume();
if(orientationEventListener != null) {
orientationEventListener.enable();
}
}
public static void onPause() {
EasyARNative.onPause();
if(orientationEventListener != null) {
orientationEventListener.disable();
}
}
}
EasyARNative.onResume 猜測需要處理獲取渲染上下文環(huán)境和執(zhí)行渲染任務(wù);EasyARNative.onPause() 猜測需要放開渲染上下文環(huán)境,并掛起渲染任務(wù)的操作。
orientationEventListener.enable() 添加設(shè)備傳感器的監(jiān)聽,orientationEventListener.disable() 取消設(shè)備傳感器的監(jiān)聽
1.3 實(shí)現(xiàn)渲染器
public class Renderer implements GLSurfaceView.Renderer {
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
MainActivity.nativeInitGL();
}
public void onSurfaceChanged(GL10 gl, int w, int h) {
MainActivity.nativeResizeGL(w, h);
}
public void onDrawFrame(GL10 gl) {
MainActivity.nativeRender();
}
}
1.3.1 初始化 OpenGL
MainActivity.nativeInitGL() 最終會調(diào)用 C++ 代碼
void HelloAR::initGL() {
renderer.init();
augmenter_ = Augmenter();
augmenter_.attachCamera(camera_);
}
-
初始化 opengl 程序
void Renderer::init() { 1. 創(chuàng)建 gl 程序 2. 創(chuàng)建頂點(diǎn)渲染器,并編譯 3. 創(chuàng)建片元渲染器,并編譯 4. 綁定渲染器,鏈接使用 5. 綁定頂點(diǎn)、顏色、投影矩陣、模型變換矩陣 6. 創(chuàng)建模型的頂點(diǎn)坐標(biāo)值,顏色坐標(biāo)值 } 初始化 EasyAR 中的場景渲染器,并綁定相機(jī)
1.3.2 保存 GLSurfaceView 視窗變化
MainActivity.nativeResizeGL(w, h) 最終會調(diào)用如下方法
void HelloAR::resizeGL(int width, int height) {
view_size = Vec2I(width, height);
}
1.3.3 渲染 OpenGL 場景
MainActivity.nativeRender() 最終會調(diào)用如下方法
void HelloAR::render() {
1. 設(shè)置清空顏色為黑色
2. 清空顏色緩沖區(qū)和深度緩沖區(qū)
3. 從 EasyAR 的渲染器中獲取當(dāng)前幀,并設(shè)置視口大小,繪制相機(jī)視頻幀作為背景
4. 設(shè)置 OpenGL 視口大小
5. 遍歷得到當(dāng)前幀中的跟蹤到的目標(biāo)對象(前面設(shè)置的圖像目標(biāo),目標(biāo)對象最多 4 個(gè),`tracker_.setSimultaneousNum(4)` 決定)
6. 從目標(biāo)對象中獲取投影矩陣(EasyAR 內(nèi)部實(shí)現(xiàn))和目標(biāo)對象在虛擬世界的大?。▁,y 軸尺寸)
7. 根據(jù)相機(jī)標(biāo)定獲取模型變換矩陣(EasyAR 內(nèi)部實(shí)現(xiàn))
8. 利用前面初始化的模型頂點(diǎn)坐標(biāo)、顏色坐標(biāo),和各矩陣,使用 OpenGL 繪制模型
}
1.4 EasyAR SDK 實(shí)現(xiàn)的功能
提供跟蹤器,利用輸入的圖像目標(biāo),實(shí)時(shí)監(jiān)測相機(jī)產(chǎn)生的幀數(shù)據(jù),并識別幀數(shù)據(jù)中的目標(biāo)對象
-
結(jié)合手機(jī)的傳感器(
EasyAR.onResume添加監(jiān)聽)、相機(jī)標(biāo)定、相機(jī)產(chǎn)生的幀數(shù)據(jù)、識別出來的目標(biāo)對象,生成虛擬場景的投影矩陣和對目標(biāo)模型的模型變換矩陣imageimage紅框?yàn)樽R別的目標(biāo)對象
藍(lán)框?yàn)闃?gòu)建的長方體模型(無光照、無法向)
提供渲染器 Argument 顯示相機(jī)產(chǎn)生的幀數(shù)據(jù)
-
支持創(chuàng)建一個(gè)或多個(gè)跟蹤器 Tracker,并分別設(shè)置同時(shí)可識別的目標(biāo)對象和數(shù)目,不過識別數(shù)目越多,消耗越大,效果越差
status &= tracker_.attachCamera(camera_); status &= tracker2_.attachCamera(camera_); tracker_.setSimultaneousNum(1); tracker2_.setSimultaneousNum(2); -
提供
VideoPlayer類,支持取出每一幀用于 OpenGL 紋理貼圖void ARVideo::openVideoFile(const std::string& path, int texid) { if(!callback_) callback_ = new CallBack(this); path_ = path; player_.setRenderTexture(texid); player_.setVideoType(VideoPlayer::kVideoTypeNormal); player_.open(path.c_str(), kStorageAssets, callback_); }支持傳入生成的紋理 id,設(shè)置給
VideoPlayer,內(nèi)部完成綁定邏輯gif -
提供
BarCodeScanner類,支持從幀數(shù)據(jù)中解析得到二維碼等數(shù)據(jù)(不解析代碼)barcode_index = frame.index(); std::string text = frame.text(); if (!text.empty()) { LOGI("got qrcode: %s", text.c_str()); }gif 模型構(gòu)建渲染、動(dòng)畫、選擇等功能完全交由用戶,提供了較大的自由度
需依賴 EasyAR 的 so,編寫 C++ 層代碼,因此在 AndroidStudio 上并沒有現(xiàn)有的框架能集成第三方顯示引擎
2 收費(fèi)版本 v2.1
2.0 之后將部分 C++ 層的類封裝到 java 層,為此不需要在 C 層編寫代碼處理模型構(gòu)建渲染等邏輯,而可以直接使用 java 層的 android.opengl.GLES20
demo 解釋以 HelloARSLAM 為例,SLAM 功能僅收費(fèi)版本支持
2.1 初始化
if (!Engine.initialize(this, key)) {
Log.e("HelloAR", "Initialization Failed.");
}
glView = new GLView(this);
...
ViewGroup preview = ((ViewGroup) findViewById(R.id.preview));
preview.addView(glView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
-
初始化 EasyAR SDK
Engine.initialize(this, key)最終調(diào)用EasyAR.initializeInner(...)代碼邏輯。邏輯類似 1.0 版本EasyAR.initialize(...) -
構(gòu)建
GLViewpublic class GLView extends GLSurfaceView { public GLView(Context context) { setEGLContextFactory(new ContextFactory()); setEGLConfigChooser(new ConfigChooser()); helloAR = new HelloAR(); this.setRenderer(...); this.setZOrderMediaOverlay(true); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); synchronized (helloAR) { if (helloAR.initialize()) { helloAR.start(); } } } } -
初始化相機(jī)、相機(jī)幀生成器
public class HelloAR { public boolean initialize() { camera = new CameraDevice(); streamer = new CameraFrameStreamer(); streamer.attachCamera(camera); boolean status = true; status &= camera.open(CameraDeviceType.Default); camera.setSize(new Vec2I(1280, 720)); return status; } }- 創(chuàng)建相機(jī)和渲染器對象,并綁定相機(jī)至渲染器,用于顯示相機(jī)視頻幀
- 打開相機(jī),并設(shè)置分辨率
-
開啟相機(jī)和相機(jī)幀生成器
public boolean start() { boolean status = true; status &= (camera != null) && camera.start(); status &= (streamer != null) && streamer.start(); camera.setFocusMode(CameraDeviceFocusMode.Continousauto); return status; }其中設(shè)置相機(jī)對焦模式為 連續(xù)對焦。其他對焦模式參見 CameraDeviceFocusMode Enum
-
點(diǎn)擊開啟跟蹤器
public boolean startTracker() { boolean status = true; if (tracker != null) { tracker.stop(); tracker.dispose(); } tracker = new ARSceneTracker(); tracker.attachStreamer(streamer); if (tracker != null) { status &= tracker.start(); } return status; }streamer: CameraFrameStreamer
tracker: ARSceneTracker- 創(chuàng)建跟蹤器 tracker
- 綁定相機(jī)幀生成器,streamer 的輸出圖像將被 tracker 使用
- tracker.start 開啟跟蹤
- 之后可以通過 FrameStreamer.peek 來獲取一幀 Frame。Frame 中包含當(dāng)前的 camera 圖像和跟蹤到的對象
- 這里的 tracker 的跟蹤,已使用 SLAM 計(jì)算相關(guān)的場景信息
2.2 處理頁面生命周期
public class GLView extends GLSurfaceView {
@Override
public void onResume() {
super.onResume();
Engine.onResume();
}
@Override
public void onPause() {
Engine.onPause();
super.onPause();
}
...
}
處理邏輯同 1.2
2.3 停止或銷毀
2.3.1 停止跟蹤器
對應(yīng)開啟跟蹤器,停止跟蹤器的邏輯如下:
public boolean stopTracker() {
boolean status = true;
if (tracker != null) {
status &= tracker.stop();
tracker.dispose();
tracker = null;
}
return status;
}
2.3.2 停止和銷毀跟蹤器、相機(jī)和幀生成器
synchronized (helloAR) {
helloAR.stop();
helloAR.dispose();
}
-
停止跟蹤器,相機(jī),幀生成器
public boolean stop() { boolean status = true; if (tracker != null) { status &= tracker.stop(); } status &= (streamer != null) && streamer.stop(); status &= (camera != null) && camera.stop(); return status; } -
銷毀跟蹤器,相機(jī),幀生成器,OpenGL 場景渲染器
public void dispose() { if (tracker != null) { tracker.dispose(); tracker = null; } box_renderer = null; if (videobg_renderer != null) { videobg_renderer.dispose(); videobg_renderer = null; } if (streamer != null) { streamer.dispose(); streamer = null; } if (camera != null) { camera.dispose(); camera = null; } }
2.4 場景渲染流程
-
初始化 OpenGL 背景和模型渲染器
public void initGL() { if (videobg_renderer != null) { videobg_renderer.dispose(); } videobg_renderer = new Renderer(); box_renderer = new BoxRenderer(); box_renderer.init(); }videobg_renderer:相機(jī)幀背景渲染器
box_renderer:模型渲染器其中
box_renderer.init()處理流程同1.3.1 初始化 OpenGL -
記錄當(dāng)前視圖的大小,用于后續(xù)計(jì)算視口大小
public void resizeGL(int width, int height) { view_size = new Vec2I(width, height); viewport_changed = true; } -
渲染視圖
邏輯同
1.3.3效果如圖
gif紅米note 4;
在較復(fù)雜背景平面效果較好,但平面監(jiān)測功能看似較弱
gifGoogle Pixel
在背景平面簡單的情況下,效果較差,即便是 Google Pixel 這種較高端的機(jī)器





