はじめに
イケメンテックラボで 「王子様のささやき朗読VR」の メインエンジニアを務めている、茨田と申します。
第5回目になる今回で、王子様のささやき朗読VRに使った技術の紹介は最後になります。
そんなラストを飾る記事は、一つの記事にまとめるほどではないけれど、つまずいた部分や解決方法のTIPS集を紹介させていただきます。
※開発中に起きたことなので、現在の最新verを使えば起きないことも含まれているかもしれませんが、ご了承ください。
▼以前の記事はこちらから
Unity周りでの問題
1. シーンを遷移したら暗くなってしまう
1.1 問題
Application.LoadLevel(Application.loadedLevelName)で他のシーンから遷移した場合にライトが暗くなってしまうことがありました。
1.2 解決方法
Unity 2017.3.1f1の場合、Window > Lighting > Settingsからライトの設定を開き、Sceneの一番下にある、Auto Generateのチェックを外します。(実行時はチェックを操作できないため停止してから行う)
次にチェックした右にあるGenerate Lightingを押すことでライティングが生成され、シーンが暗くなってしまうのを解消できました。

2. Linear Color Spaceを使うと暗くなってしまう
2.1 問題
AndroidのVRで、LinearColorSpaceを使うと、シミュレータと実機で比較した場合に実機が圧倒的に暗くなってしまいました。(Unity2018.1とUnity2017.4で確認)
2.2 解決方法
Gammaに設定するとシミュレータとほぼ同じ明るさになるため、修正されるまでこちらを使用した方がいいと思います。

参考URLのスレッドを見ると、長い間直っていない様なので、これからVR開発する場合はGammaに設定するのが無難かもしれません。
参考URL:
https://forum.unity.com/threads/gearvr-in-linear-space-problem-dark-image.455141/#post-3038503
3. 視野角(Field Of View)が操作できない
視野角で見た目が変わるので、数値を操作しつつ最適な値を探そうとしていました。
3.1 問題
実機で実行してみると、「Cannot set field of view on camera with name ‘CharacterCamera’ while VR is enabled.」と警告が出て、実機のVRだとFieldOfViewを操作できないようです。
3.2 警告の理由
VRでFieldOfViewを操作すると不快感が酷いらしく、daydream側のデバイスでロックがかかっているようでした。
参考URL:
https://forum.unity.com/threads/daydream-field-of-view.457659/
キャラクター周りで起きた問題
1. FBXからマテリアルを取り出す方法
1.1 問題
FBXを普通にインポートすると、内部にあるマテリアルは変更などが一切できません。
1.2 解決方法
他シェーダーなどを適応したくなった場合は
FBXファイルをクリック > Inspector > Materialsタブ選択 > Materialsステータス > ExtractMaterials…ボタンを押すことで、FBX外にmaterialを取り出し、編集することができます。
※Materialsタブは、Unity2017.2で追加されたボタンのため、それ以降のUnityが必要です

2. FBXに含まれたアニメーションを取り出す方法
model.fbxをプロジェクトにドロップして読み込みます。
model.fbxからanimation.animファイルを選択した状態で、macの場合command+d、winの場合はctrl+dで複製することができます。
シーン上に置いたmodelに、複製して編集などを行なったanimation.animをドロップすることで、Animatorに適応できます。
3. キャラクタの動きを適応した時に起きた問題
3.1 問題
Maya上では正常に動いているが、UnityにFBXを持って行き、アニメーションさせると、結構な頻度で目が飛び出てしまいました。
よく確認すると、目玉が飛び出ているわけではなく、顔全体が前後に動いているようでした。
3.2 問題の切り分けのために行った事
Maya以外のツールでも動くかを確認しました。
確認を行なったツールはblenderとFBXViewerで、どちらもMayaと同じように動いているため、Unity自体が読み込む時などに何かしている可能性が高いと判断して色々調べ始めました。
3.3 原因
Unityにインポート時、それぞれのアニメーションのキーフレームに圧縮がかかっていたため、顔の動きのアニメーションと髪の毛の動きのアニメーションで同期が取れなくなってしまっていました。
3.4 解決方法
インポートしたモデルを選択して、AnimationタブのAnim.CompressionをOffにする事で、アニメーションの圧縮がかからなくなり、Mayaなどで作成した時と同じ動きになりました。

ユニティちゃんトゥーンシェーダー(UTS)について
1. 基本的なところ
UTS(© UTJ/UCL)同封されているマニュアルに書いてあります。
たくさん項目がありますが、全ての項目を設定する必要はありません。
今回はここら辺を抑えておけば、ある程度見た目を調整できる部分を紹介したいと思います。
2. ベースマップについて
モデルに対してUVマッピングがされているテクスチャを設定します。
BaseColorはテクスチャに上乗せで色の設定ができる。
例えば、暗いところにいたらBaseColorを灰色にして、テクスチャ全体を暗めに表現できます。
Is_LightColor_Baseは空間上に設置されたライトの色を使うかどうかの設定で、おそらくチェックすると反映、しないと無視します。
1st_ShadeMapと2nd_ShadMapもColorとIs_LightColorをベースマップと同じように設定できます。


2.1 1st_ShadeMapと2nd_ShadeMapの作成
1st_ShadeMapと2nd_ShadeMapは本来影ができる部分に代わりにこのテクスチャを設定するようになっています。
影(暗さ)が一定の値を越えると1st_ShadeMapのテクスチャになり、さらに暗くなると2nd_ShadeMapが適応されます。
ベースマップの画像をphotshopやGimpなどで明るさやコントラストをいじって、暗めの色を作ることで、元の画像より暗めの影の様な画像ができます。

2.2 GIPM2.8で色味を変更する手順
メニューバーのファイル > 開く/インポートからベースマップの画像を開きます。
色 > 明るさ – コントラストを選択してWindowを開きます。
明るさ-60とコントラスト20に変更してOKを押します。(配置するシーンの暗さや好みによって明るさを調整してください)
ファイル > 名前をつけてエクスポートで画像を保存することでUnityで使えるようになります。
3. NormalMap
NormalMapは凹凸を強調するために使うテクスチャで、利用するためには、テクスチャのインポートセッティングでTextureTypeをNormalMapにする必要があります。
下の画像の様に、ベースマップに対して、凹凸を強調したい場所だけ、NormalMapに書き込まれています。詳細については参考URLを確認してください。
参考URL:
https://docs.unity3d.com/jp/current/Manual/StandardShaderMaterialParameterNormalMap.html

4. アニメ的な表現をしてみたい
3Dだと、見る角度によって左の王子の様に、髪の毛に目や眉毛が隠れてしまします。
「王子様のささやき朗読VR」では、元々2Dのキャラクタだったこともあり、右の王子の様にどの角度から見ても目や眉毛が髪の毛より前面に来るアニメ的な表現にも挑戦してみることになりました。

4.1 StencilMaskとStencilOut
それぞれのパーツの前後関係を変えるにはStencilMaskとStencilOutのシェーダーを設定する必要があります。
前面に出したいものにはUnityChanToonShader/Mobile/Toon_DoubleShadeWithFeather_StencilMaskのシェーダーを設定します。今回は目と眉毛のパーツに設定しました。

背面に出したいものにはUnityChanToonShader/Mobile/Toon_DoubleShadeWithFeather_StencilOutのシェーダーを設定します。今回は髪の毛のパーツに設定しました。

そしてそれぞれのシェーダーのStencil Noを同じ数字に設定します。上の2枚の画像では両方2を設定しています。この数字を設定することで、同じ数字同士のパーツのStencilMaskが前に表示、StencilOutが後ろに表示といった具合に前後関係を操作することができます。
4.2 ステンシルを使った時に起きた問題
mobile版UTSを使うとUnityでは想定通りの見た目だったのに、実機ではステンシルが効かない問題がありました。
原因はビルド時の設定で、ステンシルを使える様に設定していないとダメでした。
Edit > ProjectSettings > Player > XRSettings > VirtualRealitySDKs > Daydreamの▽を開いてDepthFormatを[24bit depth | 8-bit stencil]に設定することで、実機でもUnityと同じ見た目にすることができました。

負荷軽減周りで試したこと
Unityには負荷計測のために、Profilerというツールが標準搭載されています。
Window > Profilerでプロファイル画面が開けます。
プロファイル画面を開いたままUnityを実行すると、様々な負荷が見られるようになります。
プロファイラ上の負荷をクリックすることで、実行を一時停止して、どのような負荷がかかっているかを確認できます。
チューニングはCPU UsageとRenderingとMemoryあたりを見ながら行います。
基本的に縦軸が負荷の大きさで横軸が経過時間になっています。

1. CPU Usage
CPU Usageはメインで見るべきところで、CPUに何がどれだけ負荷をかけているか確認できます。
Renderingとそれ以外(VSyncを除く)で、どちらの負荷が大きいかを見て、どこをチューニングすべきか、ある程度あたりをつけることができます。
1.1 VSyncとは
VSyncは垂直同期のことです。除外される理由は、GPUの処理をCPUが待っているときの待機時間で、計算の負荷などではないため、除外して見られます。除外するためにはWindow左側にあるVsyncの横の黄色い■を押すことで、その数値を除外できます。


また、エディタと実機で数値が全然違うため(そもそものCPUやGPUの性能に左右されるため)、実機でしっかり確認する必要があります。
負荷の高いシェーダーなどを使っていると、かなり高い数値になっていることが多いです。
1.2 原因を特定するためにすること
CPUUsageが選択された状態だと、下に選択したフレームどの処理がどれだけ負荷をかけているかが出ています。ここから特に負荷をかけている処理が何かを特定して対応していきます。どの機能かわからない場合には、関数名などを検索することで、大体どのあたりのことか検討をつけられます。

上記画像のグラフと下のスレッドの間(縦方向の中央あたり)の左側にHierarchyのボタンがあり、これをTimeLineに変更することで、どの処理がどのタイミングで実行されて、何を待っているかなど処理のネックになっている部分を特定することに使えます。

2. Rendering
RenderingはSetPassCallsやDrawCalls、頂点数(verts)、面数(Tris)などの描画周りでどれだけ負荷がかかっているか見ることができます。
setPassCallはGPUに対して、CPUが何回描画命令を送っているかを表す数値で、少なければ少ないほど良いです。

2.1 setPassCallsを減らすためには
簡単に減らせるのは、ライトで影を描画しないように設定したり、動かないオブジェクトをstaticObjectに設定してバッチングを効かせたり、カメラにつけているポストエフェクトを削除することです。
参考URL:
http://nn-hokuson.hatenablog.com/entry/2017/02/06/222244
2.2 処理の対象の面数(Tris)を減らすには
手っ取り早いのはライトを減らす事でした。ライトを1つ、2つ、3つ増やすごとにtrisが2倍、3倍、4倍となっていました。
キャラクタ用のライトを増やして見たら、trisが142k, 204k, 266kと上がって行きました。(キャラクタのモデルは約60kポリゴンのため)
また、今回大きくtrisを減らすことができたのは、最初に使っていた通常のUTSをmobile版のUTSに切り替えたことでした。203kあったtrisが140kまで軽減されてfpsが倍以上にあがりました。
最終手段として、そもそものモデルのポリゴン数を落とすことで負荷を大きく軽減できます。
基礎のポリゴン数が高すぎると、システム的にどれだけ頑張っても限界点があるので、気をつけましょう。
3. Androidでの負荷計測
Profilerはエディタだけではなく、実機でも負荷を確認することができます。
Androidで確認するためには、ビルド設定でDevelopmentBuildをONにする必要があります。
File > BuildSettingsを開き、targetPlatformをAndroidにします。
DevelopmentBuildの項目があるのでONにすることで、実機からプロファイルするための準備ができました。
Androidと実際に通信するには、ProfilerWindowの上の方にある「Editor▼」ボタンを押して、端末を選択するか、端末が見つからない場合はIPを選択する必要がある。
※接続するPCと端末が同一ネットワーク内にいる必要があります。

IPを選択する場合は、恐らく端末ごとにIPの表示方法が違うので使う端末で検索して下さい。
以下はgalaxy s8の表示方法になります。
設定 > 端末情報 > ステータス > IPアドレスで辿ると書いてあります。
あとは端末でアプリを起動するとProfilerにデータが飛んできました。
おわりに
今回は一つの記事にまとめるほどではないけれど、つまずいた部分や解決方法のTIPS集を紹介させていただきました。
アプリを開発していると、この様に様々な問題に直面します。解決出来ることと、出来ないことがありますが、少しでも皆さんのお役にたてば嬉しいです。
「王子様のささやき朗読VR」の使った技術やTIPSの紹介にお付き合いいただき、ありがとうございました。
イケメンテックラボの第二弾も鋭意製作中ですので、次回作にもご期待ください。