天天看點

如何用AR Engine環境Mesh能力實作虛實遮擋

在AR應用中,使用者最不希望看到不真實的穿模現象發生,如虛拟形象部分身體陷入牆壁之中,或者未碰到牆壁卻已無法移動,這種不真實的互動十分影響使用者體驗。那如何才能讓避免虛拟物體的穿模問題呢?使用AR Engine的環境Mesh能力就能幫助開發者解決這個問題。

效果展示

如何用AR Engine環境Mesh能力實作虛實遮擋

實作方法

AR Engine提供實時計算并輸出畫面環境Mesh資料的能力。通過環境Mesh能力虛拟角色可以準确識别目前所處三維空間的情況,讓虛拟物體不僅僅能放置在水準面和垂直面上,還可以放置在任意可重建的曲面上。開發者可利用重建的環境Mesh實作虛實遮擋和碰撞檢測,可以讓虛拟物體藏在真實物品後,避免現實物體和虛拟物體融合現象的發生,進而實作沉浸式AR體驗。

內建步驟

開發環境要求:

JDK 1.8.211及以上。

安裝Android Studio 3.0及以上:

minSdkVersion 26及以上

targetSdkVersion 29(推薦)

compileSdkVersion 29(推薦)

Gradle 6.1.1及以上(推薦)

在華為終端裝置上的應用市場下載下傳AR Engine服務端APK(需在華為應用市場,搜尋“華為AR Engine”)并安裝到終端裝置。

測試應用的裝置:參見AREngine特性軟硬體依賴表中環境Mesh支援裝置清單。如果同時使用多個HMS Core的服務,則需要使用各個Kit對應的最大值。

開發準備

  1. 在開發應用前需要在華為開發者聯盟網站上注冊成為開發者并完成實名認證,具體方法請參見帳号注冊認證。
  2. 華為提供了Maven倉內建方式的AR Engine SDK包,在開始開發前,需要将AR Engine SDK內建到您的開發環境中。
  3. Android Studio的代碼庫配置在Gradle插件7.0以下版本、7.0版本和7.1及以上版本有所不同。請根據您目前的Gradle插件版本,選擇對應的配置過程。
  4. 以7.0為例:

打開Android Studio項目級“build.gradle”檔案,添加Maven代碼庫。

在“buildscript > repositories”中配置HMS Core SDK的Maven倉位址。

buildscript {
    	repositories {
        	google()
        	jcenter()
        	maven {url "https://developer.huawei.com/repo/" }
    	}
}
           

打開項目級“settings.gradle”檔案,配置HMS Core SDK的Maven倉位址

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    		repositories {
       			 repositories {
           			 	google()
            			jcenter()
            			maven {url "https://developer.huawei.com/repo/" }
       			 }
   			 }
}
           
  1. 添加依賴 在“dependencies”中添加如下編譯依賴:
dependencies {
    implementation 'com.huawei.hms:arenginesdk:{version}
}
           

開發步驟

  1. 建立HitResultDisplay類,這個類根據指定的參數來繪制虛拟對象
Public class HitResultDisplay implements SceneMeshComponenDisplay{
	//初始化VirtualObjectData
	VirtualObjectData mVirtualObject = new VirtualObjectData();
	//在init方法中給mVirtualObject傳入上下文
	Public void init(Context context){
		mVirtualObject.init(context);
		//傳入材質屬性
		mVirtualObject.setMaterialProperties();
	}
	//在onDrawFrame方法中傳入ARFrame,用來擷取光照估計
	Public void onDrawFrame(ARFrame arframe){
		//擷取光照估計
		ARLightEstimate le = arframe.getLightEstimate();
		//擷取目前相機視野的像素強度
		lightIntensity = le.getPixelIntensity();
		//擷取好之後,需要給mVirtualObject中一些方法傳入資料
		mVirtualObject.draw(…,…,lightIntensity,…);
		//建立handleTap方法傳入ARFrame對象來擷取坐标資訊
		handleTap(arframe);
	}
     //實作handleTap方法
     Private void handleTap(ARFrame frame){
        //用ARFrame對象調用hitTest
        List<ARHitResult> hitTestResults = frame.hitTest(tap);
        //檢測平面是否被擊中,是否在平面多邊形中被擊中
        For(int i = 0;i<hitTestResults.size();i++){
            ARHitResult hitResultTemp = hitTestResults.get(i);
            Trackable = hitResultTemp.getTrackable();
            If(trackable instanceof ARPoint && ((ARPoint) trackable).getOrientationMode() == ARPoint.OrientationMode.ESTIMATED_SURFACE_NORMAL){
                isHasHitFlag = true;
                hitResult = hitResultTemp;
            }
        }
     }
}
           
  1. 建立SceneMeshDisplay類,用來渲染場景網絡
Public class SceneMeshDiaplay implements SceneMeshComponenDisplay{
	//需要在init中實作openGL的一些操作
	Public void init(Context context){}
	//在onDrawFrame方法中擷取目前對應的環境Mesh
	Public void onDrawFrame(ARFrame arframe){
		ARSceneMesh arSceneMesh = arframe.acquireSceneMesh();
		//建立一個用來更新資料的方法把arSceneMesh傳入進去
		updateSceneMeshData(arSceneMesh);
         //arSceneMesh使用完之後需要釋放
         arSceneMesh.release();
	}
     //實作這個方法用來更新資料
     Public void updateSceneMeshData(ARSceneMesh sceneMesh){
         //傳回目前視角下環境Mesh頂點坐标數組
         FloatBuffer meshVertices = sceneMesh.getVertices();
         //傳回目前視角下環境Mesh三角面片頂點索引的數組
         IntBuffer meshTriangleIndices = sceneMesh.getTriangleIndices();
     }
}
           
  1. 建立SceneMeshRenderManager類,這個類來提供與外部場景相關的渲染管理器,

包括虛拟對象渲染管理

public class SceneMeshRenderManager implements GLSurfaceView.Render{
	//初始化更新網絡資料和執行渲染的類
	private SceneMeshDisplay mSceneMesh = new SceneMeshDisplay();
	//初始化繪制虛拟對象的類
	Private HitResultDisplay mHitResultDisplay = new HitResultDisplay();
	
	//實作onSurfaceCreated()方法
	public  void  onSurfaceCreated(){
		//需要給mSceneMesh 類和mHitResultDisplay類傳入 context
		mSceneMesh.init(mContext);
		mHitResultDisplay.init(mContext);
}	
	
	//實作onDrawFrame()方法;
	public void onDrawFrame(){
		//用ARSession對象來配置camera。
		mArSession.setCameraTexTureName();
		ARFrame arFrame = mArSession.update();
		ARCamera arCamera = arframe.getCamera();
		//把SceneMeshDisplay類需要的資料傳過去
		mSceneMesh.onDrawFrame(arframe,viewmtxs,projmtxs);
}
}
           
  1. 建立SceneMeshActivity用來展示功能
public class SceneMeshActivity extends BaseActivity{
	//提供與外部場景相關的渲染管理器,包括虛拟對象渲染管理類。
	private ScemeMeshRenderManager mSceneMeshRenderManager;
	//用來管理AR Engine的整個運作狀态,
	private ARSession mArSession;
//需要初始化一些類和對象
	protected void onCreate(Bundle savedInstanceState){
	mSceneMeshRenderManager = new SceneMeshRenderManager();
}
//在onResume方法中初始化ARSession
protected void onResume(){
	//初始化ARSession
	mArSession = new ARSession(this.getApplicationContext());
	//基于session參數建立ARWorldTrackingConfig對象
	ARConfigBase config = new ARWorldTrackingConfig(mArSession);
	//需要把ARSession傳給SceneMeshRenderManager
	mSceneMeshRenderManager.setArSession(mArSession);
//需要開啟mesh,用config調用setEnableItem方法
config.setEnableItem(ARConfigBase.ENABLE_MESH | ARConfigBase.ENABLE_DEPTH);
}
}
           

具體實作可參考示例代碼