Tutti Lab

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

VR広告SDK「Immersv」を組み込んでみました

モバイルVRアプリ開発において「マネタイズ」は大きな課題です。一般的にモバイル向けアプリ(特に個人開発者)のマネタイズ手段は広告であり、各種提供されている広告SDKをアプリに組み込むことで、アプリ内で広告を表示し、実績に応じて広告収入を得る、というものです。
しかし、これらモバイル向けアプリの広告SDKは、VR空間上に貼り付けて使用するような使用形態が想定されていないため、モバイルVRアプリにそのまま適用することはできません。
今回は、モバイルVRに特化された広告SDKである「Immersv」を、開発中のアプリに組み込み、動作確認をしてみました。
f:id:tuti107:20170307200728p:plain

SDKの請求

まずはこちら(注:2017年3月7日現在、本サイトに接続できなくなっております。一時的なものかと思いますが。。)にアクセスし、SDKを請求します。フォームに必要情報を入力・送信すると、数日で先方から応答のメールがあります。
その後、私のプロジェクト(今、Gear VR、Daydream向けにモバイルVRアプリを開発しています)の説明をし、何度かのインタラクションを経て、SDKを入手することができました。なお、Immersvは米国の会社であり、全編英語を覚悟したのですが、対応いただいた日本法人の担当の方は日本語が堪能で、問題なくコミュニケーションをとることができました。

SDKのダウンロード、組み込み

SDKは、Gear VR、Daydream/Cardbord用に、これらOculus SDK/Google VR SDKやUnityのバージョン毎に用意されており、自分の開発環境に合わせて取得・インポートします。
私は、Daydream向けビルドを考慮し、Unity Daydream Preview 5.4.2f2-GVR12で、かつ(まずは)Gear VR向けに開発をしているので、本条件に相当する「ImmersvSDK-1.31-GearVR-1.0.3.unitypackage」を使用しました。
f:id:tuti107:20170223073524p:plain

実装

Immersv SDKは非常にシンプルです。基本、SDKに同梱のPDFドキュメントに記載されているサンプルコードをそのまま使えば、ほぼ問題ありません。一応、処理の流れを記しておくと、

  • ImmersvSDK.Init(APPLICATION_ID)を呼び出す。APPLICATION_IDはSDK利用許可時に通知される。
  • ImmersvSDK.OnInitSucces()コールバック(初期化成功)にて、ImmersvSDK.Ads.LoadAd(PLACEMENT_ID)を呼び出し、広告を読み込む。なお、PLACEMENT_IDはSDK利用許可時に通知される。
  • ImmersvSDK.Ads.OnAdReady()コールバック(広告準備完了)を受け取った後、なんらかのユーザ操作(「広告を見る」ボタン押下など)を受け付けた後、ImmersvSDK.Ads.StartAd()を呼び出して、広告を再生する
  • ImmersvSDK.Ads.OnAdComplete(result)コールバック(広告再生完了)にて、ユーザが広告を最後まで見る等の条件を満たした場合(result.BillableCriteriaMetがtrue)、アプリ内仮想通貨を与える等の、ユーザに対する報酬処理を行う

という形になります。

なお、以下の公式ビデオ(英語)にて、SDK請求から実装までの詳細が公開されています。
www.youtube.com

ちなみに私の開発中アプリにSDKを組み込んだものは、このような感じです。ビデオ画質が悪いため細かな部分、特に広告閲覧後の報酬の様子がわかりづらいですが、一通り動作しています。
www.youtube.com
チケット券売機の右中央辺り(ビデオでは解像度が低くてよく見えませんが、いわゆる「広告を見てコインをゲット」を表示しています)をGAZEすることで、Immersvの広告が始まります。
広告は、2D広告を映画館風に再生するものや、360度動画等様々であり、充分楽しめる(?)内容です。
広告が終わると、コインが手に入ります(こちらも見えづらい・・)

GearVRでは、私がいつも利用しているキャプチャーアプリLollipop screen recorderが動作せず、MirrorOP for Galaxy+OBSを使用しました。ただこれだと録音ができず、解像度が低く、微妙な感じです。。

getterのケアレスミスでUnityクラッシュ

以下のコードですが、

private int _curNum;
public int curNum {
  get {
    return curNum;
  }
  set {
    _curNum = value;
  }
}

正しくは、「return _curNum;」としなければなりません。
なお、上記の間違いコードを実行すると、Unityがクラッシュします。
f:id:tuti107:20170306192256p:plain
スタックがえらいことになっています。循環参照をしまくったためでしょうね。
この不具合の検証をさらに困難にさせるのが、このgetterにアクセスしなくても、例えば本ゲッターを持つクラスの別の変数にアクセスしても落ちます(ただ、落ちない変数もある・・謎・・)。まさかgetterのミスとは気づかず、小一時間解析に要してしまいました。

Quadで円形プログレス

uGUIのImageは、fillAmountを使って簡単に円形のプログレスができるのですが、SpriteやQuadにはfillAmountに相当する機能がないようです。
こちらこちらに、Quadを使った円形プログレスの実現方法があるのですが、予め0→360度でα値を1→0とした画像を用意する必要があるようで、面倒だし汎用性がないな、と思いました。
そこで、角度(0~360度)を指定して円形プログレスをするための簡単なシェーダーコードを書いてみました。

f:id:tuti107:20170204193817p:plain

Shader "Tuti/Reticle"
{
  Properties
  {
    [NoScaleOffset] _MainTex("Texture", 2D) = "white" {}
    _Degree("Degree", Float) = 360.0
  }

  SubShader
  {
    Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" }

    ZWrite On
    Blend SrcAlpha OneMinusSrcAlpha

    Pass
    {
      CGPROGRAM
      #pragma vertex vert
      #pragma fragment frag

      #include "UnityCG.cginc"

      #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 >= 0) {
          if (deg > _Degree) {
            col.a = 0.0;
          }
        }
        else {
          if (deg + 360.0 > _Degree) {
            col.a = 0.0;
          }
        }

        return col;
      }
      ENDCG
    }
  }
}

時計の12時の位置から時計回りにする場合は、z軸90度回転+Y軸で反転してください。
ちゃんとテストはやっていませんが、とりあえず動いているようです。

Oculus Touchをつかってみる

Oculus Touchが出荷され、約一月が経ちました。Oculus StoreにはOculus Touch対応コンテンツが多数並び、Oculus Touchの凄さを体験できるわけですが、一方で開発向きとなるとまだ情報が足りない状況のようです。
今回は、Oculus Touchの練習用に作成したコンテンツを取り上げ、開発のポイントを挙げていきたいと思います。
youtu.be
コンテンツは、今年大流行したアレを、Oculus Touchで体験する、というものです。

情報収取

Oculus Touchの技術情報については、以下が必読です。

準備

Oculus Touchにて「手やアバターを表示し、なにかをつかむコンテンツを作る」ためには、以下のインストールが必要です(詳細は上記情報収集をご参照)

  • Oculus Utilities for Unity 5(こちらからダウンロード可能)
  • Oculus Avatar SDK(同じくこちらからダウンロード可能)
  • Avater Grab Sample(こちらからダウンロード可能)、なおUnity 5.5でのみ動作(Unity 5.4ではクラッシュ)とのこと
  • その後適当なプロジェクトを作成し、上記ダウンロードしたパッケージ(OculusUtilities.unitypackage, OvrAvatar.unityPackage, AvatarGrabSample.unitypackage)をそれぞれインポートします

サンプル実行

Assets/Samples/Content/AvaterWithGrab をダブルクリックして、シーンをロードします。あとは、PlayerSettings→Other SettingsのVirtual Reality Supportedをチェックし、実行してみます。
f:id:tuti107:20161231102951p:plain
つかむ・離す・投げる、一通りのことができます。

つかめるものを追加

まずは、サンプルで「つかめるもの」がどのようになっているかを見てみます。
f:id:tuti107:20161231103341p:plain
ポイントは、Grabbableというスクリプトです。これはAvater Grab Sampleに含まれるスクリプトで、これを追加したオブジェクト(かつ、Collider, Rigidbodyも追加する必要あり)がつかめるようになります。
つかんだ時、離した時の振る舞いは、Grabbable#GrabBegin()、Grabbable#GrabEnd()メソッドにそれぞれ記述します。これらメソッドはvirtualメソッドですので、Grabbableクラスの子クラスを作ってこれらメソッドをオーバーライドすれば、任意のつかむ・離す際の処理を作成することができます。

Oculus Touchのバイブレーション

OVRHapticsClipを使用します。AudioClipを引数としてOVRHapticsClipを作成することで、そのAudioClipの音量に基づいた振動を作成できます。
作成したOVRHapticsClipを引数としてOVRHaptics.RightChannel.Mix()(=右側コントローラ、左側の場合はOVRHaptics.LeftChannel.Mix())を呼び出すことで、Oculus Touchが振動します。なお、振動させるためのメソッドにはMix()以外に幾つかあり、それぞれ挙動が異なります(詳細は情報収集で挙げたサイトを御覧ください)。
以下は、スタティックメソッドSE.PlaySE()を呼び出すと、効果音鳴動+振動するサンプルです。

using UnityEngine;

public class SE : MonoBehaviour {

  public AudioSource audioSource;
  public OVRHapticsClip hapticClip;

  private static SE thisObj;

  void Start () {
    thisObj = this;
    hapticClip = new OVRHapticsClip(audioSource.clip);
  }
	
  public static void PlaySE() {
    if (thisObj != null) {
      thisObj.audioSource.Play();
      OVRHaptics.RightChannel.Mix(thisObj.hapticClip);
      OVRHaptics.LeftChannel.Mix(thisObj.hapticClip);
    }
  }
}

No Oculus Rift App ID has been provided

エディタで実行すると、問題なく実行できますが、上記のエラーがでます。Oculusサイトにて、アプリを登録してIDを取得することで、本エラーを回避できるようです(私がつくったコンテンツをエディタ上で実行する上では特に問題がなかったため、試していません)。詳細は、上記「情報収集」で挙げたサイトを御覧ください。

PP◯Pっぽい素材

終わりに

Oculus Touchは本当にすごいです。CG空間に触る、というユーザ体験は先行していたHTC Viveをも超えるものでは、と個人的には思います。

VR Trip around the world

私が開発したVRゲーム「VR Trip around the world」が、幸運にもVRゲームコンテストMSI VR JAMのファイナリストに残りました。

プレーヤーは、指定された場所で写真を撮影すべく広大なVRの世界を飛び回ります。VR世界の移動、となると最も懸念の課題が「VR酔い」です。本作では、遠くへ移動する際「巨人」になって世界を見渡し、次に行く場所を探します。「巨人」になって世界を見渡し、怪しそうな場所を探して、「元の姿」に戻り、細かな場所を探索する、という新たなVRにおける移動手段についても提案しています。
www.youtube.com

本アプリはOculus CV1専用であり、こちらから無料でダウンロードできます。
ぜひプレイいただき、気に入って頂けた方は、こちらより投票を頂けると幸いです(2016年12月28日迄)。

Vuforia for Unityを使って(擬似)ハンドトラッキング

前回、Viforia for Unityの機能を活用し、Google Cardboardでポジショントラッキングを実現する方法について記載しました。同様に、Vuforia for Unityを使うことで(擬似)ハンドトラッキングも可能です。
Vuforiaでは直接「手」を認識することはできません。しかし、手にマーカーを貼り付ける、マーカーを貼り付けたふだを持つなど、マーカ認識を通じて手の位置を認識する、ということは比較的容易に実現できます。
先日とあるVRイベント向けに開発した「Santa Claus Job Simulator」というサンタのお仕事を体験するアプリにて、この擬似ハンドトラッキングを実装してみましたので、本実装技術についてご紹介したいと思います。
www.youtube.com

ARCameraの設定

Google CardboardにてVuforia for Unityを利用した(マーカ認識による)ハンドトラッキングをする際のポイントは「VRのヘッドトラッキングを考慮した上で、Vuforiaで認識した「手」を「手の位置」に表示すること」です。
Vuforia for Unityはデフォルトでは「はじめに発見した認識対象の座標を固定し、認識対象との位置関係に応じてCameraの位置を変える」という設定になっています。VuforiaがImage Targetに指定された認識対象を発見した際、Image Targetの座標・回転が変化するのではなく、ARCamera(Main Cameraを子として含む)の座標・回転を変化させることで、相対的にImage Targetが動いているように見せます。
VRでなければ、特段問題は生じませんが、VR(Player SettingsにてVirtual Reality SupportedがON)の場合、ヘッドトラッキングによりデバイスの回転によりMain Cameraを回転させます。このため、上記のデフォルト設定では、ヘッドトラッキングの回転設定とVuforiaの回転設定が競合してしまい、ちょっと都合が悪いです。

そこで、

  • ARCameraのVuforia BehaviorコンポーネントのWorld Center Modeをデフォルトの「FIRST TARGET」から「CAMERA」に変更します。これで、認識対象物が発見された際の固定される座標はMain Cameraとなり、代わりにImage Targetの座標が変わります。
  • また、もう一点、Image TargetはMain Cameraの子とします。これは上記の通り、認識対象の位置はMain Cameraが固定であることを前提としていますから、ヘッドトラッキングによるMain Cameraの回転変化に関わらず、Image TargetからはMain Cemeraが固定に見えるようにするためです。

f:id:tuti107:20161221234833p:plain

また、Image Targetと「手」の位置関係にも工夫が必要です。Santa Claus Job Simulatorでは、マーカ付きの看板を手に持ってもらい、そのマーカを認識することで手を表示するようにしています。
f:id:tuti107:20161221233051p:plain

このためImage Target認識時に表示される手の位置・回転は、看板に貼り付けられたマーカからの相対位置・回転を設定する必要があります。
f:id:tuti107:20161221233148p:plain

高速化が不可欠

Vuforiaをハンドトラッキングに活用する上でもう一つ重要な点は、可能な限り処理の高速化を図ることです。
VR表示しながら、同時にマーカ認識するという処理は、モバイル端末にとって非常にヘビーなものとなります。
このため、Edit->Project Settings->Qualityにて処理をFastestとして描画・表示負荷を下げる等、できるだけ処理を軽くし、Vuforiaの認識処理にリソースを回してあげる必要があります。
描画負荷が高い場合等、Vuforiaの認識処理のための余裕がない場合、認識の品質は明らかに低下し、すぐに認識対象を見失ってしまいます。こうなると心地よりハンドトラッキングとは程遠くなってしまいます。

最後に

今回は、Google Cardboard向けに(擬似)ハンドトラッキング処理を実装した当方が開発したゲームを例に、Vuforiaによる(擬似)ハンドトラッキングを実装する方法について説明をしました。
モバイル向けにはVuforiaを活用したハンドトラッキング・ポジショントラッキングをどんどん活用したい、と思っていますが、最新のモバイルVRヘッドセットであるDaydream ViewではVuforiaが使えません。Google Cardboardのようにスマホカメラの位置に穴を開けるわけにはいきませんので。。
何らか他の方法を検討する必要があるな、と考えております。

UNIBOOK7で、ThetaSによる360度立体視映像配信について書かせていただきました

知人に紹介いただいたことがきっかけで、UNIBOOK7の「第8章 ThetaSを使った360度立体視映像配信」を書かせていただきました。
f:id:tuti107:20161221221355j:plain
半年ほど前、ThetaSを2台使った360度立体視の撮影・配信についてブログにかきましたが、その当時は、私の知識不足&当時作成していたアプリの要求仕様に合わない、ということから中途半端となっていた、撮影と配信、そして配信映像を視聴するためのモバイルアプリの作り方について記載しております。
本書には、その他多数の著名クリエイターが執筆した読み応えのある内容満載です。購入いただけると幸いです!

www.unity-bu.com