Tutti Lab 元シリコンバレー在住のおっさん技術者、モバイルVRアプリ開発に挑戦中 2017-11-26T17:57:13+09:00 tuti107 Hatena::Blog hatenablog://blog/10328537792367928124 iPhoneでTensorflow Liteを試してみる hatenablog://entry/8599973812321302623 2017-11-26T17:57:13+09:00 2017-11-26T17:57:13+09:00 先日モバイル向けのTensorflowである、Tensorflow Liteがリリースされました。 Tensorflow Liteは、フル機能のTensorflowと異なり、iOS/Androidデバイスにて、学習済みのモデルを高速・高性能で動作させるための仕組みであり、学習や、任意のモデルの構築(MobileNetsなどの一部のモデルしか利用できません)はできません。 しかし、Googleが提供する学習済みのMobileNetsなどを転移学習することで、任意の対象画像の分類が可能ですので、色々な用途に応用できそうです。 今回は、iOS向けのTenforflow Liteのサンプルを実行してみ… <p>先日モバイル向けのTensorflowである、<a href="https://www.tensorflow.org/mobile/tflite/">Tensorflow Lite</a>がリリースされました。<br /> Tensorflow Liteは、フル機能のTensorflowと異なり、iOS/Androidデバイスにて、学習済みのモデルを高速・高性能で動作させるための仕組みであり、学習や、任意のモデルの構築(MobileNetsなどの一部のモデルしか利用できません)はできません。<br /> しかし、Googleが提供する学習済みの<a href="https://research.googleblog.com/2017/06/mobilenets-open-source-models-for.html">MobileNets</a>などを転移学習することで、任意の対象画像の分類が可能ですので、色々な用途に応用できそうです。<br /> 今回は、iOS向けのTenforflow Liteのサンプルを実行してみます。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20171126/20171126172347.png" alt="f:id:tuti107:20171126172347p:plain" title="f:id:tuti107:20171126172347p:plain" class="hatena-fotolife" itemprop="image"></span><br /> </p> <div class="section"> <h4>Tensorlow Liteを入手</h4> <p>Tensorflow Liteは、<a href="https://github.com/tensorflow/tensorflow">Github</a>より入手できます。git clone、もしくはzipで適当なフォルダにダウンロード・任意のフォルダへ展開します(以下、tfフォルダとします)。</p> </div> <div class="section"> <h4>下準備</h4> <div class="section"> <h5>Xcode 8とビルド用のコマンドラインツールをインストール</h5> <p>コマンドラインツールは、コンソールより以下のコマンドによりインストールできます。</p> <pre class="code" data-lang="" data-unlink>xcode-select --install</pre><p>次に、以下をコンソールでタイプして各種ツールをインストールします(要homebrew)</p> <pre class="code" data-lang="" data-unlink>brew install automake brew install libtool</pre> </div> <div class="section"> <h5>Tensorflow Liteライブラリのビルド</h5> <p>コンソールにて、tfフォルダへ移動し、以下をタイプします。</p> <pre class="code" data-lang="" data-unlink>tensorflow/contrib/lite/download_dependencies.sh tensorflow/contrib/lite/build_ios_universal_lib.sh</pre><p>これで、tensorflow/contrib/lite/gen/lib/libtensorflow-lite.aが生成されます。</p> </div> <div class="section"> <h5>cameraサンプルを実行</h5> <p>サンプルは、simple, cameraの2種類あります。simpleは本当にシンプルでつまらないので、ここではcameraを紹介します。<br /> まず、cameraサンプルのプロジェクトを開く前に、以下を実行します。</p> <pre class="code" data-lang="" data-unlink>cd tensorflow/contrib/lite/examples/ios/camera pod install</pre><p>なお、cocoapodsが未導入の場合は、<a href="https://qiita.com/satoken0417/items/479bcdf91cff2634ffb1">&#x3053;&#x3061;&#x3089;</a>を参考に、cocoapodsを導入ください。</p><p>次に、/tensorflow/tensorflow/contrib/lite/examples/ios/camera/tflite_camera_example.xcodeprojをクリックして、XCodeを起動し、以下の操作を行います。</p> <ol> <li>General->Signingを正しく設定(Automatically manage signingをチェックすると楽)</li> <li>Build Phases->Link Binary With Librariesより、libPods-tflite_camera_example.aを削除</li> <li>Product->Cleanでクリーンする</li> <li>ビルド→実行</li> </ol><p>すると、以下のように、カメラを向けたものの分類がリアルタイムに行われます。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20171126/20171126175605.png" alt="f:id:tuti107:20171126175605p:plain" title="f:id:tuti107:20171126175605p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>当方の環境(iPhone X)では、30fps以上の性能が出ていました。また、身の回りのもので試しただけなのでまだなんとも、ですが、なかなかの精度で分類できていたように思います。</p> </div> <div class="section"> <h5>終わりに</h5> <p>最新のスマホ環境では、Tensorflow Lite(+MobileNet)による分類は、かなり快適に動作するようです。<br /> 次回は、UnityよりTensorflow Liteを利用し、簡単なアプリを作成してみようと思います。</p> </div> </div> tuti107 Zap Boxを試してみた hatenablog://entry/8599973812306403417 2017-10-09T22:23:29+09:00 2017-10-09T22:24:09+09:00 Kickstarterで話題になった「紙」によるモバイルMRデバイス「Zap Box」が来ました!ポジショントラッキングのためのマーカも、ハンドトラッキングのためのスティックもすべて紙でできています。この円形のマーカを部屋のあちこちに設置することで、モバイルデバイスのポジショントラッキングが可能となり、例えばARKitやARCore対応のアプリケーション相当のアプリが動きます。 ARKit/ARCoreは、特別なマーカ無しでポジショントラッキングができてしまっているので、Kickstarterでの応募当時はモバイルでMR!すごい、という感じでしたが、今となっては少し時代遅れ感は否めません・・ハ… <p>Kickstarterで話題になった「紙」によるモバイルMRデバイス「Zap Box」が来ました!</p><p><a href="https://ksr-ugc.imgix.net/assets/014/343/180/f83aa4de35e0f5a2dec70939e4fabf00_original.gif?w=680&fit=max&v=1478005546&auto=format&gif-q=50&q=92&s=23873e5c4c2129f91a95e1f8ef7e7951" class="http-image" target="_blank"><img src="https://ksr-ugc.imgix.net/assets/014/343/180/f83aa4de35e0f5a2dec70939e4fabf00_original.gif?w=680&fit=max&v=1478005546&auto=format&gif-q=50&q=92&s=23873e5c4c2129f91a95e1f8ef7e7951" class="http-image" alt="https://ksr-ugc.imgix.net/assets/014/343/180/f83aa4de35e0f5a2dec70939e4fabf00_original.gif?w=680&fit=max&v=1478005546&auto=format&gif-q=50&q=92&s=23873e5c4c2129f91a95e1f8ef7e7951"></a></p><p>ポジショントラッキングのためのマーカも、ハンドトラッキングのためのスティックもすべて紙でできています。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20171009/20171009220049.jpg" alt="f:id:tuti107:20171009220049j:plain" title="f:id:tuti107:20171009220049j:plain" class="hatena-fotolife" itemprop="image"></span></p><p>この円形のマーカを部屋のあちこちに設置することで、モバイルデバイスのポジショントラッキングが可能となり、例えばARKitやARCore対応のアプリケーション相当のアプリが動きます。<br /> ARKit/ARCoreは、特別なマーカ無しでポジショントラッキングができてしまっているので、Kickstarterでの応募当時はモバイルでMR!すごい、という感じでしたが、今となっては少し時代遅れ感は否めません・・</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20171009/20171009220512.png" alt="f:id:tuti107:20171009220512p:plain" title="f:id:tuti107:20171009220512p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>ハンドトラッキング用のデバイスにはトリガーがついています。操作感はViveと同じ感じですが、少しオーバー気味に手を動かすとすぐにトラッキングをロストしてしまいます。<br /> スマホカメラでマーカを認識するという制約上しかたがないのですが、カメラの視野角から外れるとトラッキング不能となります。<br /> また、デバイスをゆっくり動かさないとトラッキング不能となります。<br /> なお、専用のレンズを付けることで、視野角の問題はある程度解消されるようです(私は試せていません)。</p><p>なお、トリガーを引くと、マーカの一部が動きます。これを認識してアクションが起こるようです。</p><p>本デバイスに対応のアプリは、現状、公式アプリZapBox betaのみのようです。本アプリには、ミニゴルフ等のゲームが同梱されております。<br /> ただ、上記のトラッキングの問題から、操作は結構大変です・・</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20171009/20171009221431.png" alt="f:id:tuti107:20171009221431p:plain" title="f:id:tuti107:20171009221431p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>左上のCardboardアイコンをタップするとVRモードとなります。なお、立体視には対応していません。</p> <div class="section"> <h4>総評</h4> <p>色々と文句を書きましたが、コンソール型のVR的なポジショントラッキング・ハンドトラッキングのUXを$30という低価格でモバイルに持ち込んだ、というのはすごいと思います。<br /> 現状SDKは公開されていないようですが、SDKが公開されれば、対応アプリも増え、低価格帯のモバイルVR市場の一角を担えるかもしれません。</p><p>SDKが公開されたら、色々と試してみたいと思います。</p> </div> tuti107 ARCoreでポジショントラッキング hatenablog://entry/8599973812298584748 2017-09-16T18:26:30+09:00 2017-09-16T18:29:01+09:00 過去、Vuforia、Kudan、ARKitにてモバイルVR向けのポジショントラッキングを実現する方法を書いてきました。 今回は、先日Googleより発表されたARCoreを利用したポジショントラッキングを実現します。 ARCoreのインストール ARCoreを利用するためには、以下が必要です。 Android SDK version 7.0 (API Level 24)以上 Unity 2017.2.0b11以上 ARCore SDK for Unity 現状ARCoreをサポートする端末は、Galaxy S8, Pixel, Pixel XLの三種類のみです。 これら端末でARCoreを利… <p>過去、<a href="http://tuti107.hatenablog.com/entry/2016/11/27/221350">Vuforia</a>、<a href="http://tuti107.hatenablog.com/entry/2017/03/25/035918">Kudan</a>、<a href="http://tuti107.hatenablog.com/entry/2017/07/03/002307">ARKit</a>にてモバイルVR向けのポジショントラッキングを実現する方法を書いてきました。<br /> 今回は、先日Googleより発表されたARCoreを利用したポジショントラッキングを実現します。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20170916/20170916180729.png" alt="f:id:tuti107:20170916180729p:plain" title="f:id:tuti107:20170916180729p:plain" class="hatena-fotolife" itemprop="image"></span><br /> </p> <div class="section"> <h3>ARCoreのインストール</h3> <p>ARCoreを利用するためには、以下が必要です。</p> <ul> <li><a href="https://developer.android.com/studio/intro/update.html#sdk-manager">Android SDK version 7.0 (API Level 24)</a>以上</li> <li><a href="https://unity3d.com/jp/unity/beta#downloads">Unity 2017.2.0b11</a>以上</li> <li><a href="https://github.com/google-ar/arcore-unity-sdk/releases/download/sdk-preview/arcore-unity-sdk-preview.unitypackage">ARCore SDK for Unity</a></li> </ul><p>現状ARCoreを<a href="https://developers.google.com/ar/discover/#supported_devices">&#x30B5;&#x30DD;&#x30FC;&#x30C8;&#x3059;&#x308B;&#x7AEF;&#x672B;</a>は、Galaxy S8, Pixel, Pixel XLの三種類のみです。<br /> これら端末でARCoreを利用するためには、<a href="https://github.com/google-ar/arcore-android-sdk/releases/download/sdk-preview/arcore-preview.apk">arcore-preview.apk</a>をダウンロードし、adbで端末に書き込む必要があります。</p> <pre class="code" data-lang="" data-unlink>adb install -r -d arcore-preview.apk</pre> </div> <div class="section"> <h3>UnityでARCoreのサンプルを実行する</h3> <p>まず、以下の手順でARCore SDK for Unityに付属のサンプルシーン(HelloAR.unity)を動かしてみます。</p> <ul> <li>arcore-unity-sdk-preview.unitypackageをインポート</li> <li>HelloAR.unityをダブルクリック</li> <li>File->Build Settingsにて、PlatformをAndroidに切り替える</li> <li>Edit->Project Settings->Playerにて、以下の通り設定 <ul> <li>Other Settings > Multithreaded Rendering: Off</li> <li>Other Settings > Package Nameに適当な名前を設定(例えばcom.example.helloAR)</li> <li>Other Settings > Minimum API Level: Android 7.0以上</li> <li>Other Settings > Target API Level: Android 7.0 か 7.1</li> <li>XR Settings > ARCore Supported: On</li> </ul></li> <li>端末をUSB接続し、Build & Run</li> </ul><p>アプリを起動すると、数秒ほど「Searching for surfaces...」と表示され、その通り平面を探します。平面が見つかると、平面上にメッシュが表示されます。<br /> メッシュの上をタップすると、そこにドロイドくんが配置されます。技術的にはARKitと同様の感じです。</p> </div> <div class="section"> <h3>ARCoreでポジショントラッキング</h3> <p>次に、ARCoreを利用してGoogle Cardboardにてポジショントラッキングを実現します。</p><p>まず、以下のスクリプトを作成します。</p> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synStatement">using</span> UnityEngine; <span class="synStatement">using</span> GoogleARCore; <span class="synType">public</span> <span class="synType">class</span> PosiTra : MonoBehaviour { <span class="synType">private</span> Vector3 origPos; <span class="synType">void</span> Start () { origPos = gameObject.transform.position; } <span class="synType">void</span> Update () { <span class="synStatement">if</span> (Frame.TrackingState == FrameTrackingState.Tracking) { var pose = Frame.Pose; gameObject.transform.position = pose.position + origPos; } } } </pre><p>ARCoreには、カメラ位置・回転を取得するためのPoseクラスが用意されているため、非常に簡単にポジショントラッキングが実現できます。</p><p>次に、以下の通り、シーンを編集します。</p> <ul> <li>GoogleARCore/Prefabs/ARCore Device.prefabをシーンに配置。Session ComponentのFirst Person CameraをNoneにする(カメラライブビュー表示を削除するため)</li> <li>空GameObjectを作成し(Playerとします)、PosiTraコンポーネントを追加</li> <li>ARCore Deviceの子オブジェクトFirst Person CameraをPlayer配下へ移動</li> </ul><p><br /> 最後に、Edit->Project Settings->Player->XR Settings->Virtual Reality Supportedをチェックし、SDKsにCardboardを追加して、Build & Runします。</p><p><iframe width="480" height="270" src="https://www.youtube.com/embed/qPgir4BMAz8?feature=oembed" frameborder="0" allowfullscreen></iframe><cite class="hatena-citation"><a href="https://youtu.be/qPgir4BMAz8">youtu.be</a></cite><br /> </p> </div> <div class="section"> <h3>おわりに</h3> <p>ARCoreを使うと、ARKit同様の高品位なポジショントラッキングをGoogle Cardboardアプリで実現できます!・・が、対応機種が少なすぎます・・<br /> ARCoreを活用したVRアプリが量産される、ためには、ARCoreの対応機種がもっともっと増える必要がありそうです。</p> </div> tuti107 Windows Mixed Reality向けヘッドセットが来ました hatenablog://entry/8599973812292243588 2017-08-26T18:49:27+09:00 2017-08-26T18:54:06+09:00 こちらで予約受付がされていた(現状品切れ)Acer製のWindows Mixed Realityヘッドセットが、本日届きましたので、さっそく試してみました。 セットアップ ハードウェアのセットアップは非常にシンプルです。他のコンソール型のVRヘッドセットのようなたくさんのデバイス群は存在せず、上記写真のヘッドセットのみ、です。 これをPCにHDMI、USB接続するだけで、デバイスのセットアップは完了です。すばらしい!!次にソフトウェアのセットアップです。ハードウェアのセットアップが完了すると、以下のウィンドウが現れます。 セットアップも非常にシンプルで、身長入力→フロアの認識(腰の高さでヘッド… <p><a href="https://item.rakuten.co.jp/acer-direct/5jr03ww001/">&#x3053;&#x3061;&#x3089;</a>で予約受付がされていた(現状品切れ)Acer製のWindows Mixed Realityヘッドセットが、本日届きましたので、さっそく試してみました。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20170826/20170826155918.jpg" alt="f:id:tuti107:20170826155918j:plain" title="f:id:tuti107:20170826155918j:plain" class="hatena-fotolife" itemprop="image"></span><br /> </p> <div class="section"> <h3>セットアップ</h3> <p>ハードウェアのセットアップは非常にシンプルです。他のコンソール型のVRヘッドセットのようなたくさんのデバイス群は存在せず、上記写真のヘッドセットのみ、です。<br /> これをPCにHDMI、USB接続するだけで、デバイスのセットアップは完了です。すばらしい!!</p><p>次にソフトウェアのセットアップです。ハードウェアのセットアップが完了すると、以下のウィンドウが現れます。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20170826/20170826160218.png" alt="f:id:tuti107:20170826160218p:plain" title="f:id:tuti107:20170826160218p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>セットアップも非常にシンプルで、身長入力→フロアの認識(腰の高さでヘッドセットを持つだけ)→ガーディアンを設定して終了です。<br /> ガーディアン設定時に体験できますが、OculusやViveのような外部デバイスがないにも関わらず、正確にポジショントラッキングが行われています。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20170826/20170826182029.png" alt="f:id:tuti107:20170826182029p:plain" title="f:id:tuti107:20170826182029p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>その後、以下のような空間が表示されます。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20170826/20170826184050.png" alt="f:id:tuti107:20170826184050p:plain" title="f:id:tuti107:20170826184050p:plain" class="hatena-fotolife" itemprop="image"></span><br /> </p> </div> <div class="section"> <h3>動かしてみる</h3> <p>ヘッドセットは、Oculus等と比較して非常に軽く、装着感がよいです。<br /> またヘッドセットをつけて部屋を歩き回ることで、空間内を自由に歩き回ることができます。若干のラグや、実モーションに対する空間内での動きの違和感を感じましたが、それでも外部デバイスなしでここまでのポジショントラッキングができていることに感動しました。</p><p>なお、本製品には(開発者用ということもありますが)一切の入力デバイスが同梱していません。また、HoloLensのようなジェスチャによる入力(タップ、ブルーム)もありません。<br /> ただ、マウスを使って、テレポート(右クリックで写真の青カーソールを出し、離して移動)や、マウスを空間内で動かしてポインティング・クリックが可能です。このため、机に座ってVRするなら、まぁ問題ありません。</p><p>また、ヘッドセットの前方左右にカメラがついているため、てっきりこれで外部映像を入力し、立体視表示して、ホロレンズ的な体験ができる!と思っていたのですが、これは単にポジショントラッキング用のセンサーのようです。残念・・</p> </div> tuti107 MFT2017に出展したVRコンテンツ hatenablog://entry/8599973812290124770 2017-08-20T08:52:10+09:00 2017-08-20T09:12:56+09:00 今年もMaker Faire TokyoにVRコンテンツを出展しました。www.youtube.com へなちょこCooking Oculus Touchを活用したVR料理ゲーム(?)です。 食材をナイフでカットし、焼き色を付けて、皿に盛りつけます。 盛り付けが完成すると、AIがその料理に名前をつけてくれます。 食材のカット Turbo Slicer 2を使用しました。 youtu.beMeshRendererをもつオブジェクトに、Sliceableコンポーネントをセットするだけで、そのメッシュをスライスすることができます。 何点か、使用上の注意点ですが、 Sliceableのエディタ拡張のバ… <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20170820/20170820085956.jpg" alt="f:id:tuti107:20170820085956j:plain" title="f:id:tuti107:20170820085956j:plain" class="hatena-fotolife" itemprop="image"></span></p><p>今年も<a href="http://makezine.jp/event/mft2017/">Maker Faire Tokyo</a>にVRコンテンツを出展しました。</p><p><iframe width="480" height="270" src="https://www.youtube.com/embed/PI7svN1Z6S8?feature=oembed" frameborder="0" allowfullscreen></iframe><cite class="hatena-citation"><a href="https://www.youtube.com/watch?v=PI7svN1Z6S8&feature=youtu.be">www.youtube.com</a></cite><br /> </p> <div class="section"> <h3>へなちょこCooking</h3> <p>Oculus Touchを活用したVR料理ゲーム(?)です。<br /> 食材をナイフでカットし、焼き色を付けて、皿に盛りつけます。<br /> 盛り付けが完成すると、AIがその料理に名前をつけてくれます。</p> <div class="section"> <h4>食材のカット</h4> <p><a href="https://www.assetstore.unity3d.com/en/#!/content/73236">Turbo Slicer 2</a>を使用しました。<br /> <iframe width="480" height="270" src="https://www.youtube.com/embed/gILLqHMZ1ns?feature=oembed" frameborder="0" allowfullscreen></iframe><cite class="hatena-citation"><a href="https://youtu.be/gILLqHMZ1ns">youtu.be</a></cite></p><p>MeshRendererをもつオブジェクトに、Sliceableコンポーネントをセットするだけで、そのメッシュをスライスすることができます。<br /> 何点か、使用上の注意点ですが、</p> <ul> <li>Sliceableのエディタ拡張のバグ?により、断面部分のテクスチャ領域の設定がうまくいきません(設定が無視され、デフォルト値となる)</li> <li>手のちょっとしたブレなどで、大量に細かくスライスされてしまいます。何十・何百の細かなオブジェクトが生成されてしまい、処理がものすごく重くなります。私は、スライス後のオブジェクトの体積が一定以下の場合は、そのオブジェクトを削除するようにしました。</li> </ul> </div> <div class="section"> <h4>人間っぽいキャラクタの動きの実現</h4> <p>定番ですが<a href="https://www.assetstore.unity3d.com/en/#!/content/14290">Final IK</a>のVRIKを使用しました。<br /> <iframe width="480" height="270" src="https://www.youtube.com/embed/FsC679FIlII?feature=oembed" frameborder="0" allowfullscreen></iframe><cite class="hatena-citation"><a href="https://www.youtube.com/watch?v=FsC679FIlII">www.youtube.com</a></cite><br /> </p> <ul> <li>Vive用の設定情報はあるのですが、Oculus Touch用が見つからず・・ただ、OVRCameraRigに簡単に設定できました。</li> <li>Grounder IKを設定しないと、足が思うように動いてくれません(上記チュートリアルにはでてこない)。Grounder IKについては、_Demo/VRIK/VRIK(Grounder).unityが参考になります(というか、コピペ)</li> </ul> </div> <div class="section"> <h4>キャラクタの手の動き</h4> <p>OculusFingerを利用させていただきました。<br /> <iframe src="https://hatenablog-parts.com/embed?url=http%3A%2F%2Feyln.hatenablog.com%2Fentry%2F2016%2F12%2F19%2F003435" title="Oculus TouchでUnityちゃんの指を動かして遊ぼう - 遊んで航海記" class="embed-card embed-blogcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="http://eyln.hatenablog.com/entry/2016/12/19/003435">eyln.hatenablog.com</a></cite><br /> </p> </div> <div class="section"> <h4>お皿に食材を盛り付け</h4> <p>Unity 5より、Mesh ColliderはConvexにしなければならなくなり、お皿のように真ん中がくぼんた形状のMeshの当たり判定が、想定通りとなりません。そこで、多数のBoxColliderでMeshColliderをエミュレートする、<a href="https://www.assetstore.unity3d.com/en/#!/content/84867">Non Convex Mesh Collider</a>を利用しました。</p><p><iframe width="480" height="270" src="https://www.youtube.com/embed/7je6pEMC7oo?feature=oembed" frameborder="0" allowfullscreen></iframe><cite class="hatena-citation"><a href="https://youtu.be/7je6pEMC7oo">youtu.be</a></cite></p><p>使い方は簡単で、これまでMeshColliderを張り付けていたコンポーネントにNonConvexMeshColliderをセットし、Boxes Per Edgeを適当な数字として、Calculateボタンを押すだけです。Boxes Per Edgeを大きくすると、より細かなBoxColliderでエミュレートされますが、処理が重くなります。また、</p> <ul> <li>Create Child Game Objectのチェックを外すと、NonConvexMeshColliderがセットされたオブジェクトに多量のBoxColliderがセットされます。</li> <li>Create Child Game Objectのチェックをチェックした場合は、子オブジェクトが生成され、そこにBoxColliderがセットされます。この場合、衝突判定処理の修正(GetComponent→GetComponentInChildren)が必要かもしれません。</li> </ul> </div> <div class="section"> <h4>名前付け</h4> <p>im2txtを使用しました・・詳細は本作品を一緒に開発した友人にお任せだったため、よくわかりませんが・・<br /> <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fsoralab.space-ichikawa.com%2F2016%2F10%2Ftensorflow-im2txt-demo%2F" title="画像にキャプションを付ける「Show and Tell」のTensorFlow実装を試してみた⇒成功" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://soralab.space-ichikawa.com/2016/10/tensorflow-im2txt-demo/">soralab.space-ichikawa.com</a></cite></p><p>なお、im2txtは画像認識の結果を英語の文章で返すのですが、これを日本語化するために、Google Translateを使用しました。</p> </div> </div> <div class="section"> <h3>肝試しVR</h3> <p>Gear VR/Daydream/Google Cardboard向けのホラーVRゲーム(?)です。<br /> ライド型のお化け屋敷アトラクションであり、プレーヤは歩いたりしなくても、乗り物が動いて勝手に進んでいきます。<br /> 特段のゲーム性はなく、ひたすら怖いモンスターや超常現象を見て楽しむ、コンテンツです。</p><p>お化け屋敷のセットには、<a href="https://www.assetstore.unity3d.com/en/#!/content/22030">Abandoned Hospital</a>を使用しました。<br /> <iframe width="480" height="270" src="https://www.youtube.com/embed/QXqLFA7CR0Y?feature=oembed" frameborder="0" allowfullscreen></iframe><cite class="hatena-citation"><a href="https://youtu.be/QXqLFA7CR0Y">youtu.be</a></cite></p><p>・・何も出てこなくても十分に怖いです・・<br /> なお、モバイル向けのアセットではないので、そのまま使用すると、GearVR等では全然性能が出ません。涙ぐましい性能調整が必要でした・・</p><p>本コンテンツは、近々(秋になる前に)Oculus Store等に載せたいな、と画策中です。</p> </div> tuti107 ラズパイZero WでMovidius NCSを動かす hatenablog://entry/8599973812290053276 2017-08-19T11:32:49+09:00 2017-08-19T11:32:49+09:00 前回は、苦戦しながらもラズパイ3にUbuntuをインストールし、Movidiusを動かしました。 今回は、ラズパイ3ではなく、より安価・小型(でも非力)なラズパイZero Wで、Movidiusを動かしてみます。なお、ラズパイZero Wはラズパイ2・3とARMアーキテクチャが異なるため、ラズパイZero W向けのUbuntuイメージは提供されておりません。 しかし、以下のサイトに記載の通り、Movidius向けのモデル生成にはUbuntuが必要だが、生成したモデルを利用してMovidiusを動かすだけであればRaspbianで問題ないとのこと。flow-developers.hatenabl… <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20170819/20170819110519.jpg" alt="f:id:tuti107:20170819110519j:plain" title="f:id:tuti107:20170819110519j:plain" class="hatena-fotolife" itemprop="image"></span></p><p><a href="http://tuti107.hatenablog.com/entry/2017/08/18/072729">&#x524D;&#x56DE;</a>は、苦戦しながらもラズパイ3にUbuntuをインストールし、Movidiusを動かしました。<br /> 今回は、ラズパイ3ではなく、より安価・小型(でも非力)なラズパイZero Wで、Movidiusを動かしてみます。</p><p>なお、ラズパイZero Wはラズパイ2・3とARMアーキテクチャが異なるため、ラズパイZero W向けのUbuntuイメージは提供されておりません。<br /> しかし、以下のサイトに記載の通り、Movidius向けのモデル生成にはUbuntuが必要だが、生成したモデルを利用してMovidiusを動かすだけであればRaspbianで問題ないとのこと。</p><p><iframe src="https://hatenablog-parts.com/embed?url=http%3A%2F%2Fflow-developers.hatenablog.com%2Fentry%2F2017%2F08%2F12%2F235115" title="USB型DeepLearningアクセラレータ,&quot;Movidius Neural Compute Stick&quot;をRaspberryPi2で動かしてみた。 - Flow-Developers" class="embed-card embed-blogcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="http://flow-developers.hatenablog.com/entry/2017/08/12/235115">flow-developers.hatenablog.com</a></cite></p><p>このサイトはラズパイ2向けですが、基本ラズパイZero Wでも大差ないだろうと考え、試してみました。</p><p>結論から言うと、ラズパイZero Wでも一応動作しました。</p> <div class="section"> <h3>手順</h3> <p>基本、上記サイトに記載の通りです。異なる点といえば、</p> <ul> <li><a href="https://www.raspberrypi.org/downloads/raspbian/">RASPBIAN STRETCH LITE</a>を使用した</li> <li>今回はUSBカメラを接続できなかったため、前回参考した手順サイトの「ねこの認識」で動作確認をした</li> </ul><p>ことくらいです。<br /> ただ、一度Movidiusのデバイスが認識されず、ncs-fullcheckが失敗したことがあったのですが、その後なぜか、ncs-fullcheckを実行しても何も起こらなくなってしまいました。再度makeすることで治りましたが、原因はよくわかりません。</p> </div> tuti107 ラズパイにUbuntu 16.04 LTSをインストールし、Movidius NCSを試す hatenablog://entry/8599973812289225583 2017-08-18T07:27:29+09:00 2017-08-18T10:43:05+09:00 話題のMovidius NCSを入手しました。非常に安価で、かつUSBポートに差すだけで、Deep Learningによる認識処理を高速に実行できる、ということで、 ラズパイにMovidius NCSを接続すると色々面白いことができそう。ラズパイでMovidius NCSについては、以下に手順が詳細記載されております。 qiita.com基本「上記の手順の通り」なのですが、色々と苦戦・・ちゃっちゃと環境を作って色々試したいと思っていたのですが、「まずは動かす」ことで精いっぱいでした。以下、はまった点などの備忘録です。 ラズパイにUbuntuを入れる 上記サイトに記載の通り、https://wi… <p>話題のMovidius NCSを入手しました。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20170818/20170818072407.jpg" alt="f:id:tuti107:20170818072407j:plain" title="f:id:tuti107:20170818072407j:plain" class="hatena-fotolife" itemprop="image"></span></p><p>非常に安価で、かつUSBポートに差すだけで、Deep Learningによる認識処理を高速に実行できる、ということで、<br /> ラズパイにMovidius NCSを接続すると色々面白いことができそう。</p><p>ラズパイでMovidius NCSについては、以下に手順が詳細記載されております。<br /> <iframe src="https://hatenablog-parts.com/embed?url=http%3A%2F%2Fqiita.com%2Fpeisuke%2Fitems%2F87210d55a5195e24c9bc" title="Raspberrypi3でMovidius Neural Compute Stickのサンプルまでを動かしてみた - Qiita" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="http://qiita.com/peisuke/items/87210d55a5195e24c9bc">qiita.com</a></cite></p><p>基本「上記の手順の通り」なのですが、色々と苦戦・・ちゃっちゃと環境を作って色々試したいと思っていたのですが、「まずは動かす」ことで精いっぱいでした。</p><p>以下、はまった点などの備忘録です。</p> <div class="section"> <h3>ラズパイにUbuntuを入れる</h3> <p>上記サイトに記載の通り、<a href="https://wiki.ubuntu.com/ARM/RaspberryPi">https://wiki.ubuntu.com/ARM/RaspberryPi</a>からイメージを取得し、SDカードに書き込み、という手順を試みたのですが、何度やっても(上記手順サイトには記載はないが、イメージDLサイトに記載のある)Optional PPAをインストールすると、Ubuntuが起動不能になってしまいました。</p><p>途方にくれていたところ、以下のサイトを発見。<br /> <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Ftiryoh.com%2Fblog%2Farchives%2F1206" title="RaspberryPi3用Ubuntuのカーネルアップデート" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://tiryoh.com/blog/archives/1206">tiryoh.com</a></cite></p><p>本サイトの「初期設定済み版」を利用させていただきました。</p><p>なお、WindowsにてイメージをSDカードに書き込む方法ですが、<br /> 入手したイメージを、7-Zipなどで展開し、<a href="https://sourceforge.net/projects/win32diskimager/">Win32 Disk Imager</a>等で、SDカードに書き込めばOKです。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20170818/20170818065928.png" alt="f:id:tuti107:20170818065928p:plain" title="f:id:tuti107:20170818065928p:plain" class="hatena-fotolife" itemprop="image"></span><br /> </p> </div> <div class="section"> <h3>ツールキットのインストール</h3> <p>上記手順サイトの通り、</p> <pre class="code" data-lang="" data-unlink>cd bin ./setup.sh</pre><p>とすると、数時間のインストール作業のの後、「c++: internal compiler error: Killed」でエラー終了・・</p><p>UbuntuをGUI版にしたせいでしょうか?どうやらメモリー不足によるエラーのようです。そこで、</p> <pre class="code" data-lang="" data-unlink>dd if=/dev/zero of=/var/swap.img bs=1024k count=1000 mkswap /var/swap.img swapon /var/swap.img</pre><p>としてスワップ領域を追加して再度インストールしました。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20170818/20170818071037.png" alt="f:id:tuti107:20170818071037p:plain" title="f:id:tuti107:20170818071037p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>なお、この状態でmake checkすると、Import Errorが表示されますが、パスの問題のようで、再度bash立ち上げれば消えます。<br /> また、ttyでmake checkすると、今度は「Gdk-CRITICAL **: gdk_cursor_new_for_display: assertion 'GDK_IS_DISPLAY (display)' failed」が表示されます。ただ、GUIでのコンソール(MATE Terminal)では表示されないし、とりあえずよしとしました。</p> </div> <div class="section"> <h3>その後の手順</h3> <p>その後の手順は、手順サイトに記載のとおりうまくいきました。</p> </div> tuti107 ARKitを利用してGoogle Cardboardでポジショントラッキング hatenablog://entry/8599973812276212975 2017-07-03T00:23:07+09:00 2017-07-03T00:23:07+09:00 過去、モバイル向けARエンジンであるVuforia・Kudanを利用したモバイルVRでのポジショントラッキングに挑戦してきました。 今回は、先のWWDCで発表されたAppleのARエンジンであるARKitを利用したポジショントラッキングに挑戦してみます。youtu.be UnityでARKitを利用するための準備 これについては、各所で詳細な説明がされているようなので、仔細は書きませんが、 Apple Developer Programより、XCode 9 betaをインストールする 同じく、iOS11 betaをインストールする(こちらはMacからではなくiOS端末にて) Unityを5.6… <p>過去、モバイル向けARエンジンである<a href="http://tuti107.hatenablog.com/entry/2016/11/27/221350">Vuforia</a>・<a href="http://tuti107.hatenablog.com/entry/2017/03/25/035918">Kudan</a>を利用したモバイルVRでのポジショントラッキングに挑戦してきました。<br /> 今回は、先のWWDCで発表されたAppleのARエンジンであるARKitを利用したポジショントラッキングに挑戦してみます。</p><p><iframe width="480" height="270" src="https://www.youtube.com/embed/jrzffJPekRo?feature=oembed" frameborder="0" allowfullscreen></iframe><cite class="hatena-citation"><a href="https://youtu.be/jrzffJPekRo">youtu.be</a></cite></p><p></p> <div class="section"> <h3>UnityでARKitを利用するための準備</h3> <p>これについては、各所で詳細な説明がされているようなので、仔細は書きませんが、</p> <ol> <li><a href="https://developer.apple.com/develop/">Apple Developer Program</a>より、XCode 9 betaをインストールする</li> <li>同じく、iOS11 betaをインストールする(こちらはMacからではなくiOS端末にて)</li> <li>Unityを5.6.1以上にアップデートする</li> <li><a href="https://bitbucket.org/Unity-Technologies/unity-arkit-plugin">&#x3053;&#x3061;&#x3089;</a>より、unity-arkit-plugin.unitypackageをダウンロードする</li> </ol><p>となります。</p> </div> <div class="section"> <h3>ARKitでポジショントラッキング</h3> <p>まずは、Assets -> Import Package -> Custom Packageにて「unity-arkit-plugin.unitypackage」をインポートします。<br /> 次に、以下のスクリプトを作成し、適当なオブジェクト(MainCameraなど)に設定します。</p> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synStatement">using</span> System.Collections; <span class="synStatement">using</span> System.Collections.Generic; <span class="synStatement">using</span> UnityEngine; <span class="synStatement">using</span> UnityEngine.XR.iOS; <span class="synType">public</span> <span class="synType">class</span> PositionTracker : MonoBehaviour { <span class="synType">public</span> Transform vrCamera; <span class="synType">private</span> UnityARSessionNativeInterface m_session; <span class="synType">void</span> Start () { Application.targetFrameRate = <span class="synConstant">60</span>; m_session = UnityARSessionNativeInterface.GetARSessionNativeInterface(); ARKitWorldTackingSessionConfiguration config = <span class="synStatement">new</span> ARKitWorldTackingSessionConfiguration(); config.planeDetection = UnityARPlaneDetection.Horizontal; config.alignment = UnityARAlignment.UnityARAlignmentGravity; config.getPointCloudData = <span class="synConstant">true</span>; config.enableLightEstimation = <span class="synConstant">true</span>; m_session.RunWithConfig(config); } <span class="synType">void</span> Update () { <span class="synStatement">if</span> (vrCamera != <span class="synConstant">null</span>) { Matrix4x4 matrix = m_session.GetCameraPose(); vrCamera.transform.localPosition = UnityARMatrixOps.GetPosition(matrix); } } } </pre><p>次に、プレイヤーが歩き回るための適当なオブジェクト群を用意します。<br /> 私は、夏に向けてホラーコンテンツを開発すべく、以下を利用しました。<br /> <a href="https://www.assetstore.unity3d.com/jp/#!/content/18703">https://www.assetstore.unity3d.com/jp/#!/content/18703</a><br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20170703/20170703000902.png" alt="f:id:tuti107:20170703000902p:plain" title="f:id:tuti107:20170703000902p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>次に、Scene上に空のGameObject(CamParent)を生成し、Main CameraをCamParentへドラッグ&ドロップします。<br /> また、Main Cameraに貼り付けたPositionTrackerのCr Cameraに、CamParentを設定します。<br /> Main CameraのClipping Planes->nearは0.1等としてください。これで、オブジェクトに近づき過ぎても、オブジェクトが欠けづらくなります。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20170703/20170703001037.png" alt="f:id:tuti107:20170703001037p:plain" title="f:id:tuti107:20170703001037p:plain" class="hatena-fotolife" itemprop="image"></span><br /> </p> </div> <div class="section"> <h3>ビルド・実行</h3> <p>あとは、iOS向けにビルドし、実行するだけです。</p> <ol> <li>File->Build Settingsにて、PlatformをiOSにする</li> <li>Player Settingsにて、Virtual Reality Supportedにチェックを入れ、Virtual Reality SDKsにCardboardを追加する</li> <li>Bandle IdentifierやCamera Usage Description(ここが空だと、実行直後に落ちる)を適当に設定する</li> <li>Build and Runを押下する。しばらくするとXCode9 betaが起動してくるので、Signing -> Teamを正しく設定し、実行する</li> </ol><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20170703/20170703001718.png" alt="f:id:tuti107:20170703001718p:plain" title="f:id:tuti107:20170703001718p:plain" class="hatena-fotolife" itemprop="image"></span><br /> </p> </div> <div class="section"> <h3>終わりに</h3> <p>ARKitよくできていると思います。Google CardboardでVR空間を自由自在に歩き回れる日が来るとは・・感激です!<br /> 私の端末はiPhone 6S plusですが、処理落ち等は全く気にならず、精度・パフォーマンス共に素晴らしいです。<br /> ただ、iOSでしか利用できないのがネックです・・Kudanさんがポジショントラッキング機能をサポートする日が待ち遠しい・・</p> </div> tuti107 Unity5.6.1f1でAndroidManifest-main.xml merging error hatenablog://entry/10328749687253722001 2017-05-27T19:11:42+09:00 2017-05-27T19:13:18+09:00 Unityを5.6.1f1にアップデートしたら、開発中のGearVR用のアプリが「AndroidManifest-main.xml merging error」でビルド不能に・・ AndroidManifestを書き換えよ、とのことですが、何のことやら・・こちらに解決方法がありました。 C:\Program Files\Unity\Editor\Data\PlaybackEngines\AndroidPlayer\Apk のAndroidManifest.xmlをアドミンで開き(オリジナルは念のため別名で保存)、 Applicationタグの android:debuggable="true"… <p>Unityを5.6.1f1にアップデートしたら、開発中のGearVR用のアプリが「AndroidManifest-main.xml merging error」でビルド不能に・・<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20170527/20170527190302.png" alt="f:id:tuti107:20170527190302p:plain" title="f:id:tuti107:20170527190302p:plain" class="hatena-fotolife" itemprop="image"></span><br /> AndroidManifestを書き換えよ、とのことですが、何のことやら・・</p><p><a href="http://answers.unity3d.com/questions/1355171/androidmanifest-mainxml-merging-error.html">&#x3053;&#x3061;&#x3089;</a>に解決方法がありました。</p> <pre class="code" data-lang="" data-unlink>C:\Program Files\Unity\Editor\Data\PlaybackEngines\AndroidPlayer\Apk </pre><p>のAndroidManifest.xmlをアドミンで開き(オリジナルは念のため別名で保存)、<br /> Applicationタグの</p> <ul> <li>android:debuggable="true" </li> <li>android:theme="@style/UnityThemeSelector"</li> </ul><p>を削除すればよいようです。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20170527/20170527190637.png" alt="f:id:tuti107:20170527190637p:plain" title="f:id:tuti107:20170527190637p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>上記対応で、ビルド通るようになりました!</p> tuti107 Enabling or adding a Renderer during rendering hatenablog://entry/10328749687243617600 2017-05-06T15:08:28+09:00 2017-05-06T15:08:28+09:00 遅まきながらUnity5.6へアップデートしたのですが、Unity5.5で動作していたプログラムが動かなくなりました。 gameObject.SetActive()にて、"Enabling or adding a Renderer during rendering"というエラーが発生している模様。 原因は、gameObject.SetActive()をOnBecameInvisible()内で呼び出していたため、でした。 コルーチンを使って、yield return new WaitForEndOfFrame()で、フレーム終了まで待ち、上記を実行することで、回避できました。 その他は、特段問… <p>遅まきながらUnity5.6へアップデートしたのですが、Unity5.5で動作していたプログラムが動かなくなりました。<br /> gameObject.SetActive()にて、"Enabling or adding a Renderer during rendering"というエラーが発生している模様。<br /> 原因は、gameObject.SetActive()をOnBecameInvisible()内で呼び出していたため、でした。<br /> コルーチンを使って、yield return new WaitForEndOfFrame()で、フレーム終了まで待ち、上記を実行することで、回避できました。<br /> その他は、特段問題なく動いているようですが、Unityのアップデートは毎回ドキドキします・・</p> tuti107 陰影表示の際のOnBecameVisibleとOnBecameInvisibleについて hatenablog://entry/10328749687235769552 2017-04-09T11:01:35+09:00 2017-04-09T11:01:35+09:00 現在開発中のアプリで、OnBecameVisible(), OnBecameInvisible()にて、オブジェクトがカメラに映り込んでいるかどうかを判定し・・、という処理を作っていたのですが、どうにも思い通りに動かない。 明らかにカメラからオブジェクトが外れているにもかかわらず、OnBecameInvisible()が呼ばれなかったり、逆にカメラ内にオブジェクトが映っていないのに、OnBecameVisible()が呼ばれたりと。 結論としては、オブジェクトの影がカメラ内にある場合は、表示状態となっている、というものでした。例えば、以下のようにシーン配置し、Directional Light… <p>現在開発中のアプリで、OnBecameVisible(), OnBecameInvisible()にて、オブジェクトがカメラに映り込んでいるかどうかを判定し・・、という処理を作っていたのですが、どうにも思い通りに動かない。<br /> 明らかにカメラからオブジェクトが外れているにもかかわらず、OnBecameInvisible()が呼ばれなかったり、逆にカメラ内にオブジェクトが映っていないのに、OnBecameVisible()が呼ばれたりと。<br /> 結論としては、オブジェクトの影がカメラ内にある場合は、表示状態となっている、というものでした。</p><p>例えば、以下のようにシーン配置し、Directional Lightを「もうじき沈むくらいの西日」にします。そうすると、シリンダの対面の壁にシリンダの影が映ります。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20170409/20170409104740.png" alt="f:id:tuti107:20170409104740p:plain" title="f:id:tuti107:20170409104740p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>シリンダには、以下のようなコードを貼り付けて、シリンダがカメラに映っているならVisible、映っていないならInvisibleが表示されるようにします。</p> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synStatement">using</span> UnityEngine; <span class="synType">public</span> <span class="synType">class</span> VisibleChecker : MonoBehaviour { <span class="synType">public</span> TextMesh text; <span class="synType">private</span> <span class="synType">void</span> OnBecameVisible() { text.text = <span class="synConstant">&quot;Visible&quot;</span>; } <span class="synType">private</span> <span class="synType">void</span> OnBecameInvisible() { text.text = <span class="synConstant">&quot;Invisible&quot;</span>; } } </pre><p>すると、シリンダがカメラ内なら、<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20170409/20170409104918.png" alt="f:id:tuti107:20170409104918p:plain" title="f:id:tuti107:20170409104918p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>カメラ外なら、<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20170409/20170409104937.png" alt="f:id:tuti107:20170409104937p:plain" title="f:id:tuti107:20170409104937p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>となり、期待通りの動きなのですが、シリンダの影がカメラ内に映っている場合も、<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20170409/20170409105010.png" alt="f:id:tuti107:20170409105010p:plain" title="f:id:tuti107:20170409105010p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>となります。<br /> 影を付けながら、画面内にオブジェクトが映っている/映っていないときにOnBecameVisible(), OnBecameInvisible()が呼ばれるようにできないか頑張ってみましたが、結局対象オブジェクトは影がでないようにしました・・<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20170409/20170409105452.png" alt="f:id:tuti107:20170409105452p:plain" title="f:id:tuti107:20170409105452p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>よい方法ご存知の方、ぜひご連絡ください。</p> tuti107 KUDAN SDKを使って、Gear VRでどこでもドア(2) hatenablog://entry/10328749687230410157 2017-03-25T03:59:18+09:00 2017-03-26T10:06:32+09:00 前回は、Gear VRで「どこでもドアに近づく」体験を実現すべく、まずはマーカレスARが可能なKUDANをインストールしました。 今回は、KUDAN SDKを利用して、ポジショントラッキング機能を実装し、どこでもドアに近づいてみます。www.youtube.com Kudan Camera prefabをシーンに設置 KudanAR/Prefabsにある「Kudan Camera - Markerless Only」をドラッグ&ドロップし、シーン内に設置した上で、インスペクタより以下の通り設定します。 Camera→Depth = -2 (本カメラを表示対象にしないようにするため) API K… <p><a href="http://tuti107.hatenablog.com/entry/2017/03/22/195832">&#x524D;&#x56DE;</a>は、Gear VRで「どこでもドアに近づく」体験を実現すべく、まずはマーカレスARが可能なKUDANをインストールしました。<br /> 今回は、KUDAN SDKを利用して、ポジショントラッキング機能を実装し、どこでもドアに近づいてみます。</p><p><iframe width="459" height="344" src="https://www.youtube.com/embed/9w9dcdrHaNA?feature=oembed" frameborder="0" allowfullscreen></iframe><cite class="hatena-citation"><a href="https://www.youtube.com/watch?v=9w9dcdrHaNA&feature=youtu.be">www.youtube.com</a></cite><br /> </p> <div class="section"> <h4>Kudan Camera prefabをシーンに設置</h4> <p>KudanAR/Prefabsにある「Kudan Camera - Markerless Only」をドラッグ&ドロップし、シーン内に設置した上で、インスペクタより以下の通り設定します。</p> <ul> <li>Camera→Depth = -2 (本カメラを表示対象にしないようにするため)</li> <li>API Key = 前回KudanのWEBページより取得した開発用API Key</li> </ul><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20170325/20170325025958.png" alt="f:id:tuti107:20170325025958p:plain" title="f:id:tuti107:20170325025958p:plain" class="hatena-fotolife" itemprop="image"></span><br /> </p> </div> <div class="section"> <h4>ポジショントラッキング用のスクリプト作成</h4> <p>次にKudanPositionTrackerを作成します。<br /> <script src="https://gist.github.com/tuti107/c344af73963452be647ba5a340d7c200.js"> </script><cite class="hatena-citation"><a href="https://gist.github.com/tuti107/c344af73963452be647ba5a340d7c200">gist.github.com</a></cite></p><p>そして、VR用カメラ(Main Camera)の位置に、空のGameObject(player)を生成し、VR用のカメラを、player配下にぶら下げます。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20170325/20170325031447.png" alt="f:id:tuti107:20170325031447p:plain" title="f:id:tuti107:20170325031447p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>また、playerに、上記で作成したKudanPositionTrackerを設置します。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20170325/20170325031557.png" alt="f:id:tuti107:20170325031557p:plain" title="f:id:tuti107:20170325031557p:plain" class="hatena-fotolife" itemprop="image"></span><br /> 設置後、インスペクタより、以下の通り設定してください。</p> <ul> <li>Kudan Tracker = シーンに設置した「Kudan Camera - Markerless Only」</li> </ul> </div> <div class="section"> <h4>動作解説</h4> <p>「Kudan Camera - Markerless Only」に設定されているコンポーネント「Kudan Tracker」が、KUDANのAR機能の制御を行います。<br /> Kudan Trackerを利用してポジショントラッキングを行う手順は、以下のとおりです。</p> <ol> <li>FloorPlaceGetPose()を呼び出して、floorPosition, floorOrientationを取得(ドキュメントに詳細な記述はないのですが、恐らくAR空間におけるカメラの位置と向きをそれぞれ取得しているのだと思われます)</li> <li>floorPosition, floorOrientationを引数として、ArbiTrackStart()メソッドを呼び出す。これによりマーカレスARのトラッキングが開始される</li> <li>Update()内で、毎フレームArbiTrackGetPose()を呼び出し、トラック対象のポジションと向きを取得する</li> <li>トラック対象のポジションと向きを、カメラの位置に変換する</li> <li>playerのlocalPositionを、上記算出したカメラ位置とすることで、Gear VRの位置(向き)に応じた、VRカメラの位置を設定する</li> </ol> </div> <div class="section"> <h4>注意点など</h4> <ul> <li>ArbiTrackGetPose()で取得する、位置・向き情報は、前回サンプルアプリ実行時に表示された黒色カプセル型のオブジェクトの表示位置・向きとなります。RevRotateVector()は、この表示位置・向きから、カメラの位置を算出しています。</li> <li>ArbiTrackは、ArbiTrackStart()呼び出し時にトラック対象を決定しそれをトラッキングし続けますが、トラッキング対象を見失う(頭の並行移動ではトラッキング対象を見失うことはあまりありませんが、頭を回転させるとあっさり見失います)と、ArbiTrackが終了してしまいます。このため、ポジショントラッキングをし続けるためには「ArbiTrackが終了時は再開する」処理の実装が必要となります</li> <li>KudanTrackerのトラッキング開始直後に、ArbiTrackStart()を呼び出すと、例外が発生し、ArbiTrackを開始できません("KudanTrackerのトラッキング"と"マーカレスAR用のArbiTrack"は別物)。トラッキングはKudan TrackerのStart On Enableをチェックしていると、本コンポーネントの初期化のタイミングで開始されます(もしくはStartTracking()メソッドをスクリプトから呼び出すことで、トラッキングを開始できる)。そこで、直後ではなく少し時間をおいてから(上記コードでは5秒)、ArbiTrackStart()を呼び出すことで、本問題を回避しています。</li> <li>これは、マーカレスARだから仕方がないことかと思いますが、ArbiTrackでは正確な距離の測定ができません(トラック対象の寸法情報がないのだから、当然ですね)。このため、ArbiTrackGetPose()で取得できる位置情報は所定の単位(メートルとか)ではなく、ArbiTrackのトラッキング対象の位置等により毎回異なります。上記コードでは、取得した位置情報にscaleFactor(0.0004)をかけて、Unityの座標に変換していますが、この0.0004は試行錯誤で見つけた「私の部屋ではそれっぽく見える値」であり、0.0004をかけることで、Unityの座標に正しく変換できる、というものではありません。このため、scaleFactor = 0.0004では、ArbiTrackトラッキング対象とGear VRの距離によっては、頭の動きに対して、動きすぎたり、逆に全然うごかなかったり、ということが起きます。このあたりを正確にしたいなら、試行錯誤scaleFactorの値を調整するか、マーカARにするしかなさそうですね。</li> </ul> </div> <div class="section"> <h3>おわりに</h3> <p>今回は、Gear VRで「どこでもドアに近づく」体験を実現すべく、KUDANを利用してポジショントラッキング機能を実装してみました。<br /> Vuforiaで実装した際と比較し、マーカが不要であり、かつマーカがなくても結構高精度なトラッキングが実現されているため、Vuforiaよりも使い勝手のよいポジショントラッキングの実装が可能かと思います。<br /> 一方、KUDAN ARはポジショントラッキング用に作られたものでないため、トラッキング対象が視界から消えるとポジショントラッキングができなくなる等、ちゃんとしたポジショントラッキングの実現のためには、色々工夫が必要となりそうです。ただ、基本座って・前を向いて使用するアプリ、であれば、今回実装程度のものでも充分かと思います。</p> </div> tuti107 KUDAN SDKを使って、Gear VRでどこでもドア(1) hatenablog://entry/10328749687228862160 2017-03-22T19:58:32+09:00 2017-03-26T10:06:15+09:00 前回は、Unity Native VRでどこでもドア(ポータル)を実装しました。前回の予想どおり、前回開発したものをそのままGear VR向けにビルドしたところ、問題なく動作しました。 ただし、Gear VRにはポジショントラッキングがないため、どこでもドアに近づくことができず、どこでもドアの楽しさが半減してしまいます。 そこで今回は、前にVuforiaで実装したポジショントラッキングを応用し、Gear VRでもどこでもドアに近づく体験ができるようにしてみます。 tuti107.hatenablog.com KUDAN 今回は、Vuforiaではなく、日本が誇るモバイルARエンジンである「KU… <p><a href="http://tuti107.hatenablog.com/entry/2017/03/19/212656">&#x524D;&#x56DE;</a>は、Unity Native VRでどこでもドア(ポータル)を実装しました。前回の予想どおり、前回開発したものをそのままGear VR向けにビルドしたところ、問題なく動作しました。<br /> ただし、Gear VRにはポジショントラッキングがないため、どこでもドアに近づくことができず、どこでもドアの楽しさが半減してしまいます。<br /> そこで今回は、前にVuforiaで実装したポジショントラッキングを応用し、Gear VRでもどこでもドアに近づく体験ができるようにしてみます。<br /> <iframe src="http://tuti107.hatenablog.com/embed/2016/11/27/221350" title="Vuforia for UnityのExtended Tracking機能を利用してGoogle Cardboardでポジショントラッキング - Tutti Lab" class="embed-card embed-blogcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="http://tuti107.hatenablog.com/entry/2016/11/27/221350">tuti107.hatenablog.com</a></cite><br /> </p> <div class="section"> <h3>KUDAN</h3> <p>今回は、Vuforiaではなく、日本が誇るモバイルARエンジンである「KUDAN」を利用します。KUDANは、</p> <ul> <li>マーカレスARが可能!つまり、マーカをプリントして貼り付ける等の面倒な手続きなしで、ARが可能!</li> <li>収入が100万ポンド(=1.4億円)以下の個人開発者は、なんとアプリ配布用のライセンスが無料!!(Vuforiaは有料)</li> </ul><p>ということで、今の私の中ではモバイルARは、KUDAN一択です。</p> <div class="section"> <h4>KUDAN SDK Unity Pluginのダウンロード</h4> <p><a href="https://www.kudan.eu/download-kudan-ar-sdk/">&#x30C0;&#x30A6;&#x30F3;&#x30ED;&#x30FC;&#x30C9;&#x30DA;&#x30FC;&#x30B8;</a>より、Free-Downloadをクリックして、Unity Pllugin "KudanARUnity.unitypackage"をダウンロードします。<br /> 購入画面が表示されますが、0ポンドなのでご安心を。メールアドレス等の情報を入力し、画面一番下のDownloadボタンを押下すれば、ダウンロードがはじまります。</p> </div> <div class="section"> <h4>KUDAN SDK Unity Pluginのインストール</h4> <p>ダウンロードしたKudanARUnity.unitypackageを、Assets->Import Package->Custom Packageよりインポートします。</p> </div> <div class="section"> <h4>サンプルを実行してみる</h4> <p>まずは、SDKに同梱のサンプルを実行してみます。</p> <ul> <li>Assets/KudanAR/Samples/AngelScene をダブルクリックして、サンプルシーンをロードします。</li> </ul><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20170320/20170320130414.png" alt="f:id:tuti107:20170320130414p:plain" title="f:id:tuti107:20170320130414p:plain" class="hatena-fotolife" itemprop="image"></span></p> <ul> <li>Android向けにビルドをするための設定を行います。File->Build Settings..より、Build Settingsを表示し、PlatformをAndroidへスイッチします。次に、Player Settingsを押下し、インスペクタのOther Settingsー>Identifierより、以下の通りに設定します。 <ul> <li>Bundle Identifier = "eu.kudan.ar"</li> <li>Minimum API Level = 15</li> </ul></li> </ul><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20170320/20170320131705.png" alt="f:id:tuti107:20170320131705p:plain" title="f:id:tuti107:20170320131705p:plain" class="hatena-fotolife" itemprop="image"></span></p> <ul> <li>次に、シーン内のAngel Bundle/Kudan Cameraを選択し、インスペクタのKudan Trackerコンポーネントの一番下にある「Get Editor API Key」を押下します。すると以下のページが表示されるので、"click here"を書かれている箇所をクリックします。</li> </ul><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20170320/20170320131922.png" alt="f:id:tuti107:20170320131922p:plain" title="f:id:tuti107:20170320131922p:plain" class="hatena-fotolife" itemprop="image"></span><br /> ここで、開発用のバンドルID"eu.kudan.ar"に対応するLicense Keyが取得できます。このLicense Keyをコピーし、インスペクタのKudan Trackerコンポーネント内のAPI Keyにペーストします。なお、Editor API Keyは、上記MY ACCOUNTのページより、ログインすることで取得可能です。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20170320/20170320132242.png" alt="f:id:tuti107:20170320132242p:plain" title="f:id:tuti107:20170320132242p:plain" class="hatena-fotolife" itemprop="image"></span><br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20170320/20170320132346.png" alt="f:id:tuti107:20170320132346p:plain" title="f:id:tuti107:20170320132346p:plain" class="hatena-fotolife" itemprop="image"></span></p> <ul> <li>次に実行してみます。適当なAndroid端末をPCと接続し、File->Build & Runを選択します。画面右下の"Place Markerless Object"ボタンを押下すると、3Dオブジェクトが設置されます。このオブジェクトは、その設置位置に貼り付けたように、スマホを傾けたり・近づけたりすると、その通りに表示がされます。</li> </ul><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20170320/20170320133542.png" alt="f:id:tuti107:20170320133542p:plain" title="f:id:tuti107:20170320133542p:plain" class="hatena-fotolife" itemprop="image"></span><br /> </p> </div> <div class="section"> <h4>次回は、KUDANでポジショントラッキングを実装</h4> <p>今回は、Gear VRで「どこでもドアに近づく」体験を実現すべく、まずはマーカレスARが可能なKUDANをインストールしました。<br /> 次回は、KUDANを利用したポジショントラッキングの実装に挑戦します。</p> </div> </div> tuti107 どこでもドア(ポータル)をUnity NativeのVRで実現してみる hatenablog://entry/10328749687228644630 2017-03-19T21:26:56+09:00 2017-03-19T21:28:47+09:00 VRでどこでもドア体験、ぜひやってみたい!と思うのですが、関東在住ではないので、気軽に行けそうにありません。 www.doraeiga-vr.comそこで、Oculus Rift向けに作ってみようと思ったのですが、RenderTextureをごにょごにょして・・と色々考えたものの、どうやったらよいのかわからず、ネットで検索したところ以下の記事を発見しました。ただし、Vive用のコードで、あちこちVive専用のメソッド等が使われています。そこで、これをUnityのNative VR向けに移植してみました。 qiita.com RenderTextureの生成 上記のサイトでは _leftEyeR… <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20170319/20170319212451.png" alt="f:id:tuti107:20170319212451p:plain" title="f:id:tuti107:20170319212451p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>VRでどこでもドア体験、ぜひやってみたい!と思うのですが、関東在住ではないので、気軽に行けそうにありません。<br /> <iframe src="//hatenablog-parts.com/embed?url=https%3A%2F%2Fwww.doraeiga-vr.com%2Findex.html" title="ドラえもんVR「どこでもドア」 | 「映画ドラえもん のび太の南極カチコチ大冒険」公開記念特別企画" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://www.doraeiga-vr.com/index.html">www.doraeiga-vr.com</a></cite></p><p>そこで、Oculus Rift向けに作ってみようと思ったのですが、RenderTextureをごにょごにょして・・と色々考えたものの、どうやったらよいのかわからず、ネットで検索したところ以下の記事を発見しました。ただし、Vive用のコードで、あちこちVive専用のメソッド等が使われています。そこで、これをUnityのNative VR向けに移植してみました。<br /> <iframe src="//hatenablog-parts.com/embed?url=http%3A%2F%2Fqiita.com%2Fedo_m18%2Fitems%2F25fb082b30e744fe6106" title="[Unity] HTC ViveでPortal的なものを作る - Qiita" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="http://qiita.com/edo_m18/items/25fb082b30e744fe6106">qiita.com</a></cite><br /> </p> <div class="section"> <h5>RenderTextureの生成</h5> <p>上記のサイトでは</p> <pre class="code lang-cs" data-lang="cs" data-unlink>_leftEyeRenderTexture = <span class="synStatement">new</span> RenderTexture((<span class="synType">int</span>)SteamVR.instance.sceneWidth, (<span class="synType">int</span>)SteamVR.instance.sceneHeight, <span class="synConstant">24</span>); </pre><p>として、RenderTextureを作成していますが、</p> <pre class="code lang-cs" data-lang="cs" data-unlink>_leftEyeRenderTexture = <span class="synStatement">new</span> RenderTexture((<span class="synType">int</span>)VRSettings.eyeTextureWidth, (<span class="synType">int</span>)VRSettings.eyeTextureHeight, <span class="synConstant">24</span>); </pre><p>としました。</p> </div> <div class="section"> <h5>ポータル用マテリアルへRenderTextureを設定する処理</h5> <p>まず、左右カメラ視差をつくるためのオフセット設定は、</p> <pre class="code lang-cs" data-lang="cs" data-unlink>var v = _cameraForPortal.transform.localPosition; _cameraForPortal.transform.localPosition = v + <span class="synStatement">new</span> Vector3(-VrEye.stereoSeparation/<span class="synConstant">2f</span>, <span class="synConstant">0f</span>, <span class="synConstant">0f</span>); </pre><p>としました(左目の場合)。<br /> また、</p> <pre class="code lang-cs" data-lang="cs" data-unlink>Valve.VR.HmdMatrix44_t leftMatrix = SteamVR.instance.hmd.GetProjectionMatrix(Valve.VR.EVREye.Eye_Left, VrEye.nearClipPlane, VrEye.farClipPlane, Valve.VR.EGraphicsAPIConvention.API_DirectX); _cameraForPortal.projectionMatrix = HMDMatrix4x4ToMatrix4x4(leftMatrix); </pre><p>として、プロジェクションマトリクスの設定を行っている箇所は、</p> <pre class="code lang-cs" data-lang="cs" data-unlink>_cameraForPortal.projectionMatrix = VrEye.GetStereoProjectionMatrix(Camera.StereoscopicEye.Left); </pre><p>としました(左目の場合)。</p> </div> <div class="section"> <h5>ポータル用マテリアルのシェーダー処理</h5> <p>左右どちらのレンダリングか、により使用するテクスチャを切り替えている以下の処理</p> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synStatement">if</span> (unity_CameraProjection[<span class="synConstant">0</span>][<span class="synConstant">2</span>] &lt; <span class="synConstant">0</span>) { col = tex2D(_LeftEyeTexture, sUV); } <span class="synStatement">else</span> { col = tex2D(_RightEyeTexture, sUV); } </pre><p>は、</p> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synStatement">if</span> (unity_StereoEyeIndex == <span class="synConstant">0</span>) </pre><p>としました。<br /> その他は、上記サイトにて記載されているソース等の通りです。実行すると、以下のような感じに動きます。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20170319/20170319212415.gif" alt="f:id:tuti107:20170319212415g:plain" title="f:id:tuti107:20170319212415g:plain" class="hatena-fotolife" itemprop="image"></span></p><p>UnityのNativeのVR機能のみでの実装のため、Oculus Riftだけでなく、Gear VR、Google Cardboard、Daydreamでの動くと思われます。試していませんが。。</p> </div> tuti107 シーン内GameObjectの初期化の順序などについて hatenablog://entry/10328749687228145479 2017-03-18T10:49:22+09:00 2017-03-18T10:49:22+09:00 シーン内GameObjectの初期化の順序について、私はごく最近まで「シーン内のGameObjectは入れ子構造の親から順に初期化、子はシーンの上にあるものから順に初期化」される、と勘違いしていました。 +GameObject1 | | | +---GameObject2 | +---GameObject3なら、GameObject1, GameObject2, GameObject3の順で・・ ただ実際は「初期化順は不定」なのですね。今開発中のアプリで、起動時は期待した順にAwake()が呼び出されるのに、SceneManager.LoadScene()するとAwake()の呼び出し順序が入… <p>シーン内GameObjectの初期化の順序について、私はごく最近まで「シーン内のGameObjectは入れ子構造の親から順に初期化、子はシーンの上にあるものから順に初期化」される、と勘違いしていました。</p> <pre class="code" data-lang="" data-unlink>+GameObject1 | | | +---GameObject2 | +---GameObject3</pre><p>なら、GameObject1, GameObject2, GameObject3の順で・・<br /> ただ実際は「初期化順は不定」なのですね。今開発中のアプリで、起動時は期待した順にAwake()が呼び出されるのに、SceneManager.LoadScene()するとAwake()の呼び出し順序が入れ替わってしまう、という問題が発生し、この事実を知りました。</p><p>そうすると、GameObjectの初期化時に他のGameObjectを参照する必要がある場合、どうするのだとうと。例えばGameObject2の初期化時に、GameObject1のなんらかを参照する場合、GameObject1が初期化されていないと期待の動作となりません。<br /> これについては、</p> <ul> <li>自身の初期化はAwake()で</li> <li>他のGameObjectの参照を伴う初期化処理はStart()で</li> </ul><p>行う、と徹底すればよいようですね。</p><p>初歩的なことなのですが、いままでずっと勘違いをしていました。これまで開発したアプリは「たまたま動いていた」ようで・・</p> tuti107 VR広告SDK「Immersv」を組み込んでみました hatenablog://entry/10328749687220131518 2017-03-07T20:46:25+09:00 2017-03-07T20:47:09+09:00 モバイルVRアプリ開発において「マネタイズ」は大きな課題です。一般的にモバイル向けアプリ(特に個人開発者)のマネタイズ手段は広告であり、各種提供されている広告SDKをアプリに組み込むことで、アプリ内で広告を表示し、実績に応じて広告収入を得る、というものです。 しかし、これらモバイル向けアプリの広告SDKは、VR空間上に貼り付けて使用するような使用形態が想定されていないため、モバイルVRアプリにそのまま適用することはできません。 今回は、モバイルVRに特化された広告SDKである「Immersv」を、開発中のアプリに組み込み、動作確認をしてみました。 SDKの請求 まずはこちら(注:2017年3月… <p>モバイルVRアプリ開発において「マネタイズ」は大きな課題です。一般的にモバイル向けアプリ(特に個人開発者)のマネタイズ手段は広告であり、各種提供されている広告SDKをアプリに組み込むことで、アプリ内で広告を表示し、実績に応じて広告収入を得る、というものです。<br /> しかし、これらモバイル向けアプリの広告SDKは、VR空間上に貼り付けて使用するような使用形態が想定されていないため、モバイルVRアプリにそのまま適用することはできません。<br /> 今回は、モバイルVRに特化された広告SDKである「Immersv」を、開発中のアプリに組み込み、動作確認をしてみました。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20170307/20170307200728.png" alt="f:id:tuti107:20170307200728p:plain" title="f:id:tuti107:20170307200728p:plain" class="hatena-fotolife" itemprop="image"></span><br /> </p> <div class="section"> <h3>SDKの請求</h3> <p>まずは<a href="https://immersv.co/">&#x3053;&#x3061;&#x3089;</a><u><i>(注:2017年3月7日現在、本サイトに接続できなくなっております。一時的なものかと思いますが。。)</i></u>にアクセスし、SDKを請求します。フォームに必要情報を入力・送信すると、数日で先方から応答のメールがあります。<br /> その後、私のプロジェクト(今、Gear VR、Daydream向けにモバイルVRアプリを開発しています)の説明をし、何度かのインタラクションを経て、SDKを入手することができました。なお、Immersvは米国の会社であり、全編英語を覚悟したのですが、対応いただいた日本法人の担当の方は日本語が堪能で、問題なくコミュニケーションをとることができました。</p> </div> <div class="section"> <h3>SDKのダウンロード、組み込み</h3> <p>SDKは、Gear VR、Daydream/Cardbord用に、これらOculus SDK/Google VR SDKやUnityのバージョン毎に用意されており、自分の開発環境に合わせて取得・インポートします。<br /> 私は、Daydream向けビルドを考慮し、Unity Daydream Preview 5.4.2f2-GVR12で、かつ(まずは)Gear VR向けに開発をしているので、本条件に相当する「ImmersvSDK-1.31-GearVR-1.0.3.unitypackage」を使用しました。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20170223/20170223073524.png" alt="f:id:tuti107:20170223073524p:plain" title="f:id:tuti107:20170223073524p:plain" class="hatena-fotolife" itemprop="image"></span><br /> </p> </div> <div class="section"> <h3>実装</h3> <p>Immersv SDKは非常にシンプルです。基本、SDKに同梱のPDFドキュメントに記載されているサンプルコードをそのまま使えば、ほぼ問題ありません。一応、処理の流れを記しておくと、</p> <ul> <li>ImmersvSDK.Init(APPLICATION_ID)を呼び出す。APPLICATION_IDはSDK利用許可時に通知される。</li> <li>ImmersvSDK.OnInitSucces()コールバック(初期化成功)にて、ImmersvSDK.Ads.LoadAd(PLACEMENT_ID)を呼び出し、広告を読み込む。なお、PLACEMENT_IDはSDK利用許可時に通知される。</li> <li>ImmersvSDK.Ads.OnAdReady()コールバック(広告準備完了)を受け取った後、なんらかのユーザ操作(「広告を見る」ボタン押下など)を受け付けた後、ImmersvSDK.Ads.StartAd()を呼び出して、広告を再生する</li> <li>ImmersvSDK.Ads.OnAdComplete(result)コールバック(広告再生完了)にて、ユーザが広告を最後まで見る等の条件を満たした場合(result.BillableCriteriaMetがtrue)、アプリ内仮想通貨を与える等の、ユーザに対する報酬処理を行う</li> </ul><p>という形になります。</p><p>なお、以下の公式ビデオ(英語)にて、SDK請求から実装までの詳細が公開されています。<br /> <iframe width="480" height="270" src="https://www.youtube.com/embed/dxSuUXKgoeQ?feature=oembed" frameborder="0" allowfullscreen></iframe><cite class="hatena-citation"><a href="https://www.youtube.com/watch?v=dxSuUXKgoeQ">www.youtube.com</a></cite></p><p>ちなみに私の開発中アプリにSDKを組み込んだものは、このような感じです。ビデオ画質が悪いため細かな部分、特に広告閲覧後の報酬の様子がわかりづらいですが、一通り動作しています。<br /> <iframe width="480" height="270" src="https://www.youtube.com/embed/KJzEhJWqyVQ?feature=oembed" frameborder="0" allowfullscreen></iframe><cite class="hatena-citation"><a href="https://www.youtube.com/watch?v=KJzEhJWqyVQ">www.youtube.com</a></cite><br /> チケット券売機の右中央辺り(ビデオでは解像度が低くてよく見えませんが、いわゆる「広告を見てコインをゲット」を表示しています)をGAZEすることで、Immersvの広告が始まります。<br /> 広告は、2D広告を映画館風に再生するものや、360度動画等様々であり、充分楽しめる(?)内容です。<br /> 広告が終わると、コインが手に入ります(こちらも見えづらい・・)</p><p>GearVRでは、私がいつも利用しているキャプチャーアプリ<a href="https://play.google.com/store/apps/details?id=com.rivulus.screenrecording&hl=en">Lollipop screen recorder</a>が動作せず、<a href="https://play.google.com/store/apps/details?id=com.awindinc.galaxysender&hl=en">MirrorOP for Galaxy</a>+OBSを使用しました。ただこれだと録音ができず、解像度が低く、微妙な感じです。。</p> </div> tuti107 getterのケアレスミスでUnityクラッシュ hatenablog://entry/10328749687224095189 2017-03-06T19:35:57+09:00 2017-03-06T19:35:57+09:00 以下のコードですが、 private int _curNum; public int curNum { get { return curNum; } set { _curNum = value; } } 正しくは、「return _curNum;」としなければなりません。 なお、上記の間違いコードを実行すると、Unityがクラッシュします。 スタックがえらいことになっています。循環参照をしまくったためでしょうね。 この不具合の検証をさらに困難にさせるのが、このgetterにアクセスしなくても、例えば本ゲッターを持つクラスの別の変数にアクセスしても落ちます(ただ、落ちない変数もある・・謎・・)。… <p>以下のコードですが、</p> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synType">private</span> <span class="synType">int</span> _curNum; <span class="synType">public</span> <span class="synType">int</span> curNum { <span class="synStatement">get</span> { <span class="synStatement">return</span> curNum; } <span class="synStatement">set</span> { _curNum = <span class="synStatement">value</span>; } } </pre><p>正しくは、「return _curNum;」としなければなりません。<br /> なお、上記の間違いコードを実行すると、Unityがクラッシュします。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20170306/20170306192256.png" alt="f:id:tuti107:20170306192256p:plain" title="f:id:tuti107:20170306192256p:plain" class="hatena-fotolife" itemprop="image"></span><br /> スタックがえらいことになっています。循環参照をしまくったためでしょうね。<br /> この不具合の検証をさらに困難にさせるのが、このgetterにアクセスしなくても、例えば本ゲッターを持つクラスの別の変数にアクセスしても落ちます(ただ、落ちない変数もある・・謎・・)。まさかgetterのミスとは気づかず、小一時間解析に要してしまいました。</p> tuti107 Quadで円形プログレス hatenablog://entry/10328749687213406615 2017-02-04T19:48:12+09:00 2017-02-04T19:48:12+09:00 uGUIのImageは、fillAmountを使って簡単に円形のプログレスができるのですが、SpriteやQuadにはfillAmountに相当する機能がないようです。 こちらやこちらに、Quadを使った円形プログレスの実現方法があるのですが、予め0→360度でα値を1→0とした画像を用意する必要があるようで、面倒だし汎用性がないな、と思いました。 そこで、角度(0~360度)を指定して円形プログレスをするための簡単なシェーダーコードを書いてみました。 Shader "Tuti/Reticle" { Properties { [NoScaleOffset] _MainTex("Texture"… <p>uGUIのImageは、<a href="https://docs.unity3d.com/ScriptReference/UI.Image-fillAmount.html">fillAmount</a>を使って簡単に円形のプログレスができるのですが、SpriteやQuadにはfillAmountに相当する機能がないようです。<br /> <a href="http://wiki.unity3d.com/index.php/ProgressBar">&#x3053;&#x3061;&#x3089;</a>や<a href="http://www.theappguruz.com/blog/circular-progress-bar">&#x3053;&#x3061;&#x3089;</a>に、Quadを使った円形プログレスの実現方法があるのですが、予め0→360度でα値を1→0とした画像を用意する必要があるようで、面倒だし汎用性がないな、と思いました。<br /> そこで、角度(0~360度)を指定して円形プログレスをするための簡単なシェーダーコードを書いてみました。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20170204/20170204193817.png" alt="f:id:tuti107:20170204193817p:plain" title="f:id:tuti107:20170204193817p:plain" class="hatena-fotolife" itemprop="image"></span><br /> </p> <pre class="code" data-lang="" data-unlink>Shader &#34;Tuti/Reticle&#34; { Properties { [NoScaleOffset] _MainTex(&#34;Texture&#34;, 2D) = &#34;white&#34; {} _Degree(&#34;Degree&#34;, Float) = 360.0 } SubShader { Tags{ &#34;Queue&#34; = &#34;Transparent&#34; &#34;IgnoreProjector&#34; = &#34;True&#34; &#34;RenderType&#34; = &#34;Transparent&#34; } ZWrite On Blend SrcAlpha OneMinusSrcAlpha Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include &#34;UnityCG.cginc&#34; #define PI 3.14159 struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; }; sampler2D _MainTex; float _Degree; v2f vert (appdata v) { v2f o; o.vertex = mul(UNITY_MATRIX_MVP, v.vertex); o.uv = v.uv; return o; } half4 frag(v2f i) : SV_Target { half4 col = tex2D(_MainTex, i.uv); float xx = i.uv.x - 0.5; float yy = i.uv.y - 0.5; float deg = (atan2(yy, xx)) * 180.0 / PI; if (yy &gt;= 0) { if (deg &gt; _Degree) { col.a = 0.0; } } else { if (deg + 360.0 &gt; _Degree) { col.a = 0.0; } } return col; } ENDCG } } }</pre><p>時計の12時の位置から時計回りにする場合は、z軸90度回転+Y軸で反転してください。<br /> ちゃんとテストはやっていませんが、とりあえず動いているようです。</p> tuti107 Oculus Touchをつかってみる hatenablog://entry/10328749687202420072 2016-12-31T11:04:40+09:00 2016-12-31T11:04:40+09:00 Oculus Touchが出荷され、約一月が経ちました。Oculus StoreにはOculus Touch対応コンテンツが多数並び、Oculus Touchの凄さを体験できるわけですが、一方で開発向きとなるとまだ情報が足りない状況のようです。 今回は、Oculus Touchの練習用に作成したコンテンツを取り上げ、開発のポイントを挙げていきたいと思います。 youtu.be コンテンツは、今年大流行したアレを、Oculus Touchで体験する、というものです。 情報収取 Oculus Touchの技術情報については、以下が必読です。 Unity+Oculus Touchメモ→こちらでSDK… <p>Oculus Touchが出荷され、約一月が経ちました。Oculus StoreにはOculus Touch対応コンテンツが多数並び、Oculus Touchの凄さを体験できるわけですが、一方で開発向きとなるとまだ情報が足りない状況のようです。<br /> 今回は、Oculus Touchの練習用に作成したコンテンツを取り上げ、開発のポイントを挙げていきたいと思います。<br /> <iframe width="459" height="344" src="https://www.youtube.com/embed/kQNdjHRHaDc?feature=oembed" frameborder="0" allowfullscreen></iframe><cite class="hatena-citation"><a href="https://youtu.be/kQNdjHRHaDc">youtu.be</a></cite><br /> コンテンツは、今年大流行したアレを、Oculus Touchで体験する、というものです。</p> <div class="section"> <h3>情報収取</h3> <p>Oculus Touchの技術情報については、以下が必読です。</p> <ul> <li><a href="https://framesynthesis.jp/tech/unity/touch/">Unity+Oculus Touch&#x30E1;&#x30E2;</a>→こちらでSDKのインストール~Oculus Touchでものをつかむためのサンプルまで一通りわかります</li> <li><a href="http://tips.hecomi.com/entry/2016/12/25/142057">Oculus Avatar SDK &#x3092;&#x4F7F;&#x3063;&#x3066;&#x81EA;&#x5206;&#x306E;&#x30A2;&#x30D0;&#x30BF;&#x30FC;&#x3092; Unity &#x3067;&#x5229;&#x7528;&#x3059;&#x308B;&#x65B9;&#x6CD5;&#x3092;&#x8ABF;&#x3079;&#x3066;&#x307F;&#x305F;</a>→アバター表示やグリップポーズのカスタマイズ方法がわかります</li> </ul> </div> <div class="section"> <h3>準備</h3> <p>Oculus Touchにて「手やアバターを表示し、なにかをつかむコンテンツを作る」ためには、以下のインストールが必要です(詳細は上記情報収集をご参照)</p> <ul> <li>Oculus Utilities for Unity 5(<a href="https://developer3.oculus.com/downloads/">&#x3053;&#x3061;&#x3089;</a>からダウンロード可能)</li> <li>Oculus Avatar SDK(同じく<a href="https://developer3.oculus.com/downloads/">&#x3053;&#x3061;&#x3089;</a>からダウンロード可能)</li> <li>Avater Grab Sample(<a href="https://forums.oculus.com/developer/discussion/47204/avatar-grab-sample-available-unity">&#x3053;&#x3061;&#x3089;</a>からダウンロード可能)、なおUnity 5.5でのみ動作(Unity 5.4ではクラッシュ)とのこと</li> <li>その後適当なプロジェクトを作成し、上記ダウンロードしたパッケージ(OculusUtilities.unitypackage, OvrAvatar.unityPackage, AvatarGrabSample.unitypackage)をそれぞれインポートします</li> </ul> </div> <div class="section"> <h3>サンプル実行</h3> <p>Assets/Samples/Content/AvaterWithGrab をダブルクリックして、シーンをロードします。あとは、PlayerSettings→Other SettingsのVirtual Reality Supportedをチェックし、実行してみます。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161231/20161231102951.png" alt="f:id:tuti107:20161231102951p:plain" title="f:id:tuti107:20161231102951p:plain" class="hatena-fotolife" itemprop="image"></span><br /> つかむ・離す・投げる、一通りのことができます。</p> </div> <div class="section"> <h3>つかめるものを追加</h3> <p>まずは、サンプルで「つかめるもの」がどのようになっているかを見てみます。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161231/20161231103341.png" alt="f:id:tuti107:20161231103341p:plain" title="f:id:tuti107:20161231103341p:plain" class="hatena-fotolife" itemprop="image"></span><br /> ポイントは、Grabbableというスクリプトです。これはAvater Grab Sampleに含まれるスクリプトで、これを追加したオブジェクト(かつ、Collider, Rigidbodyも追加する必要あり)がつかめるようになります。<br /> つかんだ時、離した時の振る舞いは、Grabbable#GrabBegin()、Grabbable#GrabEnd()メソッドにそれぞれ記述します。これらメソッドはvirtualメソッドですので、Grabbableクラスの子クラスを作ってこれらメソッドをオーバーライドすれば、任意のつかむ・離す際の処理を作成することができます。</p> </div> <div class="section"> <h3>Oculus Touchのバイブレーション</h3> <p>OVRHapticsClipを使用します。AudioClipを引数としてOVRHapticsClipを作成することで、そのAudioClipの音量に基づいた振動を作成できます。<br /> 作成したOVRHapticsClipを引数としてOVRHaptics.RightChannel.Mix()(=右側コントローラ、左側の場合はOVRHaptics.LeftChannel.Mix())を呼び出すことで、Oculus Touchが振動します。なお、振動させるためのメソッドにはMix()以外に幾つかあり、それぞれ挙動が異なります(詳細は情報収集で挙げたサイトを御覧ください)。<br /> 以下は、スタティックメソッドSE.PlaySE()を呼び出すと、効果音鳴動+振動するサンプルです。</p> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synStatement">using</span> UnityEngine; <span class="synType">public</span> <span class="synType">class</span> SE : MonoBehaviour { <span class="synType">public</span> AudioSource audioSource; <span class="synType">public</span> OVRHapticsClip hapticClip; <span class="synType">private</span> <span class="synType">static</span> SE thisObj; <span class="synType">void</span> Start () { thisObj = <span class="synStatement">this</span>; hapticClip = <span class="synStatement">new</span> OVRHapticsClip(audioSource.clip); } <span class="synType">public</span> <span class="synType">static</span> <span class="synType">void</span> PlaySE() { <span class="synStatement">if</span> (thisObj != <span class="synConstant">null</span>) { thisObj.audioSource.Play(); OVRHaptics.RightChannel.Mix(thisObj.hapticClip); OVRHaptics.LeftChannel.Mix(thisObj.hapticClip); } } } </pre> </div> <div class="section"> <h3>No Oculus Rift App ID has been provided</h3> <p>エディタで実行すると、問題なく実行できますが、上記のエラーがでます。Oculusサイトにて、アプリを登録してIDを取得することで、本エラーを回避できるようです(私がつくったコンテンツをエディタ上で実行する上では特に問題がなかったため、試していません)。詳細は、上記「情報収集」で挙げたサイトを御覧ください。</p> </div> <div class="section"> <h3>PP◯Pっぽい素材</h3> <ul> <li>くだものの3Dモデルは<a href="https://www.assetstore.unity3d.com/jp/#!/content/65906">HD Fruits &#xFF06; Vegetables</a>を利用しました</li> <li>オフィス用品の3Dモデルは<a href="https://www.assetstore.unity3d.com/jp/#!/content/60538">PBR Stationery Objects</a>を利用しました</li> </ul> </div> <div class="section"> <h3>終わりに</h3> <p>Oculus Touchは本当にすごいです。CG空間に触る、というユーザ体験は先行していたHTC Viveをも超えるものでは、と個人的には思います。</p> </div> tuti107 VR Trip around the world hatenablog://entry/10328749687200881055 2016-12-24T14:42:14+09:00 2016-12-24T14:42:14+09:00 私が開発したVRゲーム「VR Trip around the world」が、幸運にもVRゲームコンテストMSI VR JAMのファイナリストに残りました。プレーヤーは、指定された場所で写真を撮影すべく広大なVRの世界を飛び回ります。VR世界の移動、となると最も懸念の課題が「VR酔い」です。本作では、遠くへ移動する際「巨人」になって世界を見渡し、次に行く場所を探します。「巨人」になって世界を見渡し、怪しそうな場所を探して、「元の姿」に戻り、細かな場所を探索する、という新たなVRにおける移動手段についても提案しています。 www.youtube.com本アプリはOculus CV1専用であり、こ… <p>私が開発したVRゲーム「VR Trip around the world」が、幸運にもVRゲームコンテストMSI VR JAMのファイナリストに残りました。</p><p>プレーヤーは、指定された場所で写真を撮影すべく広大なVRの世界を飛び回ります。VR世界の移動、となると最も懸念の課題が「VR酔い」です。本作では、遠くへ移動する際「巨人」になって世界を見渡し、次に行く場所を探します。「巨人」になって世界を見渡し、怪しそうな場所を探して、「元の姿」に戻り、細かな場所を探索する、という新たなVRにおける移動手段についても提案しています。<br /> <iframe width="459" height="344" src="https://www.youtube.com/embed/gJKzq0MjSDs?feature=oembed" frameborder="0" allowfullscreen></iframe><cite class="hatena-citation"><a href="https://www.youtube.com/watch?v=gJKzq0MjSDs">www.youtube.com</a></cite></p><p>本アプリはOculus CV1専用であり、<a href="https://www.wearvr.com/apps/vr-trip-around-the-world">&#x3053;&#x3061;&#x3089;</a>から無料でダウンロードできます。<br /> ぜひプレイいただき、気に入って頂けた方は、<a href="http://woobox.com/h9t35r/idzbk9">&#x3053;&#x3061;&#x3089;</a>より投票を頂けると幸いです(2016年12月28日迄)。</p> tuti107 Vuforia for Unityを使って(擬似)ハンドトラッキング hatenablog://entry/10328749687200359247 2016-12-21T23:50:39+09:00 2016-12-24T14:51:11+09:00 前回、Viforia for Unityの機能を活用し、Google Cardboardでポジショントラッキングを実現する方法について記載しました。同様に、Vuforia for Unityを使うことで(擬似)ハンドトラッキングも可能です。 Vuforiaでは直接「手」を認識することはできません。しかし、手にマーカーを貼り付ける、マーカーを貼り付けたふだを持つなど、マーカ認識を通じて手の位置を認識する、ということは比較的容易に実現できます。 先日とあるVRイベント向けに開発した「Santa Claus Job Simulator」というサンタのお仕事を体験するアプリにて、この擬似ハンドトラッキ… <p><a href="http://tuti107.hatenablog.com/entry/2016/11/27/221350">&#x524D;&#x56DE;</a>、Viforia for Unityの機能を活用し、Google Cardboardでポジショントラッキングを実現する方法について記載しました。同様に、Vuforia for Unityを使うことで(擬似)ハンドトラッキングも可能です。<br /> Vuforiaでは直接「手」を認識することはできません。しかし、手にマーカーを貼り付ける、マーカーを貼り付けたふだを持つなど、マーカ認識を通じて手の位置を認識する、ということは比較的容易に実現できます。<br /> 先日とあるVRイベント向けに開発した「Santa Claus Job Simulator」というサンタのお仕事を体験するアプリにて、この擬似ハンドトラッキングを実装してみましたので、本実装技術についてご紹介したいと思います。<br /> <iframe width="480" height="270" src="https://www.youtube.com/embed/EUSh8KJol-M?feature=oembed" frameborder="0" allowfullscreen></iframe><cite class="hatena-citation"><a href="https://www.youtube.com/watch?v=EUSh8KJol-M">www.youtube.com</a></cite><br /> </p> <div class="section"> <h3>ARCameraの設定</h3> <p>Google CardboardにてVuforia for Unityを利用した(マーカ認識による)ハンドトラッキングをする際のポイントは「VRのヘッドトラッキングを考慮した上で、Vuforiaで認識した「手」を「手の位置」に表示すること」です。<br /> Vuforia for Unityはデフォルトでは「はじめに発見した認識対象の座標を固定し、認識対象との位置関係に応じてCameraの位置を変える」という設定になっています。VuforiaがImage Targetに指定された認識対象を発見した際、Image Targetの座標・回転が変化するのではなく、ARCamera(Main Cameraを子として含む)の座標・回転を変化させることで、相対的にImage Targetが動いているように見せます。<br /> VRでなければ、特段問題は生じませんが、VR(Player SettingsにてVirtual Reality SupportedがON)の場合、ヘッドトラッキングによりデバイスの回転によりMain Cameraを回転させます。このため、上記のデフォルト設定では、ヘッドトラッキングの回転設定とVuforiaの回転設定が競合してしまい、ちょっと都合が悪いです。</p><p>そこで、</p> <ul> <li>ARCameraのVuforia BehaviorコンポーネントのWorld Center Modeをデフォルトの「FIRST TARGET」から「CAMERA」に変更します。これで、認識対象物が発見された際の固定される座標はMain Cameraとなり、代わりにImage Targetの座標が変わります。</li> <li>また、もう一点、Image TargetはMain Cameraの子とします。これは上記の通り、認識対象の位置はMain Cameraが固定であることを前提としていますから、ヘッドトラッキングによるMain Cameraの回転変化に関わらず、Image TargetからはMain Cemeraが固定に見えるようにするためです。</li> </ul><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161221/20161221234833.png" alt="f:id:tuti107:20161221234833p:plain" title="f:id:tuti107:20161221234833p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>また、Image Targetと「手」の位置関係にも工夫が必要です。Santa Claus Job Simulatorでは、マーカ付きの看板を手に持ってもらい、そのマーカを認識することで手を表示するようにしています。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161221/20161221233051.png" alt="f:id:tuti107:20161221233051p:plain" title="f:id:tuti107:20161221233051p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>このためImage Target認識時に表示される手の位置・回転は、看板に貼り付けられたマーカからの相対位置・回転を設定する必要があります。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161221/20161221233148.png" alt="f:id:tuti107:20161221233148p:plain" title="f:id:tuti107:20161221233148p:plain" class="hatena-fotolife" itemprop="image"></span><br /> </p> </div> <div class="section"> <h3>高速化が不可欠</h3> <p>Vuforiaをハンドトラッキングに活用する上でもう一つ重要な点は、可能な限り処理の高速化を図ることです。<br /> VR表示しながら、同時にマーカ認識するという処理は、モバイル端末にとって非常にヘビーなものとなります。<br /> このため、Edit->Project Settings->Qualityにて処理をFastestとして描画・表示負荷を下げる等、できるだけ処理を軽くし、Vuforiaの認識処理にリソースを回してあげる必要があります。<br /> 描画負荷が高い場合等、Vuforiaの認識処理のための余裕がない場合、認識の品質は明らかに低下し、すぐに認識対象を見失ってしまいます。こうなると心地よりハンドトラッキングとは程遠くなってしまいます。</p> </div> <div class="section"> <h3>最後に</h3> <p>今回は、Google Cardboard向けに(擬似)ハンドトラッキング処理を実装した当方が開発したゲームを例に、Vuforiaによる(擬似)ハンドトラッキングを実装する方法について説明をしました。<br /> モバイル向けにはVuforiaを活用したハンドトラッキング・ポジショントラッキングをどんどん活用したい、と思っていますが、最新のモバイルVRヘッドセットであるDaydream ViewではVuforiaが使えません。Google Cardboardのようにスマホカメラの位置に穴を開けるわけにはいきませんので。。<br /> 何らか他の方法を検討する必要があるな、と考えております。</p> </div> tuti107 UNIBOOK7で、ThetaSによる360度立体視映像配信について書かせていただきました hatenablog://entry/10328749687200330286 2016-12-21T22:15:58+09:00 2016-12-21T22:15:58+09:00 知人に紹介いただいたことがきっかけで、UNIBOOK7の「第8章 ThetaSを使った360度立体視映像配信」を書かせていただきました。 半年ほど前、ThetaSを2台使った360度立体視の撮影・配信についてブログにかきましたが、その当時は、私の知識不足&当時作成していたアプリの要求仕様に合わない、ということから中途半端となっていた、撮影と配信、そして配信映像を視聴するためのモバイルアプリの作り方について記載しております。 本書には、その他多数の著名クリエイターが執筆した読み応えのある内容満載です。購入いただけると幸いです!www.unity-bu.com <p>知人に紹介いただいたことがきっかけで、UNIBOOK7の「第8章 ThetaSを使った360度立体視映像配信」を書かせていただきました。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161221/20161221221355.jpg" alt="f:id:tuti107:20161221221355j:plain" title="f:id:tuti107:20161221221355j:plain" class="hatena-fotolife" itemprop="image"></span><br /> <a href="http://tuti107.hatenablog.com/entry/2016/04/16/140034">&#x534A;&#x5E74;&#x307B;&#x3069;&#x524D;</a>、ThetaSを2台使った360度立体視の撮影・配信についてブログにかきましたが、その当時は、私の知識不足&当時作成していたアプリの要求仕様に合わない、ということから中途半端となっていた、撮影と配信、そして配信映像を視聴するためのモバイルアプリの作り方について記載しております。<br /> 本書には、その他多数の著名クリエイターが執筆した読み応えのある内容満載です。購入いただけると幸いです!</p><p><iframe src="//hatenablog-parts.com/embed?url=http%3A%2F%2Fwww.unity-bu.com%2F2016%2F12%2Funibook7.html" title="UNIBOOK 7 + 総集編 情報【C91 – 1日目 西 む-04ab】" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="http://www.unity-bu.com/2016/12/unibook7.html">www.unity-bu.com</a></cite></p> tuti107 Oculus Touch + Daydream View hatenablog://entry/10328749687198024327 2016-12-08T22:48:36+09:00 2016-12-08T22:49:14+09:00 Oculus TouchとDaydream View、それにPixelが一気に届きました。週末はいじり倒します! <p>Oculus TouchとDaydream View、それにPixelが一気に届きました。週末はいじり倒します!<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161208/20161208224808.jpg" alt="f:id:tuti107:20161208224808j:plain" title="f:id:tuti107:20161208224808j:plain" class="hatena-fotolife" itemprop="image"></span></p> tuti107 Vuforiaの認識精度を上げるには hatenablog://entry/10328749687196856069 2016-12-01T16:31:48+09:00 2016-12-01T16:31:48+09:00 はじめに 前回、Vuforia for UnityのExtended Tracking機能を利用してGoogle Cardboardでポジショントラッキングということで記事を書いたところ、ブログ始めて以来の反響、非常に驚きました。モバイルVRでもポジショントラッキングをやりたい、というニーズは高いのかもしれません。 前回の記事では、結局十分な精度を実現できず、自分が動かなくても、プルプルとポジションが動いてしまう状態のままで公開をしましたが、その後、精度を上げる方法について調べましたので、これをご紹介しようと思います。 理想的なイメージとは? Vuforiaは、イメージの特徴点を比較することで… <div class="section"> <h3>はじめに</h3> <p>前回、<a href="http://tuti107.hatenablog.com/entry/2016/11/27/221350">Vuforia for Unity&#x306E;Extended Tracking&#x6A5F;&#x80FD;&#x3092;&#x5229;&#x7528;&#x3057;&#x3066;Google Cardboard&#x3067;&#x30DD;&#x30B8;&#x30B7;&#x30E7;&#x30F3;&#x30C8;&#x30E9;&#x30C3;&#x30AD;&#x30F3;&#x30B0;</a>ということで記事を書いたところ、ブログ始めて以来の反響、非常に驚きました。モバイルVRでもポジショントラッキングをやりたい、というニーズは高いのかもしれません。<br /> 前回の記事では、結局十分な精度を実現できず、自分が動かなくても、プルプルとポジションが動いてしまう状態のままで公開をしましたが、その後、精度を上げる方法について調べましたので、これをご紹介しようと思います。</p> </div> <div class="section"> <h3>理想的なイメージとは?</h3> <p>Vuforiaは、イメージの特徴点を比較することで、イメージを認識します。特徴点の詳細については割愛しますが、Vuforiaの場合、この特徴点は「イメージを白黒化した上で、尖った部分<br /> (三角形や長方形の角のような箇所)」に多く現れるようです(詳細は<a href="https://library.vuforia.com/articles/Solution/Natural-Features-and-Ratings">&#x3053;&#x3061;&#x3089;</a>)。<br /> 理想的なイメージは、<br /> - コントラストがはっきりしている。コントラストが低いと「尖り」が分かりづらくなるため<br /> - ごちゃごちゃしている。シンプルだと特徴点が少なくなってしまう<br /> - 規則性がない。詳細は分かりませんが、特徴点を比較する上で、規則的な配列の特徴点は、認識率を悪化させるのだと思われます<br /> もの、となります。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161201/20161201161146.jpg" alt="f:id:tuti107:20161201161146j:plain" title="f:id:tuti107:20161201161146j:plain" class="hatena-fotolife" itemprop="image"></span> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161201/20161201161155.jpg" alt="f:id:tuti107:20161201161155j:plain" title="f:id:tuti107:20161201161155j:plain" class="hatena-fotolife" itemprop="image"></span></p><p>前回は、Vuforiaがサンプルとして提供している「stones」というイメージを利用しており、上記の条件は満たしております。</p> </div> <div class="section"> <h3>印刷品質・サイズ</h3> <p>次に疑われるのは、印刷したイメージの印刷品質・サイズです(詳細は<a href="https://library.vuforia.com/articles/Best_Practices/Recommendations-for-Improving-Target-Detection-and-Tracking-Stability">&#x3053;&#x3061;&#x3089;</a>)。<br /> 印刷したイメージのサイズがあまりにも小さすぎると、カメラより入力した画像から十分な特徴点を抽出できず、認識を失敗したり・精度が不安定となります。サイズについては、端末ー印刷物間の距離(m)<印刷物のイメージの幅✖️10以下、とするのが良いようです。前回私は幅30cmでイメージを印刷したので、距離は3m以内、ということになります。私はせいぜい2m程度の距離で試したため、印刷サイズの問題ではないようです。<br /> 印刷品質については、「コントラストはっきり」「テカらない」、かつ「印刷物に折り目などがない」ものである必要があります。<br /> 私は前回、ペラッペラの再生紙に、印刷品質「普通」で印刷したため、コントラストが低く、紙がよれてしまい一部部屋の光をかなり反射している、という最悪の状況になっていました。<br /> そこで、ためしにイメージをディスプレイに表示し、試してみたところ、品質はかなり安定しました。早速<a href="http://www.yodobashi.com/ec/product/100000001000068810/index.html?_ga=1.223998491.1611784985.1468646181">&#x3053;&#x3061;&#x3089;</a>の紙を注文しました!</p> </div> <div class="section"> <h3>終わりに</h3> <p>前回品質が低かった理由は、単に印刷物が粗末だったためでした。ディスプレイ表示によるポジショントラッキングは、ゲーム等の開発に十分耐えうる品質(Oculus RiftやHTC Viveと比べればかなり低いですが)です。<br /> ちなみに、名刺サイズの用紙に認識用イメージをプリントし、手の甲に貼り付ければ、簡易ハンドトラッキング(ハンドポジショントラッキングというのが正確)も可能な旨、確認しました。これらを駆使すれば、モバイルVRでも、Oculus RiftやHTC Viveの様な没入感のあるアプリを開発することもできそうですね。</p> </div> tuti107 Vuforia for UnityのExtended Tracking機能を利用してGoogle Cardboardでポジショントラッキング hatenablog://entry/10328749687196233495 2016-11-27T22:13:50+09:00 2016-11-28T21:16:59+09:00 はじめに Google CardboardやGear VRなど、スマホを利用したいわゆる「モバイルVR」は、Oculus Rift/HTC Vive等と比較し、値段が安くお手軽にVR体験を楽しむことができます。一方で、Oculus Rift/HTC Viveには搭載されているハンドデバイス(Oculus RiftのOculus Touchはこの2016/12に出荷予定)や、ポジショントラッキング等、VRの没入感を生み出すための重要な機能が、モバイルVRには搭載されておりません。 モバイルVRでは、立体視+ヘッドトラッキング(顔=デバイスの向きをトラッキング)により、顔の向きとCG空間内の視線方… <div class="section"> <h3>はじめに</h3> <p>Google CardboardやGear VRなど、スマホを利用したいわゆる「モバイルVR」は、Oculus Rift/HTC Vive等と比較し、値段が安くお手軽にVR体験を楽しむことができます。一方で、Oculus Rift/HTC Viveには搭載されているハンドデバイス(Oculus RiftのOculus Touchはこの2016/12に出荷予定)や、ポジショントラッキング等、VRの没入感を生み出すための重要な機能が、モバイルVRには搭載されておりません。<br /> モバイルVRでは、立体視+ヘッドトラッキング(顔=デバイスの向きをトラッキング)により、顔の向きとCG空間内の視線方向を一致させることで、あたかも自分が向いている方向のCGを立体視しているような錯覚を生み出し、これが没入感を生み出します。この「立体視+ヘッドトラッキング」だけでも、相当の没入感であり、私も最初にGoogle Cardboard(Oculus Rift DK1も)を見たときには相当な感動を覚えました。しかし、ポジショントラッキングやハンドデバイスが実装されているVR、特にHTC Viveのルームスケールなんかを体験してしまうと、「立体視+ヘッドトラッキング」だけでは物足りなさを感じるようになってしまいます。<br /> ポジショントラッキングとは、その名の通り、ユーザの位置をトラッキングする機能です。ヘッドトラッキングでは顔の向きだけ任意の方向に向けることができましたが、例えば立ち上がったり・しゃがんだり・肩を左右に振っても、ユーザの視点位置は変化しません。しかし、ポジショントラッキング搭載のものであれば、例えば向こうから飛んでくるボールを避けるゲーム、なんてものも実現することができます。<br /> Oculus Rift (DK2/CV1)では、ユーザの正面の専用のカメラを設置し、このカメラがOculus Rift本体の位置を捉えることでポジショントラッキングを実現しています。HTC Viveは、Oculus Riftと逆の方法、部屋の2角に赤外線を発光するベースステーションを設置し、ヘッドセットやハンドデバイスに内蔵のカメラで赤外線を受光することでトラッキングをしているようです。これにより、ルームスケールと呼ばれる「CG空間を歩き回る体験」や、まるで自分の手がCG空間に現れたかのような秀悦な操作感が実現されています。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161127/20161127153421.png" alt="f:id:tuti107:20161127153421p:plain" title="f:id:tuti107:20161127153421p:plain" class="hatena-fotolife" itemprop="image"></span><br /> しかし、これらポジショントラッキングやハンドデバイスには高度が技術が必要となり、廉価版であるモバイルVRでの実現はコスト的に困難です。<a href="http://www.vicovr.com/">Vico VR</a>のように、ポジショントラッキングや手足による入力に対応している(らしい、コンシュマー版は未出荷)ものもありますが、それなりのお値段のようです。<br /> 今回は、有名なARエンジンである「Vuforia」を利用して、簡易なポジショントラッキングを実現してみます。</p> </div> <div class="section"> <h3>Daydream Technical Preview</h3> <p>はじめに、<a href="http://tuti107.hatenablog.com/entry/2016/11/26/131631">&#x524D;&#x56DE;</a>説明した、Daydream technical previewのUnityを起動し、適当なプロジェクトを作成してください。<br /> おそらく通常のUnity + Google VR SDK for Unityの組み合わせでの開発も可能かと思いますが、AndroidManifestがVuforia/Google VR SDKで競合するなど、いろいろと面倒な問題が発生するため、今回は割愛します。</p> </div> <div class="section"> <h3>Vuforiaのライセンスキー登録</h3> <p>Vuforiaを利用したアプリを開発する際、以下の手順でライセンスキーの登録が必要となります(下記の手順の場合は無料です)。</p> <ol> <li>Vuforiaにログインの後、<a href="https://developer.vuforia.com/targetmanager/licenseManager/licenseListing">&#x3053;&#x3061;&#x3089;</a>より、Add License Keyボタンを押下する。</li> <li>Project TypeよりDevelopmentを選択、Project Detailは、App Nameには適当な名前、DeviceはMobileを選択し、Nextボタンを押下する。</li> <li>"By clicking "Confirm" below, ..."をチェックし、Confirmボタンを押下する。</li> <li>License Managerに表示されるアプリ一覧より、2でApp Nameとして入力したものを選択する。</li> <li>License Keyが表示されるので、これをコピーする。</li> <li>Unityに戻り、ARCameraのインスペクタを開く。Vuforia BehaviorコンポーネントのApp License Keyにペーストする。</li> </ol> </div> <div class="section"> <h3>Targetの登録</h3> <p>次に、認識対象となるイメージの登録を、以下の手順で行います。</p> <ol> <li><a href="https://developer.vuforia.com/targetmanager/project/checkDeviceProjectsCreated?dataRequestedForUserId=">Target Manager</a>で、Add Databaseボタンを押下する。</li> <li>Create Databaseポップアップにて、Nameには適当なデータベース名を、TypeはDeviceを選択し、Createボタンを押下する。</li> <li>Target Managerのデータベース一覧に2で追加したデータベースが追加されるので、これを選択する。</li> <li>Add Targetボタンを押下する。</li> <li>Add Targetポップアップにて、TypeはSingle Image、Fileには適当なイメージファイル、widthには実世界(プリントアウトしたイメージ)のサイズ(単位はメートル)、Nameには適当な名前を入力し、Addボタンを押下する。</li> <li>Download Databaseボタンを押下し、Download Databaseポップアップにて、Unity Editorを選択し、Downloadボタンを押下して、データベース(unitypackage形式)をダウンロードする。</li> <li>ダウンロードしたパッケージをインポートする。すると、Assets/StreamingAssets/QCARに、データベースが読み込まれる。</li> <li>ARCAmeraオブジェクトのDatabaseLoadBehaviorコンポーネントに、Load データベース名 Databaseというチェックが現れるので、これをチェックする。また、これをチェックすると現れる「Activate」もチェックする。</li> </ol><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161127/20161127200849.png" alt="f:id:tuti107:20161127200849p:plain" title="f:id:tuti107:20161127200849p:plain" class="hatena-fotolife" itemprop="image"></span><br /> </p> </div> <div class="section"> <h3>VuforiaがTargetを認識した時の処理</h3> <p><ol> <li> Assets/Vuforia/Prefabs/ImageTargetをシーンに追加する</li> <li> ImageTargetオブジェクトのImageTargetBehaviorコンポーネントについて、インスペクタにて、TypeはPredefined、Databaseはデータベース名、Image Targetはターゲットの名前を選択する。また、Enable Extended Trackingにチェックを入れる。</li> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161127/20161127201605.png" alt="f:id:tuti107:20161127201605p:plain" title="f:id:tuti107:20161127201605p:plain" class="hatena-fotolife" itemprop="image"></span> <li> ImageTargetオブジェクト配下に適当なオブジェクト(以下の図の場合はCube)を適当なサイズで配置する。なお、Extended Trackingの効果をわかりやすくするために、縦方向に長くしています。</li> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161127/20161127202058.png" alt="f:id:tuti107:20161127202058p:plain" title="f:id:tuti107:20161127202058p:plain" class="hatena-fotolife" itemprop="image"></span> <li> 認識用のイメージを印刷する。サイズはAdd Targetにて設定した横幅とする。</ol></p><p>フロントカメラ搭載のノートPCやMacをご利用の方は、エディタ上にて実行し、印刷したターゲットのイメージにカメラを向けると、上記のCubeが撮影イメージ上に表示されます。また、Extended Trackingをチェックすると、カメラからターゲットのイメージが外れてしまってもCubeが適切な位置に表示され続けます。つまり、一度でもターゲットのイメージを認識すると、その後はターゲットのイメージの周囲の情報をリアルタイムに認識することで、ターゲットのイメージがカメラ内に無くてもまるでターゲットのイメージがカメラ内に捉えられているかのごとく振舞います(当然、周囲に動くものがある場合はダメなようです)。<br /> <iframe width="480" height="270" src="https://www.youtube.com/embed/BmxO3SZm9f8?feature=oembed" frameborder="0" allowfullscreen></iframe><cite class="hatena-citation"><a href="https://www.youtube.com/watch?v=BmxO3SZm9f8">www.youtube.com</a></cite><br /> </p> </div> <div class="section"> <h3>Google Cardboardとのインテグレーション</h3> <p><ol> <li> ARCamera配下のCameraをDisableする。また、ARCameraオブジェクトのVideoBackgroundManagerコンポーネントのEnable video backgroundチェックを外す。これらは、スマホで撮影したカメラプレビューを背景映像として表示するためのものですが、ポジショントラッキングの用途には不要なためです。</li> <li> ImageTarget配下のCube(もしくは上記で追加した何らかの3Dオブジェクト)をDisableする。こちらもポジショントラッキングには不要なためです。</li> <li> シーンのルートにEmptyObject(名前をPlayerとする)を配置、そこにCameraをぶら下げる。このCameraのTagはMainCameraとする。</li> <li> 以下のPositionTracker.csを作成し、Playerにコンポーネントとして設定する。</li></p> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synStatement">using</span> UnityEngine; <span class="synStatement">using</span> System.Collections; <span class="synType">public</span> <span class="synType">class</span> PositionTracker : MonoBehaviour { <span class="synType">public</span> GameObject arCamera; <span class="synType">void</span> Update () { var p = arCamera.transform.position; gameObject.transform.position = <span class="synStatement">new</span> Vector3 (p.x, p.z, -p.y); } } </pre><p><li> Cameraの前に適当な3Dオブジェクト(Cubeなど)をシーンに配置する。</li><br /> <li> Assets/Plugins/Android/AndroidManifest.xmlのandroid:minSdkVersionを"16"に変更する。<br /> <li> <a href="http://tuti107.hatenablog.com/entry/2016/11/26/131631">&#x524D;&#x56DE;</a>説明した手順にて、Virtual Reality Supportedをチェック、SDKをCardboardとして、Android向けにビルドする</li><br /> </ol></p> </div> <div class="section"> <h3>品質は微妙</h3> <p>アプリ起動前に、ターゲットイメージを印刷した紙を(多少ごちゃごちゃした)壁・棚などに貼り付け、この紙の方を向いて端末を起動してください。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161127/20161127220737.png" alt="f:id:tuti107:20161127220737p:plain" title="f:id:tuti107:20161127220737p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>紙に近づけばCubeが大きく、左右に動いたりしゃがんだりすればCubeはそれぞれの方向に動きます。ちゃんとシーンを構成すれば一応ポジショントラッキングのあるVR、な感じになります。以下は、Editorで実行し、前後上下左右にMacを動かした際のものです。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161128/20161128211500.gif" alt="f:id:tuti107:20161128211500g:plain" title="f:id:tuti107:20161128211500g:plain" class="hatena-fotolife" itemprop="image"></span></p><p>ただ、品質があまりよくありません。静止していてもプルプル動くし、時々変な方向に飛んでしまうし。。シンプルにARCameraの座標を使うのでは無く、ちょっと一工夫が必要のようです。<br /> また、私のAndroid端末(Galaxy S6)でGoogle Cardboard使用中にデバイスのカメラを利用するためには、Google Cardboardに切れ込みを入れる必要がありました。。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161127/20161127220810.jpg" alt="f:id:tuti107:20161127220810j:plain" title="f:id:tuti107:20161127220810j:plain" class="hatena-fotolife" itemprop="image"></span></p> </div> tuti107 Daydream technical previewを使ってみる hatenablog://entry/10328749687196018663 2016-11-26T13:16:31+09:00 2016-11-27T22:20:09+09:00 はじめに 先月注文したPixelとDaydream View、ようやく発送されました。ただ諸事情により手元に届くのは、あともう二週間後になりそうです。 今回は、実機入手後すぐに開発にとりかかるべく、Unity Daydream technical previewの導入を試してみたいと思います。 Daydream technical previewのインストール Daydream technical previewは、こちらよりダウンロードできます。本ページをずっと下にスクロールしていくと、インストーラのダウンロードボタンが見つかります。インストールが完了しましたら、デスクトップに生成されたショ… <div class="section"> <h3>はじめに</h3> <p>先月注文したPixelとDaydream View、ようやく発送されました。ただ諸事情により手元に届くのは、あともう二週間後になりそうです。<br /> 今回は、実機入手後すぐに開発にとりかかるべく、Unity Daydream technical previewの導入を試してみたいと思います。</p> </div> <div class="section"> <h3>Daydream technical previewのインストール</h3> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161126/20161126122619.png" alt="f:id:tuti107:20161126122619p:plain" title="f:id:tuti107:20161126122619p:plain" class="hatena-fotolife" itemprop="image"></span><br /> Daydream technical previewは、<a href="https://unity3d.com/jp/partners/google/daydream">&#x3053;&#x3061;&#x3089;</a>よりダウンロードできます。本ページをずっと下にスクロールしていくと、インストーラのダウンロードボタンが見つかります。</p><p>インストールが完了しましたら、デスクトップに生成されたショートカットより、Daydream technical previewを起動します。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161126/20161126122959.png" alt="f:id:tuti107:20161126122959p:plain" title="f:id:tuti107:20161126122959p:plain" class="hatena-fotolife" itemprop="image"></span><br /> すると、「普通」にUnityが起動します。ただしタイトルバーを見ると「Unity 5.4.2f2-GVR12」となっており、これがGoogle VR向けのものであることが確認できます。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161126/20161126123258.png" alt="f:id:tuti107:20161126123258p:plain" title="f:id:tuti107:20161126123258p:plain" class="hatena-fotolife" itemprop="image"></span><br /> </p> </div> <div class="section"> <h3>NativeサポートされたGoogle Cardboard</h3> <p>まずは(手元に実機もありませんので)Google Cardboard向けのアプリを作ってみます。<br /> <a href="http://tuti107.hatenablog.com/entry/2016/10/16/153443">&#x524D;&#x56DE;</a>Google VR SDK for Unityを利用したアプリの作成について詳細をした際は、「GoogleVRForUnity.unitypackage」をインポートしましたが、Daydream technical previewではGoogle CardboardがNativeサポートされているため、立体視表示やヘッドトラッキング等のVRの最低限の機能については、本SDKを利用する必要はありません。その代わりに以下の通りの設定を行います。</p> <ol> <li>File→Build Settingsより、PlatformをAndroidとする</li> <li>Player SettingsのOther Settingsにて、Virtual Reality Supportedにチェックを入れる</li> <li>+ボタンを押下し、Cardboardを選択する</li> <li>同じくOther Settingsにて、Minimum API Levelを16以上とする</li> <li>Bundle Identifierに適当なものを設定する</li> </ol><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161126/20161126125443.png" alt="f:id:tuti107:20161126125443p:plain" title="f:id:tuti107:20161126125443p:plain" class="hatena-fotolife" itemprop="image"></span></p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161126/20161126125524.png" alt="f:id:tuti107:20161126125524p:plain" title="f:id:tuti107:20161126125524p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>以上を設定の後、Build and Runにて、Android端末上にCardboard向けVRアプリが起動します。<br /> アプリといっても、Sceneに何も配置していないので、デフォルトのSkyboxが表示されるのみですが。。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161126/20161126130036.png" alt="f:id:tuti107:20161126130036p:plain" title="f:id:tuti107:20161126130036p:plain" class="hatena-fotolife" itemprop="image"></span><br /> </p> </div> <div class="section"> <h3>さいごに</h3> <p>今回は、Daydream technical previewのインストールし、とりあえず実機上で動くアプリをビルド・実行してみました。<br /> 次回は、Gaze入力等、もう少し詳細まで踏み込んでみたいと思います。</p> </div> tuti107 Google VR SDK for Unityをより詳細に見てみる hatenablog://entry/10328749687190654843 2016-10-22T09:59:44+09:00 2016-10-22T10:00:38+09:00 前回は、Google VR SDK for Unityをインストールし、付属のDemo SceneをとりあえずAndroid端末上で実行してみました。特段なにもはまることなく、スムーズに実行まですすめることができました。 一方で、Demo Scene内のオブジェクトを見てみると、Google Cardboard SDKのときにはなかった色々な名前のオブジェクトがあることに気づきました。実行画面のぱっと見、Demo SceneはGoogle Cardboard SDKのものとほぼ同じなのですが、SDK自体は大きく異なるのかも? ということで、今回はGoogle Cardboard SDKとの差異… <p><a href="http://tuti107.hatenablog.com/entry/2016/10/16/153443">&#x524D;&#x56DE;</a>は、Google VR SDK for Unityをインストールし、付属のDemo SceneをとりあえずAndroid端末上で実行してみました。特段なにもはまることなく、スムーズに実行まですすめることができました。<br /> 一方で、Demo Scene内のオブジェクトを見てみると、Google Cardboard SDKのときにはなかった色々な名前のオブジェクトがあることに気づきました。実行画面のぱっと見、Demo SceneはGoogle Cardboard SDKのものとほぼ同じなのですが、SDK自体は大きく異なるのかも?<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161021/20161021214817.png" alt="f:id:tuti107:20161021214817p:plain" title="f:id:tuti107:20161021214817p:plain" class="hatena-fotolife" itemprop="image"></span><br /> ということで、今回はGoogle Cardboard SDKとの差異を中心に、Google VR SDK for Unityついて、詳細に見てみました。</p> <div class="section"> <h3>ほぼ一緒でした</h3> <p>結論からいうと、Google VR SDK for UnityとGoogle Cardboard SDKは概ねよく似ております。名前や機能分割による違いがメインかと思います。<br /> Google Cardboard SDKは、</p> <ol> <li>CardboardMainというprefabを設置→これだけで左右眼分割表・樽状のゆがみ表示・ヘッドトラッキングの「VRっぽい機能」が動きます</li> <li>EventSystemにGaze Input Moduleを追加し、CardboardMainにGazeポインタを設置→ユーザが見ている位置にポインタが表示されます</li> <li>EventTriggerを実装した適当なオブジェクトを用意→上記ポインタがオブジェクトに入る・出る・クリックする等のイベントを受けることができます</li> </ol><p>との手順で、VRアプリを簡単に作成することができましたが、Google VR SDK for Unityでは、</p> <ol> <li>GvrViewerMain prefabを設置→左右分割・樽状のゆがみ表示・ヘッドトラッキングを行う</li> <li>EventSystemにGaze Input Moduleを追加し、Main CameraにGazeポインタとなるGvrReticle(円形のポインタ)を置く</li> <li>EventTriggerを実装した適当なオブジェクトを用意</li> </ol><p>ということでほぼ一緒です。Google Cardboard SDKでごちゃごちゃしていて複雑だったところが、すっきりした、という感じです。</p> </div> <div class="section"> <h3>GvrViewerMain</h3> <p>本オブジェクトは、左右分割・樽状のゆがみ表示・ヘッドトラッキングといったVR的機能を提供します。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161021/20161021220843.png" alt="f:id:tuti107:20161021220843p:plain" title="f:id:tuti107:20161021220843p:plain" class="hatena-fotolife" itemprop="image"></span><br /> </p> <ul> <li>VR Mode Enabledは、VR表示(左右眼分割表示)をするかどうかを設定</li> <li>Distortion Correction Enabledは、樽状の歪み表示をするかどうかを設定</li> <li>Stereo Screen Scaleは、1より低い値とすると画質悪いが高速、1より大きい値(最大2)とすると高精細だが低速となる</li> <li>Neck Model Scaleは、首のオフセットを考慮したより正確なヘッドトラッキングを行う。0(未使用)~1の値を設定。Daydream端末だとわかる感じなのでしょうか?私には設定時の違いがあまりわからず。。</li> </ul> </div> <div class="section"> <h3>Main Camera</h3> <p>こちらは、Google Cardboard SDKとほぼ一緒です。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161022/20161022083056.png" alt="f:id:tuti107:20161022083056p:plain" title="f:id:tuti107:20161022083056p:plain" class="hatena-fotolife" itemprop="image"></span><br /> 留意点としては、AudioListenerがGvrの名称に変わっていること、くらいです。なお、Google Cardboard SDKと同様なのですが、Physics Raycasterを追加しておかないと、Gazeポインタが表示されません。<br /> GvrAudioListenerによる音空間設定については、また別途の機会に触れたいと思います。</p> </div> <div class="section"> <h3>GvrReticle</h3> <p>円形上のGazeポインタです。MainCamera配下にこれを配置することで、向いている方向中央に<br /> - ポイントが乗った・外れたこと(PointerEnter/PointerExit)通知を受けるオブジェクトがある場合は、大きな円に<br /> - それ以外は点で<br /> ポインターが表示されます。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161022/20161022084337.gif" alt="f:id:tuti107:20161022084337g:plain" title="f:id:tuti107:20161022084337g:plain" class="hatena-fotolife" itemprop="image"></span><br /> インスペクタで、表示される円について詳細設定ができます。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161022/20161022091236.png" alt="f:id:tuti107:20161022091236p:plain" title="f:id:tuti107:20161022091236p:plain" class="hatena-fotolife" itemprop="image"></span></p> <ul> <li>Reticle Segmentsは、円描画の点の数を指定します。この値が少ないと円ではなく多角形な感じになります(以下は5を指定の場合)</li> </ul><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161022/20161022090925.png" alt="f:id:tuti107:20161022090925p:plain" title="f:id:tuti107:20161022090925p:plain" class="hatena-fotolife" itemprop="image"></span></p> <ul> <li>Reticle Growth Speedは、ポインタがオブジェクト上に乗った際に、点→円になる速度を指定します。すくないほどゆっくりとなります。</li> </ul><p>GvrReticleは、後述のGazeInputModuleを利用して、オブジェクトに乗った・外れた等の判定をします。なお、この(複雑な)GazeInputModuleを使用せず、シンプルにポインタ表示を行う手段としてGvrGazeがあります(こちらもいずれ紹介しようと思っています)。</p> </div> <div class="section"> <h3>GazeInputModule</h3> <p>EventSystemにGazeInputModuleを追加することで、Unityにおけるマウス・キーボード等の入力イベントの共通配信システムであるEventSystemを利用してGazeポインタ関連イベントを利用できるようになります。<br /> 例えば、DemoSceneの足元には、以下のようなボタンが配置されていますが、<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161022/20161022093134.png" alt="f:id:tuti107:20161022093134p:plain" title="f:id:tuti107:20161022093134p:plain" class="hatena-fotolife" itemprop="image"></span><br /> これらボタンが押下された場合の処理は、プログラムを記述する必要なく、簡単に設定することができます。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161022/20161022093422.png" alt="f:id:tuti107:20161022093422p:plain" title="f:id:tuti107:20161022093422p:plain" class="hatena-fotolife" itemprop="image"></span><br /> この場合は、Cubeオブジェクト、Teleportコンポーネントの、ToggleVRMode()メソッドが、ボタンクリック時に呼び出されます。<br /> EventSystemによるGaze関連イベントはボタン等のGUI部品のみならず、自作のGameObjectにも設定可能です。<br /> DemoScene内のCubeオブジェクトには、以下のようにEventTriggerコンポーネントが追加されており、そこでPointerEnter/PointerExit/PointerClickがそれぞれ追加されております。このようにすることで、このCubeオブジェクトはGazeポインターが入ったとき・出たとき・クリックされたときにそれぞれ指定のメソッドが呼び出されるようになります。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161022/20161022094323.png" alt="f:id:tuti107:20161022094323p:plain" title="f:id:tuti107:20161022094323p:plain" class="hatena-fotolife" itemprop="image"></span><br /> </p> </div> <div class="section"> <h3>さいごに</h3> <p>以上、今回はGoogle Cardboard SDKとGoogle VR SDK for Unityの差異を中心に、Google VR SDK for Unityの詳細を見ました。これら2つは非常に似た感じですが、Google VR SDK for Unityのほうが、よりシンプルにまとめられていると感じました。<br /> 今回ご紹介の内容だけで、簡単なVRコンテンツは充分に開発可能かと思います。次回は、音響周りを中心に見ていこうかな、と思っています。</p> </div> tuti107 Google VR SDK for Unityを使ってみる hatenablog://entry/10328749687189695516 2016-10-16T15:34:43+09:00 2016-10-17T06:42:56+09:00 Googleの10月4日のイベントで、最新のVRヘッドセットDaydream Viewが発表されました。軽くて装着感もよさそうだし、$79という低価格は非常に魅力的です!私も早速手に入れて色々開発してみたいところなのですが、当面日本での発売は予定されていないとのこと。残念・・ でも遅かれ早かれDaydream Viewも対応のAndroidデバイスも手に入るでしょうし、まずはDaydream向け/Cardboard向け統合のVR開発環境(?)であるGoogle VR SDKを入手し、事前に触ってみました。 今回は、Cardboard向けのデモアプリ実行まで。 環境の準備 Unity用のGoog… <p>Googleの10月4日のイベントで、最新のVRヘッドセットDaydream Viewが発表されました。軽くて装着感もよさそうだし、$79という低価格は非常に魅力的です!私も早速手に入れて色々開発してみたいところなのですが、当面日本での発売は予定されていないとのこと。残念・・<br /> でも遅かれ早かれDaydream Viewも対応のAndroidデバイスも手に入るでしょうし、まずはDaydream向け/Cardboard向け統合のVR開発環境(?)であるGoogle VR SDKを入手し、事前に触ってみました。<br /> 今回は、Cardboard向けのデモアプリ実行まで。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161016/20161016150402.png" alt="f:id:tuti107:20161016150402p:plain" title="f:id:tuti107:20161016150402p:plain" class="hatena-fotolife" itemprop="image"></span><br /> </p> <div class="section"> <h3>環境の準備</h3> <p>Unity用のGoogle VR SDKであるGoogle VR SDK for Unityを利用するためには、以下をインストールする必要があります。</p> <ul> <li><a href="https://developer.android.com/studio/index.html">Android SDK</a></li> <li>Unity 5.2.1以上。なお試していませんが、Daydream向けの開発には<a href="https://unity3d.com/jp/partners/google/daydream">Daydream technical preview</a>が必要のようです。</li> <li><a href="https://github.com/googlevr/gvr-unity-sdk/">Google VR SDK for Unity</a></li> </ul> </div> <div class="section"> <h3>まずは、デモをエディタ上で実行してみる</h3> <p>上記全てをダウンロード・インストールしましたら、Unityを起動して適当なプロジェクトを作成します。開発画面が起動しましたら、ProjectのAsset->右クリック->Import Package->Custom Packageを選択し、先程ダウンロードしたUnity VR SDK for Unityに含まれる「GoogleVRForUnity.unitypackage」をインポートします。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161016/20161016151320.png" alt="f:id:tuti107:20161016151320p:plain" title="f:id:tuti107:20161016151320p:plain" class="hatena-fotolife" itemprop="image"></span><br /> インポートしたら、Assets→Google VR→DemoScenes→HeadsetDemoのDemoScene.unityをダブルクリックします。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161016/20161016151456.png" alt="f:id:tuti107:20161016151456p:plain" title="f:id:tuti107:20161016151456p:plain" class="hatena-fotolife" itemprop="image"></span><br /> これでとりあえず実行してみると、<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161016/20161016151520.png" alt="f:id:tuti107:20161016151520p:plain" title="f:id:tuti107:20161016151520p:plain" class="hatena-fotolife" itemprop="image"></span><br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161016/20161016151528.png" alt="f:id:tuti107:20161016151528p:plain" title="f:id:tuti107:20161016151528p:plain" class="hatena-fotolife" itemprop="image"></span><br /> はい、無事に動きました。<br /> デモは、Google Cardboard SDKと変わらない感じです。ただし、SDKの作りはGoogle Cardboard SDKとは異なる感じです(しっかりと見ていませんが、ぱっと見、シーンに配置されたオブジェクトには見覚えのないものが色々配置されているようです)。</p> </div> <div class="section"> <h3>Android端末上で実行してみる</h3> <p>次に、上記エディタ上で動かしたデモをAndroidの実機にて動かしてみます。<br /> File→Build Settingsにて、Androidを選択し、<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161016/20161016152132.png" alt="f:id:tuti107:20161016152132p:plain" title="f:id:tuti107:20161016152132p:plain" class="hatena-fotolife" itemprop="image"></span><br /> Player Settingsにて、</p> <ul> <li>Other Settingsで、Bundle Identifierを適当に、Minimum API levelをAPI level 19に設定</li> </ul><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161016/20161016152322.png" alt="f:id:tuti107:20161016152322p:plain" title="f:id:tuti107:20161016152322p:plain" class="hatena-fotolife" itemprop="image"></span></p> <ul> <li>Resolution and Presentationにて、Default OrientationをLandscape Leftに設定</li> </ul><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161016/20161016152422.png" alt="f:id:tuti107:20161016152422p:plain" title="f:id:tuti107:20161016152422p:plain" class="hatena-fotolife" itemprop="image"></span><br /> します。あとは、Build And Runボタンを押すだけです。<br /> 特段問題なく、Android端末上でデモアプリが動作しました。</p> </div> <div class="section"> <h3>おわりに</h3> <p>今回は、来るDaydreamに備えて、まずはGoogle VR SDK for Unityの導入と、Google Cardboard向けのデモアプリの実行までやってみました。<br /> デモアプリを動かすまでは特に問題なく、という感じですが、上記のとおりSDKの使い方は、Google Cardboard SDKとは異なる印象です。<br /> 次回は、過去に作ったGoogle Cardboard SDK利用のアプリの移植を行って、Google VR SDK for Unityの分析を進めてみたいと思います。</p><p>ちなみに、<a href="http://tuti107.hatenablog.com/entry/2016/06/25/101956">&#x904E;&#x53BB;</a>Google Cardboard向けにKeyboardの開発を行っていましたが、<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20160625/20160625095127.png" alt="f:id:tuti107:20160625095127p:plain" title="f:id:tuti107:20160625095127p:plain" class="hatena-fotolife" itemprop="image"></span><br /> Googleが<a href="http://tabkul.com/?p=122971">&#x540C;&#x7B49;</a>のものを提供する予定のようですね。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20161016/20161016153803.jpg" alt="f:id:tuti107:20161016153803j:plain" title="f:id:tuti107:20161016153803j:plain" class="hatena-fotolife" itemprop="image"></span><br /> もっと早くに開発しておけばよかった。。</p> </div> tuti107 Unityで3DカメラIntel RealSenseを使ってみる hatenablog://entry/10328749687185850215 2016-09-23T16:09:05+09:00 2016-09-26T17:50:27+09:00 これまで、ThetaSやスマホを2台使いすることで360度立体視映像を撮影するアプリケーションを作成してきました。これら端末を2台使いすることで左右視差のある映像を録画し、背景部分を除去して背景とCGを重畳することで、かんたんに360度立体視コンテンツをつくれる、というものです。 この方法はかんたんでそれなりのクォリティを実現可能なのですが、撮影映像とCGの重畳において問題が生じる場合があります。 右目・左目映像を用意し、Google Cardboard等のVRヘッドセットでそれぞれの目がそれぞれの目の映像だけを見るようにすれば「人間は立体的に感じる」ことが可能です。しかし、コンピュータがこれ… <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20160923/20160923150741.png" alt="f:id:tuti107:20160923150741p:plain" title="f:id:tuti107:20160923150741p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>これまで、ThetaSやスマホを2台使いすることで360度立体視映像を撮影するアプリケーションを作成してきました。これら端末を2台使いすることで左右視差のある映像を録画し、背景部分を除去して背景とCGを重畳することで、かんたんに360度立体視コンテンツをつくれる、というものです。<br /> この方法はかんたんでそれなりのクォリティを実現可能なのですが、撮影映像とCGの重畳において問題が生じる場合があります。<br /> 右目・左目映像を用意し、Google Cardboard等のVRヘッドセットでそれぞれの目がそれぞれの目の映像だけを見るようにすれば「人間は立体的に感じる」ことが可能です。しかし、コンピュータがこれら左右眼用の映像から立体感(=奥行き)を認識することはかんたんではありません。<br /> このため、例えば背景部分の除去には、クロマキー(緑色の部屋を用意し、そこで撮影、映像の緑色部分を透明化することで背景除去)や、背景差分(最初にだれもいない部屋を撮影(=背景画像)、背景画像と撮影した背景映像の変化分だけを抜き出す)のような簡易的な方法を利用してきました。しかしこの方法では抜き出した部分の「立体感」をコンピュータは認識できません。このためこれらの方法では、奥行感を無視したおかしな映像が出来上がってしまう恐れがあります。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20160923/20160923151832.png" alt="f:id:tuti107:20160923151832p:plain" title="f:id:tuti107:20160923151832p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>クロマキーや背景差分で抜き出した映像部分には奥行きの情報がないため、CG・抜き出した映像どちらを前に表示するか、判別ができないためです。</p><p>Intel RealSense等の3Dカメラは、映像に「奥行き情報(=デプス)」を付加した撮影が可能です。以下のように、カラー映像(=左側)と同時にその映像のデプス(=右側)を撮影します。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20160923/20160923152027.png" alt="f:id:tuti107:20160923152027p:plain" title="f:id:tuti107:20160923152027p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>デプスは黒(=最も遠い/位置を特定不能)~白(=最も近い)をグレースケール表します。真っ黒(最も遠い)と真っ白(最も近い)がぞれぞれ、カメラから具体的にどの程度の距離離れているかについては、3Dカメラ毎に異なります。例えば今回使用するIntel RealSense R200は、認識可能な距離が0.5m~4mのため、黒=概ね4m以上(又は0.5m未満)、白=概ね0.5mとなるかと思います。</p><p>デプスを利用すると、背景部分の除去もカンタンです。例えば0.5m~4mの範囲にないもの(=デプスの黒部分)は透明、としてしまえば、4m以上向こうにあるものは一切表示されなくなります。<br /> 今回は、RealSense R200を利用し、デプスによる背景除去に挑戦してみます。</p> <div class="section"> <h3>RealSenseのセットアップ</h3> <p>まずは<a href="https://software.intel.com/en-us/intel-realsense-sdk/download">&#x3053;&#x3061;&#x3089;</a>から、ドライバとSDKのダウンロード、及びRealSense R200を購入します。<br /> Step1では、R200 Camera Driverをダウンロードしてください。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20160923/20160923110949.png" alt="f:id:tuti107:20160923110949p:plain" title="f:id:tuti107:20160923110949p:plain" class="hatena-fotolife" itemprop="image"></span><br /> そしてStep2(SDKのダウンロード)、Step3(オプション機能、必要に応じて)を順次ダウンロードし、最後にRealSense R200を購入します。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20160923/20160923152950.png" alt="f:id:tuti107:20160923152950p:plain" title="f:id:tuti107:20160923152950p:plain" class="hatena-fotolife" itemprop="image"></span><br /> RealSense R200を購入するためには、アカウントを作成する必要があります。画面右上のSign Inを押下し、Login or Create an Account画面の右下 CREATE AN ACCOUNTボタンを押下してCreate an Account画面へ移動し、必要情報を入力してアカウントを作成してください。メール認証の後、アカウントがアクティベートされます。<br /> サインインした状態で、Add to Cartボタンを押下し、画面右側のCHECK OUTボタンを押下してください。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20160923/20160923111714.png" alt="f:id:tuti107:20160923111714p:plain" title="f:id:tuti107:20160923111714p:plain" class="hatena-fotolife" itemprop="image"></span><br /> Billing Information画面では、領収書送付先の住所を入力します。<br /> 例えば 〒123-4567 北海道GHI市ABC町12-34 DEFビルディング1001号室 Tutti Labさん、電話番号090-1234-5678の場合は、以下の感じの入力になるかと思います。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20160923/20160923112845.png" alt="f:id:tuti107:20160923112845p:plain" title="f:id:tuti107:20160923112845p:plain" class="hatena-fotolife" itemprop="image"></span><br /> Shipping Informationでは、先程入力したBilling Addressを選択します。領収書と配送品の送り先が異なる場合のみ、こちらを別途入力します。<br /> Shipping Methodでは配送方法を選択します。Standard (5 - 7 business days) とPriority (2 - 3 business days)が選べますが、ほとんど金額が変わらないため、Priorityのほうがよいのでは?と思います。なお、私の場合、Priorityを選択し、注文後4日で配送されました。<br /> Payment Informationでは支払い方法が選べますが、Credit or Debit Cardを選択することになると思います。必要な情報を入力し、最後にOrder ReviewでPLACE ORDERを押せば完了です。<br /> RealSense R200が無事配送されましたら、USB3.0でPCと接続の後、ダウンロード済みのドライバー、SDKをインストールしてください。</p> </div> <div class="section"> <h3>UnityでRealSense R200を利用する</h3> <p>SDKには、UnityでRealSense R200を利用するためのアセットが含まれています。<br /> Unityを起動して適当なプロジェクトを生成の後、File→Build Settingsより、Target PlatformをWindowsに、Architectureをx86又はx86_64にそれぞれ設定します(Mac等でも使う方法があるようですが、割愛します)。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20160923/20160923114911.png" alt="f:id:tuti107:20160923114911p:plain" title="f:id:tuti107:20160923114911p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>次に、RealSense Unity Toolkitをインポートします。<br /> ProjectのAssetsで右クリック→Import Package→Custom Packageより、C:\Program Files (x86)\Intel\RSSDK\framework\Unity\UnityToolkit.unitypackageをインポートしてください。<br /> インポート後、まずはAssets/RSUnityToolkit/Prefabs/Imageをシーンにドラッグ&ドロップします。<br /> この状態で実行すると、下記のように、画面中央に小さくカメラプレビューが表示されます。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20160923/20160923115222.png" alt="f:id:tuti107:20160923115222p:plain" title="f:id:tuti107:20160923115222p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>RealSense Unity Toolkitは、上記Imageプレファブのように、各種認識、3D撮像、3DスキャンやAR等の機能をプレファブとして提供しています。開発者はこれらプレファブを利用することで殆どコードを書くことなくRealSenseの機能を活用したアプリケーションを作れる、、はずなのですが、実際はそううまくはいきません。<br /> 試しに、Image以外のプレファブ、例えばDebug Viewerをシーンに放り込んで実行してみると、エラーが発生し、動作しません。</p><p>これは、R200には存在しない機能の利用を試みたために発生したエラーです。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20160923/20160923115810.png" alt="f:id:tuti107:20160923115810p:plain" title="f:id:tuti107:20160923115810p:plain" class="hatena-fotolife" itemprop="image"></span></p><p><a href="http://www.buildinsider.net/small/newsensor/realsenser200">&#x3053;&#x3061;&#x3089;</a>のサイトに、R200とF200という2つのRealSenseの機能比較表があります。ご覧の通り、たくさんの機能に✕がついているのがわかります。<br /> このF200(現在は後継機のSR300)とR200、用途が異なります。F200は例えばPCディスプレイの上部等に設置するインカメラであり、PC利用時に有用な機能(顔認識でのログイン、ハンドジェスチャー認識、~1.5m程度のデプス取得など)が想定されています。一方でR200は、スマホやタブレットのアウトカメラに設置されることを想定されており、建物の3Dスキャンや、~4mのデプス取得等に利用します。<br /> F200側の機能も非常に魅力的ですので、これらを使ったアプリケーションを開発してみたい方は、F200の購入も検討いただければよいかと思います。</p> </div> <div class="section"> <h3>背景除去アプリケーションを作成してみる</h3> <p>Imageプレファブを利用して、背景除去機能を実装してみます。以下は、Imageプレファブのインスペクタです。Stream Outで出力される映像の種別を選択できます。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20160923/20160923121228.png" alt="f:id:tuti107:20160923121228p:plain" title="f:id:tuti107:20160923121228p:plain" class="hatena-fotolife" itemprop="image"></span></p> <ul> <li>STREAM_TYPE_COLORは、RGB映像を出力します</li> <li>STREAM_TYPE_DEPTHは、デプスをグレースケールで出力します</li> <li>STREAM_TYPE_IRは、赤外線センサー映像をグレースケールで出力します(F200のみ)</li> <li>STREAM_TYPE_LEFTは、赤外線センサー(左側)映像をグレースケールで出力します(R200のみ)</li> <li>STREAM_TYPE_RIGHTは、赤外線センサー(右側)映像をグレースケールで出力します(R200のみ)</li> </ul><p>今回は、1)STREAM_TYPE_DEPTHを指定したImageでデプスを生成、2)STREAM_TYPE_COLORを指定したImageは、1)のデプスを利用して一定距離の範囲外のピクセルを透明にする、として、背景を取り除きます。</p><p>まず、空オブジェクト「Images」を作成、その下に2つのImageプレファブを置きます。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20160923/20160923121830.png" alt="f:id:tuti107:20160923121830p:plain" title="f:id:tuti107:20160923121830p:plain" class="hatena-fotolife" itemprop="image"></span><br /> 次に、Assets/RSUnityToolkit/Internal/Materials/UnlitMatを2つコピー(Ctrl+D)し、それぞれDepth, RGBという名前とします。<br /> これらマテリアルDepth, RGBをそれぞれImageプレファブより生成したGameObject Depth, RGBにそれぞれ設定します。こうしておかないと、GameObject Depth, RGBはそれぞれ初期設定のマテリアルUnlitMatに映像を出力するため、Stream Outの設定にかかわらず両方ともRGB出力、もしくは両方ともデプス出力、となってしまいます。</p><p>次にデプスに応じて透過色設定をするためのシェーダーを作成します。<br /> ProjectのAssets内の適当なフォルダにて、右クリック→Create→Shader→Image Effect Shaderを選択してください。ファイル名は、TransparentEffectとします。なお「シェーダーとは?」については、他の書籍・ブログ等をご参照いただければと思います。以下では、今回実装部のみ説明をしていきます。</p> <pre class="code" data-lang="" data-unlink>Shader &#34;Tuti/TransparentEffectShader&#34; { Properties { _MainTex (&#34;Main Texture&#34;, 2D) = &#34;white&#34; {} [NoScaleOffset] _DepthTex(&#34;Depth Texture&#34;, 2D) = &#34;white&#34; {} // add _LowerLimitDepth(&#34;LowerLimitDepth&#34;, float) = 0.6 // add _UpperLimitDepth(&#34;UpperLimitDepth&#34;, float) = 1 // add _MainTexRegion(&#34;Main Texture region&#34;, Vector) = (0, 0, 1, 1) // add _DepthTexRegion(&#34;Depth Texture region&#34;, Vector) = (0, 0, 1, 1) // add } SubShader { // No culling or depth // Cull Off ZWrite Off ZTest Always // remove Tags{ &#34;RenderType&#34; = &#34;Transparent&#34; &#34;Queue&#34; = &#34;Transparent&#34; &#34;ForceNoShadowCasting&#34; = &#34;True&#34; } // add Cull Off // add ZWrite On // add Blend SrcAlpha OneMinusSrcAlpha // add Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include &#34;UnityCG.cginc&#34; struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; }; v2f vert (appdata v) { v2f o; o.vertex = mul(UNITY_MATRIX_MVP, v.vertex); o.uv = v.uv; return o; } sampler2D _MainTex; sampler2D _DepthTex; // add float _LowerLimitDepth; // add float _UpperLimitDepth; // add float4 _MainTexRegion; // add float4 _DepthTexRegion; // add // updated everything fixed4 frag(v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv); if (i.uv.x &lt; _MainTexRegion.x || i.uv.x &gt; _MainTexRegion.x + _MainTexRegion.z || i.uv.y &lt; _MainTexRegion.y || i.uv.y &gt; _MainTexRegion.y + _MainTexRegion.w) { col.a = 0; } else { float2 uv1 = float2((i.uv.x - _MainTexRegion.x)*1/_MainTexRegion.z, (i.uv.y - _MainTexRegion.y)*1/_MainTexRegion.w); float2 uv2 = float2(uv1.x*_DepthTexRegion.z + _DepthTexRegion.x, uv1.y*_DepthTexRegion.w + _DepthTexRegion.y); fixed4 dcol = tex2D(_DepthTex, uv2); if (dcol.r &lt; _LowerLimitDepth || dcol.r &gt; _UpperLimitDepth) { col.a = 0; } } return col; } ENDCG } } }</pre><p>上記で「add」「remove」及び「update」としている部分が、デフォルトのImage Effect Shaderとの差分です。<br /> まずProperties部ですが、デプスのテスクチャー_DepthTex、画素透明化の下限(_LowerLimitDepth)と上限(_UpperLimitDepth)、RGB出力側の有効矩形(_MainTexRegion)、デプス側の有効矩形(_DepthTexRegion)です。具体的には、frag()関数部にて説明をしますが、このようにしておくことで、インスペクタ等から各値が指定可能となります。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20160923/20160923125548.png" alt="f:id:tuti107:20160923125548p:plain" title="f:id:tuti107:20160923125548p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>次にTags部等についてですが、透過表示を可能とするため、このような設定としています。<br /> 変数宣言部については、上記Properties部にて宣言したプロパティに対応する変数を宣言しています。なおテクスチャはsample2D型となります。<br /> フラグメントシェーダー部(frag()関数)では、各座標(i.uv)について、</p> <ul> <li>i.uvがRGB出力側の有効矩形(_MainTexRegion)の範囲外なら透明とする</li> <li>RGB出力側の有効矩形(_MainTexRegion)の範囲内の座標i.uvを、デプス側の有効矩形内の座標uv2に変換</li> <li>uv2のデプスが_LowerLimitDepth未満、又は_UpperLimitDepthより上なら、透明とする</li> <li>それ以外の場合は、i.uvのテクスチャをそのまま表示する</li> </ul><p>との処理を行っています。<br /> なぜこのような面倒なことをやる必要があるのか、ですが、私が現時点で調べた範囲では、残念ながらRGB映像とデプス映像の位置を一致させる方法がありません。このため、RGB映像とデプス映像の座標のずれを考慮の上で、RGB映像内のある座標aに対応するデプス映像a'(0:黒~1:白)が_LowerLimitDepth, _UpperLimitDepthの範囲内なら座標aのRGB映像の色を、範囲外なら透明を出力する、としています。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20160923/20160923152757.png" alt="f:id:tuti107:20160923152757p:plain" title="f:id:tuti107:20160923152757p:plain" class="hatena-fotolife" itemprop="image"></span></p><p>また、このシェーダーにデプス映像のテクスチャを設定する目的で、以下のスクリプトも用意します。</p> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synStatement">using</span> UnityEngine; <span class="synStatement">using</span> System.Collections; [RequireComponent(<span class="synStatement">typeof</span>(DrawImages))] <span class="synType">public</span> <span class="synType">class</span> TransparentController : MonoBehaviour { <span class="synType">public</span> Material rgbMat; <span class="synType">private</span> Material depthMat; <span class="synType">private</span> Texture2D depthTex; <span class="synComment">// Use this for initialization</span> <span class="synType">void</span> Start () { depthMat = GetComponent&lt;Renderer&gt;().material; } <span class="synComment">// Update is called once per frame</span> <span class="synType">void</span> Update () { <span class="synStatement">if</span> (depthTex == <span class="synConstant">null</span>) { <span class="synStatement">if</span> (depthMat.mainTexture != <span class="synConstant">null</span>) { depthTex = (Texture2D)depthMat.mainTexture; rgbMat.SetTexture(<span class="synConstant">&quot;_DepthTex&quot;</span>, depthTex); } } } } </pre><p>このスクリプトは、DrawImages(Imageプレファブに設定されているコンポーネント)にて、RealSense R200から入力したデプス映像をTexture2Dとしてマテリアルに設定した際、このテクスチャを先シェーダー(TransparentEffect)の_DepthTexに設定します。<br /> これにより、TransparentEffectは変数_DepthTexにてデプス映像を利用することが可能となります。</p><p>最後に、GameObject RGB, Depth, MainCameraを以下のとおり設定します。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20160923/20160923135344.png" alt="f:id:tuti107:20160923135344p:plain" title="f:id:tuti107:20160923135344p:plain" class="hatena-fotolife" itemprop="image"></span><br /> まずRGBですが、レイヤを新たに作成したShownとします(理由は後述します)。<br /> 画面いっぱいに映像を表示するため、Position/Rotation/Scaleは以上のとおりとします。<br /> DrawImagesのStream OutはSTREAM_TYPE_COLORとします。RGB映像を出力するためです。<br /> マテリアルは、上記で生成したマテリアルRGBを設定の上、Shaderは作成したTuti/TransparentEffectShaderとします。<br /> 透明化するデプス値の下限(LowerLimitDepth)、及び上限(UpperLimitDepth)はそれぞれ、0.4, 1としました。ここは任意調整してください。<br /> RGB映像の有効矩形については、(X:0, Y:0, Width(表示はZ):0.94, Height(表示はW):1)としました。<br /> デプス映像の有効矩形については、(X:0.0076, Y:0, Width(表示はZ):0.924, Height(表示はW):1)としました。<br /> これら矩形範囲は、RGB映像・デプス映像のスクリーンショットから両映像の内容が一致する矩形となるよう調整してみましたが、もしかするとカメラ個体差等あるかもしれません。任意調整をお願いします。<br /> 今回は発見できませんでしたが、(必ず)RGB映像とデプス映像の位置を一致させるための設定があると信じています。見つけ次第またブログで書きたいと思います。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20160923/20160923140254.png" alt="f:id:tuti107:20160923140254p:plain" title="f:id:tuti107:20160923140254p:plain" class="hatena-fotolife" itemprop="image"></span><br /> Depthについては、こんな感じです。<br /> レイヤは新たに作成したHiddenとします。デプス映像は画面に表示する必要がありませんので、カメラのカリング設定で非表示とするよう、レイヤを分けました。<br /> デバッグ用にRGB映像の上に並べて配置したため、Position/Rotation/Scaleは以上としました。<br /> DrawImagesのStream OutはSTREAM_TYPE_DEPTHとします。デプス映像を出力するためです。<br /> 上記で作成したスクリプトTransparentControllerをコンポーネントとして貼り付けます。RgbMatには、上記RGBマテリアルを設定してください。<br /> マテリアルは、上記で生成したマテリアルDepthを設定します。</p><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20160923/20160923140653.png" alt="f:id:tuti107:20160923140653p:plain" title="f:id:tuti107:20160923140653p:plain" class="hatena-fotolife" itemprop="image"></span><br /> 最後にメインカメラは、こんな感じです。<br /> RGB映像のみ画面表示とするため、Culling Maskは「Shown」とします。<br /> また、ProjectionをOrthographicとします。Z方向の遠近感は不要とためです。</p> </div> <div class="section"> <h3>おわりに</h3> <p>今回は、(買いたてほやほやの)RealScene R200を使用して、背景除去にチャレンジしてみました。<br /> とりあえず背景除去はできるのですが、ノイズがひどかったり、髪の毛のデプス情報がとれないため、髪の毛部分が透明になったりと、改善の余地がおおいです。今後、すこしずつ改善をしていこうと思います。<br /> また、上記で説明のとおり、RGB映像とデプス映像の位置をあわせる設定がわかっておりません。ご存知の方おりましたら、コメント等いただけると幸いです。<br /> 最後に、USB3.0の延長ケーブルの利用には気をつけてください。付属のUSBケーブルは非常に短いため、2mの延長ケーブルを購入、接続を試みたのですが、カメラが認識されず。。ネットで調べてみると、既知の問題のようでした(延長するとカメラへの給電が落ち動かなくなる、ハブも同様)。<s>セルフパワーのハブなら動く、との情報もありましたので、試してみようと思います。</s></p><p>【追記】<br /> <a href="http://www2.elecom.co.jp/products/U3H-T410SWH.html">&#x3053;&#x3061;&#x3089;</a>のセルフパワー型HUBで試したところ、無事動作しました。</p> </div> tuti107 プリレンダリングの360度立体視CGとカメラ入力映像を重畳し、エンコードする hatenablog://entry/10328749687178249880 2016-08-14T13:34:03+09:00 2016-08-14T13:34:03+09:00 前回は、Maker Faire Tokyoに出展したVR撮影システムについて、360度立体視CGのプリレンダリングのやり方について書きました。 今回は、その360度立体視CGと、カメラ(魚眼レンズを付けたiPhone2台)入力画像を重畳し、mp4動画ファイルとしてエンコードする方法について書きたいと思います。【第1回】VR撮影システムのハードウェア構成 【第2回】360度立体視CG映像を作る 【第3回】カメラ入力映像と重畳しエンコードする(今回) 左右眼用のカメラ入力映像をFisheye形式からEquirectangular形式に変換 基本的なやり方は、Theta二台から立体視用Equirec… <p>前回は、Maker Faire Tokyoに出展したVR撮影システムについて、360度立体視CGのプリレンダリングのやり方について書きました。<br /> 今回は、その360度立体視CGと、カメラ(魚眼レンズを付けたiPhone2台)入力画像を重畳し、mp4動画ファイルとしてエンコードする方法について書きたいと思います。</p><p><a href="http://tuti107.hatenablog.com/entry/2016/08/08/103718">&#x3010;&#x7B2C;&#xFF11;&#x56DE;&#x3011;VR&#x64AE;&#x5F71;&#x30B7;&#x30B9;&#x30C6;&#x30E0;&#x306E;&#x30CF;&#x30FC;&#x30C9;&#x30A6;&#x30A7;&#x30A2;&#x69CB;&#x6210;</a><br /> <a href="http://tuti107.hatenablog.com/entry/2016/08/11/114442">&#x3010;&#x7B2C;&#xFF12;&#x56DE;&#x3011;360&#x5EA6;&#x7ACB;&#x4F53;&#x8996;CG&#x6620;&#x50CF;&#x3092;&#x4F5C;&#x308B;</a><br /> 【第3回】カメラ入力映像と重畳しエンコードする(今回)</p> <div class="section"> <h3>左右眼用のカメラ入力映像をFisheye形式からEquirectangular形式に変換</h3> <p>基本的なやり方は、<a href="http://tuti107.hatenablog.com/entry/2016/05/31/071050">Theta&#x4E8C;&#x53F0;&#x304B;&#x3089;&#x7ACB;&#x4F53;&#x8996;&#x7528;Equirectangular&#x6620;&#x50CF;&#x3092;&#x3064;&#x304F;&#x308B;</a>にて書いた内容と同じです。しかし、Theta Shader Packをそのまま利用することはできません。iPhoneからのカメラ入力映像は確かにFisheye形式なのですが、Thetaからの入力映像と異なり、左右が欠けてしまっています。<br /> 以下の図はMaker Faire Bay Area時のカメラ入力映像の取り込み・変換の仕組みです。左右眼用のThetaからの入力映像のうち、前方のFisheye映像をTheta Shader Packを利用してEquirectanguler形式に変換する、という方法をとっていました。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20160810/20160810144242.png" alt="f:id:tuti107:20160810144242p:plain" title="f:id:tuti107:20160810144242p:plain" class="hatena-fotolife" itemprop="image"></span><br /> 今回のMaker Faire Tokyoでのカメラ入力映像の取り込み・変換の仕組みは以下のとおりです。<br /> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20160810/20160810174152.png" alt="f:id:tuti107:20160810174152p:plain" title="f:id:tuti107:20160810174152p:plain" class="hatena-fotolife" itemprop="image"></span><br /> iPhoneには魚眼レンズが取り付けられているため、カメラ入力映像はFisheyeのように歪んだ状態となっています。ただiPhone縦持ちで撮影する際、縦方向と比較して横方向の画角は狭くなってしまいます。iPhoneSE + Gizcamの場合、図の緑の破線(画角180度を示す)がカメラ入力映像からはみ出してしまいます。つまり、iPhoneからのカメラ入力映像の中にはThetaのように完全なFisheye形式の画像は含まれません。iPhoneからの入力画像を、上記Theta Shader Packの要領でEquirectanguler形式に変換すると図のように、ひょうたん(?)のような形になります。このため、大半はTheta Shader Packの処理のままで良いのですが、</p> <ul> <li>はみ出ている部分は透明にする</li> <li>Fisheye部の径を適切な値とする</li> </ul><p>2点の修正が必要となります。</p> </div> <div class="section"> <h3>フルHDサイズのオフスクリーンテクスチャを60fpsでエンコードする</h3> <p>前回のMaker Faire Bay Areaでは、ffmpegでリアルタイムエンコードするスクリプトを生成し、それを使用しました。しかし、その方法では12-15fpsが限界であり、せっかく搭載されている GeForce970の力を生かしたハイパフォーマンスなエンコードをできるようにしたいと思っていました。<br /> そこで見つけたのが、<a href="https://www.assetstore.unity3d.com/jp/#!/content/56254">GPU Video Encoder</a>です。$100とかなり高価なアセットですが、複数のFull HDサイズのRenderテクスチャを同時に60fpsでエンコードできるスグレモノです。<br /> 本アセットの利用手順は次の通りです。<br /> ※本アセットは、Windows x86_64 DX11環境かつ、NVIDIAのHWエンコーダ(nvenc)が利用できる環境でのみ動作します。それ以外の環境では使用できませんのでご注意を(高価ですし)</p> <ul> <li><a href="https://www.assetstore.unity3d.com/en/#!/content/3586">Spicy Pixel Concurrency Kit</a>をダウンロード・インポートする(本アセットと依存関係にあります)</li> <li>1920x1080のRender Textureを左右眼用作成する(それぞれの名前を"LeftTex", "RightTex"とする。Render Textureは、Projectにて右クリック→Create→Render Textureで作成、インスペクタのSizeにて1920x1080を設定できる)</li> <li>それぞれのRender Textureに描画するCameraについて、Clear FlagsをSolid Color、Backgroundを緑(R=0, G=255, B=0)とする。のちほどffmpegでクロマキー処理をする際に「抜く色」として緑を使用するため</li> <li>GameObjectを二つ生成する(ぞれぞれの名前を"Left", "Right"とする)。それぞれにMovie Record Cameraスクリプトを追加し、それぞれインスペクタより、Textureにはぞれぞれ上記で作成したRender Texture(LeftTex, RightTex)、Output File Path/Output File Titleにはそれぞれ生成するファイルのパスと名称を設定する</li> </ul><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/t/tuti107/20160810/20160810182039.png" alt="f:id:tuti107:20160810182039p:plain" title="f:id:tuti107:20160810182039p:plain" class="hatena-fotolife" itemprop="image"></span><br /> あとは、エンコードを開始したいタイミングで、MovieRecordCamera#StartMovieRecord()を、終了したいタイミングでMovieRecordCamera#EndMovieRecord()を呼ぶだけです。スクリプトとしては以下のような感じで、上記生成したGameObject left, rightのMovieRecordCameraコンポーネントをインスペクタで下記leftCam, rightCamに設定しておき、開始・終了のタイミング(以下の例ではRキー押下)でそれぞれのメソッドを呼び出します。</p> <pre class="code lang-cs" data-lang="cs" data-unlink><span class="synStatement">using</span> UnityEngine; <span class="synStatement">using</span> System.Collections; <span class="synStatement">using</span> GPUVideoEncoder; <span class="synType">public</span> <span class="synType">class</span> CameraController : MonoBehaviour { <span class="synType">public</span> MovieRecordCamera leftCam; <span class="synType">public</span> MovieRecordCamera rightCam; <span class="synType">private</span> <span class="synType">bool</span> isRecording; <span class="synComment">// Use this for initialization</span> <span class="synType">void</span> Start () { isRecording = <span class="synConstant">false</span>; } <span class="synComment">// Update is called once per frame</span> <span class="synType">void</span> Update () { <span class="synStatement">if</span> (Input.GetKeyUp (KeyCode.R)) { isRecording = !isRecording; <span class="synStatement">if</span> (isRecording) { leftCam.StartMovieRecord (); rightCam.StartMovieRecord (); } <span class="synStatement">else</span> { leftCam.EndMovieRecord (); rightCam.EndMovieRecord (); } } } } </pre><p>すると、左右それぞれ用の.h264ファイルが、それぞれ指定したパス・ファイル名にて生成されます。このファイルをffmpegにてmp4に変換すれば、完成です。</p> <pre class="code" data-lang="" data-unlink>ffmpeg.exe -i movieLeft.h264 -vcodec copy movieLeft.mp4</pre> </div> <div class="section"> <h3>左目用・右目用の撮影映像を縦に並べる</h3> <p>次に上記生成した、左目用・右目用のmp4ファイルを縦に並べたmp4ファイルを生成します。以下のコマンド一発で変換可能です。</p> <pre class="code" data-lang="" data-unlink>ffmpeg.exe -i movieLeft.mp4 -i movieRight.mp4 -vcodec nvenc -filter_complex vstack -b:v 8M movie.mp4</pre> </div> <div class="section"> <h3>プリレンダリングの360度立体視CGとカメラ入力映像を重畳し、エンコードする</h3> <p>最後に、上記縦に並べた左右眼用の撮影映像と、前回生成したプリレンダリングの360度立体視CGを重畳します。これもffmpegのコマンド一発でオッケーです。</p> <pre class="code" data-lang="" data-unlink>ffmpeg.exe -i movie.mp4 -f lavfi -t 10 -i \&#34;movie = filename = a.mp4:loop = 0, setpts = N / (FRAME_RATE * TB)\&#34; -filter_complex [0:0]colorkey=0x00ff00:0.5:.2[a1];[1:0][a1]overlay out.mp4</pre> <ul> <li>"t"オプションには作成する映像の時間(秒)を設定します</li> <li>"movie=..."部分は重畳する360度立体視CG映像についての設定です。"loop = 0, setpts = N / (FRAME_RATE * TB)"とすることで、360度立体視CG映像の再生時間が-tで指定した時間より短い場合、これをループ再生します</li> <li>"filter_complex"オプションには、クロマキーと映像重畳を設定します。colorkeyに緑色(0x00ff00)を指定することで緑色が透過します。なお":"で句切られた他の2つの数値は、クロマキーする範囲について指定しています。この2つの数字は試行錯誤で設定したものであり、数値とクロマキーの度合いの関係はいまいちわかっておりません。</li> </ul><p>これで、プリレンダリングの360度立体視CGとカメラ入力映像を重畳した動画ファイルが完成です!</p> </div> <div class="section"> <h3>終わりに</h3> <p>3回に渡り、Maker Faire Tokyoに出展した「スマホ2台を利用したVR映像作成システム」について書きました。記載量が莫大になってしまうため、実装詳細については触れませんでしたが、ご興味おありの方はその旨いろいろご質問いただけると嬉しいです。</p> </div> tuti107