Late Stage Reprojection

Late Stage Reprojection (LSR) は、ユーザーが移動したときにホログラムを安定化するのに役立つハードウェア機能です。

静的モデルは、移動時に位置を視覚的に維持する必要があります。 これが不安定であるように見える場合、この動作は LSR の問題を示唆している可能性があります。 アニメーションや分解図などの追加の動的な変形によって、この動作がマスクされる可能性があることに注意してください。

2つの異なる LSR モード (Planar LSR または Depth LSR) を選択できます。 どちらの LSR モードでも、ホログラムの安定性が向上しますが、それぞれに独自の制限があります。 まず、Depth LSR を試してみてください。ほとんどの場合、より良い結果が得られます。

LSR モードを設定する方法

どの LSR モードを使用するかは、クライアント アプリケーションが深度バッファーを送信するかどうかによって決まります。 深度バッファーが送信されると Depth LSR、それ以外の場合は Planar LSR が使用されます。

次の段落では、Unity とネイティブ アプリケーションでそれぞれ深度バッファーの送信がどのように行われるかについて説明します。

Unity

Unity エディターの File > Build Settings に移動します。 左下にある Player Settings を選択し、Player > XR Settings > Virtual Reality SDKs > Windows Mixed RealityEnable Depth Buffer Sharing がオンになっているかどうかを確認します。

Depth Buffer Sharing Enabled flag

オンである場合、アプリでは Depth LSR が使用されます。それ以外の場合は、Planar LSR が使用されます。

OpenXR を使用する場合は、常に深度バッファーを送信する必要があります。 この設定は、XR Plug-in Management > OpenXR にあります。 次に、OpenXR プラグインの拡張機能によって、再投影モードを変更できます。

using Microsoft.MixedReality.OpenXR;

public class OverrideReprojection : MonoBehaviour
{
    void OnEnable()
    {
        RenderPipelineManager.endCameraRendering += RenderPipelineManager_endCameraRendering;
    }
    void OnDisable()
    {
        RenderPipelineManager.endCameraRendering -= RenderPipelineManager_endCameraRendering;
    }

    // When using the Universal Render Pipeline, OnPostRender has to be called manually.
    private void RenderPipelineManager_endCameraRendering(ScriptableRenderContext context, Camera camera)
    {
        OnPostRender();
    }

    // Called directly when using Unity's legacy renderer.
    private void OnPostRender()
    {
        ReprojectionSettings reprojectionSettings = default;
        reprojectionSettings.ReprojectionMode = ReprojectionMode.PlanarManual; // Or your favorite reprojection mode.
        
        // In case of PlanarManual you also need to provide a focus point here.
        reprojectionSettings.ReprojectionPlaneOverridePosition = ...;
        reprojectionSettings.ReprojectionPlaneOverrideNormal = ...;
        reprojectionSettings.ReprojectionPlaneOverrideVelocity = ...;

        foreach (ViewConfiguration viewConfiguration in ViewConfiguration.EnabledViewConfigurations)
        {
            if (viewConfiguration.IsActive && viewConfiguration.SupportedReprojectionModes.Contains(reprojectionSettings.ReprojectionMode))
            {
                viewConfiguration.SetReprojectionSettings(reprojectionSettings);
            }
        }
    }
}

ネイティブ C++ アプリケーション

深度バッファーの送信は、WMR または OpenXR のバージョンとは関係なく、ネイティブ C++ バインド コードによって完全に制御されます。 満たす必要のある条件は、GraphicsBinding::BlitRemoteFrame が呼び出されたときに、深度バッファーをグラフィックス API にバインドしなければならないことだけです。

Depth LSR

Depth LSR を機能させるには、クライアント アプリケーションが、LSR 中に考慮する必要があるすべての関連ジオメトリを含む有効な深度バッファーを指定する必要があります。

Depth LSR は、指定された深度バッファーの内容に基づいて、ビデオ フレームの安定化を試みます。 結果として、透明なオブジェクトなど、レンダリングされていないコンテンツは、LSR によって調整することはできず、不安定で再投影される可能性があります。

透過的なオブジェクトの再プロジェクションの不安定性を軽減するには、深度バッファーの書き込みを強制することができます。 カラー および PBR のマテリアルについては、TransparencyWritesDepth のマテリアル フラグを参照してください。 ただし、このフラグを有効にすると、透明/不透明なオブジェクトの相互作用のビジュアル品質が低下する可能性があります。

Planar LSR

Planar LSR には、Depth LSR のように、ピクセルごとの深度情報は含まれません。 代わりに、各フレームで提供する必要がある平面に基づいてすべてのコンテンツを再投影します。

Planar LSR は、指定された平面に近いオブジェクトを適切に再投影します。 オブジェクトが離れていればいるほど、不安定になります。 Depth LSR の方が、異なる深さでオブジェクトを再投影することに優れていますが、Planar LSR は、平面と整列しているコンテンツに適しています。

Unity で Planar LSR を構成する

プレーン パラメーターは、いわゆるフォーカス ポイントから派生しています。 WMR を使用する場合、フォーカス ポイントを UnityEngine.XR.WSA.HolographicSettings.SetFocusPointForFrame によってフレームごとに設定する必要があります。 詳細については、Unity のフォーカス ポイント API に関するページを参照してください。 OpenXR の場合、フォーカス ポイントは、前のセクションで示した ReprojectionSettings を使用して設定する必要があります。 フォーカス ポイントを設定しない場合は、フォールバックが自動で選択されます。 ただし多くの場合、自動フォールバックの結果は最適ではありません。

フォーカス ポイントは自分で計算できますが、Remote Rendering ホストによって計算されたものを基にした方が適切な場合もあります。 RemoteManagerUnity.CurrentSession.GraphicsBinding.GetRemoteFocusPoint を呼び出してこれを取得します。

通常、クライアントとホストの両方が、もう片方が認識していないコンテンツ (クライアント上の UI 要素など) を表示します。 そのため、リモートのフォーカス ポイントをローカルで計算されたフォーカス ポイントと結合することは理にかなっていると考えられます。

2 つの連続するフレームで計算されたフォーカス ポイントは、大きく異なる場合があります。 単にそのまま使用すると、ホログラムがあちこち飛び回って表示される可能性があります。 この動作を回避するには、前のフォーカス ポイントと現在のフォーカス ポイントを補間することをお勧めします。

再プロジェクションのポーズ モード

ハイブリッド レンダリングにおける一般的な問題として、リモート ポーズ (つまり座標空間) の内容はサーバーによって予測されるのに対して、ローカル ポーズは実際の現在の内容であるため、リモートとローカルの内容が異なるポーズに配置されるという点が挙げられます。 ただし、レンダリング フレームの終了時には、リモートとローカルの両方のコンテンツを配置し、ディスプレイに表示にする必要があります。 次の図は、ローカルとリモートのポーズをディスプレイ ビューポートと比較して変換する例を示しています。

Diagram that illustrates remote and local pose in relation to target viewport.

使用する GraphicsBinding に応じて、ARR は、上記で説明した LSR モードと直交して動作する最大 3 つの再投影モードを提供します。 これらのモードは、Remote pose modeLocal pose modePassthrough pose modeと呼ばれます。 LSR モードとは異なり、これらのポーズ モードでは、リモート コンテンツとローカル コンテンツの結合方法が定義されます。 このモードを選択すると、実行時のパフォーマンスに関してローカル コンテンツの視覚品質が低下するため、アプリケーションでどちらのオプションが適切か慎重に検討する必要があります。 以下の考慮事項を参照してください。

Remote pose mode

Remote pose mode は ARR における既定のモードです。 このモードでは、ローカル コンテンツは、リモート フレームからのリモート ポーズを使用して、受信するリモート イメージ ストリーム上にレンダリングされます。 次に、最終的な再プロジェクションのために、結合された結果が OS に転送されます。 この方法では 1 つの再プロジェクションのみが使用されますが、最終的な修正はラウンドトリップ間隔に基づいて行われるため、完全な再プロジェクション エラーがローカル コンテンツにも適用されます。 その結果、大きな修正デルタによって、UI 要素を含むローカル ジオメトリの大きなゆがみが発生する可能性があります。

上の図を使用すると、次の変換が Remote pose mode に適用されます。

Reprojection steps in remote pose mode.

Local pose mode

このモードでは、再プロジェクションは 2 つの異なる手順に分割されます。最初の手順では、リモート コンテンツはローカル ポーズの場所に再投影されます。つまり、既定では、ローカル コンテンツが VR/AR デバイスでレンダリングされる領域です。 その後、通常のローカル ポーズを使用して、この事前変換されたイメージの上にローカル コンテンツがレンダリングされます。 2 番目の手順で、最終的な再プロジェクションのために、結合された結果が OS に転送されます。 この 2 回目の再プロジェクションでは、小規模なデルタ (実際には ARR が存在しなかった場合に使用されるのと同じデルタ) しか発生しないため、ローカル コンテンツでの歪みアーティファクトは大幅に軽減されます。

その結果、次の図のようになります。

Reprojection steps in local pose mode.

Passthrough pose mode

このポーズ モードは基本的に Remote pose mode と同じように動作し、ローカルコンテンツとリモートコンテンツがリモート空間で組み合わされていることを意味します。 ただし、コンテンツは組み合わせ後に再投影されず、リモート ポーズ空間に残ります。 このモードの主な利点は、結果の画像が再投影アーチファクトの影響を受けないことです。

概念的には、このモードは従来のクラウドストリーミングアプリケーションと比較できます。 待機時間が長いため、ヘッドマウントシナリオには適していませんが、より高い画質が必要なデスクトップやその他のフラットスクリーンアプリケーションに代わる実行可能な選択肢です。 したがって、当面は GraphicsBindingSimD3D11 でのみ利用可能です。

パフォーマンスと品質に関する考慮事項

ポーズ モードの選択は、視覚品質とパフォーマンスに影響します。 HoloLens 2 デバイスのLocal pose modeで追加の再プロジェクションを実行するために、クライアント側にかかる追加のランタイム コストは、1 フレームあたりの GPU 時間にして約 1 ミリ秒程度です。 クライアント アプリケーションのフレーム予算が既に 16 ミリ秒に近い場合は、この追加コストを考慮に入れる必要があります。 一方、ローカル コンテンツがない、または歪みアーティファクトを発生させない程度のローカル コンテンツしかないようなアプリケーションもあります。 このような場合、リモート コンテンツの再プロジェクションの品質に影響がないため、Local pose mode では視覚効果は一切得られません。

このため、一般的なアドバイスとして、ユース ケースごとにモードをテストすることで、ビジュアル品質で得られるメリットが、余分なパフォーマンス上のオーバーヘッドに見合うかどうかを確認することをお勧めします。 また、重要な UI が表示されている場合にのみローカル モードを有効にするなど、モードを動的に切り替えることもできます。

実行時にPose modeを変更する方法

次のクライアント API を使用して、実行時にモードを変更できます。

RenderingSession session = ...;
session.GraphicsBinding.SetPoseMode(PoseMode.Local); // set local pose mode
ApiHandle<RenderingSession> session = ...;
session->GetGraphicsBinding()->SetPoseMode(PoseMode::Local); // set local pose mode

一般的に、モードは、グラフィックス バインド オブジェクトが使用可能な場合に、いつでも変更できます。 GraphicsBindingSimD3D11 には重要な違いがあります。プロキシ テクスチャで初期化されている場合、ポーズ モードは PoseMode.Remote にだけ変更できます。 そうでない場合、ポーズモードは、グラフィック バインディングが再初期化されるまで、PoseMode.LocalPoseMode.Passthrough の間でのみ切り替えることができます。 GraphicsBindingSimD3d11.InitSimulation の 2 つのオーバーロードを参照してください。これは、ID3D11Texture2D オブジェクトのネイティブ ポインター (プロキシ パス) を受け取るか、目的なユーザー ビューポートの widthheight (非プロキシ パス) を受け取ります。

Desktop Unity ランタイムに関する考慮事項

GraphicsBindingSimD3D11 のテクニカル バックグラウンドと、Unity のオフスクリーン レンダリングのしくみに起因し、ARR Unity ランタイムでは、ユーザーが次のように RemoteManagerUnity の起動時に目的のポーズ モードを指定する必要があります。

public static void InitRemoteManager(Camera camera)
{
    RemoteUnityClientInit clientInit = new RemoteUnityClientInit(camera, PoseMode.Remote);
    RemoteManagerUnity.InitializeManager(clientInit);
}

PoseMode.Remote が指定される場合、グラフィックス バインドがオフスクリーン プロキシ テクスチャで初期化され、すべてのレンダリングが Unity シーンのメイン カメラからプロキシ カメラにリダイレクトされます。 このコード パスは、ランタイム ポーズ モードを PoseMode.Remote に変更する必要がある場合にのみ使用することが推奨されます。 ポーズモードが指定されていない場合、ARR Unity ランタイムは現在のプラットフォームに応じて適切な既定値を選択します。

警告

プロキシ カメラのリダイレクトは、シーン レンダリングでメイン カメラの使用が求められる他の Unity 拡張機能で利用できない (互換性がない) 場合があります。 プロキシ カメラは、他の場所でクリエまたは登録が必要な場合、RemoteManagerUnity.ProxyCamera プロパティ経由で取得できます。 具体的には、Cinemachine プラグインについては、次のトラブルシューティング エントリを参照してください。UnityCinemachine プラグインはリモート ポーズ モードでは機能しません

代わりに PoseMode.Local または PoseMode.Passthrough を使用する場合、グラフィックス バインドはオフスクリーン プロキシ テクスチャで初期化されず、Unity シーンのメイン カメラを使用してレンダリングする高速パスが使用されます。 それぞれのユースケースが実行時にリモートポーズモードを必要とする場合は、RemoteManagerUnity 初期化時に PoseMode.Remote を指定する必要があります。 Unity のメインカメラで直接レンダリングする方が効率的で、他の Unity 拡張機能の問題を防ぐことができます。 したがって、プロキシ以外のレンダリング パスを使用することをお勧めします。

次のステップ