🌎

【Android/Kotlin】AR機能を色々なパターンで試してみる

に公開

はじめに

この記事では Android 端末での AR 機能について、色々なパターンで動作検証してみた内容を記載しています。
主な検証パターンとしては以下です。

  • SceneView:ARSceneView
  • SceneView:SceneView
  • Scene Viewer
  • model-viewer
  • Kotlin アプリから Unity 呼び出し


※ AR 機能についての大まかな全体像については、以下の記事にまとめていますので、そちらをご参照ください。

https://zenn.dev/ncdc/articles/ar_composition

動作環境・備考

  • Android Studio Narwhal Feature Drop | 2025.1.2
  • Kotlin + Jetpack Compose
  • 検証端末: Pixel 8a (Android 14)
  • サンプルコードでは View 部分の実装のみ記載です。
  • 動作確認をしたいだけの実装であるため、コーディングのお作法的なところは御愛嬌ください。
  • 動作検証用のアプリは、AndroidStudio で New Project / Empty Activity で作成したアプリをベースにしています。
  • 動作確認に使用する 3D モデルは、以下のリポジトリのものを使用しています。
    https://github.com/KhronosGroup/glTF-Sample-Assets/tree/main/Models


SceneView:ARSceneView

まずはアプリ内で AR 機能を完結させるパターンを試してみます。

3D コンテンツのレンダリングには、SceneView ライブラリを使用します。

https://github.com/SceneView/sceneview-android

SceneView ライブラリは主に以下のコンポーネントを提供してくれます。
ここでは AR 機能を試したいので、ARSceneView コンポーネントを使用します。

  • Sceneview: 3D rendering capabilities using Filament
  • ARSceneview: Augmented Reality capabilities using Filament and ARCore


ARSceneView を使う際は、ライブラリ内部で ARCore を参照しているため、特段アプリ側で ARCore の import 等は不要となります。
ただ、内部的には ARCore を使用しているため、ARCore で必要となる権限設定はアプリ側でも行う必要があります。

https://developers.google.com/ar/develop/java/enable-arcore?hl=ja

toml
# libs.versions.toml
# build.gradleへの追記は省略
[versions]
+ lifecycleViewmodelCompose = "2.6.1"
+ arsceneview = "2.3.0"

[libraries]
+ arsceneview = { group = "io.github.sceneview", name = "arsceneview", version.ref = "arsceneview" }
xml
# AndroidManifest.xml
+ <uses-permission android:name="android.permission.CAMERA" />
+ <uses-feature android:name="android.hardware.camera.ar" android:required="true" />
+ <meta-data android:name="com.google.ar.core" android:value="required" tools:replace="android:value" />


続いて、README の Basic Usage を参考に、アプリの View 箇所に、ARSceneView を使った AR 表示のコードを実装します。
3D モデルファイルについては、assets/models/ に配置しておきます。

val engine = rememberEngine()
val modelLoader = rememberModelLoader(engine)

// 3Dモデルノードを一度だけ作成して保持
val modelNode = remember {
    ModelNode(
        // assetsフォルダから3DモデルファイルをロードしてModelInstanceを作成
        modelInstance = modelLoader.createModelInstance(
            assetFileLocation = "models/ABeautifulGame.glb"
        ),
        // モデルのスケール設定
        scaleToUnits = 0.5f
    ).apply {
        // モデルの初期位置を設定
        // x: 0f (左右中央)
        // y: -0.5f (中央からやや下)
        // z: -1f (カメラから1メートル前方)
        position = io.github.sceneview.math.Position(x = 0f, y = -0.5f, z = -1f)
    }
}

Box(modifier = Modifier.fillMaxSize()) {
    /**
     * AR設定と3Dモデルの表示
     *
     * - engine: Filamentレンダリングエンジン
     * - modelLoader: 3Dモデル(GLB/GLTF)をロードするためのローダー
     * - childNodes: シーンに配置する3Dモデルノードのリスト
     * - sessionConfiguration: ARCore セッションの設定
     */
    ARScene(
        modifier = Modifier.fillMaxSize(),
        engine = engine,
        modelLoader = modelLoader,
        childNodes = rememberNodes {
            add(modelNode)
        },
        sessionConfiguration = { session, config ->
            /**
             * デプスモード(深度認識)の設定
             *
             * デバイスがサポートしている場合は自動デプス検出を有効化
             * これにより3Dオブジェクトが現実世界の物体の前後関係を正しく認識可能
             */
            config.depthMode = when (session.isDepthModeSupported(Config.DepthMode.AUTOMATIC)) {
                true -> Config.DepthMode.AUTOMATIC
                else -> Config.DepthMode.DISABLED
            }
            /**
             * 光推定モードの設定
             *
             * 現実世界の照明条件を3Dモデルに反映し、よりリアルな描画を実現
             */
            config.lightEstimationMode = Config.LightEstimationMode.ENVIRONMENTAL_HDR
        }
    )
}

簡易設定ではありますが、これぐらいのコード量で最低限の AR 表示が実装できました。
画像ではわかりづらいですが、ARCore が提供する環境認識モーショントラッキングも機能していることが確認できました。
→ 照明が暗い空間で AR を起動すると、3D モデルの明るさも連動して暗くなることが確認できました。


SceneView: SceneView

続いて、SceneView ライブラリのもう一つの主機能である SceneView コンポーネントを使用してみたいと思います。

SceneView コンポーネントは、AR 機能を使用しない純粋な 3D モデルのレンダリング機能を提供してくれます。
なので、アプリ側としても ARCore の導入は不要ですし、ライブラリ内部でも ARCore は参照されないため、ARCore で必要となる権限等の設定も不要です。

先ほど同様、README の Basic Usage を参考に、アプリの View 箇所に、SceneView を使って 3D モデルをレンダリングするコードを実装します。

val engine = rememberEngine()
val modelLoader = rememberModelLoader(engine)

// 3Dモデルノードを一度だけ作成して保持
val modelNode = remember {
    ModelNode(
        // assetsフォルダから3DモデルファイルをロードしてModelInstanceを作成
        modelInstance = modelLoader.createModelInstance(
            assetFileLocation = "models/ABeautifulGame.glb"
        ),
        // モデルのスケール設定
        scaleToUnits = 0.5f
    ).apply {
        // モデルの初期位置を設定
        // x: 0f (左右中央)
        // y: -0.5f (中央からやや下)
        // z: -2f (カメラから2メートル前方)
        position = Position(x = 0f, y = -0.5f, z = -2f)
    }
}

Box(modifier = Modifier.fillMaxSize()) {
    /**
     * 3Dシーンの設定
     *
     * - engine: Filamentレンダリングエンジン
     * - modelLoader: 3Dモデル(GLB/GLTF)をロードするためのローダー
     * - childNodes: シーンに配置する3Dモデルノードのリスト
     */
    Scene(
        modifier = Modifier.fillMaxSize(),
        engine = engine,
        modelLoader = modelLoader,
        childNodes = rememberNodes {
            add(modelNode)
        }
    )
}

同じく簡易設定ですが、ライブラリを使用すると、比較的容易に 3D モデルのレンダリングが実装できました。

SceneView の利用の場合は、ARCore の特徴である環境認識モーショントラッキング等は一切行われていないため、AR 機能ではなく、単なる 3D モデルの描画となります。


Scene Viewer

今までの実装では、アプリ独自(アプリ内で完結)の機能として、AR 機能や 3D モデルレンダリング機能を実装するパターンでした。

次は Android 端末に標準搭載されている Scene Viewer 機能を使用して、AR プレビューを表示してみたいと思います。

https://developers.google.com/ar/develop/scene-viewer?hl=ja


Scene Viewer は、Android 端末に標準搭載されている"Google アプリ"上で動作する AR プレビュー機能です。

アプリ側で AR 機能を一から開発するコストをかけずに、手軽に AR コンテンツを提供したい場合に有効です。


Chrome から AR プレビューを起動


Scene Viewer はブラウザからでも呼び出せますが、まずはブラウザからではなく、アプリから呼び出すパターンを試してみます。

@Composable
fun SceneViewerScreen() {
    // サンプルの3DモデルURL(glTF形式)
    val modelUrl = "https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/Avocado/glTF-Binary/Avocado.glb"

    // Scene Viewer の Intent URI を構築
    val sceneViewerUri = "https://arvr.google.com/scene-viewer/1.0".toUri()
    .buildUpon()
    .appendQueryParameter("file", modelUrl)
    .appendQueryParameter("mode", "ar_only")
    .build()

    // Scene Viewer を起動
    val intent = Intent(Intent.ACTION_VIEW).apply {
        data = sceneViewerUri
        setPackage("com.google.android.googlequicksearchbox")
    }

    val context = LocalContext.current
    context.startActivity(intent)
}

AR プレビューが、アプリからでも呼び出せることが確認できました。


※ プレビュー画面で描画できるまでに時間がかかるため一部カットしています


model-viewer

Web ブラウザから AR プレビューを起動してみるパターンもせっかくなので試してみます。

ブラウザから AR プレビューを起動する場合、Google が提供している model-viewer ライブラリを使用すると良さそうです。

構成としては、アプリ内に WebView を配置し、その中で <model-viewer> を使用した HTML を適当にベタ書きして試してみました。

※ 実際にはモバイルアプリで model-viewer を使用するケースはあまりないかもしれませんが、一応動作確認のために試してみました。

https://modelviewer.dev/


上記のサメの Gif 動画と同様に、一連のプレビュー機能が機能することを確認できました。(画面右下の AR アイコン?を Tap すると AR プレビューも正常に起動しました。)
※ 注意点としては、AR プレビュー(Scene Viewer)は Web 機能であるため、通信環境が必要となる点は一つポイントです。


Kotlin アプリから Unity 呼び出し

最後に、Kotlin で作成した Android アプリから Unity を呼び出すパターンを試してみます。

Unity 側で、Unity as a Library という機能が提供されており、これを使用すると、ネイティブ実装の Android アプリから Unity の機能を呼び出すことが可能となるみたいです。

https://unity.com/ja/features/unity-as-a-library

Unity についてはあまり詳しくないので、先人の知恵(記事)を参考にしつつ、とりあえずの動作確認はできました。

→ ここだけで結構の量になったので、別記事に切り出しました。

<作成中...>


まとめ

Android アプリでの超基本的な AR 機能について、色々と動作検証してみた内容を記載しました。

ライブラリを用いると、簡単な AR 機能であれば比較的容易に実装できることがわかりました。

ただ、ワールド座標やオブジェクト座標などの各種座標のカスタムや、AR のマーカー対応だったり、諸々の高度な実装を実現したい場合は、ARCore や AR 技術の知識が更に必要となってくるなと感じました。
必要に応じて ARCore や AR 自体の知識も深めていきたいところです。

参考

【参考リンク】
GitHubで編集を提案
NCDCエンジニアブログ

Discussion