Share via


延遲階段重新投影

後期重新投影 (LSR) 是一項硬體功能,可協助在使用者移動時穩定全像投影。

靜態模型 (Static models) 預期會在您四處移動時,以視覺化方式維護其位置。 如果它們看起來不穩定,則此行為可能暗示會有 LSR 問題。 請注意,動畫或暴增檢視等額外動態轉換可能會掩蓋此行為。

您可以在兩個不同的 LSR 模式之做選擇,也就是平面 LSR深度 LSR。 這兩種 LSR 模式可改善全像投影的穩定性 (雖然它們有不同的限制)。 從嘗試深度 LSR 開始,因為它在大部分的情況下可以提供更好的結果。

如何設定 LSR 模式

使用哪一種 LSR 模式取決於用戶端應用程式是否提交深度緩衝區。 如果提交了深度緩衝區,則會使用深度 LSR,否則會使用平面 LSR

下列段落說明如何在 Unity 和原生應用程式中分別完成提交深度緩衝區的動作。

Unity

在 Unity 編輯器中,移至 File > Build Settings。 選取左下角的 Player Settings,然後在 Player > XR Settings > Virtual Reality SDKs > Windows Mixed Reality 底下檢查是否已核取Enable Depth Buffer Sharing

Depth Buffer Sharing Enabled flag

如果已核取,您的應用程式會使用「深度 LSR」,否則會使用「平面 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++ 應用程式

提交深度緩衝區完全受原生 C++ 繫結程式碼的控制,與 WMR 或 OpenXR 版本無關。 唯一需要符合的條件是在呼叫 GraphicsBinding::BlitRemoteFrame 時,深度緩衝區必須繫結至圖形 API。

深度 LSR

若要讓「深度 LSR」能夠運作,用戶端應用程式必須提供有效的深度緩衝區,其中包含在 LSR 期間要考慮的所有相關幾何。

「深度 LSR」會根據提供的深度緩衝區內容來嘗試穩定視訊畫面。 因此,尚未呈現給它的內容 (例如透明物體) 無法由 LSR 調整,而且可能會顯示不穩定和重新投影構件。

若要減輕透明物體的重新投影不穩定問題,您可以強制寫入深度緩衝區。 請參閱顏色PBR 材料的材料旗標 TransparencyWritesDepth。 不過請注意,啟用此旗標時,透明/不透明物件互動的視覺品質可能會受影響。

平面 LSR

「平面 LSR」沒有像「深度 LSR」一樣有每個像素的深度資訊。 相反地,它會根據您必須提供給每個畫面格的平面來重新投影所有內容。

「平面 LSR」會最完善地重新投影那些靠近所提供的平面的物體。 物體越遠,它看起來就越不穩定。 雖然「深度 LSR」較適合在不同的深度重新投影物件,但「平面 LSR」可能更適合與平面對齊的內容。

在 Unity 中設定「平面 LSR」

平面參數衍生自所謂的焦點。 使用 WMR 時,每一個畫面格都必須透過 UnityEngine.XR.WSA.HolographicSettings.SetFocusPointForFrame 來設定焦點。 如需詳細資訊,請參閱 Unity 焦點 API。 對於 OpenXR,必須透過前節中所示的 ReprojectionSettings 來設定焦點。 如果您未設定焦點點,將會為您選擇一個後援方案。 不過,自動後援方案通常會導致次佳的結果。

您可以自行計算焦點 (儘管將它基於遠端呈現主機所計算的焦點可能是合理的)。 請呼叫 RemoteManagerUnity.CurrentSession.GraphicsBinding.GetRemoteFocusPoint 來取得那個。

用戶端和主機通常會呈現另一端不知道的內容,例如用戶端上的 UI 元素。 因此,將遠端焦點與本機計算的焦點結合起來可能是合理的。

在兩個連續畫面格中計算的焦點可能會有很大的差異。 僅依原狀使用它們可能會導致全像投影似乎在跳來跳去。 若要避免這種行為,建議您在先前和目前的焦點之間進行插補。

重新投影姿勢模式

混合呈現的一般問題範圍可以這樣表述:遠端和本機內容位於不同的姿勢內 (也就是座標空間),因為遠端姿勢是由伺服器預測,而本機姿勢是實際的目前姿勢。 不過,在呈現畫面格的結尾中,遠端和本機內容都必須對齊並帶入顯示畫面中。 下圖顯示與顯示檢視區相比,本機和遠端姿勢的轉換範例:

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

根據所使用的 GraphicsBinding,ARR 會提供最多三種重新投影模式,這些模式會與上面討論的 LSR 模式正交運作。 這些模式稱為Remote pose modeLocal pose modePassthrough pose mode。 不同於 LSR 模式,姿勢模式會定義遠端和本機內容的組合方式。 模式的選擇會以本機內容的視覺品質來換取執行時間效能,因此應用程式應該仔細考慮哪一個選項適合是合適的。 請參閱下面的考量事項。

Remote pose mode

Remote pose mode是 ARR 中的預設模式。 在此模式中,本機內容會使用來自遠端畫面格中的遠端姿勢,在傳入遠端影像串流之上呈現。 然後,合併的結果會轉送至 OS 以進行最後的重新投影。 雖然此方法只會使用一個重新投影,但最終的更正是以來回間隔為基礎,因此完整重新投影錯誤也會套用至本機內容。 因此,較大的更正差異可能會導致本機幾何大幅失真,包括 UI 元素。

使用上圖,下列的轉換會在Remote pose mode套用:

Reprojection steps in remote pose mode.

Local pose mode

在此模式中,重新投影分成兩個不同的步驟:在第一個步驟中,遠端內容會重新投影到本機姿勢空間,也就是依預設本機內容會在 VR/AR 裝置上藉以呈現的空間。 之後,會使用通常的本機姿勢,在此預先轉換的影像上方呈現本機內容。 在第二個步驟中,合併的結果會轉送至 OS 以進行最後的重新投影。 因為此第二次重新投影只會產生小差異 (事實上,是在 ARR 不存在時會使用的同一個差異),所以本機內容上的失真構件會大幅降低。

因此,圖例看起來如下:

Reprojection steps in local pose mode.

Passthrough pose mode

此姿勢模式的運作基本上與Remote pose mode相同,表示本機和遠端內容會在遠端空間中結合。 不過,內容不會在結合之後重新投影,而是保留在遠端姿勢空間中。 此模式的主要優點是,產生的影像不會受到重新投影構件的影響。

在概念上,此模式可以與傳統的雲端串流應用程式進行比較。 由於它所產生的高延遲,因此不適用於前端掛接的情況,但對於桌面和其他需要更高影像品質的平面螢幕應用程式來說,它是一個可行的替代方案。 因此,它暫時只能在 GraphicsBindingSimD3D11 上使用。

效能和品質考慮

姿勢模式的選擇對視覺品質與效能會有影響。 在 HoloLens 2 裝置上以Local pose mode進行額外重新投影的用戶端上的額外執行時間成本相當於每一個 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 (如果它已使用 Proxy 紋理進行初始化)。 如果這不是這種情況,則只能在 PoseMode.LocalPoseMode.Passthrough 之間切換姿勢模式,直到圖形繫結重新初始化為止。 請參閱 GraphicsBindingSimD3d11.InitSimulation 的兩個負荷,其採用了 ID3D11Texture2D 物件的原生指標 (Proxy 路徑),或採用了所需使用者檢視區的 widthheight (非 Proxy 路徑)。

桌面 Unity 執行時間考慮

由於 GraphicsBindingSimD3D11 的技術背景,以及螢幕外呈現轉譯在 Unity 中運作方式的事實,ARR Unity 執行時間會要求使用者在啟動 RemoteManagerUnity 時指定所需的姿勢模式,如下所示:

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

如果 PoseMode.Remote 已指定,圖形繫結將會以螢幕外 Proxy 紋理來進行初始化,而且所有呈現轉譯都會從 Unity 場景的主要相機中重新導向至 Proxy 相機。 只有在需要執行時間姿勢模式變更為 PoseMode.Remote 時,才建議使用此程式碼路徑。 如果未指定任何的姿勢模式,則 ARR Unity 執行時間會根據目前的平台來選取適當的預設值。

警告

Proxy 相機重新導向可能與其他 Unity 擴充功能不相容,其預期主要相機進行場景呈現轉譯。 如果需要在其他地方查詢或登錄 Proxy 相機,則可以透過 RemoteManagerUnity.ProxyCamera 屬性來擷取 Proxy 相機。 具體而言,如需 Cinemachine 外掛程式,請參閱下列的疑難排解項目:Unity Cinemachine 外掛程式無法在遠端姿勢模式中運作

如果改用 PoseMode.LocalPoseMode.Passthrough,圖形繫結將不會使用螢幕外 Proxy 紋理來進行初始化,而會使用利用 Unity 場景的主要相機來呈現轉譯的快速路徑。 如果個別的使用案例在執行時間需要遠端姿勢模式,則 PoseMode.Remote 應在 RemoteManagerUnity 初始化時加以指定。 使用 Unity 的主要相機來直接呈現轉譯是更有效率的,而且可以防止其他 Unity 擴充功能發生問題。 因此,建議使用非 Proxy 的呈現轉譯路徑。

下一步