Tutti Lab

元シリコンバレー在住のおっさん技術者、モバイルVRアプリ開発に挑戦中

MFT2017に出展したVRコンテンツ

f:id:tuti107:20170820085956j:plain

今年もMaker Faire TokyoにVRコンテンツを出展しました。

www.youtube.com

へなちょこCooking

Oculus Touchを活用したVR料理ゲーム(?)です。
食材をナイフでカットし、焼き色を付けて、皿に盛りつけます。
盛り付けが完成すると、AIがその料理に名前をつけてくれます。

食材のカット

Turbo Slicer 2を使用しました。
youtu.be

MeshRendererをもつオブジェクトに、Sliceableコンポーネントをセットするだけで、そのメッシュをスライスすることができます。
何点か、使用上の注意点ですが、

  • Sliceableのエディタ拡張のバグ?により、断面部分のテクスチャ領域の設定がうまくいきません(設定が無視され、デフォルト値となる)
  • 手のちょっとしたブレなどで、大量に細かくスライスされてしまいます。何十・何百の細かなオブジェクトが生成されてしまい、処理がものすごく重くなります。私は、スライス後のオブジェクトの体積が一定以下の場合は、そのオブジェクトを削除するようにしました。

人間っぽいキャラクタの動きの実現

定番ですがFinal IKのVRIKを使用しました。
www.youtube.com

  • Vive用の設定情報はあるのですが、Oculus Touch用が見つからず・・ただ、OVRCameraRigに簡単に設定できました。
  • Grounder IKを設定しないと、足が思うように動いてくれません(上記チュートリアルにはでてこない)。Grounder IKについては、_Demo/VRIK/VRIK(Grounder).unityが参考になります(というか、コピペ)

キャラクタの手の動き

OculusFingerを利用させていただきました。
eyln.hatenablog.com

お皿に食材を盛り付け

Unity 5より、Mesh ColliderはConvexにしなければならなくなり、お皿のように真ん中がくぼんた形状のMeshの当たり判定が、想定通りとなりません。そこで、多数のBoxColliderでMeshColliderをエミュレートする、Non Convex Mesh Colliderを利用しました。

youtu.be

使い方は簡単で、これまでMeshColliderを張り付けていたコンポーネントにNonConvexMeshColliderをセットし、Boxes Per Edgeを適当な数字として、Calculateボタンを押すだけです。Boxes Per Edgeを大きくすると、より細かなBoxColliderでエミュレートされますが、処理が重くなります。また、

  • Create Child Game Objectのチェックを外すと、NonConvexMeshColliderがセットされたオブジェクトに多量のBoxColliderがセットされます。
  • Create Child Game Objectのチェックをチェックした場合は、子オブジェクトが生成され、そこにBoxColliderがセットされます。この場合、衝突判定処理の修正(GetComponent→GetComponentInChildren)が必要かもしれません。

名前付け

im2txtを使用しました・・詳細は本作品を一緒に開発した友人にお任せだったため、よくわかりませんが・・
soralab.space-ichikawa.com

なお、im2txtは画像認識の結果を英語の文章で返すのですが、これを日本語化するために、Google Translateを使用しました。

肝試しVR

Gear VR/Daydream/Google Cardboard向けのホラーVRゲーム(?)です。
ライド型のお化け屋敷アトラクションであり、プレーヤは歩いたりしなくても、乗り物が動いて勝手に進んでいきます。
特段のゲーム性はなく、ひたすら怖いモンスターや超常現象を見て楽しむ、コンテンツです。

お化け屋敷のセットには、Abandoned Hospitalを使用しました。
youtu.be

・・何も出てこなくても十分に怖いです・・
なお、モバイル向けのアセットではないので、そのまま使用すると、GearVR等では全然性能が出ません。涙ぐましい性能調整が必要でした・・

本コンテンツは、近々(秋になる前に)Oculus Store等に載せたいな、と画策中です。

ラズパイZero WでMovidius NCSを動かす

f:id:tuti107:20170819110519j:plain

前回は、苦戦しながらもラズパイ3にUbuntuをインストールし、Movidiusを動かしました。
今回は、ラズパイ3ではなく、より安価・小型(でも非力)なラズパイZero Wで、Movidiusを動かしてみます。

なお、ラズパイZero Wはラズパイ2・3とARMアーキテクチャが異なるため、ラズパイZero W向けのUbuntuイメージは提供されておりません。
しかし、以下のサイトに記載の通り、Movidius向けのモデル生成にはUbuntuが必要だが、生成したモデルを利用してMovidiusを動かすだけであればRaspbianで問題ないとのこと。

flow-developers.hatenablog.com

このサイトはラズパイ2向けですが、基本ラズパイZero Wでも大差ないだろうと考え、試してみました。

結論から言うと、ラズパイZero Wでも一応動作しました。

手順

基本、上記サイトに記載の通りです。異なる点といえば、

  • RASPBIAN STRETCH LITEを使用した
  • 今回はUSBカメラを接続できなかったため、前回参考した手順サイトの「ねこの認識」で動作確認をした

ことくらいです。
ただ、一度Movidiusのデバイスが認識されず、ncs-fullcheckが失敗したことがあったのですが、その後なぜか、ncs-fullcheckを実行しても何も起こらなくなってしまいました。再度makeすることで治りましたが、原因はよくわかりません。

ラズパイにUbuntu 16.04 LTSをインストールし、Movidius NCSを試す

話題のMovidius NCSを入手しました。

f:id:tuti107:20170818072407j:plain

非常に安価で、かつUSBポートに差すだけで、Deep Learningによる認識処理を高速に実行できる、ということで、
ラズパイにMovidius NCSを接続すると色々面白いことができそう。

ラズパイでMovidius NCSについては、以下に手順が詳細記載されております。
qiita.com

基本「上記の手順の通り」なのですが、色々と苦戦・・ちゃっちゃと環境を作って色々試したいと思っていたのですが、「まずは動かす」ことで精いっぱいでした。

以下、はまった点などの備忘録です。

ラズパイにUbuntuを入れる

上記サイトに記載の通り、https://wiki.ubuntu.com/ARM/RaspberryPiからイメージを取得し、SDカードに書き込み、という手順を試みたのですが、何度やっても(上記手順サイトには記載はないが、イメージDLサイトに記載のある)Optional PPAをインストールすると、Ubuntuが起動不能になってしまいました。

途方にくれていたところ、以下のサイトを発見。
tiryoh.com

本サイトの「初期設定済み版」を利用させていただきました。

なお、WindowsにてイメージをSDカードに書き込む方法ですが、
入手したイメージを、7-Zipなどで展開し、Win32 Disk Imager等で、SDカードに書き込めばOKです。

f:id:tuti107:20170818065928p:plain

ツールキットのインストール

上記手順サイトの通り、

cd bin
./setup.sh

とすると、数時間のインストール作業のの後、「c++: internal compiler error: Killed」でエラー終了・・

UbuntuをGUI版にしたせいでしょうか?どうやらメモリー不足によるエラーのようです。そこで、

dd if=/dev/zero of=/var/swap.img bs=1024k count=1000
mkswap /var/swap.img
swapon /var/swap.img

としてスワップ領域を追加して再度インストールしました。

f:id:tuti107:20170818071037p:plain

なお、この状態でmake checkすると、Import Errorが表示されますが、パスの問題のようで、再度bash立ち上げれば消えます。
また、ttyでmake checkすると、今度は「Gdk-CRITICAL **: gdk_cursor_new_for_display: assertion 'GDK_IS_DISPLAY (display)' failed」が表示されます。ただ、GUIでのコンソール(MATE Terminal)では表示されないし、とりあえずよしとしました。

その後の手順

その後の手順は、手順サイトに記載のとおりうまくいきました。

ARKitを利用してGoogle Cardboardでポジショントラッキング

過去、モバイル向けARエンジンであるVuforiaKudanを利用したモバイルVRでのポジショントラッキングに挑戦してきました。
今回は、先のWWDCで発表されたAppleのARエンジンであるARKitを利用したポジショントラッキングに挑戦してみます。

youtu.be

UnityでARKitを利用するための準備

これについては、各所で詳細な説明がされているようなので、仔細は書きませんが、

  1. Apple Developer Programより、XCode 9 betaをインストールする
  2. 同じく、iOS11 betaをインストールする(こちらはMacからではなくiOS端末にて)
  3. Unityを5.6.1以上にアップデートする
  4. こちらより、unity-arkit-plugin.unitypackageをダウンロードする

となります。

ARKitでポジショントラッキング

まずは、Assets -> Import Package -> Custom Packageにて「unity-arkit-plugin.unitypackage」をインポートします。
次に、以下のスクリプトを作成し、適当なオブジェクト(MainCameraなど)に設定します。

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

public class PositionTracker : MonoBehaviour {

	public Transform vrCamera;
	private UnityARSessionNativeInterface m_session;

	void Start () {
		Application.targetFrameRate = 60;
		m_session = UnityARSessionNativeInterface.GetARSessionNativeInterface();
		ARKitWorldTackingSessionConfiguration config = new ARKitWorldTackingSessionConfiguration();
		config.planeDetection = UnityARPlaneDetection.Horizontal;
		config.alignment = UnityARAlignment.UnityARAlignmentGravity;
		config.getPointCloudData = true;
		config.enableLightEstimation = true;
		m_session.RunWithConfig(config);
	}
	
	void Update () {

		if (vrCamera != null) {
			Matrix4x4 matrix = m_session.GetCameraPose();
			vrCamera.transform.localPosition = UnityARMatrixOps.GetPosition(matrix);
		}
	}
}

次に、プレイヤーが歩き回るための適当なオブジェクト群を用意します。
私は、夏に向けてホラーコンテンツを開発すべく、以下を利用しました。
https://www.assetstore.unity3d.com/jp/#!/content/18703
f:id:tuti107:20170703000902p:plain

次に、Scene上に空のGameObject(CamParent)を生成し、Main CameraをCamParentへドラッグ&ドロップします。
また、Main Cameraに貼り付けたPositionTrackerのCr Cameraに、CamParentを設定します。
Main CameraのClipping Planes->nearは0.1等としてください。これで、オブジェクトに近づき過ぎても、オブジェクトが欠けづらくなります。
f:id:tuti107:20170703001037p:plain

ビルド・実行

あとは、iOS向けにビルドし、実行するだけです。

  1. File->Build Settingsにて、PlatformをiOSにする
  2. Player Settingsにて、Virtual Reality Supportedにチェックを入れ、Virtual Reality SDKsにCardboardを追加する
  3. Bandle IdentifierやCamera Usage Description(ここが空だと、実行直後に落ちる)を適当に設定する
  4. Build and Runを押下する。しばらくするとXCode9 betaが起動してくるので、Signing -> Teamを正しく設定し、実行する

f:id:tuti107:20170703001718p:plain

終わりに

ARKitよくできていると思います。Google CardboardでVR空間を自由自在に歩き回れる日が来るとは・・感激です!
私の端末はiPhone 6S plusですが、処理落ち等は全く気にならず、精度・パフォーマンス共に素晴らしいです。
ただ、iOSでしか利用できないのがネックです・・Kudanさんがポジショントラッキング機能をサポートする日が待ち遠しい・・

Unity5.6.1f1でAndroidManifest-main.xml merging error

Unityを5.6.1f1にアップデートしたら、開発中のGearVR用のアプリが「AndroidManifest-main.xml merging error」でビルド不能に・・
f:id:tuti107:20170527190302p:plain
AndroidManifestを書き換えよ、とのことですが、何のことやら・・

こちらに解決方法がありました。

C:\Program Files\Unity\Editor\Data\PlaybackEngines\AndroidPlayer\Apk 

のAndroidManifest.xmlをアドミンで開き(オリジナルは念のため別名で保存)、
Applicationタグの

  • android:debuggable="true"
  • android:theme="@style/UnityThemeSelector"

を削除すればよいようです。
f:id:tuti107:20170527190637p:plain

上記対応で、ビルド通るようになりました!

Enabling or adding a Renderer during rendering

遅まきながらUnity5.6へアップデートしたのですが、Unity5.5で動作していたプログラムが動かなくなりました。
gameObject.SetActive()にて、"Enabling or adding a Renderer during rendering"というエラーが発生している模様。
原因は、gameObject.SetActive()をOnBecameInvisible()内で呼び出していたため、でした。
コルーチンを使って、yield return new WaitForEndOfFrame()で、フレーム終了まで待ち、上記を実行することで、回避できました。
その他は、特段問題なく動いているようですが、Unityのアップデートは毎回ドキドキします・・

陰影表示の際のOnBecameVisibleとOnBecameInvisibleについて

現在開発中のアプリで、OnBecameVisible(), OnBecameInvisible()にて、オブジェクトがカメラに映り込んでいるかどうかを判定し・・、という処理を作っていたのですが、どうにも思い通りに動かない。
明らかにカメラからオブジェクトが外れているにもかかわらず、OnBecameInvisible()が呼ばれなかったり、逆にカメラ内にオブジェクトが映っていないのに、OnBecameVisible()が呼ばれたりと。
結論としては、オブジェクトの影がカメラ内にある場合は、表示状態となっている、というものでした。

例えば、以下のようにシーン配置し、Directional Lightを「もうじき沈むくらいの西日」にします。そうすると、シリンダの対面の壁にシリンダの影が映ります。
f:id:tuti107:20170409104740p:plain

シリンダには、以下のようなコードを貼り付けて、シリンダがカメラに映っているならVisible、映っていないならInvisibleが表示されるようにします。

using UnityEngine;

public class VisibleChecker : MonoBehaviour {

  public TextMesh text;

  private void OnBecameVisible()
  {
    text.text = "Visible";
  }

  private void OnBecameInvisible()
  {
    text.text = "Invisible";
  }
}

すると、シリンダがカメラ内なら、
f:id:tuti107:20170409104918p:plain

カメラ外なら、
f:id:tuti107:20170409104937p:plain

となり、期待通りの動きなのですが、シリンダの影がカメラ内に映っている場合も、
f:id:tuti107:20170409105010p:plain

となります。
影を付けながら、画面内にオブジェクトが映っている/映っていないときにOnBecameVisible(), OnBecameInvisible()が呼ばれるようにできないか頑張ってみましたが、結局対象オブジェクトは影がでないようにしました・・
f:id:tuti107:20170409105452p:plain

よい方法ご存知の方、ぜひご連絡ください。