ARCore-普及篇

ARCore 是一個用於在 Android 上構建加強現實應用的平臺

ARCore 三個特色

  • 運動跟蹤 (讓手機能夠理解和跟蹤它相對於現實世界的位置)
    1. ARCore 會檢測捕獲的攝像頭圖像中的視覺差別特徵(稱爲特徵點),並使用這些點來計算其位置變化
    2. 這些視覺信息將與設備 IMU, IMU, 慣性測量單元 (Inertial measurement unit , 能夠經過這個視頻直觀得感覺下 的慣性測量結果結合,估測攝像頭隨着時間推移而相對於周圍世界的姿態
    3. 經過將渲染 3D 內容的虛擬攝像頭的姿態與 ARCore 提供的設備攝像頭的姿態對齊,渲染的虛擬圖像能夠疊加到從設備攝像頭獲取的圖像上,讓虛擬內容看起來就像現實世界的一部分同樣(實際就是採集攝像頭圖像,獲取到攝像頭的姿式狀態後再把虛擬物體進行相應轉換)
  • 環境理解 (讓手機能夠檢測平坦水平表面(例如地面或咖啡桌)的大小和位置)
    1. 經過檢測特徵點和平面檢測真實世界的平面等(所以沒法檢測沒有特徵點的物體,例如:純色平面)
  • 光估值 (讓手機能夠估測環境當前的光照條件)
    1. 主要經過採集的圖像的平均色值以及手機傳感器來進行綜合計算
    2. 這樣把光照值應用到虛擬物體上就會更加真實。

ArCore 是如何把真實和虛擬結合起來的

使用opengl es 渲染

如何渲染紋理貼圖

接觸過opengl的同窗應該很容易知道,opengl最大的特色就是模擬真實視角以及渲染場景模型,java

你能夠用 ==3DMax==、==Maya== 建立本身的模型並貼圖,綁定、 輸出成opengl支持的文件,並展現。 固然這是opengl最重要的應用。 如今市面上也有不少 美顏相機,包括 抖音的一些變臉特效, 他們的作法是==(以Android平臺爲例)==經過Camera獲取圖像,並對經過識別算法捕捉到特定部位, 用特殊設計的矩陣算法進行變換,好比拉伸、擠壓等, 而後把==處理事後的圖像== 以貼圖的方式呈如今屏幕上。 固然圖像並非簡簡單單得直接像平時開發同樣

imageview.setImage(...)
複製代碼

由於要考慮到物體自己座標、世界座標、視圖座標 等,因此要進行轉換才能最終對應到屏幕中的座標,由於攝像頭的姿式一直在變。攝像頭每一次變動,圖像跟着轉換,就好像真的是經過攝像頭在觀察真實世界。android

opengl生態

因此,google是聰明的,它利用 opengl的生態來發展本身的生態,==幾乎全部能在opengl es上應用的庫、模型,均可以直接拿來使用==, 只不過要換成ARCore本身的視圖矩陣轉換。git

再者,絕不誇張得說, 若是有人說 ==ARCore(android)是opengl es的一個庫==,Android原生開發者應該無言以對吧。====github

環境理解以及運動跟蹤和opengl es結合

ARCore提供可跟蹤對象信息

ARCore經過特徵點識別,可以鑑別出紋理平面、位置、光線等, 這裏暫時只考慮平面識別。 獲得平面信息(無論是哪一個朝向,上平面OR下平面),平面是有姿態和邊界的,平面是一個可跟蹤對象, 能夠在平面上建立Anchor,這是咱們用來標定具體對象的位置信息,能夠把平面想象成桌子,能夠在桌子上聽任何像放的東西, 好比放一個蘋果,蘋果的位置必須由平面提供接口建立, 由於它和桌子保持相對性,這裏的Anchor至關於蘋果的抽象,由於Anchor只是包含世界座標的信息和姿態並不表明具體物體, 咱們能夠把Anchor的座標姿態信息應用到具體物體上(如 蘋果), 那麼蘋果就會出如今桌子上。 ==opengl es==就是利用Anchor信息來在真實世界中放置本身的模型,模型疊加在真實世界視圖中,這就是加強現實。算法

ARCore提供其餘信息

光有Anchor還不夠, 上面的Anchor只是物體自己位置信息, 並無考慮到 攝像頭、投影等因素, 其實這和opengl裏是同樣的, 物體自己的座標要通過 視圖矩陣==ViewMatrix==和投影矩陣==ProjectionMatrix==的轉換才能最終轉換成==屏幕座標==, 這些信息ARCore也會提供,除此以外還會提供 ==光照信息==、==攝像頭姿態==等api

這些信息之前其實都是由opengl 本身產生指定, 如今變動爲由ARCore提供而已。session

運動跟蹤

前面說過 運動跟蹤是他的特色之一, 意思就是你在攝像頭的當前位置方式一些物體 鏡頭位置轉移,再回來的時候物體還在, 這就是他對環境的理解對物體的跟蹤。app

ARCore支持狀況

前言-指紋適配

接觸過Android中指紋Api的人應該知道, 6.0以前雖然各廠商都有指紋支持,可是並無統一Api,若是硬是要作,只能判斷具體機型,調用對應Api。ide

  • 但會有不少問題:
    1. 機型太多,不可能覆蓋
    2. 兼容問題,api說變就變
    3. 維護困難

所以不少大廠致力於這些事情,好比聯想的Fido指紋庫, 原理是和每個手機廠商合做,廠商會提供特定的api給聯想去調用, 因此合做的廠商在手機出廠時會有預置的 APK, Android app開發者利用聯想提供的jar來實現和內置apk以及手機底層通訊,開發者就能夠把關注點從繁瑣的適配中轉移到Fido的使用上。post

google的作法

google也是這個策略, 可是也有不一樣的地方, 不一樣在於,他並無預置應用在廠商手機上, 而是在特定時候纔會去下載該特殊apk(後面會詳細說明),至於爲何要採起這個作法,後面能夠探討 [^preLoad], 因此一個設備不光須要這歌特殊的apk一樣也須要廠商的接口支持。

  • 所以一個機型是否支持ARCore取決於兩個條件:
    1. 廠商支持^supportDevice,提供了特定API
    2. 手機中安裝了google特殊的apk

針對開法者

今天4月初的時候,ARCore release了1.1版本, 相對於以前版本改動點不是特別多,可是提供了 特殊apk,開發者能夠在支持ARCore的手機上安裝它(考慮到牆的問題,某些開發者沒法從google商店獲取),或者安裝在 ==3.1 Studio==的模擬器上。

目前google正和國內華爲、小米、 魅族等各大廠商進行合做, 4月初的時候,小米上架了特殊apk ==ARCore== 被我逮到了,不過還處在試驗期,不支持下載,還無聊得舉報了一下。

ARCore集成

依賴包引入

implementation 'com.google.ar:core:1.0.0'
複製代碼

menifest申明

google把ARcore應用分爲兩類,==AR Required== 或者 ==AR Optional==,what?

==AR Required==

意味着您的應用在沒有AR的狀況下不可用。Play商店僅在支持ARCore的設備上提供您的應用。

當用戶安裝AR需求應用時,Play商店將自動安裝ARCore。可是,若是用戶卸載了ARCore ,您的應用仍然必須執行其餘運行時檢查。
複製代碼
<uses-sdk android:minSdkVersion = 「 {24或更高版本}「 /> 
   ... 
<uses-feature android:name = 」android.hardware.camera.ar​​「 android:required = 」true「 />    
<application>     ... 
	<meta-data android:name = 」com.google .ar.core「 android:value = 」required「 />
複製代碼

==AR Optional==

您的應用程序包含一個或多個AR功能,若是用戶的設備支持ARCore,則會激活該功能。可是該應用程序能夠在不支持ARCore的設備上安裝和運行。

當用戶安裝AR可選應用程序時,Play商店不會自動安裝ARCore。

要將您的應用做爲AR可選,須要修改menifest以包含如下條目:

<uses-sdk android:minSdkVersion = 「 {14或更高版本}「 /> 
   ... 
<application> 
    ... 
<meta-data android:name = 」com.google.ar.core「 android:value = 」optional「 />   
     ...
複製代碼

opengl es部分

宿主Activity實現GLSurfaceView.Renderer接口

public class MainActivity extends AppCompatActivity implements GLSurfaceView.Renderer{
	//初始化opengl的一些參數,例如初始化着色器
	void onSurfaceCreated(GL10 gl, EGLConfig config){}
	//接收視口的變化,在傳統opengl應用中若是涉及到Camera,能夠設置
	//projection matrix
	void onSurfaceChanged(GL10 gl, int width, int height){}
	//具體渲染的地方,在ARCore中會按必定頻率被調用,
	//這個方法中咱們能夠獲取攝像頭信息、投影/視圖矩陣信息、光照信息等
	void onDrawFrame(GL10 gl){}
	}
複製代碼

初始化畫布而且設置上面的Render接口

// Set up renderer.
    surfaceView.setPreserveEGLContextOnPause(true);
    surfaceView.setEGLContextClientVersion(2);
    surfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0); // Alpha used for plane blending.
    surfaceView.setRenderer(this);
    surfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
複製代碼

檢查設備是否支持ARCore

在展現咱們ARCore相關UI元素以前,咱們須要檢查當前設備是否支持,不然Session會建立失敗。

void maybeEnableArButton() {
  // Likely called from Activity.onCreate() of an activity with AR buttons.
  ArCoreApk.Availability availability = ArCoreApk.getInstance().checkAvailability(this);
  if (availability.isTransient()) {
    // re-query at 5Hz while we check compatibility.
    new Handler().postDelayed(new Runnable() {
      @Override
      public void run() {
        maybeEnableArButton();
      }
    }, 200);
  }
  if (availability.isSupported()) {
    mArButton.setVisibility(View.VISIBLE);
    mArButton.setEnabled(true);
    // indicator on the button.
  } else { // unsupported or unknown
    mArButton.setVisibility(View.INVISIBLE);
    mArButton.setEnabled(false);
  }
}
複製代碼

因爲該檢查方式會啓用遠程服務查詢,因此無論設備是否支持都會當即返回UNKNOWN_CHECKING, 因此要循環查詢,但不會重複請求查詢服務,只是獲取查詢結果, 上段代碼中:

if (availability.isTransient())
複製代碼

該方法返回true表示當前狀態知識臨時狀態,也就是說查詢服務尚未返回,你應該繼續去撈結果, 直到 返回 ==SUPPORTED== 或者 ==UNSUPPORTED==,大體流程以下圖:

檢查是否安裝了ARCore

上面咱們說過,一個設備可否使用ARCore,有兩個條件,==設備自己支持== 和 ==安裝了特殊apk(ARCore)== 所以,咱們還須要檢查==是否安裝了ARCore==

// Set to true ensures requestInstall() triggers installation if necessary.
private boolean mUserRequestedInstall = true;

// in onResume:
try {
  if (mSession == null) {
    switch (ArCoreApk.getInstance().requestInstall(this, mUserRequestedInstall)) {
      case INSTALLED:
        mSession = new Session(this);
        // Success.
        break;
      case INSTALL_REQUESTED:
        // Ensures next invocation of requestInstall() will either return
        // INSTALLED or throw an exception.
        mUserRequestedInstall = false;
        return;
    }
  }
} catch (UnavailableUserDeclinedInstallationException e) {
  // Display an appropriate message to the user and return gracefully.
  return;
} catch (...) {  // current catch statements
  ...
  return;  // mSession is still null
}
複製代碼

也許有人困惑第2行的變量有什麼用,我也困惑了一頓飯的時間,先不急,咱們先看看上面代碼對應的流程圖:

對於上面的流程,你有沒有困惑過,當設備不支持時,何時返回 INSTALL_REQUESTED 何時返回 exception, 這就取決於代碼中第 2行的變量啦, 若是爲 false時,系統則認爲你已經提示過用戶安裝了,所以會跑出一個異常,這時候你能夠簡單得 Toast提示, 爲 true時會引導用戶去各市場下載==ARCore==, 固然目前只有google市場,其餘廠商的市場還在商談中。

渲染

前面說過,渲染請求都是在public void onDrawFrame(GL10 gl)發起的,在這個方法裏咱們能夠獲取 sdk提供給咱們的信息傳遞給==opengl es==進行渲染

//session是在上文建立的ARCore回話,是貫穿於整個宿主Activity生命週期的
session.setCameraTextureName(backgroundRenderer.getTextureId());
//獲取到當前幀的信息
Frame frame = session.update();
//獲取Camera對象,Camera對象有咱們所須要的絕大部分渲染信息
Camera camera = frame.getCamera();

//這個就是投影矩陣
float[] projmtx = new float[16];
camera.getProjectionMatrix(projmtx, 0, 0.1f, 100.0f);
//這個就是視圖矩陣
float[] viewmtx = new float[16];
camera.getViewMatrix(viewmtx, 0);
//光照信息
final float lightIntensity = frame.getLightEstimate().getPixelIntensity();

//pikaqiuRender是我定義的opengl的渲染類,把這三個要素丟給opengl es其實就沒有ARCore啥事了,
//其他工做就交給opengl es了
pikaqiuRender.draw(viewmtx, projmtx, lightIntensity);
複製代碼

Session和Activity生命週期

因爲ARCore檢測以及opengl es渲染實在是太耗資源了, 個人mac開啓ARCore應用,小風扇就直接轉起來了,實在太恐怖, 因此在沒必要要的時候儘可能避免渲染,這裏要特別提醒的是,注意 ==session== 和 ==surfaceView== 的次序。

onPause()

@Override
    protected void onPause() {
        super.onPause();
        if (session != null) {
            // Note that the order matters - GLSurfaceView is paused first so that it does not try
            // to query the session. If Session is paused before GLSurfaceView, GLSurfaceView may
            // still call session.update() and get a SessionPausedException.
            displayRotationHelper.onPause();
            surfaceView.onPause();
            session.pause();
        }
    }
複製代碼

onResume()

@Override
    protected void onResume() {
        super.onResume();
        if(session == null){
            checkAndInitSession();
        }
        showLoadingMessage();
        try{
            session.resume();
        }catch (CameraNotAvailableException e){
            Log.d(TAG, "CameraNotAvailableException");
            e.printStackTrace();
        }
        surfaceView.onResume();
        displayRotationHelper.onResume();
    }
複製代碼

demo

模型沒有添加燈光參數, 添加燈光後會更加真實一點

普及篇就寫到這裏了,你們若是感興趣的話後面能夠針對官方demo以及api作一些解讀

[^IMU]: IMU, 慣性測量單元 (Inertial measurement unit , 能夠經過這個視頻直觀得感覺下

[^preload]: 不採用預置方法,可能有兩個緣由: 1. 特殊apk能夠被卸載,一旦卸載手機則沒法支持ARCore,2. 採用特殊時機下載的方式,能夠防止這種狀況,而且支持升級。

相關文章
相關標籤/搜索