Late Stage Reprojection (LSR)
Late Stage Projection (LSR) ist ein Hardwarefeature, mit dem Hologramme bei Bewegungen des Benutzers stabilisiert werden.
Bei statischen Modellen wird erwartet, dass sie ihre Position visuell beibehalten, wenn sich Benutzer darin bewegen. Wenn Sie instabil scheinen, kann dieses Verhalten auf LSR-Probleme hindeuten. Beachten Sie, dass zusätzliche dynamische Transformationen (etwa Animationen oder Explosionsansichten) dieses Verhalten maskieren können.
Sie können zwischen zwei verschiedenen LSR-Modi wählen, nämlich zwischen Planar-LSR und Tiefen-LSR. Beide LSR-Modi verbessern die Hologrammstabilität, auch wenn sie jeweils Einschränkungen aufweisen. Beginnen Sie mit dem Testen von Tiefen-LSR, da damit in den meisten Fällen bessere Ergebnisse erzielt werden.
Festlegen des LSR-Modus
Welcher der LSR-Modi verwendet wird, hängt davon ab, ob die Clientanwendung einen Tiefenpuffer übermittelt. Wenn der Tiefenpuffer übermittelt wird, wird Tiefen-LSR verwendet, andernfalls planare LSR.
In den folgenden Abschnitten wird erläutert, wie die Übermittlung des Tiefenpuffers in Unity bzw. nativen Anwendungen erfolgt.
Unity
Wechseln Sie im Unity-Editor zu File > Build Settings. Wählen Sie links unten Player Settings aus, und überprüfen Sie dann unter Player > XR Settings > Virtual Reality SDKs > Windows Mixed Reality, ob Enable Depth Buffer Sharing aktiviert ist:
Wenn dies der Fall ist, verwendet Ihre App Tiefen-LSR, andernfalls wird Planar-LSR verwendet.
Bei Verwendung von OpenXR sollte der Tiefenpuffer immer übermittelt werden. Die Einstellung finden Sie unter XR Plug-in Management > OpenXR. Der Neuprojektionsmodus kann dann über eine Erweiterung im OpenXR-Plug-In geändert werden:
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);
}
}
}
}
Native C++-Anwendungen
Die Übermittlung des Tiefenpuffers steht unabhängig von der WMR- oder OpenXR-Version vollständig unter der Kontrolle des nativen C++-Bindungscodes. Die einzig geltende Bedingung ist, dass zum Zeitpunkt des Aufrufs von GraphicsBinding::BlitRemoteFrame
ein Tiefenpuffer an die Grafik-API gebunden sein muss.
Tiefen-LSR
Damit Tiefen-LSR funktioniert, muss die Clientanwendung einen gültigen Tiefenpuffer bereitstellen, der die gesamte relevante Geometrie enthält, die während des LSR-Vorgangs berücksichtigt werden muss.
Tiefen-LSR versucht, den Videoframe basierend auf dem Inhalt des angegebenen Tiefenpuffers zu stabilisieren. Folglich können Inhalte, die nicht in ihm gerendert wurden (z. B. transparente Objekte), nicht von LSR angepasst werden, und es kann zu Instabilität und Umprojizierungsartefakten kommen.
Um Instabilitäten der Neuprojektion bei transparenten Objekten zu vermeiden, können Sie das Schreiben von Tiefenpuffern erzwingen. Weitere Informationen zu den Farb- und PBR-Materialien finden Sie im Materialflag TransparencyWritesDepth. Beachten Sie jedoch, dass die visuelle Qualität der Interaktion zwischen transparenten/nicht transparenten Objekten beim Aktivieren dieses Flags beeinträchtigt werden kann.
Planar-LSR
Planar-LSR nutzt keine Tiefeninformationen pro Pixel, wie es bei Tiefen-LSR der Fall ist. Stattdessen wird der gesamte Inhalt auf Grundlage einer Ebene umprojiziert, die Sie für jeden Frame bereitstellen müssen.
Planar-LSR projiziert diese Objekte am besten um, die in der Nähe der angegebenen Ebene liegen. Je weiter entfernt ein Objekt ist, desto instabiler sieht es aus. Obwohl Tiefen-LSR bei der Umprojizierung von Objekten in unterschiedlichen Tiefen besser ist, kann Planar-LSR besser für Inhalte geeignet sein, die sich gut an einer Ebene ausrichten lassen.
Konfigurieren von Planar-LSR in Unity
Die Ebenenparameter werden von einem so genannten Fokuspunkt abgeleitet. Verwenden Sie WMR, muss der Fokuspunkt bei jedem Bild durch UnityEngine.XR.WSA.HolographicSettings.SetFocusPointForFrame
festgelegt werden. Ausführliche Informationen dazu finden Sie in der Fokuspunkt-API von Unity. Für OpenXR muss der Fokuspunkt wie im vorherigen Abschnitt gezeigt über die ReprojectionSettings
festgelegt werden.
Wenn Sie keinen Fokuspunkt festlegen, wird ein Fallback für Sie ausgewählt. Dieser automatische Fallback führt jedoch häufig zu suboptimalen Ergebnissen.
Sie können den Fokuspunkt selbst berechnen, aber es kann sinnvoll sein, ihn auf dem vom Remote Rendering-Host berechneten Wert basieren zu lassen. Rufen Sie RemoteManagerUnity.CurrentSession.GraphicsBinding.GetRemoteFocusPoint
auf, um diesen Wert abzurufen.
Normalerweise rendern sowohl der Client als auch der Host Inhalte, die der jeweils anderen Seite unbekannt sind, z. B. Benutzeroberflächenelemente auf dem Client. Daher ist es möglicherweise sinnvoll, den Remotefokuspunkt mit einem lokal berechneten zu kombinieren.
Die Fokuspunkte, die in zwei aufeinander folgenden Frames berechnet werden, können sich erheblich unterscheiden. Wenn Sie diese einfach unverändert verwenden, kann dies dazu führen, dass Hologramme zu „springen“ scheinen. Um dieses Verhalten zu verhindern, ist eine Interpolation zwischen den vorherigen und den aktuellen Fokuspunkten empfehlenswert.
Posenmodi für die Neuprojektion
Der allgemeine Problembereich beim Hybridrendering kann in dieser Weise angegeben werden: Remote- und lokale Inhalte befinden sich in unterschiedlichen Posen (d. h. Koordinatenräumen), da die Remotepose vom Server vorhergesagt wird, während die lokale Pose die reale aktuelle Pose ist. Am Ende eines Renderingframes müssen jedoch sowohl Remote- als auch lokaler Inhalt ausgerichtet und zur Anzeige gebracht werden. Die folgende Abbildung zeigt ein Beispiel, bei dem lokale und Remoteposen im Vergleich zum Anzeige-Viewport verschoben werden:
Je nach Verwendung von GraphicsBinding
stellt ARR bis zu drei Reprojektionsmodi bereit, die orthogonal zum oben beschriebenen LSR-Modus arbeiten. Diese Modi werden als Remote pose mode, Local pose mode und Passthrough pose mode bezeichnet. Im Gegensatz zum LSR-Modus definieren die Posenmodi, wie Remote- und lokale Inhalte kombiniert werden. Die Wahl des Modus nimmt eine Einschränkung der Darstellungsqualität lokaler Inhalte zugunsten der Leistung zur Laufzeit in Kauf, daher sollte bei Anwendungen sorgfältig überlegt werden, welche Option geeignet ist. Weitere Überlegungen dazu finden Sie weiter unten.
Remote pose mode
Remote pose mode ist der Standardmodus in ARR. In diesem Modus wird der lokale Inhalt über dem eingehenden Remotebildstream mithilfe der Remotepose aus dem Remoteframe gerendert. Anschließend wird das kombinierte Ergebnis für die endgültige Neuprojektion an das Betriebssystem weitergeleitet. Bei diesem Ansatz wird zwar nur eine Neuprojektion verwendet, die endgültige Korrektur basiert jedoch auf dem Roundtripintervall, sodass der vollständige Neuprojektionsfehler auch auf den lokalen Inhalt angewendet wird. Infolgedessen kann das große Korrekturdelta zu erheblichen Verzerrungen der lokalen Geometrie führen, einschließlich Elementen der Benutzeroberfläche.
Ausgehend von der Abbildung oben wird in Remote pose mode die folgende Transformation angewendet:
Local pose mode
In diesem Modus wird die Neuprojektion in zwei unterschiedliche Schritte aufgeteilt: Im ersten Schritt wird der Remoteinhalt neu in den lokalen Posenraum projiziert, das ist der Raum, in dem standardmäßig bei VR/AR-Geräten der lokale Inhalt gerendert wird. Anschließend wird der lokale Inhalt mithilfe der üblichen lokalen Pose auf diesem vorab verschobenen Bild gerendert. Im zweiten Schritt wird das kombinierte Ergebnis für die endgültige Neuprojektion an das Betriebssystem weitergeleitet. Da bei dieser zweiten Neuprojektion nur ein kleines Delta auftritt – dasselbe Delta, das verwendet werden würde, wenn ARR nicht vorhanden wäre – werden die Verzerrungsartefakte für lokale Inhalte erheblich verringert.
Entsprechend sieht die Abbildung wie diese aus:
Passthrough pose mode
Dieser Posenmodus verhält sich im Wesentlichen wie Remote pose mode, was bedeutet, dass die lokalen und Remoteinhalte im Remotebereich kombiniert werden. Der Inhalt wird jedoch nach der Kombination nicht neu projektiert, bleibt aber im Remote-Posenbereich. Der Hauptvorteil dieses Modus besteht darin, dass das resultierende Bild von Reprojektionsartefakten nicht beeinflusst wird.
Konzeptionell kann dieser Modus mit herkömmlichen Cloudstreaming-Anwendungen verglichen werden. Aufgrund der hohen Latenz ist er nicht für Head-Mounted-Szenarien geeignet, sondern eine lebensfähige Alternative für Desktop- und andere Flatscreen-Anwendungen, bei denen eine höhere Bildqualität gewünscht wird. Es ist daher zurzeit nur auf GraphicsBindingSimD3D11
verfügbar.
Überlegungen zu Leistung und Qualität
Die Wahl des Posenmodus hat Auswirkungen auf die Darstellungsqualität und die Leistung. Die zusätzlichen Laufzeitkosten auf der Clientseite für das Ausführen der zusätzlichen Neuprojektion im Local pose mode auf einem HoloLens 2-Gerät betragen etwa 1 Millisekunde GPU-Zeit pro Frame. Diese Zusatzkosten müssen berücksichtigt werden, wenn die Clientanwendung dem Rahmenbudget von 16 Millisekunden bereits nahe kommt. Andererseits gibt es Anwendungstypen ohne lokalen Inhalt oder mit lokalem Inhalt, der nicht anfällig für Verzerrungsartefakte ist. In diesen Fällen bietet Local pose mode keinen visuellen Vorteil, da die Qualität der Neuprojektion von Remoteinhalten nicht betroffen ist.
Der allgemeine Rat wäre daher, die Modi für jeden Anwendungsfall einzeln zu testen und zu prüfen, ob der Gewinn an Darstellungsqualität den zusätzlichen Leistungsaufwand rechtfertigt. Es ist auch möglich, den Modus dynamisch umschalten, z. B. den lokalen Modus nur zu aktivieren, wenn wichtige Elemente der Benutzeroberfläche angezeigt werden.
Ändern des Pose mode zur Laufzeit
Die folgende Client-API kann verwendet werden, um den Modus zur Laufzeit zu ändern:
RenderingSession session = ...;
session.GraphicsBinding.SetPoseMode(PoseMode.Local); // set local pose mode
ApiHandle<RenderingSession> session = ...;
session->GetGraphicsBinding()->SetPoseMode(PoseMode::Local); // set local pose mode
Im Allgemeinen kann der Modus jederzeit geändert werden, wenn das Grafikbindungsobjekt verfügbar ist. Es gibt einen wichtigen Unterschied bei GraphicsBindingSimD3D11
: Der Posenmodus kann nur in PoseMode.Remote
geändert werden, wenn er mit Proxytexturen initialisiert wurde. Wenn dies nicht der Fall ist, kann der Pose-Modus nur zwischen PoseMode.Local
und PoseMode.Passthrough
bis zur Neueinstellung der Grafikbindung umgeschaltet werden. Sehen Sie sich die beiden Überladungen von GraphicsBindingSimD3d11.InitSimulation
an, die entweder native Zeiger auf ID3D11Texture2D-Objekte (Proxypfad) oder width
und height
des gewünschten Benutzer-Viewports (Pfad ohne Proxy) verwenden.
Überlegungen zur Desktop-Unity-Runtime
Aufgrund des technischen Hintergrunds von GraphicsBindingSimD3D11
und der Funktionsweise des nicht sichtbaren Renderings in Unity erfordert die ARR-Unity-Runtime, dass der Benutzer beim Start von RemoteManagerUnity
wie folgt den gewünschten Posenmodus angibt:
public static void InitRemoteManager(Camera camera)
{
RemoteUnityClientInit clientInit = new RemoteUnityClientInit(camera, PoseMode.Remote);
RemoteManagerUnity.InitializeManager(clientInit);
}
Wenn PoseMode.Remote
angegeben wird, wird die Grafikbindung mit nicht sichtbaren Proxytexturen initialisiert, und das gesamte Rendering wird von der Hauptkamera der Unity-Szene an eine Proxykamera umgeleitet. Dieser Codepfad wird nur für die Verwendung empfohlen, wenn Änderungen am Runtimeposenmodus für PoseMode.Remote
erforderlich sind. Wenn kein Posenmodus angegeben ist, wählt die ARR Unity-Laufzeit je nach aktueller Plattform einen geeigneten Standardwert aus.
Warnung
Die Umleitung der Proxykamera ist möglicherweise nicht mit anderen Unity-Erweiterungen kompatibel, die ein Szenenrendering mit der Hauptkamera erwarten. Die Proxykamera kann über die Eigenschaft RemoteManagerUnity.ProxyCamera
abgerufen werden, wenn sie an anderer Stelle abgefragt oder registriert werden muss. Lesen Sie speziell für das Cinemachine
-Plug-In diesen Problembehandlungseintrag: Das Unity-Plug-In Cinemachine
funktioniert nicht im Remoteposenmodus.
Wird stattdessen PoseMode.Local
oder PoseMode.Passthrough
verwendet, wird die Grafikbindung nicht mit nicht sichtbaren Proxytexturen initialisiert, und es wird ein schneller Pfad verwendet, der die Hauptkamera der Unity-Szene zum Rendern nutzt. Wenn der jeweilige Anwendungsfall zur Laufzeit Änderungen am Posenmodus erfordert, muss bei der Initialisierung von RemoteManagerUnity
PoseMode.Remote
angegeben werden. Das direkte Rendern mit der Hauptkamera von Unity ist effizienter und kann Probleme mit anderen Unity-Erweiterungen verhindern. Daher empfiehlt es sich, den Nicht-Proxy-Renderingpfad zu verwenden.