Unity durchsuchen

XR-Grafik: Beste Vorgehensweisen und Problemlösungen

Last updated: January 2019

Was Sie von dieser Seite mitnehmen: Unmengen Tipps zur Optimierung Ihrer Shader und wie Sie den Post-Processing-Stack am effizientesten einsetzen. Dies ist ein großartiger, lesenswerter Artikel, wenn Sie Programmiererfahrung mit Unity haben und bereits (oder schon bald) XR-Inhalte (VR- und/oder AR-Inhalte) entwickeln.

Im Moment unterstützt Unity vier Rendering-Methoden für XR: Multi-Pass, Single-Pass, das vor Kurzem veröffentlichte Single-Pass-Instancing und Android-Single-Pass (ähnlich wie Single-Pass-Instancing).

Multi-Pass
  • Erfordert ein zweimaliges Durchgehen des Szenen-Graphs: für jede der beiden Texturen, die jeweils ein Auge repräsentieren, rendern Sie Ihre Objekte/Szenen einzeln.
  • Die GPU-Arbeitslast wird nicht auf die Texturen verteilt, was Multi-Pass zum am wenigsten effizienten Rendering-Pfad macht; aber es funktioniert standardmäßig auf den meisten Geräten und erfordert demnach kaum Änderungen.
  • Verteilt Culling-Rendering und teilweise das Rendering der Schattenerzeugung.
Single-Pass
  • Bei dieser Methode werden zwei Texturen in eine größere Textur gepackt (die sogenannte "Double-Wide-Textur").
  • Für das erste aufzurufende Objekt wird der Viewport (das "Ansichtsfenster" bzw. der Anzeigebereich) auf die linke Seite eingestellt und das Objekt wird aufgerufen; dann wechselt der Viewport auf die rechte Seite und das Objekt wird erneut aufgerufen.
  • Der Szenen-Graph wird nur einmal durchlaufen, der Prozess verläuft also auf der CPU schneller. Allerdings sind viele GPU-Statusänderungen erforderlich, um den Prozess durchzuführen.

Unity XR-Grafik

Ein einfaches Beispiel für Single-Pass-Stereo-Rendering

Single-Pass-Instancing
  • Verfügbar in Version 2017.2 und höher.
  • Die beiden Texturen, die die Augen repräsentieren, sind beide in einem Textur-Array enthalten (zwei Teile desselben Textur-Arrays).
  • Wenn der Viewport eingestellt ist, wird er automatisch auf beide Teile angewendet. Wird ein Aufruf ausgegeben, wird ein Instance-Draw durchgeführt, der doppelt so groß wie normal ist. Je nach Instanz bestimmt Unity, zu welchem Teil gerendert wird.
  • Erfordert die gleiche GPU-Arbeitslast, aber weniger Drawcalls und Arbeit für die CPU und ist somit deutlich performanter.
  • Verfügbar für:
    • Windows 10 mit D3D11 und aktuellen Grafiktreibern.
    • Hololens.
    • PS4.
  • Verwendet Multi-Pass, wenn die Erweiterung vom Zielgerät nicht unterstützt wird.
  • DrawProceduralIndirect(...) erfordert manuelle Änderungen.

Unity XR-Grafik

Ein Beispiel für Single-Pass-Instancing

Fügen Sie Ihren Shadern diese Makros hinzu, wenn Sie Single-Pass-Instancing verwenden

Wenn Sie Single-Pass-Instancing verwenden möchten, empfehlen wir Ihnen, Ihren Vertex- und Fragment-Shadern die folgenden Makros hinzuzufügen. Das Hinzufügen ist ganz einfach, und alle in Unity integrierten Shader sind so aktualisiert, dass sie diese Makros unterstützen.

Für Vertex-Shader:

Unity XR-Grafik

Shader-Code ohne Makros.

Unity XR-Grafik

Derselbe Code mit hinzugefügtem ersten Makro ...

Unity XR-Grafik

Das zweite Makro ...

Unity XR-Grafik

Das dritte ...

Unity XR-Grafik

Und das vierte hinzugefügte Makro.

Wenn Sie unity_StereoEyeIndex im Fragment-Shader verwenden müssen, tun Sie Folgendes:

Unity XR-Grafik

Einige Unterschiede bei der Verwendung von RenderScale im Vergleich zu RenderViewportScale

Nehmen wir an, Sie möchten die Größe einer Textur von 1 auf 0,5 ändern. Wenn Sie dies mit RenderScale tun, wird die Originaltextur gelöscht und eine neue Textur mit der Größe von einem Viertel des Originals erstellt, 0,5 in jeder Dimension.

Dynamisch durchgeführt ist dies ein aufwändiger Vorgang, während Ihr Spiel läuft – RenderViewportScale wäre hier die effizientere Wahl. Es setzt den Viewport auf einen kleineren Abschnitt derselben Textur und rendert zu diesem Abschnitt, und zwar so:

Unity XR-Grafik

Dies ist wesentlich effizienter, allerdings treten dabei einige Probleme auf, da Sie nur einen Teil der Textur verwenden (Näheres dazu finden Sie unten bei den weiteren Tipps zu RenderViewportScale).

RenderScale löscht also Texturen und erstellt dann neue Texturen, während RenderViewportScale den Viewport modifiziert. Weitere Unterschiede sind:

  • XRSettings.eyeTextureResolutionScale ist für die tatsächliche Texturgröße, während XRSettings.renderViewportScale sich auf den Viewport bezieht, der für das Rendern benutzt wird.
  • XRSettings.renderViewportScale wird nicht unterstützt, wenn Deferred Rendering benutzt wird.
  • Skalierung wird auf beide Dimensionen angewendet, ein Wert von 0,5 führt also zu einem Bild von 25 % der Originalgröße.
Der Post-Processing-Stack in XR: Herausforderungen und Lösungen

Die neueste Version des hervorragenden Post-Processing-Stacks ist da. Die Verwendung des Stacks für XR-Inhalte hält einige Herausforderungen bereit. Im nächsten Abschnitt erfahren Sie, mit welchen Lösungen Sie ihnen begegnen können.

Herausforderung

Um viele der neuesten Nachbearbeitungseffekte zu nutzen, müssen Sie zu einem "zwischengeschalteten" Render-Target rendern, der auf Source-Target-Größe und -Eigenschaften basiert:

  • Im Fall von "Double-Wide" ist die Render-Textur mindestens doppelt so breit wie der jeweile Anteil für das einzelne Auge.
  • Beim Single-Pass-Instancing und dem ähnlichen Android-Single-Pass muss ein Textur-Array erstellt werden, mit einem Teilbereich je Auge.

Lösung

  • Um bei der Erzeugung des richtigen Bildschirm-Texturformats Fehleinschätzungen zu vermeiden und Zeit zu sparen, bietet Unity XRSettings.eyeTextureDesc (Version 2017.2 und höher).
  • Dies gibt einen RenderTextureDescriptor aus, der vom Engine-XRTexture-Manager bezogen wird, das heißt, er ist so konfiguriert, dass er den Engine-verwalteten Texturen entspricht.
  • Sie können RenderTextureDescriptor auch von einer Source-Textur abfragen, wenn diese verfügbar ist. Wenn Sie beispielsweise die ältere MonoBehaviour.OnRenderImage-Infrastruktur verwenden, verfügen Sie über diese Source-Textur und können den Descriptor direkt darüber erhalten.
  • Indem Sie eyeTextureDesc verwenden, können Sie sämtliche RenderTexture-Zuweisungen mit dem vorhandenen RenderTextureDescriptor ersetzen, was effizienter als die manuelle Erzeugung von Parametern ist.
  • RenderTextureDescriptor ist eine einfachere API; sie entspricht den zugrunde liegenden APIs. Wenn Sie explizite Argumente verwenden, packt Unity diese in einen RenderTextureDescriptor und leitet alles in die Core-Engine weiter (in RenderTexture.GetTemporary oder CommandBuffer.GetTemporaryRT). Sie verwalten also auf der Sript-Seite, statt die Verwaltungsaufgaben von einem Zwischen-Layer durchführen zu lassen.

Herausforderung

Bei XR gibt es einen Unterschied zwischen der physikalischen Render-Texturgröße und logischer Bildschirm-/Augengröße.

Lösung

  • Für die zwischenzeitliche Render-Texturzuweisung verwenden Sie eyeTextureDesc, da dies die physikalischen Texturen zuweist.
  • Wenn Sie Script- oder Shader-Logik haben, die auf der Bildschirmgröße basiert (wenn Sie beispielsweise Bildschirm-Parameter in einem Shader verwenden oder eine Textur-Pyramide erstellen), wollen Sie diese auf der logischen Größe basieren. Dafür können Sie XRSettings.eyeTextureHeight und XRSettings.eyeTextureWidth verwenden.
  • Diese repräsentieren "Je Auge"-Texturgrößen, die notwendig sind, um die Bildschirmgröße zu erkennen, die Sie verwenden.
  • Beachten Sie, dass im Falle von "Double-Wide" eyeTextureWidth nicht ganz halb so breit wie eyeTextureDesc ist. Das liegt am Descriptor, der für die Texturzuweisung zunächst die Breite verdoppelt und dann etwas "auffüllt", um sicherzustellen, dass alles für das Mip-Mapping eingestellt ist.

Hier ist ein Code-Beispiel, das Sie vor der Verfügbarkeit von eyeTextureWidth verwendet haben könnten, um zu bestimmen, welche Breite für den Bildschirm festgelegt werden soll:

Unity XR-Grafik

Jetzt können Sie die Bildschirmbreite einfach aus eyeTexturewidth beziehen und beispielsweise für ein Seitenverhältnis verwenden, wie Sie im Script-Beispiel unten sehen:

Unity XR-Grafik

Herausforderung

Wie stellen Sie sicher, dass Ihre Textur-Koordinaten für Stereo korrekt sind und die Ausgabe für das richtige Auge erfolgt?

Wenn Sie mit einer Double-Wide-Textur arbeiten, müssen Sie sicherstellen, dass Ihre Texturkoordinaten aus der richtigen Hälfte der Textur entnommen werden (jede Hälfte entspricht einem Auge).

Lösung

Sie benötigen einen Textur-Koordinatenkorrektor für jedes Auge, und dafür bietet Unity zwei Lösungen:

1. TRANSFORM_TEX-Makro

  • This is a macro that works with _ST properties in ShaderLab. It’s most commonly used with MainTex_ST.
  • Die _ST-Eigenschaft wird vor jedem Auge im Single-Pass-Double-Wide-Modus aktualisiert, wobei Ihre Texturkoordinaten in den richtigen Bereich für das Auge umgeformt werden. Außerdem wird RenderViewportScale automatisch eingesetzt.

Unity XR-Grafik

  • Unity will populate _ST shader properties if you’re using Graphics.Blit or CommandBuffer.Blit:
    • MainTex_ST wird immer eingepflegt.
    • Andere _ST-Eigenschaften werden befüllt, wenn die Textur eine XR-Textur ist (VRTextureUsage!=None).
  • Dieses Makro funktioniert nicht mit benutzerdefinierten Blit-Prozessen wie BlitFullScreenTriangle im neuesten Post-Processing-Stack.

2. unity_StereoScaleOffset- und UnityStereoTransformScreenSpaceTex/TransformStereoScreenSpaceTex-Helper-Funktionen

unity_StereoScaleOffset ist ein Array von float4s:

  • UnityStereoTransformScreenSpaceTex
  • TransformStereoScreenSpaceTex

Es ist als Teil des Single-Pass-Konstantenblocks festgelegt (UnityStereoGlobals). Mit Unity_StereoEyeIndex wird darauf indiziert.

Im Fall von Double-Wide wird unity_StereoEyeIndex in einem Konstantenpuffer gebunden und jede Abfrage erhält die Aktualisierung für das richtige Auge. Der Wert für das linke Auge wird also in einen Konstantenpuffer gesetzt und das linke Auge fragt ab: Dann wird der Wert für das rechte Auge in den Konstantenpuffer gesetzt und das rechte Auge fragt ab. Die Shader-Instanzen wissen, dass sie für alle verwendeten Abfragen den korrekten Wert für den unity_StereoEyeIndex im Konstantenpuffer finden können.

Unity XR-Grafik

Sobald klar ist, dass unity_StereoEyeIndex korrekt befüllt ist, kann es mit der Gewissheit verwendet werden, dass es das richtige Element aus unity_StereoScaleOffset auswählt.

Unity XR-Grafik

Die Verwendung von unity_StereoScaleOffset hat allerdings auch Nachteile:

  • Es ist nur für Single-Pass verfügbar, Sie müssen also die Helper-Methoden einsetzen.
  • Die direkte Verwendung führt zu Multi-Pass und monoskopischer Darstellung und ergibt Shader-Kompilierungsfehler. Zusätzlich wird dabei RenderViewportScale nicht berücksichtigt.

Wie können Sie sicherstellen, dass Ihre Textur-Arrays richtig festgelegt werden und die Auswahl aus dem richtigen Teilbereich erfolgt?

Lösung

Verwenden Sie UNITY_DECLARE_SCREENSPACE_TEXTURE. Shader-Sprachen behandeln Textur-Arrays anders als "normale" Texturen. Single-Pass-Instancing und Android-Single-Pass verwenden Textur-Arrays, also muss sichergestellt werden, dass wir unsere Texturen richtig festgelegen.

Unity XR-Grafik

Ähnlich wie bei Double-Wide muss aus dem richtigen Augenbereich abgefragt werden; diese Augenbereiche werden in die Abschnitte des Textur-Arrays platziert. Wir können den richtigen Abschnitt über unity_StereoEyeIndex und durch die Verwendung des Makros UNITY_SAMPLE_SCREENSPACE_TEXTURE beziehen.

Unity XR-Grafik

Verwaltung von RenderViewportScale: Ein paar wichtige Punkte

  • unity_StereoScaleOffset bietet keinen Support.
  • Die meisten Nachbearbeitungseffekte verwenden CommandBuffer.Blit/Graphics.Blit, können also _MainTex_ST (oder andere _ST-Methoden) verwenden, um RenderViewportScale zu unterstützen.
  • Aber mit v.2 von Unitys Post-Processing-Stack können Sie diese Methoden nicht anwenden. Um das Problem zu umgehen, müssen Sie eine eigene Unterstützung bereitstellen. Dies ist ziemlich einfach, da v.2 des Stacks eine eigene Version der Shader-Infrastruktur verwendet, was bedeutet, dass sie leicht außer Kraft zu setzen ist. Tun Sie einfach Folgendes:

    Auf der Shader-Seite:

    • Erstellen Sie eine neue Konstante: Legen Sie "float rvsGlobal" in xrLib.hlsl fest
    • Bearbeiten Sie TransformStereoScreenSpaceTex nach, um rvsGlobal zu verwenden

    Auf der Script-Seite:

    • Verbinden Sie den RenderViewportScale-Wert als globale Eigenschaft des Scripts
    • Verwenden Sie Shader.SetGlobalFloat

    Unity XR-Grafik

Stereo-Clamping

Wenn Sie Neighborhood-Screen-Space-Samples in einem Shader erstellen, soll im Single-Pass-Double-Wide nicht versehentlich das falsche Auge oder außerhalb des gültigen RenderViewportScale-Bereichs abgefragt werden. Hier kommt UnityStereoClamp ins Spiel: Es "klemmt" die Koordinaten-Samples einfach an den richtigen Teil der Textur.

Unity XR-Grafik

Die Verwendung ist ganz einfach, allerdings ist eine manuelle Inspektion erforderlich, um Neighborhood-Samples zu finden.

Wo immer Sie von der durch Interpolation erzeugten Texturkoordinate abweichen, müssen Sie wahrscheinlich Unity.StereoClamp() verwenden.

  • Achten Sie auf Effekte, die durch das Sampling derselben Koordinate aufgrund von Clamping entstehen.
  • Verwenden Sie es nicht im Vertex-Shader.
Und noch ein paar Tipps:
  • Überlegen Sie sich, ob die von Ihnen verwendeten Nachbearbeitungseffekte für XR sinnvoll sind.
    • Most temporal effects add blurriness and latency
    • Simulating depth of field, a property of physical cameras, in a headset, which is simulating the human eye, will most likely cause nausea.
  • Wenn Sie Multi-Pass verwenden und History-Texturen beibehalten, benötigen Sie ein Set von History-Texturen je Auge.
    • Singular history set could/will lead to sharing history between eyes!
    • Works automatically with single-pass
  • Wenn Probleme auftreten, probieren Sie verschiedene Stereo-Rendering-Modi aus.
    • Nature of artifact can provide hints on where things might be going wrong.
Weitere Ressourcen

Wir wollen es wissen! Haben Ihnen diese Inhalte gefallen?

Ja, weiter so. Na ja. Könnte besser sein.
Alles klar

Wir verwenden Cookies, damit wir Ihnen die beste Nutzererfahrung auf unserer Website bieten können. Weitere Informationen erhalten Sie in unserer Cookie-Richtlinie.