ARコンテンツ作成勉強会
はじめようARCore
#AR_Fukuoka
自己紹介
氏名:吉永崇(Takashi Yoshinaga)
所属:九州先端科学技術研究所(ISIT)
専門:AR/VRを用いた情報可視化と各種計測
コミュニティ:ARコンテンツ作成勉強会
Twitterと勉強会ページで情報を発信しています
#AR_Fukuoka Googleで「AR勉強会」で検索
FacebookグループのxR九州もよろしく
ARCore
Googleが提供する次世代ARプラットフォーム。普通のスマホでマーカーレスARを実現。
【主要機能】
(1) 自己位置推定 (Motion Tracking)
(2) 明るさ推定 (Light Estimation)
(3) 平面認識 (Environmental Understanding)
(4) マーカー認識 (Augmented Image)
(5) 空間共有 (Cloud Anchor)
今日紹介する内容
Googleが提供する次世代ARプラットフォーム。普通のスマホでマーカーレスARを実現。
【主要機能】
(1) 自己位置推定 (Motion Tracking)
(2) 明るさ推定 (Light Estimation)
(3) 平面認識 (Environmental Understanding)
(4) マーカー認識 (Augmented Image)
(5) 空間共有 (Cloud Anchor)
Unityのプロジェクトを作成 (1/2)
Unityを起動後、画面右上のNEWをクリック
New
Unityのプロジェクトを作成 (2/2)
プロジェクト名・保存場所・3Dを指定してCreate project
3D
最後にクリック
プロジェクト名
保存場所
ARCoreのインポート
①Assets
②Import Package
→ Custom Package
③arcore-unity-sdk-xxx
④開く
演習用フォルダの作成(1/3)
Assetsを右クリック
演習用フォルダの作成(2/3)
①Create ②Folder
演習用フォルダの作成(3/3)
フォルダ名をSample1に変更
一旦、現状を保存
Sample1を開く
コンテンツ名を入力
(例:sample1)
保存
Ctrl + Sで現状のコンテンツ(Scene)を保存
※この後もこまめに保存しましょう
自己位置推定:Motion Tracking
ARCore用のカメラの設定(1/2)
Main Cameraを削除
ARCore用のカメラの設定(2/2)
①GoogleARCore → Prefabs
② ARCore Device
③ドラッグ&ドロップ
表示オブジェクトの追加(1/2)
①GoogleARCore → Examples → Common → Prefabs
② Andy Diffuse
③ドラッグ&ドロップ
表示オブジェクトの追加(2/2)
①AndyDiffuse
② Positionのzを0.5[m]
ビルドの準備
①File
② Build Settings
ビルドの準備
②Switch Platform
① Android
③Player Settings
ビルドの準備
①Product Nameを入力
② Other Settings
③ Multithreaded Renderingをオフ
ビルドの準備
①Package Nameを設定
例) com.arfukuoka.test1
② Minimum API Levelを
Android 7.0に設定
ビルドの準備
①XR Settings
② ARCore Supportedをオン
ビルドと実機インストール
①File
② Build & Run
ビルドと実機インストール
①インストーラー(apk)の名前を設定
② 保存
床や壁の認識:Environmental Understanding
Environmental Understandingを有効化(1/5)
①Sample1
Environmental Understandingを有効化(2/5)
①右クリック
②Create
③GoogleARCore
④SessionConfig
Environmental Understandingを有効化(3/5)
ファイル名を変更(例:sample1)
Environmental Understandingを有効化(4/5)
①sample1.assetをクリック
【Plane Finding Mode】
(1)Disabled:平面認識なし
(2)Horizontal And Vertical:
水平面と垂直面を認識
(3)Horizontal:
水平面のみ認識
(4)Vertical
垂直面のみ認識
今回は(2)~(4)から好きなのを選択
Environmental Understandingを有効化(5/5)
①ARCore Deviceをクリック
②Sample1フォルダ
③sample1.asset
④Session Configに
ドラッグ&ドロップ
平面可視化スクリプト (1/4)
①空白を右クリック
② Create Empty
平面可視化スクリプト (2/4)
①GameObjectの名前を
Controllerに変更
平面可視化スクリプト (3/4)
①Controllerをクリック
②AddComponent
③Detected Plane Generator
平面可視化スクリプト (4/4)
②GoogleARCore → Examples → Common → Prefabs
③DetectedPlaneVisualizer
①Controller
④Detected Plane
Prefabにドラッグ&ドロップ
水平面
垂直面
タップした平面にCGを置く
①Controller
②Add Component
タップした平面にCGを置く
①New Script
③Create and Add
②PutScript
タップした平面にCGを置く
①Controllerをクリック
②PutScriptを
ダブルクリック
スクリプトの記述
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using GoogleARCore;
public class PutScript : MonoBehaviour {
public GameObject andy; //CG(Andy)を扱う変数
void Start () {
}
void Update () {
//タップの検出
//タップした画面の座標と3D空間座標の対応付け
//Andyをその位置に置く
}
}
スクリプトの記述
void Update () {
Touch touch;
if (Input.touchCount < 1 ||
(touch = Input.GetTouch(0)).phase != TouchPhase.Began)
{
return; //画面に触れていないor既に触れている最中なら何もしない
}
//タップした座標にAndyを移動。
TrackableHit hit;
TrackableHitFlags filter = TrackableHitFlags.PlaneWithinPolygon;
Frame.Raycast(touch.position.x, touch.position.y, filter, out hit)
}
touch.position
hit
Began→タップ開始時
平面を構成するポリゴンの
内側をタップ判定の対象に
スクリプトの記述
void Update () {
Touch touch;
if (Input.touchCount < 1 ||
(touch = Input.GetTouch(0)).phase != TouchPhase.Began)
{
return; //画面に触れていないor既に触れている最中なら何もしない
}
//タップした座標にAndyを移動。対象は認識した平面
TrackableHit hit;
TrackableHitFlags filter = TrackableHitFlags.PlaneWithinPolygon;
if (Frame.Raycast(touch.position.x, touch.position.y, filter, out hit))
{
//Andyの3D座標を指定するコードを記述(次頁)
}
}
touch.position
hit
スクリプトの記述
if (Frame.Raycast(touch.position.x, touch.position.y, filter, out hit))
{
//平面にヒット & 裏面でなければAndyを置く
if ((hit.Trackable is DetectedPlane ) &&
Vector3.Dot(Camera.main.transform.position - hit.Pose.position,
hit.Pose.rotation * Vector3.up) > 0)
{
//Andyの位置・姿勢を指定
andy.transform.position = hit.Pose.position;
andy.transform.rotation = hit.Pose.rotation;
andy.transform.Rotate(0, 180, 0, Space.Self);
//Anchorを設定
var anchor = hit.Trackable.CreateAnchor(hit.Pose);
andy.transform.parent = anchor.transform;
}
}
スクリプトとGameObjectのAndyの対応付け
②AndyDiffuse
①Controller
③Put ScriptのAndy
にドラッグ&ドロップ
マーカー認識:Augmented Image
次の演習の準備
①Assets
③Create
②右クリック
④Folder
次の演習の準備
フォルダ名をSample2に変更
次の演習の準備
File→New Scene
一旦、現状を保存
Sample2を開く
コンテンツ名を入力
(例:sample2)
保存
Ctrl+Sで保存&コンテンツ(Scene)名設定
ARCore用カメラの設定
Main Cameraを削除
ARCore用カメラの設定
①GoogleARCore → Prefabs
② ARCore Device
③ドラッグ&ドロップ
設定ファイルを作成
②右クリック
③Create
④GoogleARCore
⑤SessionConfig
①Sample2
設定ファイルを作成
ファイル名を変更(例:sample2)
設定ファイルを作成
①ARCore Deviceをクリック
②Sample2フォルダ
③sample2.asset
④Session Configに
ドラッグ&ドロップ
補足
①sample2.assetをクリック
②下記を適宜設定
・Plane Fitting Mode
・Enable Light Estimation
※マーカー認識には影響なし
次の手順
このあと
Augmented Image Database
にマーカ情報の一覧を登録する
マーカー画像を追加
②sample2
①Sample(事前配布)フォルダ
③AndyMarkerとearthを
ドラッグ&ドロップ
画像をマーカーとして登録
AndyMarkerとearthを選択
画像をマーカーとして登録
①どちらかの画像を右クリック
②Create
③GoogleARCore
④AugmentedImage
Database
画像をマーカーとして登録
マーカー画像のデータベース
解説
マーカーの横幅の実寸サイズ[m]
※0の場合、ARCore側で勝手に
推定してくれる。
ただし、サイズをしておいた方が
位置合わせは安定する
Qualityは認識のしやすさ
※75以上推奨 by Google
Sample2.asset
NewDatabase.asset
Polyから表示オブジェクトを入手
Googleが提供する3Dデータ共有サービス
https://poly.google.com/
オブジェクトの入手
ここから検索
画面上部のフォームから「Astronaut」で検索
オブジェクトの入手
ここをクリック
オブジェクトの入手
ダウンロード
→ OBJファイル
Zip解凍後、
フォルダ名が長いので
Astronautに変更
CGをプロジェクトに追加
sample2
①Sample(事前配布)フォルダ
③Astronautフォルダ
をドラッグ&ドロップ
CGをプロジェクトに追加
Astronautが追加されていればOK
マーカー認識用スクリプトの作成
①右クリック
②Create Empty
マーカー認識用スクリプトの作成
GameObjectの名前を
Controllerに変更(※任意)
マーカー認識用スクリプトの作成
①AddComponent
マーカー認識用スクリプトの作成
①New Script
③Create and Add
②MarkerScript
マーカー認識用スクリプトの作成
②ダブルクリック
①Controller
スクリプトの記述
using GoogleARCore;
public class MarkerScript : MonoBehaviour
{
public GameObject andy;//Andyオブジェクト
public GameObject astronaut;//宇宙飛行士オブジェクト
//マーカー番号(int)とCG(GameObject)の対応を記録する辞書
Dictionary<int,GameObject> markerDic = new Dictionary<int,GameObject>();
//フレーム毎に認識されたマーカーを一時的に覚えておく
List<AugmentedImage> markers = new List<AugmentedImage>();
void Start()
{
}
void Update()
{
//マーカーとCGの対応付けを行う(次頁)
}
}
CGとスクリプトの対応付け(Andy)
①Controller
②GoogleARCore → Examples → Common → Prefabs
③Andy Diffuse
④Andyに
ドラッグ&ドロップ
CGとスクリプトの対応付け(Astronaut)
①Controller
②Sample2 → Astronaut
③Astronaut
④Astronautに
ドラッグ&ドロップ
void Update()
{
//計測情報が更新されたマーカーを取得
Session.GetTrackables<AugmentedImage>
(markers,TrackableQueryFilter.Updated);
//マーカーに対してCGとの対応付けor解除を行う
foreach (var image in markers)
{
int index = image.DatabaseIndex; //マーカーの番号
GameObject obj = null; //上記indexに対応するCGを格納(予定)
markerDic.TryGetValue(index, out obj);//indexに対応するCGを取得
if (image.TrackingState == TrackingState.Tracking && obj == null)
{
//マーカーとCGの対応付けと辞書(markerDic)登録を行う
}
else if (image.TrackingState == TrackingState.Stopped && obj != null)
{
//計測をやめたマーカーに対して行う処理
}
}
}
void Update()
{
//計測情報が更新されたマーカーを取得
Session.GetTrackables<AugmentedImage>
(markers,TrackableQueryFilter.Updated);
//マーカーに対してCGとの対応付けor解除を行う
foreach (var image in markers)
{
int index = image.DatabaseIndex; //マーカーの番号
GameObject obj = null; //上記indexに対応するCGを格納(予定)
markerDic.TryGetValue(index, out obj);//indexに対応するCGを取得
if (image.TrackingState == TrackingState.Tracking && obj == null)
{
//マーカーとCGの対応付けと辞書(markerDic)登録を行う
}
else if (image.TrackingState == TrackingState.Stopped && obj != null)
{
//計測をやめたマーカーに対して行う処理
}
}
}
if (image.TrackingState == TrackingState.Tracking && obj == null)
{
Anchor anchor = image.CreateAnchor(image.CenterPose);
switch (index)
{
case 0://AndyMarker
obj = GameObject.Instantiate(andy, anchor.transform);
obj.transform.localPosition = new Vector3(0, 0.02f, -0.04f);
obj.transform.Rotate(-90, 0, 0, Space.Self);
obj.transform.localScale =new Vector3(0.3f, 0.3f, 0.3f);
markerDic.Add(index, obj);
break;
case 1://earth
obj = GameObject.Instantiate(astronaut, anchor.transform);
obj.transform.localPosition = new Vector3(0.02f, 0.04f, 0);
obj.transform.Rotate(90, 0, 0, Space.Self);
obj.transform.localScale = new Vector3(0.01f, 0.01f, 0.01f);
markerDic.Add(index, obj);
break;
}
}
void Update()
{
//計測情報が更新されたマーカーを取得
Session.GetTrackables<AugmentedImage>
(markers,TrackableQueryFilter.Updated);
//マーカーに対してCGとの対応付けor解除を行う
foreach (var image in markers){
int index = image.DatabaseIndex; //マーカーの番号
GameObject obj = null; //上記indexに対応するCGを格納(予定)
markerDic.TryGetValue(index, out obj);//indexに対応するCGを取得
if (image.TrackingState == TrackingState.Tracking && obj == null)
{
//マーカーとCGの対応付けと辞書(markerDic)登録を行う
}
else if (image.TrackingState == TrackingState.Stopped && obj != null)
{
markerDic.Remove(image.DatabaseIndex);
Destroy(obj);
}
}
}
空間共有:CloudAnchor
今回は同一端末で空間共有(TangoのArea Learning的な使い方)
下準備
Sample1を選択し、Ctrl +D
下準備
フォルダ名をsample3
下準備
ファイル名をそれぞれsample3
下準備
sample3.unityをダブルクリック
下準備
①ARCoreDevice
②sample3.asset
③Session Configに
ドラッグ&ドロップ
下準備
②PutScriptを選択してCtrl+D
①Assets
下準備
PutScript1をAnchorScriptに変更
下準備
AnchorScriptをダブルクリック
下準備
using UnityEngine;
using GoogleARCore;
public class AnchorScript : MonoBehaviour {
public GameObject andy;
// Use this for initialization
void Start()
{
}
// Update is called once per frame
void Update () {
Touch touch;
if (Input.touchCount < 1 ||
(touch = Input.GetTouch(0)).phase != TouchPhase.Began)
{
return;
}
クラス名をAnchorScriptに変更
下準備
①Controller
②PutScript右の
③Remove Component
下準備
①AnchorScript
下準備
①Controller
②AndyDiffuse
③Anchor Scriptの
Andyにドラグ&ドロップ
動くことを確認
UI作成
①右クリック
②UI
③Button
UI作成
①Canvasをダブルクリック
②ボタンが見えるアングルにする
UI作成
①Canvas
②Scale With Screen Size
UI作成
②クリック
③top-centerをクリック
①Button
UI作成
上方の中央右寄りに
Pos X: 90
Pos Y: -35
Height: 60
UI作成
①Button→Text
②ボタンに表示される
文字をHostに変更
UI作成
①右クリック
②UI
③Button
UI作成
①Button(1)
②クリック
③top-centerをクリック
UI作成
上方の中央右寄りに
Pos X: -90
Pos Y: -35
Height: 60
UI作成
①Button(1)→Text
②ボタンに表示される
文字をResolveに変更
UI作成
①右クリック
②UI
③Text
UI作成
①右クリック
②UI
③Text
UI作成
①Text
②クリック
③bottom-centerをクリック
UI作成
下方の中央右寄りに
Pos X: 0
Pos Y: 30
Width:400
Height: 60
UI作成
①Font Sizeを50に変更
②Alignmentを左右・上下
両方とも中央寄せ
Cloud Anchorの使用を許可
①ARCoreDevice
②Sample3をダブルクリック
Cloud Anchorの使用を許可
Enable Cloud Anchorをオン
GCP側の設定
作成
GCP側の設定
プロジェクト名をつける
GCP側の設定
選択
GCP側の設定
今作ったプロジェクトを選択
GCP側の設定
ARCore Cloud Anchor APIで検索
GCP側の設定
有効にする
APIキーの作成
①メニュー
②APIとサービス
③認証情報
APIキーの作成
①認証情報を作成
②APIキー
APIキーの作成
コピー
GCP側の設定
①Edit
②Project Settings
③ARCore
GCP側の設定
②Close
①APIキーをはりつけ
これから追加する内容(概要)
public class AnchorScript : MonoBehaviour {
// Use this for initialization
void Start()
{
}
public void HostAnchor()
{
}
public void ResolveAnchor()
{
}
void Update () {
//ボタンと画面どちらをタップしたのかを認識
//画面をタップしたらAndyを対応する3次元位置に配置
//最新のAnchorを覚えておく
}
}
環境情報を
クラウドに保存
CloudAnchorで
位置合わせ
画面タップとボタンタップの区別
using UnityEngine.EventSystems;
public class AnchorScript : MonoBehaviour {
/*Start,HostAnchor,ResolveAnchorはスペースの都合上省略*/
void Update () {
if (EventSystem.current.IsPointerOverGameObject
(Input.GetTouch(0).fingerId))
{
return;
}
Touch touch;
if (Input.touchCount < 1 ||
(touch = Input.GetTouch(0)).phase != TouchPhase.Began)
{
return;
}
//タッチした座標にAndyを移動。対象は認識した平面
TrackableHit hit;
TrackableHitFlags filter = TrackableHitFlags.PlaneWithinPolygon;
/*以下省略*/
最新のAnchorを記録
Anchor currentAnchor = null;
void Update () {
/*途中省略*/
if (Frame.Raycast(touch.position.x, touch.position.y, filter, out hit))
{
if ((hit.Trackable is DetectedPlane) &&
Vector3.Dot(Camera.main.transform.position –
hit.Pose.position,hit.Pose.rotation * Vector3.up) > 0)
{
//Andyの位置・姿勢を指定
andy.transform.position = hit.Pose.position;
andy.transform.rotation = hit.Pose.rotation;
andy.transform.Rotate(0, 180, 0, Space.Self);
//Anchorを設定
var anchor = hit.Trackable.CreateAnchor(hit.Pose);
andy.transform.parent = anchor.transform;
currentAnchor = anchor;
}
}
}
最新のAnchorをクラウドに保存
using GoogleARCore;
using UnityEngine.EventSystems;
using GoogleARCore.CrossPlatform;
using UnityEngine.UI;
public class AnchorScript : MonoBehaviour {
public GameObject andy;
Anchor currentAnchor = null;
public Text txt; //保存や位置合わせのステータス表示
bool connecting = false; //クラウドに接続中or切断
void Start()
{
}
public void HostAnchor()
{
}
環境情報をクラウドに保存 (次頁)
最新のAnchorをクラウドに保存
public void HostAnchor()
{
if (connecting || currentAnchor == null) { return; }
connecting = true;
txt.text = "hosting...";
//環境情報をアップロード
XPSession.CreateCloudAnchor(currentAnchor).ThenAction(result =>
{
connecting = false;
if (result.Response != CloudServiceResponse.Success)
{
txt.text = "Please Try Again";
return;
}
txt.text = result.Anchor.CloudId;
});
}
Textオブジェクトとスクリプトの対応付け
①Textを見つけておく
②Controller
②Anchor Scriptの
Txtにドラッグ&ドロップ
ボタン押下時にAnchorをクラウドに保存(1/4)
②On Click()内の+
①Button
ボタン押下時にAnchorをクラウドに保存(2/4)
①Controller
②Noneと書かれた
箇所にドラッグ&ドロップ
ボタン押下時にAnchorをクラウドに保存(3/4)
No Function
ボタン押下時にAnchorをクラウドに保存(4/4)
①AnchorScript
②HostAnchor
【動作確認の手順】
① 平面を認識させる
② Andyを配置
③ Hostボタンをタップ
④ 画面下の文字がhosting...になる
⑤ アップロードに成功するとIDが表示される
【この後やりたいこと】
① アプリをいったん終了して再起動
② 前操作で生成したIDでCGP側に問い合わせ
③ CloudAnchorで現在の空間とマッチング
④ CGが同じ位置に表示!
まずはIDをスマホ内に保存
Cloud AnchorのIDを保存
using UnityEngine.EventSystems;
using UnityEngine.UI;
using System.IO;
public class AnchorScript : MonoBehaviour {
public GameObject andy;
Anchor currentAnchor = null;
public Text txt;
bool connecting = false;
string filePath;
void Start()
{
filePath = Application.persistentDataPath + "/anchorID.txt";
}
public void HostAnchor()
{
}
環境情報をクラウドに保存 (編集中)
環境情報をクラウドに保存
public void HostAnchor()
{
if (connecting || currentAnchor == null) { return; }
connecting = true;
txt.text = "hosting..."; //環境情報をアップロード
XPSession.CreateCloudAnchor(currentAnchor).ThenAction(result =>
{
connecting = false;
if (result.Response != CloudServiceResponse.Success)
{
txt.text = "Please Try Again";
return;
}
txt.text = "";
StreamWriter sw = new StreamWriter(filePath, false);
sw.WriteLine(result.Anchor.CloudId);
sw.Close();
});
}
次のステップ
public class AnchorScript : MonoBehaviour {
// Use this for initialization
void Start()
{
}
public void HostAnchor()
{
}
public void ResolveAnchor()
{
}
void Update () {
//ボタンと画面どちらをタップしたかを認識
//画面をタップしたらAndyを対応する3次元位置に配置
//最新のAnchorを覚えておく
}
}
環境情報を
クラウドに保存
CloudAnchorで
位置合わせ
テキストファイルの読み込み
public void ResolveAnchor()
{
if (connecting) { return; }
connecting = true;
// txtファイルを読み込む
FileInfo file = new FileInfo(filePath);
StreamReader sr = new StreamReader(file.OpenRead());
string cloudAnchorId = sr.ReadLine();
sr.Close();
txt.text=cloudAnchorId;
}
ボタン押下時にファイル読み込み&位置合わせ
②On Click()内の+
①Button(1)
ボタン押下時にファイル読み込み&位置合わせ
①Controller
ボタン押下時にファイル読み込み&位置合わせ
No Function
ボタン押下時にファイル読み込み&位置合わせ
①AnchorScript
②ResolveAnchor
【動作確認の手順】
① Andyをどこかに配置
② Hostボタンを押しクラウドに保存
③ アプリを終了
④ アプリを再び起動
⑤ Resolveボタンをタップ
⑥ 画面下にIDが表示されればOK
【この後やりたいこと】
① IDを使ってARCoreAPI(クラウド)
に問い合わせ
③ CloudAnchorで空間のマッチング
④ CGが以前と同じ位置に表示!
ここにIDが表示される
CloucAnchorを用いた位置合わせ
public void ResolveAnchor()
{
/*さっきまで書いていたところは省略*/
//txt.text = cloudAnchorId;
txt.text ="resolving...";
XPSession.ResolveCloudAnchor(cloudAnchorId).ThenAction(result =>
{
connecting = false;
if (result.Response != CloudServiceResponse.Success)
{
txt.text = "Please Try Again";
return;
}
txt.text = "";
andy.transform.position = result.Anchor.transform.position;
andy.transform.rotation = result.Anchor.transform.rotation;
andy.transform.Rotate(0, 180, 0, Space.Self);
andy.transform.parent = result.Anchor.transform;
});
}
完成

はじめようArcore (修正版)