はじめに
初めまして、イケメンテックラボでエンジニアをしています寺林です。
イケメンテックラボでは様々な表現の研究や最新の技術の検証などを行なっています。その一環で今回はGoogleの提供しているARのSDKであるARCoreを使ってみてとても簡単にARを実現することができたので紹介をします!
まずはそもそもARとは何なのか、どういう技術なのかについて説明します。
※この記事はUnityをすでに使ったことがあり、ビルドなどの作業ができる人向けになります。
そもそもARとは?
ARとはAugmented Realityの略で拡張現実という意味です。カメラやレンズを通じて映される現実世界に映像や3Dモデル、UIなどを合成し表示する(拡張する)技術です。
動画のようにデバイスを通じて現実世界にある物の上にCG映像などを乗せる事ができます。
最近ではFacebookがARグラス開発に人材を大量に投入したりと、話題が尽きません。(https://www.gizmodo.jp/2019/01/facebook-arglasses-human-resource.html)
ARCoreとは

ARCoreはGoogleが提供しているSDKです。平面認識を使ったマーカーレスARや画像を認識してオブジェクトを出現させるマーカーARなどが扱えるようになります。
どんなことができるかはGoogleの出している動画を見れば大体理解できると思います。
ARCoreの正式版がリリースされたのは昨年2月、まだ出て1年経っていませんが積極的にアップデートが行われており、現在注目されているSDKのひとつです。今回はこのARCoreをUnityで使ってみました
必要なもの
ARCoreは対応端末が現状決して多くないです。
持っている端末がこの中にない場合は残念ですがARCoreは使えません。
これから対応端末は増えていくと思います。
またiOS端末でもARCoreは動くのですが今回はAndroid端末で動かしています。
確認環境
・この記事はUnity2018.3.1で実装をしています
・ARCoreSDKは1.6で実装しました。
・検証端末としてPixel3を使用しています
準備
まず検証する上でAndroidビルドができるUnity環境が必要です。
AndroidStudioとAndroidSDK7.0(API Level 24)以上とNDKが必要です。
Unity側にもAndroidBuildSupportを入れておく必要があります。
その部分の設定はできているという前提で進めさせていただきます。
ARCoreのUnity用SDKはこちらからDLします
最新版の.unitypackageを落としてきましょう。

新規Unityプロジェクトを作成しパッケージをインポートしましょう。

サンプルシーンをビルドする
インポートが終わったらまずはサンプルシーンをビルドしてみましょう。
Assets/GoogleARCore/Examples/HelloAR/Scenes/HelloAR を開きます。

なにやらいろんなオブジェクトがありますが先ずはビルドしましょう。
ビルド設定
File > BuildSettingsを開きます。

Androidを選択し、Switch Platformをクリックしビルドの対象をAndroidにします。
PlayerSettingsをクリックし以下の設定を変更します。
- PackageNameを変更、重複のない、ユニークなものに変更しましょう

- Minimum API LevelとTarget API LevelをAndroid7.0以上に設定します


- ARCore Supportedにチェックを入れます

ビルド
設定ができたら早速ビルドしてみましょう。
USBデバッグが有効になっているARCore対応端末を接続しBuild And Runをクリックしビルドをします。ビルドが成功するとアプリが立ち上がります。

アプリが立ち上がるとカメラのビューが出てきます。
この状態はARCoreが平面を探している状態になります。 端末を少し動かしたりして平面を探してやります。

平面を見つけるとその部分がこのようにグリッド状に表示されます。表示されている部分がARCoreで平面として認識されている範囲になります。この範囲内をタップしてやると…

このようにAndroidのキャラクターが出現します。いっぱい出せます。
画像を写して表示させるマーカーARと違い、指定した場所に出現させることができます。
ちゃんと横からも見れます。しっかりと地に足をつけてますね

さて、では次にこのサンプルがどのように動作しているのか軽く見てみましょう
サンプルシーンの作りを見てみる
サンプルシーンのオブジェクトを改めて見てみましょう。

上から順にどういった役割をしているか説明していきます。
Example Controller
ExampleControllerにはHelloARControllerというスクリプトがアタッチされています。この中で行なっていることは大きく分けて3つです
- 権限のチェック(Androidデバイスのカメラの権限)
- タップしてオブジェクトを平面上に配置する処理
- UIの表示制御
タップしてオブジェクトを生成する部分のコードは以下の部分です
HelloARController.cs 100行目より
Touch touch; if (Input.touchCount < 1 || (touch = Input.GetTouch(0)).phase != TouchPhase.Began) { return; } // Raycast against the location the player touched to search for planes. TrackableHit hit; TrackableHitFlags raycastFilter = TrackableHitFlags.PlaneWithinPolygon | TrackableHitFlags.FeaturePointWithSurfaceNormal; if (Frame.Raycast(touch.position.x, touch.position.y, raycastFilter, out hit)) { // Use hit pose and camera pose to check if hittest is from the // back of the plane, if it is, no need to create the anchor. if ((hit.Trackable is DetectedPlane) && Vector3.Dot(FirstPersonCamera.transform.position - hit.Pose.position, hit.Pose.rotation * Vector3.up) < 0) { Debug.Log("Hit at back of the current DetectedPlane"); } else { // Choose the Andy model for the Trackable that got hit. GameObject prefab; if (hit.Trackable is FeaturePoint) { prefab = AndyPointPrefab; } else { prefab = AndyPlanePrefab; } // Instantiate Andy model at the hit pose. var andyObject = Instantiate(prefab, hit.Pose.position, hit.Pose.rotation); // Compensate for the hitPose rotation facing away from the raycast (i.e. camera). andyObject.transform.Rotate(0, k_ModelRotation, 0, Space.Self); // Create an anchor to allow ARCore to track the hitpoint as understanding of the physical // world evolves. var anchor = hit.Trackable.CreateAnchor(hit.Pose); // Make Andy model a child of the anchor. andyObject.transform.parent = anchor.transform; } }
画面をタップした際にそのタップした場所に平面が存在するかどうかを判断するのが以下の部分です。
HelloARController.cs 108行目
TrackableHit hit; TrackableHitFlags raycastFilter = TrackableHitFlags.PlaneWithinPolygon | TrackableHitFlags.FeaturePointWithSurfaceNormal; if (Frame.Raycast(touch.position.x, touch.position.y, raycastFilter, out hit)) {
Frame.Raycast関数を使うとタッチしたスクリーン座標の先にARCoreで検知している平面が存在すればtrueを返し、その平面の情報をhit変数にいれてくれます、
これによりタップした平面の位置などを知ることができるのでそこにオブジェクトを生成することで平面上に物が置かれているように見せる事ができます。
ARCoreDevice
ARCoreDeviceにはARCoreSessionというスクリプトがアタッチされています

ARCoreSessionではSessionConfigに設定されている設定を元にARの設定を行い実行します。
SessionConfigの中身はこんな感じです。

設定できる項目はこんな感じです
MatchCameraFramerate … デバイスのカメラのフレームレートに合わせてUnity側のフレームレートを遅延させます。これを有効にしておくと無駄なレンダリングも減るのでバッテリー消費を抑えることができます。基本有効でいいと思います。
PlaneFindingMode … ARCoreの平面検出モードを設定します。水平面のみ、垂直面のみ、両方などを設定できます。ARアプリの用途に合わせて変更します。
EnableLightEstimation … 環境光推定を使うかどうかです。環境光推定については後ほど紹介します。
EnableCloudAnchor … クラウドアンカーを有効にするかどうかです。アプリ上で生成したアンカーをネットワークを介して他のユーザーと共有させる機能です。今回は使いませんのでオフにしています。
AugmentedImageDatabase … マーカーARを利用する際に使用するマーカーのデータベースファイルを入れます。今回は利用しないので無しです。
CameraFocusMode … カメラのフォーカスモードを設定します。Fixedが固定、Autoがオートフォーカスです。アプリによりますが基本Autoでいいのかなと思います。
First Person Camera
ARCoreDeviceの子オブジェクトとしてFirstPersonCameraがあります。
なんとなく想像つくと思いますがこれがARをやる際のカメラ映像を写す役割を持っています。

Camera、TrackablePoseDriver、ARCoreBackgroundRendererの3つがアタッチされています。
Cameraは普通にUnityのカメラです。従来のUnityで使っているのと変わらず設定も特に特別なことはしません。
TrackedPoseDriverはデバイスの動きを検知してGameObjectの位置を変更する時に使う。。みたいなものだと思います。現実世界(カメラ映像)に合わせてAR空間上にあるオブジェクトを動かす際に必要です。
ARCoreBackgroundRendererはその名の通り、カメラから入力されてくる映像を背景としてレンダリングするものです。特にいじることはないと思います。専用に用意されたMaterial(ARBackground.mat)をセットしてやることで背景にカメラ映像を写すことができます。
Plane Generator

これは平面検知時に表示される格子状のオブジェクトを生成するものです。
その部分の見た目を変えたい!というときにはここを弄ることになります(後々解説したいと思います)
PointCloud

PointCloud(点群)は、平面を探しているときに検出される特徴点を視覚化するためのものです。特徴点とは画像内で際立っている点の事を言います。
ARCoreではこの特徴点を元に平面の推定などを行なっているようです。
なので逆に白い床やテーブルと言った、特徴の少ない物の場合平面の推定がうまく行かない場合があります。

iPhoneの林檎マークのあたりに青い点が見えると思います。これが特徴点です。
この点を表示するのに使います。
Environmental Light

これはARCoreの機能の一つ、環境光推定を使用するために使います
環境光推定とはどういったものかというと、HelloARシーンでこのようにモデルを出している状態で、

部屋の電気を消すなどしてカメラの映像を暗くします。すると、

このようにモデルも暗くなるという機能です。
この機能を提供しているコンポーネントがEnvironmentalLightになります。
この機能を利用するためには専用のシェーダーが必要です。というのも、このEnvironmentalLight.csのなかに
// Apply color correction along with normalized pixel intensity in gamma space. Shader.SetGlobalColor("_GlobalColorCorrection", Frame.LightEstimate.ColorCorrection * normalizedIntensity); // Set _GlobalLightEstimation for backward compatibility. Shader.SetGlobalFloat("_GlobalLightEstimation", normalizedIntensity);
このような処理があります。
Shader.SetGlobalColor、Shader.SetGlobalFloatメソッドは全てのシェーダーに対して、第一引数の変数に値を入れるというものです。
そのため自前のシェーダーでこの機能を使う場合は_GlobalColorCorrectionや_GlobalLightEstimationといった変数をシェーダー内で定義してやる必要があります(今回は導入記事なので深くは触れません)
特にそういった事をしない場合はHelloAR用にサンプルのシェーダーが用意されていますのでこちらを使いましょう。

ARCore/DiffuseWithLightEstimation というシェーダーです。画面タップ時に生成されるAndroidのキャラクターのprefabに設定されています。
このシェーダーを好きなモデルなどに適用させれば環境光推定の結果を受ける事ができます。
以上がHelloARシーンの作りの簡単な説明になります。DirectionalLightやCanvas、EventSystemは特にARCore特有のものはないので説明はしません。
Unity上で動作確認をする
ARはデバイスのカメラを使う都合上、デバイス上での実行が必要不可欠です。
しかし毎回毎回ビルドしているとやはり時間が掛かってしまい、決して開発効率が良いとは言えません。
ARCoreはではデバイスとPCをUSBで接続するだけでUnity上で確認できるよう、Instant Previewという機能が提供されています。

File→ProjectSettings→ARCoreを開き、

Instant Preview enabledにチェックを入れます。

デバイスを接続してPlayするとこのようなウインドウが出てくるのでOkayをクリックします。
するとAndroidデバイスにInstantPreviewアプリがインストールされ、以後実行時に自動的に立ち上がるようになります。

多少デバイスよりは粗くなりますがカメラの映像がUnity上にも表示されるようになります。カメラの映像が遅れたりといったことも特になく、快適です。

デバイス側で画面をタップすればデバイス実行時と同じようにオブジェクトを生成できます。
ただし、この機能を使う上で一点だけコード側で対応が必要になります。
Inputを使っているクラスで、usingをする際に
#if UNITY_EDITOR using Input = InstantPreviewInput; #endif
このようにエディタの場合だけInputのなかにInstantPreviewInputを入れてやる必要があります。
こうすることで接続中のデバイスの画面タッチを受け取ってくれるようになります。
まとめ
ARCoreの導入からサンプルシーンのビルドまで紹介しました。まだARCoreは対象端末がそこまで多くはないですが平面検知の精度の高さやデバッグのしやすさ(大事)からこれから更に注目されていくと思います。
次回以降もAR関係で紹介記事を書いていこうと思いますのでよろしくお願いします!