りゃおの学びログ

ARを中心に学びを記録

【ARCore】AugmentedImageを試す

ARCoreのAugmentedImageを試してみました。

ARCoreでは1.2.0から追加された機能ですが、Viewforiaなどの先行していたARではむしろこっちの方が普通なイメージでした。
ARKitでも1.5で追加された機能です。

開発環境

  • Windows10
  • Unity 2018.1.7f1
  • ARCore SDK for Unity v1.3.0
  • Galaxy S9

ARCoreのサンプルを実行

まずはそのままの設定で実行してみました。
f:id:ryaoucn:20180819093946j:plain
画像に合わせて四隅に額縁が表示されるようです。
プリントアウトしなくてもディスプレイで表示した画像でしっかり検知してくれます。
ただ見ての通り、ターゲットの画像がかなーりぼやけてますので、
"AugmentedImagesSessionConfig"の"Camera Focus Mode"を"Auto"にして実行してみました。
f:id:ryaoucn:20180819093325j:plain
かなりはっきりと差が出ましたね。
AugmentedImageを使う場合はオートフォーカスは有効にした方がよさそうです。

表示するオブジェクトを変えてみる

額縁ではなくユニティちゃんを表示してみます。
まずは"ExampleController"にアタッチされているスクリプト、"AugmentedImageExampleController"を確認してみたところ、
このスクリプト自体はARCoreがあらかじめ"Example Database"に登録されている画像が検出された場合、設定されている"AugmentedImageVisualizerPrefab"を配置するためのもののようです。
"Example Database"は"AugmentedImagesSessionConfig"の中で指定されています。

"AugmentedImageVisualizerPrefab"に設定されている"AugmentedImageVisualizer"には"AugmentedImageVisualizer"スクリプトがアタッチされており、どうやらこのスクリプトで表示するオブジェクトやそのオブジェクトの表示位置を制御しているよう。
ということはこのスクリプトを書き換えればいけそう。

サンプル用の額縁が表示するオブジェクトとして設定されているので、それをユニティちゃんに書き換え、その他いらない部分を削除。
Update()の中で表示位置を設定しているので、画像の真ん中に表示するようにしました。
※このときユニティちゃんのプレハブのRotationのyを180にしておかないと正面をむいてくれません。

namespace GoogleARCore.Examples.AugmentedImage
{
    using System;
    using System.Collections.Generic;
    using System.Runtime.InteropServices;
    using GoogleARCore;
    using GoogleARCoreInternal;
    using UnityEngine;

    public class AugmentedImageVisualizer : MonoBehaviour
    {
        public AugmentedImage Image;

        public GameObject Kohaku_chan;

        public void Update()
        {
            if (Image == null || Image.TrackingState != TrackingState.Tracking)
            {
                Kohaku_chan.SetActive(false);
                return;
            }

            float halfWidth = Image.ExtentX / 2;
            float halfHeight = Image.ExtentZ / 2;
            Kohaku_chan.transform.localPosition = (halfWidth * Vector3.zero) + (halfHeight * Vector3.zero);

            Kohaku_chan.SetActive(true);
        }
    }
}

このスクリプトをユニティちゃんプレハブにアタッチし、ユニティちゃんプレハブを"ExampleController"の"AugmentedImageVisualizerPrefab"に設定します。

結果1

f:id:ryaoucn:20180819121121j:plain
よしよし。

画像ごとに表示するオブジェクトの出し分け

サンプルではどの画像を検出しても同じ額縁を表示するようですが、画像ごとに異なるオブジェクトを表示できないか試してみました。

今度は"AugmentedImageExampleController"の方を書き換えました。
"AugmentedImageVisualizerPrefab"にあたるものを3つ用意します。
さらに検出された画像からデータベースに紐づけられているインデックスを取得し、インデックスごとに表示する"AugmentedImageVisualizerPrefab"を変えるように修正しました。

                AugmentedImageVisualizer visualizer = null;
                m_Visualizers.TryGetValue(image.DatabaseIndex, out visualizer);
                if (image.TrackingState == TrackingState.Tracking && visualizer == null)
                {
                    // Create an anchor to ensure that ARCore keeps tracking this augmented image.
                    Anchor anchor = image.CreateAnchor(image.CenterPose);
                    // ----------------------ここから----------------------
                    if (image.DatabaseIndex == 6)
                    {
                        visualizer = (AugmentedImageVisualizer)Instantiate(AugmentedImageVisualizerPrefab1, anchor.transform);
                    }
                    else if (image.DatabaseIndex == 7)
                    {
                        visualizer = (AugmentedImageVisualizer)Instantiate(AugmentedImageVisualizerPrefab2, anchor.transform);
                    }
                    else
                    {
                        visualizer = (AugmentedImageVisualizer)Instantiate(AugmentedImageVisualizerPrefab3, anchor.transform);
                    }
                    // ----------------------ここまで----------------------
                    visualizer.Image = image;
                    m_Visualizers.Add(image.DatabaseIndex, visualizer);
                }
                else if (image.TrackingState == TrackingState.Stopped && visualizer != null)
                {
                    m_Visualizers.Remove(image.DatabaseIndex);
                    GameObject.Destroy(visualizer.gameObject);
                }

結果2

f:id:ryaoucn:20180819131154j:plain
ちゃんとできてますね。

今回は表示するオブジェクトごとに"AugmentedImageVisualizer"スクリプトをアタッチして、"ExampleController"にセットするということをやりましたが、それだと表示するオブジェクトが増えると面倒なことになるのでうまい方法を考えたいですね。

気になってるやつ

このARの活用例が凄くかっこいいので真似してみたい。
こちらはARKitでやっているようですね。
今度ARCoreでやってみたいです。

【ARCore】タップした平面のDetectedPlaneTypeを識別して配置するオブジェクトの出し分けをする

1.4.0で追加された、自分の中では一番気になる変更点であるDetectedPlaneTypeについて調べてみました。

変更の内容

新規に"DetectedPlaneType.cs"が追加され、水平面の上向きと下向き、垂直面を区別してenumで管理するようになったようです。
github.com

それに併せ、"DetectedPlane.cs"に"PlaneType"が追加され、DetectedPlaneTypeを取得できるようになっています。
github.com

やったこと

  • HelloARサンプルを修正し、タップした平面の水平面上向き、水平面下向き、垂直面のどれなのかを識別
  • 平面のタイプに合わせて配置するARオブジェクトを変える
    • 水平面上向き:みさきちゃん
    • 水平面下向き:ゆうこちゃん
    • 垂直面:こはくちゃん

(点にはアンディが配置されます)

開発環境

  • Windows10
  • Unity 2018.1.7f1
  • ARCore SDK for Unity v1.3.0
  • Galaxy S9

HelloARサンプルのコードを修正

今回は3種類(+1)のオブジェクトが必要になるので、先にpublicで宣言しておきます。
そしてUpdate()内の、画面がタップされた場所に対してヒットテストを行いオブジェクトを配置する処理の部分を修正します。

// 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 = AndyPrefab;
        } else if (hit.Trackable is DetectedPlane) {
            // ---------------↓ここから---------------
            // hit.TracableをDetectedPlaneにキャスト
            DetectedPlane plane = hit.Trackable as DetectedPlane;
            // 平面の種類ごとにモデルの出し分け
            switch (plane.PlaneType) {
                case DetectedPlaneType.HorizontalUpwardFacing:
                    // 水平面上向き
                    prefab = MisakiPrefab;
                    break;
                case DetectedPlaneType.HorizontalDownwardFacing:
                    // 水平面下向き
                    prefab = YukoPrefab;
                    break;
                default:
                    // 垂直面
                    prefab = KohakuPrefab;
                    break;
            }
            // ---------------↑ここまで---------------
        } else {
            prefab = KohakuPrefab;
        }

        // Instantiate Andy model at the hit pose.
        var arObject = Instantiate(prefab, hit.Pose.position, hit.Pose.rotation);

        // Compensate for the hitPose rotation facing away from the raycast (i.e. camera).
        arObject.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.
        arObject.transform.parent = anchor.transform;
    }
}

ヒットテストにより取得した"Trackable"が"DetectedPlane"だった場合の処理を修正しています。
"hit.Trackable"を"DetectedPlane"型にキャストし、"PlaneType"により"DetectedPlaneType"を取得します。
そして、平面のタイプごとにAR空間に配置するオブジェクトの出し分けを行っています。
※Instantiateするオブジェクトの変数名を変えていますが(andyObject→arObject)処理には影響しません。

結果

このように、平面のタイプによって出現するオブジェクトを変えることをができました。


つまづいたこと

はじめ、タップした平面の情報を取得する方法が分からず、PlaneTypeを呼び出せずにいました。
ソースコードを眺めていてようやくDetectedPlaneがTrackableを継承しているものだということに気づき、Raycastで取得したhit.Trackableをキャストするという方法に至りました。

終わりに

タップした平面をDetectedPlane型にする方法が学べたので、これでDetectedPlaneType以外の情報を取得できそうです。

もっと他にいい方法があるよ、という場合はどうか教えてください。

【ARCore】検出平面に衝突判定をつける

検出された平面にアンカーを作成してオブジェクトを置く方法が確認できたので、今度は平面にオブジェクトを乗っける方法を勉強しました。

以下の記事を参考にさせていただき、ARCore1.3.0のサンプルソースに合うよう一部修正しました。
tks-yoshinaga.hatenablog.com

開発環境

  • Windows10
  • Unity 2018.1.7f1
  • ARCore SDK for Unity v1.3.0
  • Galaxy S9

修正箇所

  • "TrackedPlaneVisualizer"は1.3.0では"DetectedPlaneVisualizer"
  • "Frame.GetNewPlanes(ref m_newPlanes);"は1.3.0では"Session.GetTrackables(m_AllPlanes);"
  • "// Iterate over planes found in this frame and instantiate corresponding GameObjects to visualize them."以下の検出平面を可視化する処理は"Plane Generator"がやってくれてるので1.3.0のHelloARサンプルのコードにはない

また、生成するCubeにマテリアルを明示的に指定してやらないとUnity特有のピンク(紫?)色になってしまうので、以下のコードでランダムに色を付けるようにしました。

// マテリアルの設定
Material material = new Material(Shader.Find("Diffuse")) {
    color = new Color(Random.value, Random.value, Random.value)
};
cube.GetComponent<Renderer>().material = material;

結果


・・・一投目がピンク色で失敗したかと思いました。紛らわしい。

おまけ

先日文面上で確認した1.4.0の変更点についても一部を実際に動かして確認してみました。

オートフォーカスモードのオン/オフ切り替えについてはDeffaultSessionConfigの中に項目が増えていました。
f:id:ryaoucn:20180805190817j:plain
実際に動かしてみましたが、たしかにオートフォーカスしてくれてるような。。。
すみません、はっきりわかる画像を用意できませんでした。

また、HelloARサンプルも検出された平面に置くか点に置くかでアンディが色分けされていました。
f:id:ryaoucn:20180805191056j:plain
平面に置いたアンディはこっちを向いてくれますが、点に配置したアンディはうまくこっちをむいてくれません。なんでだろ。

他の変更点についても後々確認していこうと思います。

ARCore SDK for Unity 1.4.0の変更点を確認

8/3にARCoreのバージョンが更新されていたので内容をメモ。
翻訳間違ってたらごめんなさい。

破壊的変更

なし

新規API

  • Frame.Raycast()

指定された原点と方向から構成された任意のレイでヒットテストを行い、原点から一番近いヒット情報を返す。

  • Frame.RaycasAll()

指定された原点と方向から構成された任意のレイでヒットテストを行い、すべてのヒット情報を返す。

  • ARCoreSession.CamereaFocusMode

ARカメラのオートフォーカス設定のオン/オフ切り替え

  • ARCore Camra Cofiguration API

ARCore CameraはARCoreSession.RegisterChooseCameraConfigurationCallbackにより、有効な設定リストからカメラ設定を選択できるようにするコールバックを登録する。

廃止されたもの

なし

仕様変更

  • ARCore iOSのサポートはデフォルトでは無効になるようになった。
  • ARCore iOSのARCore Cloud Anchorのサポートを有効にするには、"iOS Support Enable"チェックボックスにチェックを入れる。

  ※ARCore Project Setting画面は"Edit > Project Setting > ARCore"で開ける

その他の変更

  • CloudAnchorのサンプルシーン名をAPIと同じ"CloudAnchors"に変更。
  • ARCoreのComputerVisionサンプルは、CPUイメージの解像度を設定するオプションをサポートするようになった。
  • DetectedPlaneは以下の3タイプになった。
    • HorizontalUpwardFacing
    • HorizontalDownwardFacing
    • Vertical

  →これまで水平面と垂直面の2種類だけだったのが、水平面の表面、裏面を区別するようになったっぽい?

  • インスタントプレビューをarcore_instant_preview_*に変更。(GoogleVRとのコンフリクトを防ぐため)
  • HelloARサンプルにおいて、平面には緑のアンディを、特異点には青のアンディを配置するようになった。
  • ComputerVisionサンプルのエッジ検出アルゴリズムは、行ストライド(?)がカメラ画像幅よりもピクセルが多い場合を処理するようになった。

バグ修正

  • 一部のスクリプトコンポーネントのドキュメントURLを修正し、インスペクタのドキュメントアイコンをクリックすると対応するARCoreリファレンスドキュメントが開かれるようになった。
  • ARCore SDKを含むUnityプロジェクトはスタンドアロンMacWindowsLinux)向けのビルドができない問題を修正。

ARCoreを実践する

ARCore SDK for Unityのサンプルは一通り見終えたので、実際にシーンを1から作成しました。

開発環境

  • Windows10
  • Unity 2018.1.7f1
  • ARCore SDK for Unity v1.3.0
  • Galaxy S9

やったこと

  • Point Cloudを可視化
  • 検知した平面のタップした位置に神林ゆうこちゃんを召喚

以下の記事を参考にさせていただきました。
jyuko49.hatenablog.com

"GoogleARCore"のサンプルにある"HelloAR"とほぼ同じことをやっただけですが、シーンを丸ごとコピーをせずに組み立てました。
ただスクリプトなどは参考にしたりそのまま使ったりしました。

"HelloAR"ではどこに平面を検知したが分かりやすいように可視化してありましたが、今回は無しにしました。
なので、検知済み平面の可視化のためのプレハブである"Plane Generator"や"DetectedPlaneVIsualizer"は使用しません。

UIも表示しないので、引用した"HelloARController"のUI表示にあたる部分も省きました。

結果

ビルドし、実機で動作させた結果がこちら
f:id:ryaoucn:20180730195219j:plain
Point Cloudもゆうこちゃんも表示されています。

つまづいたこと

今回プロジェクト作成時にはじめからある"Main Camera"に"ARCoreBackgroundRenderer(Script)"と"TrackedPoseDriver(Script)"をアタッチし、"Pose Source"も"Color Camera"に変更したのですが、Point Cloudが表示されず平面を検知されませんでした。

"HellowAR"の"FirstPersonCamera"と比較したところ、"Camera"の設定値にある"Depth"が"-1"になっていたことが原因でした。
これによりレンダリングの順序に差ができることによって特異点の検出がうまくいかなかった・・・のでしょうか・・・。
"Depth"を"0"にしたところ、正常に検出されるようになりました。

とくにこだわりがなければGoogleARCoreのPrefabs配下の"ARCore Device"を持ってきた方が早いですね。

はじめまして

はじめまして、りゃおです。
ARに興味を持ち、どうせ勉強するなら記録をしっかり残すかということでブログを開設しました。

まだ学び始めですが、日々過ごしずつ書き溜めていきます。
しばらくはARCoreを中心に学んでいこうと思います。