前言
Code from HelloArActivity.
Plane detection 是最大最有用的feature,這里講解跟plane相關的知識點。
文檔學習
一、疑點
Sceneform 概覽
通過配置,啟用 ARCore
二、圖像特征點匹配
- 圖像數據庫
- 新建並加入圖片
Bitmap bitmap;
try (InputStream inputStream = getAssets().open("dog.jpg")) { bitmap = BitmapFactory.decodeStream(inputStream); } catch (IOException e) { Log.e(TAG, "I/O exception loading augmented image bitmap.", e); }
int index = imageDatabase.addImage("dog", bitmap, imageWidthInMeters);
- 加載已有數據庫
InputStream inputStream = context.getAssets().open("example.imgdb");
AugmentedImageDatabase imageDatabase = AugmentedImageDatabase.deserialize(inputStream);
從圖像目錄創建圖像數據庫
./arcoreimg build-db --input_images_directory=/path/to/images \ --output_db_path=/path/to/myimages.imgdb
從圖像列表文件創建數據庫(首選)
./arcoreimg build-db --input_image_list_path=/path/to/image_list_file.txt \ --output_db_path=/path/to/myimages.imgdb
- 開始追蹤圖像
config.setAugmentedImageDatabase(imageDatabase);
session.configure(config);
- 循環 Detect 每一幀
// Update loop, in onDrawFrame().
Frame frame = mSession.update(); Collection<AugmentedImage> updatedAugmentedImages = frame.getUpdatedTrackables(AugmentedImage.class); for (AugmentedImage img : updatedAugmentedImages) { // Developers can: // 1. Check tracking state. // 2. Render something based on the pose, or attach an anchor.
if (img.getTrackingState() == TrackingState.TRACKING) { // You can also check which image this is based on getName().
if (img.getIndex() == dogIndex) { // TODO: Render a 3D version of a dog in front of img.getCenterPose().
} else if (img.getIndex() == catIndex) { // TODO: Render a 3D version of a cat in front of img.getCenterPose().
} } }
三、arcoreimg
是一個命令行工具
僅適用於 Android 和 Android NDK 開發者。
它內置於 Unity SDK 和 ARCore Unreal 插件中。
檢查參考圖像的質量:
./arcoreimg eval-img dog.png
四、Cloud Anchor
雲錨點,它允許多個設備使用雲來共享真實世界場景中的對象信息。
這將啟用多個 ARCore 設備用戶之間的協作,以便他們可以共同在公共空間內繪制或操縱AR對象,從每個人的角度正確顯示跟蹤的對象位置。
五、Sceneform
- 添加一個
ArFragment
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.google.ar.sceneform.samples.hellosceneform.HelloSceneformActivity">
<fragment android:name="com.google.ar.sceneform.ux.ArFragment" android:id="@+id/ux_fragment" android:layout_width="match_parent" android:layout_height="match_parent" />
</FrameLayout>
- ArSceneView
Fragment 會自動執行某些規定的檢查后,Fragment 會創建一個 ArSceneView。
- 創建一個可渲染對象
Renderable
是一個 3D 模型,包括可由 Sceneform 在界面上渲染的網格、材料和紋理。
3D模型文件格式:obj文件
由Alias|Wavefront公司為3D建模和動畫軟件"Advanced Visualizer"開發的一種標准,適合用於3D軟件模型之間的互導,也可以通過Maya讀寫。
OBJ文件是一種文本文件,可以直接用寫字板打開進行查看和編輯修改。另外,有一種與此相關二進制文件格式(*.MOD),其作為專利未公開,因此這里不作討論。
(1) 根據一個 3D 資產文件 andy.obj
創建可渲染對象。
apply plugin: 'com.google.ar.sceneform.plugin' sceneform.asset('sampledata/models/andy.obj', 'default', 'sampledata/models/andy.sfa', 'src/main/res/raw/andy')
(2) 將資源加載為可渲染對象的方法
ModelRenderable.builder() .setSource(this, R.raw.andy) .build() .thenAccept(renderable -> andyRenderable = renderable) .exceptionally( throwable -> { Log.e(TAG, "Unable to load Renderable.", throwable); return null; });
- 節點 node
每個節點都包含 Sceneform 對其進行渲染所需的全部信息(包括其位置、屏幕方向和可渲染對象)以及與其進行交互所需的全部信息(包括其碰撞形狀和事件偵聽器)。
可將節點添加到其他節點,從而形成父級-子級關系。 當節點是其他節點的子級時,將隨其父級移動、旋轉和縮放——就好比您的臂部會在您的身體移動時隨之擺動。
一個節點可有多個子級,但只能有一個父級,從而形成一種樹狀結構。 這種結構稱為場景圖。
- Node node = new Node(); node.setParent(arFragment.getArSceneView().getScene()); node.setRenderable(andyRenderable);
六、構建三維場景
-
res > layout 中創建一個布局文件
Node
with
setRenderable(Renderable)
.
- 構建 ViewRenderable
ViewRenderable.builder() .setView(this, R.layout.test_view) .build() .thenAccept(renderable -> testViewRenderable = renderable);
七、以動畫形式呈現節點
The official one is Unity, this is Java version: https://github.com/fjbatresv/arcore_solar_system
- 通過 ObjectAnimator 以動畫形式呈現
聚光燈強度的示例:
final int durationInMilliseconds = 1000; final float minimumIntensity = 1000.0f; final float maximumIntensity = 3000.0f; ValueAnimator intensityAnimator = ObjectAnimator.ofFloat( spotlightNode.getLight(), "intensity", minimumIntensity, maximumIntensity); intensityAnimator.setDuration(durationInMilliseconds); intensityAnimator.setRepeatCount(ValueAnimator.INFINITE); intensityAnimator.setRepeatMode(ValueAnimator.REVERSE); intensityAnimator.start();
重寫節點的 onUpdate()
,在幀與幀之間將其以動畫形式呈現。
@Override public void onUpdate(FrameTime frameTime) { Vector3 cameraPosition = getScene().getCamera().getWorldPosition(); Vector3 cardPosition = infoCard.getWorldPosition(); Vector3 direction = Vector3.subtract(cameraPosition, cardPosition); Quaternion lookRotation = Quaternion.lookRotation(direction, Vector3.up()); infoCard.setWorldRotation(lookRotation); }
- 添加燈
Light spotLightYellow = Light.builder(this, Light.Type.FOCUSED_SPOTLIGHT) .setColor(new Color(android.graphics.Color.YELLOW)) .setShadowCastingEnabled(true) .build();
- 平面顯示
渲染檢測到的平面的默認材料和紋理。
Texture.Sampler sampler = Texture.Sampler.builder() .setMinMagFilter(Texture.Sampler.MagFilter.LINEAR) .setWrapMode(Texture.Sampler.WrapMode.REPEAT) .build(); Texture.builder() .setSource(this, R.drawable.custom_texture) .setSampler(sampler) .build() .thenAccept(texture -> { arSceneView .getPlanerRenderer() .getMaterial() .setTexture(PlaneRenderer.MATERIAL_TEXTURE, customTexture); });
- 陰影
默認情況下,太陽已啟用陰影投射,但燈未啟用。 調用 setShadowCastingEnabled()
可以打開陰影投射。