Unity Pro Tips

オフィシャル記事詳細

Unity での VR とモバイル AR のグラフィックスに関するベストプラクティス

<このページで学べる内容>

バーチャルリアリティ(VR)と拡張現実(AR)のグラフィックスを制作するための Unity のツールとワークフローは進化し続けています。XR エバンジェリストの Dan Miller が、ハイエンド VR とモバイル AR 向けの高パフォーマンスなグラフィックスを制作する方法に関する最新のベストプラクティスを紹介します。

Unity でのステレオレンダリングの 3 つの方法

マルチパスでは、眼を片目ごとにレンダリングを行うため、シーングラフを 2 回実行する必要があります。マルチパスステレオレンダリングは、リソースが限られていないハイエンドデバイスを想定した、写実的なビジュアルで制作する場合に使用します。これは 3 つの手法の中で一番コストがかかるものですが、ある種ディスプレイやテクノロジーに対してはとても簡単でわかりやすいため、通常は最初にサポートされる方法です。

シングルパスでは、2 つのテクスチャを 1 つの大きいテクスチャ(「2 倍幅のテクスチャ」と呼ばれます)にパッキングします。シーングラフを 1 回しか実行しないので、CPU での処理速度は格段に速くなります。ただ、大量の状態変更が追加で発生するため、より多くの GPU が必要になります。  

シングルパスインスタンシングは、一番選ばれることの多い方法です。この方法では、それぞれの目のレンダリングを格納した 2 つのテクスチャを含む、1 つのテクスチャ配列を使用します。必要とされる GPU の処理量はシングルパスと変わりませんが、ドローコールと CPU の処理量は少なく済ませられるので、圧倒的に効率が良くなります。 

特定のプラットフォームや任意のスクリプタブルレンダーパイプラインなどにおいて VR のレンダリングの問題が発生してしまった場合は、これらの 3 つの設定をちょっと切り替えてみて、うまくいくかどうか見てみてはいかがでしょうか。

ステレオレンダリングを使用するための頂点シェーダーコードの変更

これらの変更を行うと、記述するカスタムシェーダーがすべて 2 倍幅でインスタンス化されるようになります。

下に示したコード例は、通常の頂点シェーダーのコードです。最初に行うべきことは、上部にあるアプリデータにマクロを追加することです。次に、出力ステレオを追加し、最後のステップの頂点パスで、インスタンス ID をリンクさせてステレオ出力を初期化できるようにします。この例含めその他の例については、シェーダーに関するドキュメントでご確認いただけます。 

EyeTextureResolutionScale と RenderViewportScale EyeTextureResolutionScale (旧称 RenderScale) は、目のテクスチャの実際のサイズをデバイスのデフォルト解像度の乗数として指定する値です。値 1.0 を指定すると、対象の XR デバイスで指定されているデフォルトの目のテクスチャの解像度が使用されます。1.0 未満の値を指定するとそれより低い解像度の目のテクスチャが使用され、1.0 を超える値を指定するとそれより高い解像度が使用されます。 

EyeTextureResolutionScale を使用すると、VR エクスペリエンス全体での解像度を上げたり下げたりできます。これは、ハイエンド VR エクスペリエンスを Oculus Quest またはモバイルプラットフォームに移植する場合や、逆にモバイル VR プロジェクトをハイエンドデバイスに移植する場合などにも使用できます。 

RenderViewportScale は動的であり、実行時に調整されます。これは、割り当てられている目のテクスチャをどれだけレンダリングに使用するかを制御します。値の範囲は 0.0 ~ 1.0 です。たとえば、シーン内に大量の特殊効果があるが許容可能なフレームレートを維持したい場合、RenderViewportScale を使用して実行時に解像度を下げることができます。 

まとめると、EyeTextureResolutionScale は VR 体験全体に対して設定する値であるのに対し、RenderViewportScale は体験中の特定の箇所または仕様について実行時に調整できる、ということです。

Vertex shader changes for stereo rendering.cs (C#)

Struct appdata
{
Float3 pos;
UNITY_VERTEX_INPUT_INSTANCE_ID; // uint instancedID : SV_InstanceID;
};

struct v2f
{
Float4 vertex : SV_POSITION;
UNITY_VERTEX_OUTPUT_STEREO // uint stereoEyeIndex : SV_RenderTargetIndex; 
};

v2f vert (appdata v)
{
	v2f o;
	UNITY_SETUP_INSTANCE_IED(v); // unity_StereoEyeIndex = v.instanceID & 0x01;
	UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); // o.stereoEyeIndex = unity_stereoEyeIndex
o.vertex = UnityObejctToClipPos(v.vertex);
return o;

}

XR 用のスクリプタブルレンダーパイプライン

VR 用の HD レンダーパイプライン

2019.3 のパッケージバージョン 7.2.0 で、HD レンダーパイプライン(HDRP)は検証済みとなり、VR での使用が可能になっています。VR プロジェクトで HDRP を使用する際にサポートされる機能については、こちらのブログ記事をご覧ください。

VR 用のユニバーサルレンダーパイプライン

ユニバーサルレンダーパイプラインは、すべての VR プラットフォームでサポートされています。ただし、モバイル側にいくつかの制約があります。URP のフォーラムドキュメント、および 2019.3 時点での機能について説明しているこの最近のブログ記事で、常に最新情報をチェックしてください。 

ポストプロセッシング

ポストプロセッシングエフェクトの中には、XR でうまく機能するものと、XR にあまり適さないものがあります。たとえば、瞬間的なエフェクトはぼやけの原因になることがあり、物理カメラのプロパティをシミュレートする被写界深度は VR 酔いの原因になることがあります。VR コンテンツにポストプロセッシングエフェクトを正しく適用する方法については、Dan によるヒントをご覧ください。

モバイル AR でポストプロセッシングエフェクトを適用する場合は、エフェクトがカメラのフィード全体に適用されることに注意してください。たとえば、ブルームのエフェクトを適用した場合、ユーザーが外に出て太陽を見上げたり室内の光を直接見たりすると逆効果になることがあります。  

AR Foundation

AR Foundation は、ARKit、ARCore、Magic Leap、HoloLens 向けの体験を構築するための Unity のクロスプラットフォームフレームワークです。一度 Unity でプロジェクトをビルドすれば、選択したプラットフォーム用の SDK が AR Foundation で使用されるようになります。詳細については以下をご覧ください。

AR Foundation のドキュメント

Unity で AR 開発を始める

XR プラグインフレームワーク

一般的に AR プロジェクトの開発時に要となる 3 つのポイントを以下に示します。 

  1. モバイル AR アプリでは、レンダリングが 2 回行われることになります。カメラのフィードがレンダリングされた後、デジタルコンテンツがその上にレンダリングされます。 
  2. 現実世界では、ほとんどすべてのものが影を落とすか、または影を受けます。影というものは、AR コンテンツにおいては重要な存在です。これは、影がオブジェクトを「接地」させるもので、これによりオブジェクトの存在する場所をユーザーが細かく把握するのに役立つからです。ただし、オクルージョンと影はデフォルトでは有効ではなく、特別なシェーダーが必要となります。 
  3. AR プロジェクトでの開発の際には、「これが色々な場所の多種多様な環境下で使用されることになる」ということを常に留意しておくようにしましょう。

ヘッドマウント AR 用の「リバース」ライティング

現在の AR プラットフォームは、加法的ディスプレイです。つまり、ライトはディスプレイに加えることになり、黒やその他の暗い色のインスタンスはすべて透明にレンダリングされることになります。  

上の画像で、左端の球体は下半分が透けた状態になっています。ライトが真上から当たっている場合での通常のシェーディングモデルがこれです。上は明るく、下に行くほど暗くなっています。これは、物理ベースレンダリングとセルフシャドウイングによって暗い領域ができるためです。アイトラッキングなどの独自のプラットフォーム機能を使用することで、この問題を解決することができます。また、Magic Leap では、さきほどの例でいえば中央と右側の球体のような形で、これをシェーディング技術処理することもできます。この技術では、オブジェクトを明るい状態から暗い状態へと移行させる代わりに、球体全体に対して同じシェーディングを維持したうえで上部を明るくする形で処理します。

リバースシャドウと色付きのシャドウ

Dan が紹介するこのヒントは、オーストリアのザンクトペルテン応用科学大学のデジタルヘルスケアとスマートエンジニアリング分野の講師である Andreas Jakl 氏から提供されたものです。これは影が差すであろう場所に輪郭を描き、その輪郭を真っ白な状態から完全に透明になるまでフェザリングする、という形の手法です。これが Unity ではどのようになるかは、Andreas Jakl 氏によって提供された上の画像の通りです。リバースシャドウを適用して黒を透かしとして使用することで、コンテンツを自然な形で追加ディスプレイに溶け込ませています。 

試す価値のある他の手法としては、色付きシャドウ、つまり影に黒やグレー以外の色を使用してみることです。いずれにせよ、色は透明になるようレンダリングされるからです。これは、たとえばエイリアンをテーマとするゲームや体験でオブジェクトに紫の影が差すようにするなど、ある特定の種類のコンテンツで試してみてください。

AR のオクルージョン

モバイル用の特殊なオクルージョンシェーダーは、こちらにあります。環境下における平面や一部のメッシュに適用できます。フィーチャーポイントで使用して、オクルージョンのスピードを高めることができます。フィーチャーポイントとは、点群の中の特別な点のことです。AR デバイスはカメラと画像解析を使用して、その環境のマップの構築に使用される、ワールド内の特別な点を追跡します。これらは通常、木目の表面の節など、出現頻度の高い要素です。それらのフィーチャーポイントに小さなオブジェクトを適用し、コンテンツのオクルーダー(遮蔽する要素)として機能させることができます。 

利用できるもう 1 つの手法は、ARKit 3.0 で利用できる AR Foundation Human Body サブシステムです。このサブシステムにより、アプリで人間のステンシルセグメンテーション画像とデプスセグメンテーション画像を利用できます。ステンシルセグメンテーション画像は、ピクセルごとに、そのピクセルに人間が含まれるかどうかを識別します。デプスセグメンテーション画像は、認識された人間と相互に関係がある各ピクセルのデバイスからの推定距離で構成されます。これらのセグメンテーション画像を一緒に使用すれば、レンダリングされた 3D コンテンツが現実世界の人間を踏まえることで、リアルな陰影をつけてくれるようになります。

シャドウ

Unity での開発経験がある方であれば、オブジェクトそのものや光源など、影を設定する方法は幅広く存在し、調整するための品質設定も豊富なのは、ご存知ではないかと思います。ただ、いずれにせよデスクトップで開発してモバイルデバイスにデプロイする場合は、設定をちょっと微調整しなければいけない可能性についても留意しておくべきです。 

静的オブジェクトの場合は、ブロブシャドウを使用しましょう。そうすれば、対象のオブジェクトの形状またはオブジェクトの周囲に汎用的な円が作成され、オブジェクトを対象のエクスペリエンス中へ溶け込ませることができます。

ARKit 用の環境プローブ

これらのプローブは、カメラ画像を使用して AR セッション中に環境テクスチャーを生成します。スキャン中に平面やフィーチャーポイントに関連付けられた色データを検出します。そこからキューブマップが構築され、それがリフレクションプローブに適用されることで、現実の物理環境がデジタルコンテンツに反映されます。この機能は、家具が置かれた屋内空間など、平面のフィーチャーポイントが明確に定義された環境で最も効果的です。

ARCore 上の環境 HDR

この機能は、ディレクショナルライトの回転方向を調整することで、この機能とリンクするリアルタイムなディレクショナルライトを作成し、影の差す方向を現実世界で見ている方向と一致させることができます。この手法は、デジタルコンテンツを現実世界の一部であるかのように感じさせるのに有効です。また、アンビエント球面調和関数により、全方向からのアンビエントイルミネーションをモデリングし、デジタルコンテンツをより現実世界に近づけるのに役立ちます。最後に、HDR キューブマップによりスペキュラーハイライトとスペキュラー反射が提供されます。 

これらの機能が実際にどのように動作するかについては、こちらの Dan による Unite Copenhagen の講演のデモ(約 25 分間)をご覧ください。

この記事はいかがでしたか?