おもちゃラボ

Unityで遊びを作ってます

Unityで始めるARKit入門 光源推定編

前回はUnityとARKitを使って、タップしたところにリンゴを配置するサンプルを作りました🍎

nn-hokuson.hatenablog.com

今回はARKitで光源推定をする方法を紹介します。ARでライトを配置する場合、そのままライトをおいただけでは光源の強さや色合いが決まっているため、モデルが映像になじまず違和感がある絵になってしまいます。

ARKitでは撮影された映像から光源の強さと色合いを推定できる機能があるので、それを使用してみましょう。

f:id:nn_hokuson:20180925231804j:plain

ARKitの光源推定でできること

 ARは様々な環境で撮影するため、その環境に応じたライトの設定をすることで、より自然なARに見せることができるようになります。ARKitでもARKit1.5からは光源推定ができる機能が提供されるようになりました。

このARKitの光源推定で推定できる値は次の2つになります。

  • ライトの強さ
  • ライトの色温度

どちらからライトが当たっているか、というライトの位置と方向は推定できないことに注意してください。ARKitで得られる光源の推定値をUnityのライトに設定することで、より自然なARを実現できます。

f:id:nn_hokuson:20180925233222p:plain:w450

光源推定を使ってみる

といってもUnityの場合はデフォルトで光源推定が有効になっているので、やることはありません(笑)1からシーンを作る場合を考えて、設定する項目を見ておきましょう。必要なのは次の二点です。

  1. Enable Light Estimationのチェックを入れる
  2. ライトにUnityARAmbientスクリプトをアタッチする

まずはヒエラルキーウインドウのARCameraManagerの中の「Enable Light Estimation」のチェックボックスにチェックを入れます。

f:id:nn_hokuson:20180925204013p:plain:w350

続いてヒエラルキーウインドウのDirectional lightに、Assets/UnityARKitPlugin/Plugins/iOS/UnityARKitHelpers/に含まれるUnityARAmbientスクリプトをアタッチします。

f:id:nn_hokuson:20180925204042p:plain:w350

このスクリプトの中身を見ておきましょう。

using System.Runtime.InteropServices;
using UnityEngine.XR.iOS;

namespace UnityEngine.XR.iOS
{
  public class UnityARAmbient : MonoBehaviour
  {

    private Light l;

    public void Start()
    {
      l = GetComponent<Light>();
			UnityARSessionNativeInterface.ARFrameUpdatedEvent += UpdateLightEstimation;
    }

    void UpdateLightEstimation(UnityARCamera camera)
    {
      if (camera.lightData.arLightingType == LightDataType.LightEstimate) {
        float newai = camera.lightData.arLightEstimate.ambientIntensity;
        l.intensity = newai / 1000.0f;
        l.colorTemperature = camera.lightData.arLightEstimate.ambientColorTemperature;
      }
    }

    void OnDestroy() {
      UnityARSessionNativeInterface.ARFrameUpdatedEvent -= UpdateLightEstimation;
    }
  }
}

Startメソッドの中では、ARKit側でARFrameUpdatedEventが呼びだれたタイミング(フレーム更新時)でUnityのUpdateLightEstimationが実行されるようにイベントを追加しています。

UpdateLightEstimationではARKitで得られたライトの推定強度(ambientIntensity)と推定色温度(ambientColorTemperature)をUnityのDirectional lightに設定しています。

実行結果

Unityでビルドして実行してみましょう。部屋のライトをつけたときと消したときで見え方が変わったら成功です。

f:id:nn_hokuson:20180925232049g:plain:w500

Unityで始めるARKit入門 モデル配置編

前回はUnityからARKitを使う方法と、デモを動かすところまで実行しました。今回はタッチしたところにモデルを配置するプログラムを作ってみましょう。

f:id:nn_hokuson:20180920201828j:plain

シーンを作成する

今回も前回ダウンロードしたARKitのUnity用プラグインを使用します。
まだ、入手していない方はこちらからダウンロードしてください。

https://bitbucket.org/Unity-Technologies/unity-arkit-plugin/get/arkit2.0_beta.zip

次に今回作るARKitのアプリ用にシーンを用意します。
/Assets/UnityARKitPlugin/Examples/ARKitSceneを一度開きます。メニューから「File」→「Save Scene as...」を選択して、「Test」という名前で保存しましょう。

f:id:nn_hokuson:20180920195520p:plain:w350

これでTestというシーンが新しく作られます。これを使って実験していきましょう。

不要なオブジェクトを削除する

ヒエラルキービューには色々なARKitで使うオブジェクトがすでに配置されています。それぞれに役割がある(各内容は前回の記事参照)のですが、ヤヤコシイので不要なものは削除しましょう。

nn-hokuson.hatenablog.com

今回は次のオブジェクトが不要ですので、ヒエラルキービューから削除してください。

  • RandomCube
  • HitCubeParent
  • GeneratePlanes
  • ARKitControl
  • PointCloudExample
  • AR3DOFCameraManager

次のような状態になっていればOKです。PointCloudParticleExampleは検出された特徴点を表示するもので、デバッグ用に便利なので残しています。

f:id:nn_hokuson:20180920195539p:plain:w270

タッチを検出するプログラムを作る

続いてタッチを検出するプログラムを作ります。プロジェクトウィンドウで右クリックし、「Create」→「C# Script」を選択してください。ファイル名は「TouchController」にしました。

f:id:nn_hokuson:20180920195549p:plain:w270

スクリプトファイルが作成できたら、次のスクリプトを入力してください。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.iOS;

public class TouchController : MonoBehaviour
{
    public GameObject cubePrefab;

    void HitTest(ARPoint point)
    {
        List<ARHitTestResult> hitResults = UnityARSessionNativeInterface
            .GetARSessionNativeInterface()
            .HitTest(point, ARHitTestResultType.ARHitTestResultTypeExistingPlaneUsingExtent);

        // 平面とあたっていた場合
        if (hitResults.Count > 0)
        {
            GameObject g = Instantiate(cubePrefab);
            g.transform.position = UnityARMatrixOps.GetPosition(hitResults[0].worldTransform);
            g.transform.rotation = UnityARMatrixOps.GetRotation(hitResults[0].worldTransform);
        }            
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.touchCount > 0 )
        {
            var touch = Input.GetTouch(0);
            if (touch.phase == TouchPhase.Began)
            {
                var screenPosition = Camera.main.ScreenToViewportPoint(touch.position);
                ARPoint point = new ARPoint
                {
                    x = screenPosition.x,
                    y = screenPosition.y
                };

                // 平面との当たり判定
                HitTest(point);
            }
        }
    }
}

Updateメソッドの中でタッチの検出を行っています。タッチされた場合は、ScreenToViewportPointメソッドを使ってタッチ座標をスクリーン座標からビュー座標に変換しています。

平面との当たり判定はHitTestメソッドの中で行っています。当たり判定に関してはUnityの機能ではなくARKitに用意されているHitTestメソッドを使います。

HitTestメソッドの第二引数には当たり判定を行う対象を指定します。指定できるオプションには次のようなものがあります。

オプション名 役割
ARHitTestResultTypeFeaturePoint ARKittで検出された特徴点
ARHitTestResultTypeEstimatedHorizontalPlane 平面の推定値
ARHitTestResultTypeExistingPlane 検出された平面
ARHitTestResultTypeExistingPlaneUsingExtent 検出された平面の内側領域


ここでは、検出された面に対して当たり判定を行いたいので、ARHitTestResultTypeExistingPlaneUsingExtentを指定しています。

タッチした座標が平面とあたっていた場合は、Prefabに登録したモデルのインスタンスを作成しています。SwiftでARKitを使う場合は、SceneKitでNodeを登録・・・ということが必要になりますが、ここらへんが簡単にできるのがUnityの強みですね!

配置する3DモデルのPrefabを作る

配置するモデルは何でもかまわないので、今回はAsset Storeから次のものを利用させていただきました。

Prefabを登録する際は、そのスケールに注意してください。Unityでは標準の立方体(Create→3D Object→Cube)の1辺を1mとして扱うので、その大きさのままARKitで表示してしまうと、大きすぎます。

大きすぎるだけならまだしも、モデルの内側に入っちゃって、あれ表示されてない??なんてこともあるので注意です・・・

最後にスクリプトをアタッチしてPrefabを登録しましょう。ヒエラルキーウインドウのCreate→Create Emptyで空のゲームオブジェクト(Game)を作成し、そこに先程作ったTouchControllerをアタッチしてください。次にインスペクタのPrefab欄に表示したいモデルのPrefabを登録します。

f:id:nn_hokuson:20180920200407j:plain

書き出して実行する

これで今回のアプリは完成です。メニューバーから「File」→「BuildSettings」を選択してビルドウインドウを開いてください。Scene In Buildに「Test.scene」をドラッグ&ドロップします。TargetをiOSにしてから、Buildボタンで書き出してください。

f:id:nn_hokuson:20180920200538j:plain:w400

実機で実行して画面をタップすると、どんどんリンゴが生成されます🍎🍎🍎

f:id:nn_hokuson:20180920202146g:plain:w500

次回は、ARで表示したモデルをより自然に見せるための「光源推定」について紹介します。

nn-hokuson.hatenablog.com

参考書

SwiftでARKitの基本的な機能を学べる書籍「SwiftでつくるARKit超入門」を執筆しました!
128ページ、2000円でBOOTHで絶賛発売中です!
booth.pm

Unityで始めるARKit入門 セットアップ編

先日(2018年9月18日)ようやくiOS12がリリースされて、ARKitもARKit 2.0にバージョンアップされました。ここではARKit 2.0の特徴と、UnityからARKitの機能を使う方法を紹介します。

ARKitの参考書を書きました。BOOTHで販売中です!こちらはUnityではなくswiftを使ったサンプルですが、Unityを使う場合にも役に立つ構成なので、ぜひチェックしてみてください。

booth.pm

今回の目次は次のようになります。

ARKitとは

 ARKit とは、Appleが発表したAR(Augmented Reality)のフレームワークです。ARKit は従来のマーカーを使ったARの仕組みとは大きく異なり、VIO(Visual Inertial Odometry)と呼ばれる技術を使うことで、高い精度でカメラの位置と向きをリアルタイム検出することができます。

 ARKit 1.5からは水平面だけではなく、壁などの垂直面の検出が可能になりました。2018年に発表されたARKit2.0では、AR空間の永続化や複数人でのARの共有など新機能が多数追加され、ますます高機能で使いやすいフレームワークになっています。

f:id:nn_hokuson:20181011215215j:plain

ここではもう少し、ARKit 2.0で追加された機能について見ておきましょう。

ARKit 2.0で追加された機能

ARKit 2.0ではARの機能が大幅に強化されました。追加された主な機能には次のようなものがあります。

ARの永続化と複数人での共有

ARの永続化って・・・日本語的に分かりにくいですね(笑)ARで配置したオブジェクトをずっとそこに置いておける機能、という感じでしょうか。また、複数人でのAR共有は、みんなで一匹のポケモンを倒すイメージですね。遊びが広がりそうです。
f:id:nn_hokuson:20190530214251p:plain:w500

画像トラッキング

画像の検出はARKit1.5のころからできたのですが、あくまでも「検出」でした。画像を動かすとその上のオブジェクトはついてこないという・・・それがARKit2.0からはできるようになりました。Vuforiaなどのライブラリと同じことができるようになったということですね。
f:id:nn_hokuson:20190530215016j:plain:w600

3Dオブジェクトをマーカーにする

ARマーカーといえば2Dの画像がメインでしたが、フィギュアや模型などの3Dオブジェクトもマーカーにできるようになりました。といっても精度はまだあまり良くなくて、ARKit 1.5の頃の画像検出と同じぐらいです。
f:id:nn_hokuson:20190530215433p:plain:w170

環境マップの使用

ARKit 1.5では環境光の強さや色温度を推定して、Unityのエディタ上で配置したライトの値を調節する光源推定という機能がありました。ARKit 2.0では金属などの映り込みもより自然にみせるため、カメラで撮影した映像をモデルに写し込む環境マップという機能が追加されました。
f:id:nn_hokuson:20190530215541j:plain:w170

ARKitに必要なもの

UnityでARKit を動かすためには、ソフトウエアとして次の3点が必要になります。

  1. Unity
  2. Xcode
  3. ARKit Plugin

ARKit 2.0を動かすためにはXcodeは10.0以上、iOSはiOS12以上である必要があります。Xcodeは次のURLからダウンロードできます。

Xcode

Xcode

  • Apple
  • 開発ツール
  • 無料

Unityのバージョン指定はありませんが、なるべく新しいバージョンのUnityをダウンロードしておくと良いでしょう。

unity3d.com

 ARKitはiOS11以降で、A9プロセッサ以上のプロセッサを搭載するiPhoneやiPad(iPhone 6s以降のモデル、iPad Pro以降)が必要になります。ここではXcode10.0とiOS12を使って説明します。

また、UnityからARKit を使うためにはARKit 専用のPluginが必要になります。ARKitとサンプルコードが一緒になったzipが次のサイトで配布されているので、次のURLからダウンロードしてください。

https://bitbucket.org/Unity-Technologies/unity-arkit-plugin/get/arkit2.0_beta.zip
※UnityのAsset Storeで配布されているARKitはARKit1.5のものです。UnityでARKit2.0を使いたい場合は上記URLからダウンロードして下さい。

Unityのプロジェクトを実行してみる

次に先ほどダウンロードしたARKitのプロジェクトを開きます。
「Assets/UnityARKitPlugin/Examples/UnityARKitScene」のシーンをダブルクリックして開いてください。

f:id:nn_hokuson:20180919202320j:plain

とりあえずビルドしてみましょう。
メニューバーから「File→Build Settings」を選択してBuild Settingsウインドウを開き、PlatformをiOSに変更、Scene In Buildから「UnityARKitPlugin/Examples/UnityARKitScene」を選択してください。

f:id:nn_hokuson:20180919201813j:plain:w400

iPhoneの実機でARKitのサンプルを実行してみましょう。ARKit Remoteというアプリを使うとエミュレータでも動作確認できますが、ここではまず実機にインストールして試してみて下さい。

少しカメラを動かすと撮影した映像から特徴点が黄色い点で表示されます。また、平面が見つかった場合は青い枠の四角で表示されます。また、見つかった平面をタップすると、平面の上にのるかたちで立方体が表示されます。

f:id:nn_hokuson:20190616095859j:plain:w550

自分の好きなモデルを表示する

ARKitを使って自分の好きなモデルを表示してみましょう。UnityとARKitを使えば、スクリプトを書かなくても、モデルの変更が出来ます。まずはプロジェクトウィンドウに表示したいモデルをインポートしましょう。ここでは「ひよこ」のモデルを使用します。

次に表示したいモデルをプロジェクトウィンドウからシーンビューにドラッグ&ドロップして、HitCubeに重なるように配置しましょう。

f:id:nn_hokuson:20190614201959j:plain:w600

配置できたら、ヒエラルキーウインドウでドラック&ドロップして、HitCubeParetの子要素にします。

f:id:nn_hokuson:20190614201525p:plain:w300

最後にHitCubeは表示しないようにMesh Rnedererのチェックボックスを外しておきます。これだけでOKです。

f:id:nn_hokuson:20190614202213j:plain

ARKitのプロジェクトをビルドして、差し替えたモデルが表示されていることを確認しましょう!

f:id:nn_hokuson:20190616132013j:plain:w600

各オブジェクトの役割

一通り動作が確認できたところで、ヒエラルキーウインドウに配置されている各オブジェクトの役割を簡単に見ておきましょう。

f:id:nn_hokuson:20180920194617p:plain:w250

PointCloudParticleExample

撮影した画像中から得られた特徴点を表示するコンポーネントです。表示する特徴点の色やサイズ、個数などはこのコンポーネントから変更できます。

GeneratePlanes

ARKitでは画像中の特徴点を多数含むような平面を、床面(または壁面)として検出します。このコンポーネントはARKitで検出された平面を描画します。平面のPrefabはUnityARKitPlugin/Examples/Common/Prefabsに入っています。このままではダサいので、もう少しいい感じの表示に変更する方法は次回以降説明します。

ARKitCameraManager

ARKitの各種設定を行います。設定できる値には次のようなものがあります。

意味
Plane Detection 検出する平面を床にするか壁にするか
Get Point Cloud 特徴点を取得するか
Enable Light Estimation 光源の強さや色温度の推定を行うか
Enable Auto Focus オートフォーカスを使用するかどうか
Environment Texture 環境マップを使用するか、ARKitで自動生成するか

HitCubeParent

見つかった平面をタップしたときにそこに立方体を置くスクリプトです。実際に当たり判定をやっているのは子要素のHitCubeにアタッチされた「UnityARHitTestExample」コンポーネントです。こちらも後ほど詳しく説明します。

Swiftでの勉強のススメ

ARKitをUnityから使う方法を駆け足で見てきました。特にスクリプトを書いたりすることなく簡単にARが表示できてしまうのはやはりUnityの便利さゆえですね。

ただ、Unity経由で毎回実機にインストールするのは手間ですし、機能を一つ一つ試すというのは、あまり向いていません。UnityにはARKit Remoteというリモートデバッグの仕組みもあるのですが、実験するならUnityよりもSwift+Xcodeのほうが実験しやすいかもしれません。

Swiftでやってみたいけど、あんまり触ったことなくて・・・って人のために!!「SwiftでつくるARKit超入門」を執筆しました!128ページ、2000円です。BOOTHで絶賛発売中です!

booth.pm
 
逆に実際のゲームを作るとなるとUnityのほうが断然速いです。
Unityを使ってARKitを学ぶためのまとめ記事はこちらです。
ARKitの各種機能の紹介と、ARKitを使ったもぐらたたきの作り方を紹介しています。
こちらも参考にしてみて下さい。
nn-hokuson.hatenablog.com