Tutti Lab

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

【Unity開発Tips】InputFieldクリック時にバーチャルキーボードを表示【モバイル未解決】

前回少し触れましたが、VR向けバーチャルキーボードを開発しています。
入力したいInputFieldの方を向いてクリック(Cardboard右上のボタンを押下)すると、バーチャルキーボードが現れ、任意の文字を入力、最後にキーボード右下の「キーボードを隠す」ボタンをクリックして、入力終了、という具合に利用するものです。
f:id:tuti107:20160625095117p:plainf:id:tuti107:20160625095127p:plainf:id:tuti107:20160625095139p:plain
バーチャルキーボードの表示については、InputFieldを継承したクラス(VRKeyInputField)にて、OnSelect()、及びOnDeselect()メソッドをそれぞれオーバーライドし、

  • InputFieldクリック時に、オーバーライドしたOnSelect()メソッドにて、バーチャルキーボードを表示
  • OnDeselect()メソッドをオーバーライドして「何もしないようにする」→そうしないと、InputFieldからGaze(赤丸)が外れると、InputFieldからフォーカスが外れてしまう
  • 「キーボードを隠す」ボタンがクリックされたら、InputFieldのOnDeselect()を呼び出して、フォーカスを外す

としています。以上でEditor上では、上記写真のようにうまく動作します。

iOS/Androidではうまく動かない

ただ、これをiOS/Androidで動かすと、以下のようにOS固有のタッチスクリーンキーボードが表示されてしまいます。
f:id:tuti107:20160625100159j:plain
これをどうやって表示しないようにするか、タッチスクリーンキーボード表示後に「TouchScreenKeyboard#active = false」する等、色々トライしましたが、うまくいかず、ネット上の先人の知恵を探すも、解決したという情報を見つけられませんでした。
とあるサイトで、InputField、TouchScreenKeyboardのソース(と思われるもの)を発見したのですが、本ソースコードを見ると、

  • InputFieldは、InputFieldが入力可能状態となった際(クリックされる等)、TouchScreenKeyboard.isSupportedがtrueなら、TouchScreenKeyboard.Open()を呼び出して、OS固有のタッチスクリーンキーボードを表示
  • TouchScreenKeyboard.isSupportedは、RuntimePlatform(= Application.platform)が、RuntimePlatform.IPhonePlayer又はRuntimePlatform.Androidなら、trueとなる

となっている模様。設定などでタッチスクリーンキーボードを表示しない様にはできない感じです。
仕方がないので、MyInputFieldを開発するか、という状況です。。

本件、解決の方法をご存知の方おりましたら、ぜひ教えていただければ、と思います。

【Unity開発Tips】Unityライブラリに含まれるクラスのエディタ拡張

Unityを使って開発をしていると、例えばuGUIのGUI部品(ボタンなど)に独自拡張を加えたい、という場合が出てきます。この拡張が、例えば見た目等、Inspectorの内容を変更するものだったり、子オブジェクトを追加したり、ということで対応可能であれば、インスペクタ・Heirarchyでの情報更新・追加で済みます。しかし、イベント発生時の挙動を変えたい(ボタンからフォーカスが外れた時に何らかの処理をしたい、等)場合は、本GUI部品を制御するスクリプトを継承したスクリプトを作る必要が出てきます。

using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.EventSystems;

public class VRKeyInputField : InputField {

	[System.Serializable]
	public class KeyboardTranform {
		public Vector3 keyboardPosition = new Vector3 (0, -1.5f, 0);
		public float keyboardScale = 2;
	}
	public KeyboardTranform keyboardTransform = new KeyboardTranform();

	public override void OnSelect(BaseEventData eventData) {
		base.OnSelect (eventData);
		Board.NotifyActiveInputField (this);
	}
}

現在、以前にご紹介したバーチャルキーボードのパワーアップ版を開発中です。本バージョンでは、バーチャルキーボードでの入力をInputFieldに反映させるようにしています。このバーチャルキーボードを、InputFieldがクリックされた際に表示するようにするために、uGUIのInputFieldクラスを拡張したもの(の抜粋)が、上記のスクリプトです。
f:id:tuti107:20160618051627p:plain
本スクリプトは、InputFieldが選択された際に呼ばれるOnSelect()メソッドをオーバーライドして、InputFieldが選択された際にバーチャルキーボード表示の要求を行っています。また、この表示の要求が行われた際、keyboardTransformに設定された内容に従い、バーチャルキーボードの表示位置や大きさを調整しています。

publicフィールドがInspectorに表示されない

上記KeyboardTransformはSystem.Serializableであり、またkeyboardTransformはpublicフィールドのため、インスペクタに表示されることを期待しますが、これだとkeyboardTransformは表示されません。InputFieldのインスペクタ表示はエディタ拡張によって実現されており、本エディタ拡張を行っているスクリプトInputFieldEditorが、keyboardTransformを表示対象としていないためです(当たり前ですが)。
keyboardTransformをインスペクタ表示させるためには、InputFieldEditorを継承したエディタ拡張を作成する必要があります。

InputFieldEditorのエディタ拡張スクリプト

using UnityEngine;
using UnityEditor;
using System.Collections;

[CustomEditor(typeof(VRKeyInputField))]
public class VRKeyInputFieldInspector : UnityEditor.UI.InputFieldEditor {

	public override void OnInspectorGUI() {

		VRKeyInputField f = (VRKeyInputField)target;
		EditorGUILayout.LabelField ("Virtual Keyboard transform:");

		EditorGUI.BeginChangeCheck ();

		EditorGUI.indentLevel++;

		f.keyboardTransform.keyboardPosition = EditorGUILayout.Vector3Field ("LocalPosition", f.keyboardTransform.keyboardPosition);

		EditorGUILayout.BeginHorizontal ();
		EditorGUILayout.PrefixLabel ("Scale");
		f.keyboardTransform.keyboardScale = EditorGUILayout.FloatField (f.keyboardTransform.keyboardScale);
		EditorGUILayout.EndHorizontal ();

		EditorGUI.indentLevel--;

		EditorGUILayout.Separator ();

		if (EditorGUI.EndChangeCheck ()) {
			EditorUtility.SetDirty (target);
		}

		base.OnInspectorGUI ();
	}
}

上記スクリプトを、ProjectのEditorフォルダ内に配置することで、keyboardTransformがインスペクタに表示されるようになります。
エディタ拡張の詳細については、たくさんの書籍やサイトがあるためそちらを参照頂ければと思いますが、ざっくりと説明すると、

  • [CustomEditor(typeof(***))]にて、指定されたクラス***のインスペクタの表示を拡張する
  • OnInspectorGUI()メソッドにて、インスペクタに具体的にどのように表示されるかを記述する

という感じです。
上記のスクリプトにより、VRKeyInputFieldのインスペクタ表示は下記の通りとなります。
f:id:tuti107:20160618052902p:plain
Interactable以下は、もともとのInputFieldに関するものです。Virtual Keyboard transform:の項目が追加され、keyboardTransformの内容の表示と変更ができるようになっています。例えばscaleを5とすると、上記(デフォルトは2)と比較して、キーボードが大きく表示されております。
f:id:tuti107:20160618053407p:plain

最後に

バーチャルキーボードを開発する上で、ハマってしまったUnityライブラリに含まれるクラスを継承したクラスを作成した際の、インスペクタ表示の問題について、書いてみました。以外と触れられている記事がなく、結構はまってしまったので、備忘録として残しておこうと思いました。

AWE2016で見つけたクールな技術

先日AWE2016というARのカンファレンス・展示会へ行ってきました。そこで見つけたSEEBRIGHT RIPPLEという端末が非常に面白かったので、紹介したいと思います。
f:id:tuti107:20160618031358p:plain
この端末、スマートフォンを差し込んで使うタイプのAR端末です。この端末、非常に安価($39.99)であり、各ニュース記事では、Google CardboardのAR版として取り上げられているようです。
専用のコンテンツアプリをスマホ上で起動し、本端末にスマホをセットして、端末下部分(アクリル?)を覗き込むと、立体視CG映像が目の前に現れます。私が体験したデモは、コースターの上に描かれた模様を認識し、その模様の上に太陽系が張り付いたように見える、というものでした。フレームレートが低くレイテンシーがかなりあったため、若干微妙な感じではありましたが(これはコンテンツの作りの問題かと思いますが)、それでも高々$40の端末で出来る体験としては充分と感じました。
詳しくは下記の動画をご参照ください。
arnews.tv

視野角は52度とのこと、最近の流行りのVR端末群と比べると見劣りしますが、デモで見た感じでは、それほど視野角の狭さは気になりませんでした。
こちらのサイトから既に購入可能(6月中に発送)のようです。が、私はBilling Addressの入力から先に進むことができませんでした。メールで問い合わせたものの、今の所回答は無しです。Checkout画面に描かれている電話番号や問い合わせのe-mailアドレスもなんだかおかしな感じなので、もしかするとまだこの購入用サイト、機能していないのかもしれません。
うまく購入できましたら、本端末でのARアプリ開発にもチャレンジしてみようと思っています。
(【6/21追記】本日再挑戦したところ、問題なく注文処理を行う事が出来ました。やはり購入サイトが機能していなかったようです)。

Cardboardで360度立体視動画ビューワーを作る(4)

はじめに

過去3回に渡り、メーカーフェアー出展向けということで、360度立体視動画ビューワーを開発しました。前回は、Theta2台で撮影した360度映像をOculus CV1で再生する、というものを開発しました。これにより、Thetaから取り込んだ右目・左目用の360度動画をそれぞれ右目・左目にリアルタイム表示できるため「まるで自分がThetaの設置場所にいるような」感覚を作り出せます。
ただし、メーカーフェアー向けの出展物として考えると、Oculus1台だと見に来られたお客様の一部にしかVR体験をしてもらえませんし、また当初考えていた「Thetaをミニスタジアム内で動かす」ことにより作られた映像は思ったよりオモシロイものではありませんでした。
このような背景から出展物のコンセプトであった「Theta2台で撮影した映像をリアルタイムで閲覧」の「リアルタイム」をあきらめ、その代わりにVR(ある種ARっぽいですが)の特徴を活かし、CGとTheta撮影映像を合成して、自分だけのオモシロVR映像を作成し、その場で見れる・家族や友達に見せびらかせる、というものにしました。結果としてこのコンセプト変更はとてもうまく行き、メーカーフェアー期間中は、たくさんの子どもたちが当ブースでオモシロVRムービーを撮影、閲覧して楽しんでいってくれました。
以下では、本コンセプト変更により開発した「Theta映像とCGを重畳したムービークリップの作成と録画」について書いてみようと思います。
f:id:tuti107:20160530024145j:plain

Theta二台から立体視用Equirectangular映像をつくる

前回ご紹介したTheta Shader Packを利用して、上下にEquirectangular形式のTheta映像を並べてみます。なお、後ろ側については、視差が逆転してしまう、展示においても撮影者(=私)が映っているだけで価値が低いとの理由から、全面180度分のみを利用します。
f:id:tuti107:20160530030235p:plain
まずはCameraの設定ですが、以下のとおりとします。
f:id:tuti107:20160530030354p:plain
ポイントはProjectionをOrthographicとしている点です。こうすることで、Z座標を大きくしても表示の前後関係には影響しますが、遠近法で小さく表示されることはありません。今回は2つのQuad縦に並べて表示するだけであり、表示物に遠近感をつける必要がないため、このような設定としています。
左右Theta映像を映すQuadについては、以下のとおりです。表示位置とカメラ映像のインデックス(CamIndex)以外は同じ内容です。
f:id:tuti107:20160530030841p:plain

f:id:tuti107:20160530030848p:plain
ここまでは、前回とほぼ同様(Theta映像の貼り付け対象がSphere→Quad)です。なお、上記の通り360度全体ではなく、全面180度のみの利用にしておりますので、MaterialはThetaEquiRectangular(Forward)に変更しました。

CGと重畳する

次は、上記Theta映像の透過処理と、背景CGの作成です。まず、Theta映像の透過については、上記Materialに設定されているシェーダー用コード「ThetaRealtimeEquirectangular.shader」を一部修正し、緑色は透過とするようにします(いわゆるグリーンバックのクロマキー処理です)。なお、本ソースコードについては私の著作物ではありませんので、改修をした部分のみ、下記に記載します。

	SubShader
	{
		//Tags{ "RenderType" = "Overlay" "Queue" = "Overlay" "ForceNoShadowCasting" = "True" }
		Tags{ "RenderType" = "Transparent" "Queue" = "Transparent" "ForceNoShadowCasting" = "True" }

		//ZTest Always
		Cull Off
		//ZWrite Off
		ZWrite On
		Blend SrcAlpha OneMinusSrcAlpha

まずは、描画順序の設定です。元々(コメント部)を非コメント部の内容に変更することで、背面にあるオブジェクトとTheta映像を映すQuadが重畳表示されます。詳細についてはシェーダ用コードの解説サイトをご参照いただければと思いますが、ざっくり、上記とすることでQuadの背面にあるオブジェクトも描画され、さらにQuadと重ねあわせた(透過部には背面のオブジェクトが表示)形式になります。
次に、frag(v2f i)関数の末尾に以下のクロマキー処理を追加します。

	half maxCol = col.r > col.g ? col.r : col.g > col.b ? col.g : col.b;
	half rateR = col.r / maxCol;
	half rateG = col.g / maxCol;
	half rateB = col.b / maxCol;
	if (rateG >= 0.9 && rateR < 0.8) {
	  col.a = 0.0;
	}

ピクセルの色が緑の場合は、アルファを0にして非表示とする、それだけの処理です。緑の判定は若干意味不明かもしれませんが、環境光等を考慮していろいろ試行錯誤した結果、このような条件となりました(最適、とは思っていません。。)。
次に背景CGについてです。背景CGについては「普通に」Unityシーン内にオブジェクトを設置し、Skyboxを設定して作成するのですが、最後にこのシーンを360度EquiRectangular形式に展開する必要があります。今回はDomeMaster Texture Generator for Unity利用しました。これを利用することで、シーン内に配置したオブジェクト群からFisheyeまたはEquirectangular映像を生成することができます。まずは上記をダウンロードし、DomeMaster.csと、Equirectangular.shaderをプロジェクトに取り込んでください。その後、適当なMaterialを作成し、シェーダーをEquirectanglarとしてください(私はER.matとしました)。
このマテリアルをはりつけたQuadを以下のとおり2つ生成します。
f:id:tuti107:20160530062639p:plain

f:id:tuti107:20160530062646p:plain
差異はY座標だけです。
つぎに実際に重畳する風景を作成します。私は、Moon Environmentというアセットを使いました。有料($30)ですが、リアルな月面風景を実現できるので、(そのような用途がある方に限りますが)おすすめです。
この風景は、適当に作成したEmpty Object配下に生成し、最後にレイヤーを設定してください。この背景が、以下で説明する背景のEquirectangular化以外のカメラに写り込まないようにするためです。
f:id:tuti107:20160530063431p:plain
またこの背景は、原点から遠い場所に設置するほうがよいです。実行時には特に影響はありませんが、エディット時にこの背景が原点付近にあると結構邪魔です。
最後に、背景をEquirectangular化するためのスクリプトを設置します。背景の中心に適当なカメラを設置し、Add Component→Dome Masterでスクリプトを追加します。さらに、カメラのCulling Maskを上記作成した背景のレイヤーと一致させます。これだけで、上記で設置した2つのQuadにEquirectangular形式の背景が描画されるようになります。
f:id:tuti107:20160530064313p:plain
これは、PCのディスプレイ右下に緑色長方形のものをおいて撮影したものです。その部分が透過され背景の月面が見えています。
なお、上記では背景は単一カメラで生成したものを両眼用に設置しているため、立体表示されません。上記のDome Masterを配置したカメラを2つ置くことで立体視対応もできますが、いろいろと複雑な改造が必要となってしまうため、ここでは触れません。

録画する

上記で生成したTheta映像とCGを重畳したものをMP4形式にエンコードします。本エンコードにはffmpegを利用しました。このffmpegにはNVidia GPUを利用したエンコード(nvenc)のオプションがあり、一般的には非常に高負荷な処理であるMP4エンコードを高速実施することが可能となります。第二回でMAC+OBSでエンコードの負荷が高すぎて全然フレームレートが出ない件についてふれましたが、nvencを利用すればこのような心配は不要です。ただし、nvencオプションはデフォルトのWindows用バイナリでは無効化されており、有効化するためにはffmpegをビルドする必要があります。
ffmpegのビルド(Windows版)は、こちらを利用すれば、そこそこの手間でビルドが可能です。ただ、VirtualBoxをインストールしたり、そこにUbuntuをインストールしたり、それでビルドをしてもエラーが多発したり、とそれなりに敷居が高いです。
nvenc対応のffmpegのビルドができたら、次に以下のスクリプトを用意します。

gist4b9ea7a85dc70d8b1d0eff515a7168c8
このスクリプトを適当なGameObjectに貼り付け、以下の通りインスペクタより設定します。
f:id:tuti107:20160531064738p:plain
突貫で開発した、ということもあり、まずffmpegを起動し、毎フレーム、インスペクタにてDisp Cameraに指定されたカメラの内容をjpeg化して標準出力でffmpegへパイプ渡し、MP4へエンコードする、という手順をとっています。速度性能はいまいちであり、nvencでがんばった分を差し引いても、当方のPC環境で20fpsがやっと、という感じです。この辺りは今後もう少し見なおしていいきたいと考えています。
なお、video Dirは、エンコードしたMP4動画を配置するディレクトリを指定します。ファイル名は生成時間で自動生成しています。

まとめ

これまで4回に渡り、メーカーフェア出展に向けた360度立体視動画ビューワ(+レコードアプリ)を開発してきました。開発期間が短く、かなり突貫実装となってしまいましたが、それでもたくさんの方(特にキッズ)に使ってもらい、喜んでもらえたというのは、開発者冥利につきます。
本件は、今後もブラッシュアップを続け、できればメーカーフェア東京にも出展したい、と考えております。本ブログを見ている方とメーカーフェア東京で出会えたら、非常に嬉しいです!

メイカーフェアー終了しました!

5月20日~22日、シリコンバレーのサンマテオにて開催されたメイカーフェアーに、VRシステムを出展しました。
本システム、THETA Sを2機使用して左右両眼用の360度立体視動画を撮影、本撮影映像のバックに「月面」「ハロウィーン風」等の背景を重畳して「オモシロVR映像」を生成、また作ったその場でGoogle Cardboardを利用して視聴できる、というものです。三日間でなんと214名の方(主にキッズ)が撮影し、その3-4倍の方に視聴して頂きました!お客様が休む間もなくひっきりなしに訪れる状況、ほんとうに疲れましたが、たくさんの方に楽しんでいただけて本当によかったです。
f:id:tuti107:20160524120838j:plain
今日から通常モードということで、まずは本開発技術について、ブログで書いていこうかと思います。

SVVRで見つけた興味深い技術たち

現在、5月21日に開催のメイカーフェアーに向けて睡眠時間を削りながら開発の毎日です。このような状況なので、開発ブログは少しお休みして、先月末に開催されたSVVRで見つけた興味深い技術をご紹介したいと思います。

VORTX

VORTXは、WhirlwindVRという会社が開発しているデバイスです。このデバイスは、VRコンテンツと連動して、強い・弱い/温風・冷風を発生させます。デモでは、自軍の城へ攻め込んでくるモンスターを撃退するというCGムービーに合わせて、例えばドラゴンが炎を吐いたら温風が、近くで爆発が起きたら強い風がくる、という感じで、風を利用してVRの臨場感を高めていました。端末がタワーPC並にでかく、ご家庭では一体どこに設置したものやらという感じではありますが、面白い端末だと思いました。
f:id:tuti107:20160513141243j:plain
なお数ヶ月中にSDKを公開する、とのことでした。

Cardboardで360度立体視動画ビューワーを作る(3)

はじめに

前回は、左右両眼用のThetaSのビデオ(RICOH THETA UVC BlendeによりEquirectangular形式に変換されたもの)を縦につなぎあわせ、OBSでHLS形式のライブストリーミングを生成、nginxで配信、Cardboard側で閲覧する、というシステムを作りました。しかし、一応動いたものの、遅延が激しく・フレームレートも出ない、との状況であり、実用には程遠い状況です。
今回は「リアルタイム性」を重視し、一度Cardboardを離れて、Oculus Riftで、左右両眼用ThetaSを利用した360度立体視を行ってみます。

UnityをOculus Riftに対応させる

現状Oculus Riftに対応しているUnityは、バージョン5.3.4p1及び、5.4βです。今回は5.3.4p1をインストールしました。5.3.4p1はこちらから入手できます。
次に、Oculus開発者サイトより、OVRPlugin for Unity 5をダウンロードします。ダウンロード後、C:\Program Files\Unity\Editor\Data\VR\oculusの内容を全削除した後、OVRPlugin_Unity5_1.3.0.zipを解凍したもので置き換えます。

Theta SのFish Eye形式動画をEquirectanguler形式に変換

前回記載した通り、RICOH THETA UVC Blenderを利用すれば、Equirectanguler形式で直接動画を取得できるため、変換処理等は不要となります。ところが、当方環境の問題なのか、Windows版の制限なのか、Theta Sを2台接続(両方ともTHETA UVC Registerで登録)した場合、いずれか一方のみのRICOH THETA UVC Blenderしか利用できません。再接続、再登録を何度か試みましたがうまくいかず。このため、Theta Shader Packを利用して、Unity側にて形式変換をすることにしました。
上記サイトよりThetaShaderPack_20150926.zipをダウンロード・展開し、適当なフォルダへ格納してください。次に、Assetsを右クリック→Import Package→Custom Packageを選び、ここで展開したThetaShaderPack.unitypackageをインポートします。
次にWebカメラの映像をテクスチャとして利用すべく、簡単なスクリプトを用意します(こちらを参考にしました)。

gistc83f1ae3dfa5c5c3f19f7ac346e3d48c
合わせて前回ご紹介したsphere100.fbxを用意します。Assets内の適当なフォルダに放り込んだ上で、2つこれをHierarchyへドラッグ&ドロップ、それぞれの名前をSphere100_L, Sphere100_Rとします。
インスペクタにて、それぞれ以下のように設定します。なお、camIndexはそれぞれ、左右用ThetaのFisheye形式UVCを指定してください(当方の環境では0及び1でした)。
f:id:tuti107:20160504123929p:plainf:id:tuti107:20160504123940p:plain
マテリアルには、上記インポートしたTheta Shader Packに含まれるThetaRealtimeEquirectanguler (Both).matを指定ください。
また、LayerをぞれぞれLEFT、RIGHTとしてください。
次に、左右両眼用のカメラを作成し、それぞれSphere100_L, Sphere100_Rのみ見えるよう設定します。Hierarchyにて右クリック→Cameraを二度実施し、作成したカメラをそれぞれCameraL,CameraRとしてください。CameraLは左目用なので、Culling MaskからRIGHTを外してください。またTarget EyeはLeftとしてください。同様に、CameraRはCulling MaskからLEFTを外し、Target EyeをRightとしてください。
最後に、File→Build Setting→Player Setting→Other Settingにて、Virtual Reality Supportedをオンとします。OculusはUnityにてネイティブでサポートされているため、CardboardのときのようなSDKのインポートや各種設定は不要、これだけでOculusで利用可能となります。
f:id:tuti107:20160504131534p:plain

まとめ

今回はOculusを使って、左右両眼用Thetaの入力画像をUnityで形式変換し表示するアプリを作成しました。またVirtual Reality Supportedをオンとして、本アプリをVR対応としました。前回のように、ストリーミング変換やライブ配信をやっていないこともあり、Thetaで撮影した映像をリアルタイムで楽しむことが可能です。またCardboardと比較して高画質・首の動きにすいつくようなヘッドポジショニングであり、非常に心地よいです。
ちなみに、詳細説明は省略しますが、前回同様Equirectanguler形式の左右映像を縦に並べ、OBSでエンコードし、nginxへアップロード・配信するアプリも作成してみました。前回のMACの場合のようにコマ落ちは発生しませんでした(NVidiaのH.264エンコーダのおかげかと思います。さすがGeForce970)。ただし相変わらず20秒程度の遅延は発生します。リアルタイムは難しいようです。