Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

„Love and Deep Space“ ist ein surrealistisches, immersives 3D-Interaktions-Handyspiel zum Thema Liebe, das seit seiner Veröffentlichung am 18. Januar 2024 weltweit mehr als 70 Millionen Spieler verzeichnet und auf der Gamescom 2025 als bestes Handyspiel ausgezeichnet wurde.

Im Rahmen der heutigen Veranstaltung „Unite Shanghai 2025“ gaben die Engine-Entwickler von „Love and Deep Space“ erstmals einen detaillierten Einblick in die technischen Hintergründe des Spiels und enthüllten, wie sie mithilfe einer Render-Pipeline auf Film- und Fernseh-Niveau sowie realistischer und detailreicher physikalischer Effekte eine authentische und greifbare Welt für die Spieler erschaffen!

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

△ Der Saal war bis auf den letzten Platz gefüllt.

Die beliebteste Veranstaltung der diesjährigen Unity-Konferenz

Bei Diezhi gehören der Austausch und das kreative Schaffen zum Arbeitsalltag. Im vergangenen Jahr hat Diezhi fast 40 interne Veranstaltungen organisiert, an denen insgesamt 5.000 Mitarbeiter teilgenommen haben!Dabei entfielen über 50 % auf technische Vorträge! Die Referenten kamen aus verschiedenen Produktionsteams und technischen Abteilungen; die Inhalte umfassten praktische Erfahrungen und innovative Forschungsansätze aus unterschiedlichen Bereichen. In einer offenen Atmosphäre des Austauschs konnten Fachwissen und Ideen frei fließen und sich voll entfalten!

Im Folgenden finden Sie einen vollständigen Rückblick auf den technischen Vortrag zu „Love and Deep Space“:

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

Die Inhalte zum Rendering-Framework von „Love and Deep Space“ umfassen im Wesentlichen drei Bereiche: [Optimierung des Szenen-Renderings, Beleuchtungskonzepte und Pipeline-Design sowie Schattenoptimierung].Im Laufe der Entwicklung haben wir auf Basis von Unity 2019 tiefgreifende Änderungen am Quellcode der Engine vorgenommen und eine maßgeschneiderte SRP-Pipeline entwickelt. Derzeit läuft die Android-Version online mit GLES 3.1; in Zukunft wird auch eine Vulkan-Version veröffentlicht, um die Leistung kontinuierlich zu verbessern und den Anforderungen der Spieler an ein Spiel in hoher Qualität gerecht zu werden.

1. Optimierung der Szenenrendering

Im Rahmen der Optimierung der Szenenrendering-Prozesse haben wir ein Szenenrendering-System namens „RendererGroupRenderer“ entwickelt, bei dem jeder Rendering-Durchlauf als „RenderGroup“ bezeichnet wird. Mit diesem System haben wir die folgenden Funktionen realisiert:

  • Beschreibung der benutzerdefinierten statischen Szene: Wir haben die GameObjects entfernt, um Leistungseinbußen bei der Aktualisierung einer großen Anzahl von GameObjects zu vermeiden.
  • Optimierung der CPU→GPU-Upload-Häufigkeit: Dies umfasst hauptsächlich den Upload von InstanceData und ConstantBuffer. Was die Optimierung des InstanceData-Uploads betrifft, haben wir zu Beginn des Projekts für kleine Innenraumszenen einen statisch generierten InstanceDataBuffer in Verbindung mit einer BVH-Segmentierung und -Beschneidung verwendet.Im Laufe des Projekts stiegen die Anforderungen an die Szenengenauigkeit kontinuierlich an, sodass wir später dazu übergingen, das Trimmen und das Füllen der Instanzen auf der GPU-Seite durchzuführen. Die Optimierung des Uploads von „ConstantBuffer“ wird im nachfolgenden Abschnitt „Leistungsoptimierung pro DrawCall“ ausführlich erläutert.
  • Parallele Grobzusammenführung auf der CPU-Seite mittels Burst+Job-System: Für statische Objekte haben wir mithilfe des Burst+Job-Systems ein hochgradig paralleles Zusammenführungssystem realisiert, bei dem die Grobzusammenführung ausschließlich auf der CPU-Seite erfolgt, während die feinere Zusammenführung der GPU überlassen wird.

Datenformat von InstanceData

Die in der Branche häufig verwendete Form von InstanceData als Constant Buffer weist einige Nachteile auf, wie beispielsweise eine Größenbeschränkung von 64 KB, einen kleinen Constant Buffer sowie die Gefahr von Cache-Durchbrüchen bei dynamischer Indizierung, was zu Leistungseinbußen führt.Eine weitere gängige Methode ist die Übertragung von InstanceData über SSBO. Allerdings ist die Leseleistung bei diesem Ansatz in der Regel schlechter als bei einem Constant Buffer, bei dem kein Cache-Break-Through auftritt. Zudem unterstützen einige Android-Geräte unter GLES das Lesen von SSBO im Vertex-Shader nicht, was die Kompatibilität einschränkt.Zudem weisen beide Ansätze ein gemeinsames Problem auf: Sie sind auf dynamische Indizierung angewiesen und daher für die Leistung von Low-End-Smartphones ungünstig.

Als Lösung für die oben genannten Probleme schlagen wir einen Ansatz vor, bei dem „alter Wein in neuen Schläuchen“ zum Einsatz kommt – „Vertex Stream based Instance Data“.

  • Verwenden Sie den Vertex-Stream des „PerInstance“-Schritts als Instanzpuffer;
  • Nutzung des Vertex-Fetch-Caches, keine dynamischen Indizes erforderlich, hohe Cache-Trefferquote, keine Kompatibilitätsprobleme;
  • Die Ausgabe erfolgt über den ComputeShader in den Instance-Vertex-Buffer, wodurch eine GPU-gesteuerte Verarbeitung realisiert wird.

Verwendung des Vertex-Streams mit „PerInstance Step“ als Instance-Buffer – dies ist eine Instance-Methode, die bereits seit den Anfängen des GPU-Instancing unterstützt wird und sowohl die durch dynamische Indizierung verursachten Leistungsprobleme als auch Kompatibilitätsprobleme mit SSBO vermeidet.Außerdem können wir über den ComputeShader eine Ausgabe in den Instance-Vertex-Buffer vornehmen, um unter GLES eine GPU-gesteuerte Instanzierung mit hoher Kompatibilität zu realisieren.

Da die Unity-Engine auf der unteren Ebene den „PerInstance Step“-Vertex-Stream nicht unterstützt, haben wir schließlich entsprechende Anpassungen an der Engine vorgenommen. Letztendlich wird der oberen Ebene eine im CommandBuffer hinzugefügte Schnittstelle namens „DrawMeshInstancedTraditional“ zur Verfügung gestellt, die ein weiteres Mesh als Instanzdaten als Eingabe benötigt.Außerdem haben wir entsprechende Schnittstellen hinzugefügt, um die Vertex-Semantik für die einzelnen Datensegmente im Instanz-Mesh zu konfigurieren.

GPU-gestützt

Wir weisen den IndirectParameter-Puffer und den Instance-Datenpuffer im Voraus entsprechend der Anzahl der Gruppen und Instanzen zu (wobei beim Instance-Datenpuffer lediglich Speicherplatz im Voraus zugewiesen wird; die eigentlichen Daten werden erst beim GPU-Culling eingefügt).

Gleichzeitig berechnen wir den Instance-Offset jeder Gruppe im Voraus und speichern ihn im Parameter „InstanceStart“, wobei während des gesamten Vorgangs nur ein Instance-Buffer gebunden wird.

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

Darüber hinaus müssen wir einen Informationspuffer für jedes einzelne Objekt erstellen (der Informationen wie GroupID, LOD-Entfernungsbereich, Begrenzungen und Transformation enthält), um bei der GPU-Beschneidung die Eigenschaften jedes Objekts abrufen zu können.

  • CPU-Trimmung: Vor der GPU-Trimmung führen wir zunächst eine grobe CPU-Trimmung durch, um festzustellen, ob die Gruppe als Ganzes sichtbar ist.Ausgehend von einem Stamm-Bounding-Box vergleichen wir das Verhältnis zwischen der Summe der Volumina der Objekt-Bounding-Boxen und dem Volumen der zusammengefassten Bounding-Box. Liegt dieser Wert unterhalb eines Schwellenwerts, wird die Bounding-Box rekursiv aufgeteilt (hauptsächlich, um zu vermeiden, dass zwei Objekte zu weit voneinander entfernt sind und dadurch eine übergroße Gesamt-Bounding-Box entsteht).Gleichzeitig wird die Sichtbarkeit der Gruppe mithilfe von PVS weiter beurteilt. Da wir kein „IndirectExecute“ wie in DX12 haben, kann unser GPU-Culling lediglich die Anzahl der Instanzen reduzieren, aber nicht die Drawcalls der gesamten Gruppe eliminieren. Daher ist es notwendig, vollständig unsichtbare Gruppen durch CPU-Culling so genau wie möglich auszusortieren.
  • GPU-Trimming: Beim GPU-Trimming wird in einem einzigen Dispatch für alle Gruppen ein objektweises 3-Stufen-Trimming durchgeführt, das Sichtkegel-Trimming, LOD-Trimming und Hiz-Occlusion-Culling umfasst. Durch das Trimming wird die „Instance Count“ des Parameters um 1 erhöht und die „InstanceData“ ausgegeben.
  • Schattenausblendung: Wir haben uns an der von „Dragon’s Dogma“ vorgestellten Methode orientiert und die Bildtiefe erneut in den Schattenraum projiziert, um eine „Shadow Receiver Mask“ zu erstellen. Wenn sich das vom „Shadow Caster“ projizierte Volumen nicht mit der Maske überschneidet, kann es ausgeblendet werden, um das Rendern überflüssiger Schatten zu vermeiden.

Was den Abschnitt „Warum wir keine Cluster/Meshlets implementiert haben“ betrifft, so ist zum einen der grundlegende Overhead auf Mobilgeräten relativ hoch, und zum anderen gibt es bei der Implementierung von Clustern unter GLES Kompatibilitätsprobleme. Nach Abwägung aller Faktoren sind wir zu dem Schluss gekommen, dass die Optimierung der Leistung pro DrawCall uns eine kostenlose und direkte Leistungssteigerung bringt.

Leistungsoptimierung für einzelne DrawCalls

Bei unseren bisherigen Beobachtungen haben wir festgestellt, dass bei vielen Optimierungen der CPU-Laufzeit beim Rendern oft zu viel Wert auf die Anzahl der DrawCalls gelegt wird, während die Laufzeit jedes einzelnen DrawCalls vernachlässigt wird. Wir sind der Ansicht, dass die Reduzierung der Anzahl der DrawCalls nur eine Optimierungsmethode unter vielen ist und dass letztendlich die CPU-Laufzeit der einzige Maßstab ist.

Moderne Mobilgeräte und Grafikstandards sind bereits seit Langem in der Lage, eine große Anzahl von DrawCalls zu bewältigen. Dies wurde auch vom HypeHype-Engine-Team auf der Siggraph 2023 vorgestellt – sie testeten 10.000 DrawCalls mit verschiedenen Meshes und Texturen auf einem iPhone 6s, was nur 11,27 ms dauerte.Auch andere vergleichbare Android-Geräte können im Grunde genommen eine Bildrate von über 60 Bildern pro Sekunde aufrechterhalten. Als Metal im Jahr 2014 gerade erst auf den Markt kam, wurde bereits das Motto ausgegeben, zehnmal mehr DrawCalls als GLES zu verarbeiten.

Auch heute, 11 Jahre später, sind wir immer noch mit einer zu hohen Anzahl an DrawCalls konfrontiert. Dies ist hauptsächlich auf verschiedene Belastungsfaktoren zurückzuführen, darunter zu häufige PSO-Wechsel, das Übermitteln und Kopieren von Puffern, die Rendering-Logik der Engine sowie zu viele RHI-Schnittstellenaufrufe, die alle die CPU zusätzlich belasten.Daher sind wir der Ansicht, dass man sich bei der Leistungsoptimierung nicht nur auf die Anzahl der DrawCalls konzentrieren sollte, sondern all diese Faktoren umfassend berücksichtigen muss.

  • Optimierung des PSO-Wechsels: Hängt hauptsächlich vom Kompromiss zwischen der Anzahl der Shader-Varianten und der Shader-Komplexität im jeweiligen Projekt ab.Die RenderGroup-Renderwarteschlange wird nach der Priorität von Shadern, Materialien und Meshes sortiert. Gleichzeitig wenden wir eine spezielle Behandlung für Schatten an: Materialien ohne Alpha-Test werden einheitlich mit demselben Shader für die Schatten-Tiefe gerendert, um die Häufigkeit der PSO-Wechsel beim Rendern von Schatten zu reduzieren.
  • Optimierung der Pufferübertragung: Unter GLES verursachen das Zuweisen und Aufheben der Zuweisung von Puffern einen erheblichen Overhead. Das von modernen RHI unterstützte „Persistent Map“-Verfahren kann zwar die Upload-Dauer deutlich verkürzen, kann jedoch die mehrfachen Kopiervorgänge sowie die memcmp-Operationen beim Datentransfer vom Hauptthread zum Render-Thread und schließlich in den Pufferspeicher nicht vermeiden.Daher haben wir die folgenden drei gezielten Strategien angewendet, um den Buffer-Upload deutlich zu reduzieren:
    • PerRendererBuffer speichert die Parameter für jeden einzelnen Renderer (z. B. die Umgebungsbeleuchtung SH, der ein Objekt ausgesetzt ist) in einem Uniform-Buffer, der vom Renderer-Objekt verwaltet wird, und bindet diese beim Rendern direkt;
    • PerShaderBuffer ist für Uniform-Buffer gedacht, die nicht für jedes Material separat angepasst werden müssen, und wird nur einmal beim Shader-Wechsel übermittelt. Im Vergleich zu PerRendererBuffer ist PerShaderBuffer flexibler und unterstützt verschiedene Shader-Varianten;
    • Für PerMaterialBuffer haben wir den Code des SRP-Batchers genutzt, um die Buffer für jedes Material vorab zu generieren und direkt zu binden.
  • Optimierung der Rendering-Logik: Um Flexibilität und Stabilität zu gewährleisten, führen kommerzielle Spiel-Engines beim Rendern komplexe logische Abwägungen durch. In der Unity-Engine wird beispielsweise bei jedem Aufruf von „Draw“ zunächst die Funktion „ApplyMaterial“ aufgerufen, die vor dem Rendern alle Rendering-Zustände und -Parameter aktualisiert. Bei einer großen Anzahl von „DrawCalls“ entsteht dadurch ein erheblicher Zeitaufwand. Daher haben wir folgende Optimierungen vorgenommen:
    • Die Schnittstelle „ApplyMaterial“ wurde separat ausgegliedert und wird von der übergeordneten Ebene nur dann aktiv aufgerufen, wenn ein Material oder ein Parameter gewechselt werden muss;
    • Verwenden Sie die vereinfachte, spezielle Schnittstelle, wenn Sie nur „PerMaterialBuffer“ ändern.

Nach der Optimierung hat sich die Ausführungszeit unserer CPU bei gleicher Anzahl an DrawCalls um ein Drittel verkürzt.

  • Optimierung der RHI-Aufrufe: Das Hauptziel der Optimierung der RHI-Aufrufe besteht darin, die Anzahl der Grafik-API-Aufrufe – mit Ausnahme von „Draw Primitive“ – zu reduzieren. Zu den konkreten Optimierungsmaßnahmen gehören:
    • Zusammenführung von Vertex- und Index-Buffern mit identischem Stride, um das Binden von VB/IB bei jedem Draw Call zu vermeiden, wodurch der Zeitaufwand um 15 % reduziert wird;
    • Wenn sich die Ressource nicht geändert hat, wird die Konfiguration des DescriptorSet übersprungen, wodurch sich der Zeitaufwand um weitere 30 % reduziert; wenn der Vorgang „SetDescriptors“ selbst viel Zeit in Anspruch nimmt und das Wechseln des Descriptors zudem den Zeitaufwand für den nächsten Zeichnungsvorgang erhöht, wird dies im Best-Practice-Leitfaden von Arm erläutert.

Wir haben die Laufzeit von 5.000 DrawCalls auf Android-Geräten der unteren Preisklasse getestet. Bei Verwendung des nativen Renderers der Engine betrug die Laufzeit des Render-Threads 34,79 ms.Nachdem wir die Buffer-Übermittlung und die Rendering-Logik optimiert hatten, sank die Laufzeit auf 22,97 ms. Durch eine weitere Optimierung der Anzahl der RHI-Aufrufe konnte die Laufzeit noch einmal deutlich auf 11,8 ms gesenkt werden. Letztendlich konnten wir die CPU-Laufzeit bei gleichbleibender Anzahl an DrawCalls auf weniger als ein Drittel des ursprünglichen Wertes reduzieren.

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

Ergebnisse der Benchmark-Tests zu weiteren Optimierungsversuchen

Wir haben außerdem einige neue RHI-Funktionen ausprobiert, darunter:

  • Multi-Draw Indirect (MDI): Bringt auf unterstützten Geräten deutliche Optimierungen mit sich und behebt bis zu einem gewissen Grad das Problem, dass die GPU-Occlusion-Culling möglicherweise leere DrawCalls übergibt (weniger Übermittlungen auf der CPU-Seite);
  • Bindless: Die Leistung von Bindless ist jedoch nicht zufriedenstellend; selbst auf den neuesten Android-Geräten treten mysteriöse negative Optimierungen auf. Durch die Kombination von MDI und Bindless können wir zwar fast alle Objekte mit einem einzigen DrawCall rendern, doch die CPU-Laufzeit ist dabei sogar höher als ohne Batch-Verarbeitung. Dies ist ein Beispiel dafür, was passieren kann, wenn man sich zu sehr auf die Anzahl der DrawCalls konzentriert.Natürlich hoffen wir, dass zukünftige Mobilchips Bindless besser unterstützen werden. Derzeit versuchen wir, auf Basis von Unity Texture Streaming ein Feedback-freies SVT-System als Alternative zu entwickeln; diese Lösung befindet sich noch in der Testphase.

Den Ergebnissen der Benchmark-Szenarien zufolge hat der RenderGroupRenderer im Vergleich zum ursprünglichen Rendering ohne Instanzierung die Anzahl der DrawCalls um ein Drittel reduziert,die Laufzeit der Render-Threads wurde um drei Viertel deutlich verkürzt, und auch die Laufzeit des Haupt-Threads sank um zwei Drittel (obwohl die Laufzeit in C# zunahm, führten die native Clipping-Verarbeitung der Engine und die verkürzte Laufzeit der GameObject-Aktualisierungen insgesamt dennoch zu einer erheblichen Optimierung).

2. Beleuchtungskonzept

Beleuchtungskonzept

  • Forward-Rendering-Pipeline:

Wir haben uns in diesem Projekt für die Forward-Rendering-Pipeline entschieden, wobei wir folgende Aspekte berücksichtigt haben: Erstens bietet die Forward-Pipeline Vorteile bei der Bewältigung komplexer und vielschichtiger grafischer Anforderungen, und wir müssen uns keine Gedanken darüber machen, ob das Hinzufügen bestimmter Materialeigenschaften zu einer Vergrößerung des GBuffers führt.

Zweitens sind herkömmliche Deferred-Pipelines für mobile Plattformen hinsichtlich der Bandbreite nicht besonders geeignet. OnePassDeferred weist hingegen einige Einschränkungen hinsichtlich der Flexibilität auf, beispielsweise ist es nicht möglich, die Größe des RT während des RenderPasses zu ändern, und es können keine Pixelinhalte außerhalb der aktuellen Position abgerufen werden.

Unter GLES gibt es auch Kompatibilitätsprobleme bei „FrameBufferFetch“: Verschiedene Chips unterstützen eine unterschiedliche Anzahl von RTs; manche unterstützen nur einen RT, sodass eine Umsetzung über PLS erforderlich ist. Unsere Tests haben jedoch gezeigt, dass die Leistung von PLS nicht zufriedenstellend ist.

Außerdem reichen die vier objektbezogenen Lichtquellen, die die Engine standardmäßig bereitstellt, für größere Objekte nicht aus, weshalb wir „Forward+“ ausprobiert haben. Allerdings ist „Forward+“ auf älteren Geräten zu rechenintensiv. Wenn man die maximale Anzahl der Lichtquellen pro Kachel begrenzt, lässt sich die Anzahl der Lichtquellen innerhalb einer Kachel bei Kamerabewegungen nicht kontrollieren, und ein Überschreiten des Grenzwerts führt zu Darstellungsfehlern.

Um diese Probleme zu lösen, haben wir eine horizontale Aufteilung des Spielraums in Kacheln eingeführt – standardmäßig mit einer Kachelgröße von 2 Metern, verteilt vor der Kamera, mit maximal 4 Lichtquellen pro Kachel und einer 128×128-Indexkarte. Diese Aufteilung sorgt für stabile Überlappungen der Lichtquellen innerhalb der Kacheln und erleichtert es, bei der Produktion rechtzeitig Probleme mit Überschreitungen zu erkennen.

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!
  • Verbesserungen an der Vulkan-Versionspipeline

Wir haben in die Pipeline für zukünftige Vulkan-Versionen einen auf Subpass basierenden Light Pre-Pass integriert.

Im Pre-Z-Pass geben wir einen vereinfachten GBuffer-RT aus und speichern ihn.Da unsere lokale Beleuchtung ein vereinfachtes PBR-Modell ohne Fresnel-Effekt verwendet, müssen wir im GBuffer weder Specular noch Albedo ausgeben, sondern packen lediglich Normal, Roughness und einige spezielle Material-IDs oder Attributinformationen in einen RGBA8-GBuffer.Anschließend können wir einen Lichtvolumen-Rendering-Prozess ähnlich dem Deferred Shading durchlaufen und die Ergebnisse der Geometriebeleuchtung im Tile Memory speichern.

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

Anschließend zeichnen wir das Objekt im Shading-Pass erneut und rufen diese Beleuchtungsdaten ab. In Kombination mit den beim Rendern ermittelten Materialeigenschaften wie der Albedo erhalten wir so das endgültige Beleuchtungsergebnis.

Wir kodieren den für TAA erforderlichen MotionVector als RGBA8, wobei R + G == 0 bedeutet, dass keine gültige Geschwindigkeit vorliegt. Auf diese Weise können bestimmte Materialien, die keine Geschwindigkeit ausgeben, andere Informationen im BA-Kanal speichern.

Bei einfachen Vegetationselementen, die in großer Anzahl vorkommen, speichern wir beispielsweise deren UV-Informationen im BA-Kanal von MotionVector. Auf diese Weise müssen wir im Shading-Pass lediglich im Nachbearbeitungsprozess die Geometrieinformationen aus dem GBuffer und die UV-Informationen aus MotionVector abrufen, um das Materialverhalten der Vegetation wiederherzustellen.

Der Pipeline-Ablauf in der Vulkan-Version sieht im Großen und Ganzen wie folgt aus: Zunächst gibt der PreZ-Pass die Tiefenwerte, den GBuffer und den Bewegungsvektor aus, anschließend wird die Okklusionsausblendung der Schatten berechnet,anschließend wird das Tiefen-Rendering der Schatten durchgeführt, gefolgt von Berechnungen wie Ambient Occlusion (AO) und Screen-Space-SSS. Danach gelangen wir in den NativeRenderPass, in dessen SubPass die Schattenmaske (ShadowMask) und der Licht-Pre-Pass berechnet sowie der normale Shading-Pass ausgeführt werden. Schließlich wird der RenderPass beendet und es werden weitere Post-Processing-Pässe ausgeführt.

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

Die Verbesserungen an der Vulkan-Versionspipeline weisen jedoch gewisse Einschränkungen auf. So kann beispielsweise der „Light Pre-Pass“ nur das Standard-Beleuchtungsmodell ersetzen; für Beleuchtungsmodelle, die mehr Gbuffer-Kanäle erfordern, muss weiterhin „Forward+“ verwendet werden.

Wir bieten jedoch eine Option zur Auswahl einzelner Lichtquellen an, mit der das „Standard Lit Model“ für eine bestimmte Lichtquelle erzwungen und auf alle Materialien einheitlich angewendet werden kann. Auf diese Weise lassen sich – unter Verzicht auf die Genauigkeit des Beleuchtungsmodells – Pixel innerhalb derselben Kachel von mehr als vier Lichtquellen beeinflussen.

  • Guten Morgen

Für den Bereich der diffusen GI verwenden wir den eher traditionellen Ansatz mit Lightmaps und Light Probes, wobei die Lightmaps ausschließlich Informationen zum indirekten Licht speichern.während wir bei den Light Probes neben dem üblichen Modus mit einem einzelnen Abtastpunkt pro Objekt auch einen Modus mit mehreren Abtastpunkten anbieten, bei dem für jedes Objekt mehrere Abtastpunkte festgelegt werden können, wobei die Interpolation anhand der Schwerpunktskoordinaten von Liniensegmenten, Dreiecken oder Tetraedern erfolgt.

In den beiden folgenden Vergleichsbildern zeigt das linke Bild das Ergebnis mit einem einzigen Messpunkt, wobei der untere Rand des Rechtecks einheitlich von Umgebungslicht beleuchtet wird; das rechte Bild zeigt hingegen das Ergebnis mit zwei Messpunkten, wobei zu erkennen ist, dass die linke und die rechte Seite unterschiedlich von indirektem Licht beleuchtet werden.

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

Was die Specular-GI betrifft, stützen wir uns hauptsächlich auf Reflection Probes, die mit AABB-Korrektur versehen sind. Bei bestimmten Boden- oder Wasserflächen verwenden wir darüber hinaus auch flache Reflexionsproxies. Man kann sich das im Großen und Ganzen als eine Art HLOD vorstellen, das speziell für die Darstellung von Reflexionen gedacht ist.

Darüber hinaus haben wir uns an der Vorgehensweise von „God of War“ orientiert und die CubeMap der Reflection Probes normalisiert. Konkret bedeutet dies, dass anhand der Pixel der CubeMap ein SH-Koeffizient für die Umgebungsbeleuchtung generiert wird. Anschließend wird die Pixelfarbe der CubeMap durch die Umgebungsbeleuchtung in dieser Richtung dividiert, um eine normalisierte CubeMap zu erhalten.Beim eigentlichen Rendering wird dann die tatsächliche Umgebungsbeleuchtung, die jedes Pixel in Reflexionsrichtung empfängt, mit dem entsprechenden CubeMap-Pixel multipliziert, um die Reflexionsfarbe wiederherzustellen.

Der Vorteil dieser Vorgehensweise besteht darin, dass selbst wenn eine große Anzahl von Objekten dieselbe Reflection Probe abtastet, die Reflexionen aus verschiedenen Bereichen unterschiedliche Hell-Dunkel-Unterschiede erzeugen können.

3. Optimierung der Schatten

Funktionsdesign

Das grundlegende Design unseres Schattensystems sieht wie folgt aus:

  • CSM+ für Stufe 3 mit Charakter-Nahaufnahmeschatten / POSM für mehrere Charaktere: CSM+ der Stufe 3 von Cascade mit charakter-spezifischen Nahaufnahmeschatten; in bestimmten Szenen mit mehreren Charakteren wird POSM (Per-Object Shadow Map) verwendet;
  • Unterstützt die Projektion von zwei Kegelstrahlern;
  • ScreenSpaceShadowMask: Das Ergebnis der oben genannten Schattenberechnung wird auf eine RGBA8-ScreenSpaceShadowMask ausgegeben;
  • R: Richtungsschatten, G: Lokaler Schatten 1, B: Lokaler Schatten 2, A: AO: Der R-Kanal speichert die Schatten der Hauptlichtquelle, die Kanäle G und B speichern die Schatten der Kegellampen, und der A-Kanal speichert die AO-Informationen.

Abstand zum Ausschluss

Zunächst haben wir eine einfache Entfernungsausblendung vorgenommen, bei der die Tiefenwerte der Dreiecksknoten im ScreenSpaceShadow-Post-Processing entsprechend der Schattenentfernung angepasst wurden. Anschließend wurde mit „ZTest Greater“ gerendert, um die Schattenberechnungen außerhalb der Schattenentfernung auszublenden.

Da bei der Berechnung von Schatten die Tiefe abgetastet werden muss, benötigen wir zwei Kopien der Tiefe, die jeweils für „Test“ und „Sample“ verwendet werden. Wir kopieren im „NativeRenderPass“ eine Kopie des „Memoryless“-Tiefenpuffers für „Test“, um zusätzliche Lese- und Schreibbandbreite so weit wie möglich zu vermeiden.

Erkennung von Halbschattenbereichen

Wir haben eine Funktion zur Erkennung von Halbschattenbereichen hinzugefügt: Zunächst wird ein PCF bei 1/4 der Auflösung berechnet, anschließend wird im Shadow-Pass bei voller Auflösung eine 1/4-Maske abgetastet, und nur für Pixel, deren Schattenwerte im mittleren Bereich liegen, wird ein PCF bei voller Auflösung durchgeführt. Auf diese Weise wird der Rechenaufwand reduziert, ohne dass die Bildqualität beeinträchtigt wird.

Um zu vermeiden, dass es dabei zu Ungenauigkeiten bei der Pixelerkennung in bestimmten Details kommt, berechnen wir zwei Sätze von Normalen: einen auf der Grundlage der Teilableitung der Position im 1/4-Puffer und einen auf der Grundlage der vier Tiefenwerte des Gather-Datensatzes in voller Auflösung. Ist der Winkel zwischen den Normalen größer als der Schwellenwert, wird das Pixel mit niedriger Auflösung als unzuverlässig eingestuft und es wird zwangsweise ein PCF in voller Auflösung durchgeführt.

Im Folgenden ist die Debug-Ansicht der Szene dargestellt. Die roten Bereiche haben wir als Halbschattenbereich identifiziert; nur für diese Pixel wird die PCF in voller Auflösung ausgeführt.

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

Pixelweise Vorspannung

Wir haben den „Shadow Bias“ auf Pixelebene mithilfe des „Receiver Plane Depth Bias“-Algorithmus implementiert. Das Prinzip dahinter ist relativ einfach: Zunächst wird die Kettenregel in zwei Dimensionen auf die Partialableitungen der Schattenkoordinaten im Bildschirmraum angewendet, um die Partialableitungen im Schattenraum zu berechnen.

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

Mithilfe der partiellen Ableitung und des PCF-Abtastversatzes können wir den Bias-Wert berechnen. Für den Mittelpunkt haben wir das Bias-Ergebnis mit einem Versatz von einem Pixel als Start-Bias hinzugefügt.

Die folgende Abbildung zeigt die Vergleichsergebnisse zwischen festem Bias und pixelweiser Bias-Einstellung:

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

Auf dem linken Bild wird ein fester Bias-Wert verwendet. Man sieht, dass am unteren Rand der Box ein Bereich mit Lichteinfall vorhanden ist und dass auf den Oberflächen, die nahezu senkrecht zur Beleuchtungsrichtung stehen, teilweise Verzerrungen durch Eigenschatten auftreten. Nach der Anwendung eines pixelweisen Bias (rechtes Bild) erhöhen wir den Bias nur in den Bereichen mit hoher Teilableitung, wodurch wir das Problem der Verzerrungen durch Eigenschatten lösen können, ohne die Detailwiedergabe zu beeinträchtigen.

Wenn die Bildschirmtiefe jedoch nicht kontinuierlich ist, kann die pixelgenaue Bias-Einstellung zu falschen Ergebnissen führen, was wiederum Lichtlecks zur Folge haben kann. Um dieses Problem zu beheben, muss der Grafiker den maximalen und minimalen Bereich der Bias-Einstellung manuell festlegen.

Scrollen einer zwischengespeicherten Schattenkarte

Für Szenarien mit einer hohen Anzahl an DrawCalls haben wir außerdem „Scrolling Cached Shadow Map“ getestet, was im Einzelnen Folgendes umfasst:

  • Caching der CSM-Tiefe: Bei Objekten, die sowohl im vorherigen als auch im aktuellen Frame vollständig vom Schattenkegel umschlossen sind, wird die CSM des vorherigen Frames auf die Projektionsposition des aktuellen Frames übertragen, um direkt die Schattentiefe zu ermitteln und so ein direktes Rendern des Objekts zu vermeiden;
  • Das Scrolling wird nur auf die letzte Kaskadenebene angewendet. Wenn der Kaskadenbereich relativ klein ist, schneiden sich viele Objekte mit dem Sichtkegel, wodurch die Optimierungswirkung eingeschränkt wird;
  • Den Cache in Abständen von mehreren Frames aktualisieren, um die Belastung der Bandbreite zu verringern.

In Zukunft planen wir außerdem, Local ShadowMap Atlas sowie Caching-Mechanismen zu unterstützen. Wir werden die Projektion von mehr als zwei lokalen Lichtquellen unterstützen und die Genauigkeit von ShadowDepth dynamisch an den Bildschirmanteil der Lichtquelle anpassen. Für lokale Lichtquellen in großer Entfernung werden wir zudem statische Caching-Unterstützung einführen.

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

1. Beleuchtungskonzept für Charaktere

Bei der Beleuchtung von Charakteren sind sicherlich die meisten von Ihnen schon einmal mehr oder weniger mit den folgenden Problemtypen konfrontiert worden:

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

Wenn man diese Fragen genauer betrachtet, lassen sie sich auf die folgenden drei Anforderungen zusammenfassen:

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

Auf der Grundlage der oben genannten Anforderungen haben wir konkrete Beleuchtungskonzepte für die einzelnen Figuren entworfen.

Die Beleuchtung setzt sich aus 【direktem Licht】 und 【indirektem Licht】 zusammen. In der Regel gibt es nur eine parallele Lichtquelle – wir bezeichnen diese üblicherweise als Hauptlicht. Das Hauptlicht beleuchtet die Szene auf normale Weise, doch bei der Beleuchtung von Figuren behalten wir seine Richtung bei und überschreiben die Farbe und Helligkeit des Hauptlichts mithilfe einer Art Post-Processing-Box. Die konkrete Umsetzung erfolgt wie folgt:

  • Übergebe dem Shader zusätzlich die Farbe des Hauptlichts des Charakters; die Farbe, die der Shader des Charakters beim Abrufen des Hauptlichts erhält, ist dann die Farbe des Hauptlichts des Charakters;
  • Dem Charakter wurde eine zusätzliche, nicht projizierte Parallellichtquelle als Konturlicht zugewiesen;
  • Gleichzeitig wurden zwei zusätzliche Lichtquellen für die Figur reserviert. Diese zusätzlichen Lichtquellen können aus einer beliebigen Kombination aus Punktlicht und Strahlern bestehen und beleuchten die Figuren sowie die Objekte in der Szene innerhalb ihres Wirkungsbereichs (da in einem 2-Meter-Raster maximal vier zusätzliche Lichtquellen zulässig sind, wurden zwei Lichtquellen der Figur zugewiesen).

Für das indirekte Licht verwenden wir das LightProbe-System von Unity, um Probes zu erstellen. Wir haben den Teil, der das indirekte Licht in den Probes speichert, selbst implementiert und dabei zwei separate Sätze von Probes eingerichtet – einen für die Szene und einen für die Charaktere –, die jeweils separat gespeichert und verwendet werden;

Für Umgebungslicht und Glanzlichter verwenden wir dieselbe Reflexionssonde; für bestimmte Materialien bieten wir jedoch die Option, eine CubeMap auf dem Material einzustellen, um die Reflexionssonde der Umgebung zu überlagern.

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

Wir speichern diese Beleuchtungsdaten, die die Charaktere beeinflussen, in einem ScriptableObject, das der Lichtdesigner anpasst und anschließend als Vorlage speichert;Die Abbildung unten rechts zeigt die gespeicherten Informationen zum Beleuchtungskonzept des Charakters. Sie umfassen die beiden oben erwähnten parallelen Lichtquellen, zwei zusätzliche Lichtquellen sowie die von den Proben gespeicherten Shader und einige Informationen, die in den Post-Processing-Boxen zusätzlich angepasst werden können, sowie die Angabe, ob benutzerdefinierte Reflexionsproben verwendet werden sollen.

Schließlich wird die Verwaltung über einen Manager erfolgen, der ähnlich wie ein Stapel funktioniert. Die Wahl des Stapelansatzes hängt eng mit der konkreten Nutzung zusammen – in der Regel ist neben dem Laden neuer Beleuchtungskonzepte die häufigste Funktion das Wiederherstellen des vorherigen Beleuchtungskonzepts, weshalb wir uns für den Stapelansatz entschieden haben.

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

Zu diesem Zeitpunkt verfügt dieses Konzept bereits über Funktionen wie die Trennung von Charakteren und Szenen, das Umschalten in Echtzeit sowie die Unterstützung benutzerdefinierter Vorlagen. Abschließend haben wir das Umschalten der Beleuchtungskonzepte als Ereignisaktion im Story-Editor definiert, wodurch sich Beleuchtungsanimationen nahtlos aneinanderreihen lassen.

Die Lichteffekte, die miteinander kombiniert werden können, sind wie folgt dargestellt:

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

Die folgende Abbildung zeigt das projektbezogene Story-Editor-Tool. Im Wesentlichen lassen sich alle Parameter in Bezug auf Beleuchtung und Schatten sowie ein Teil der Nachbearbeitung und der physikalischen Effekte über diesen Story-Editor steuern.

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

2. Nahaufnahme im Schatten

Licht und Schatten sind seit jeher untrennbar miteinander verbunden. Wie bereits erwähnt, besteht unser Schattenkonzept aus einem dreistufigen CSM sowie Nahaufnahmen-Schatten. Das Prinzip besteht darin, einen frei wählbaren Knochen des Charakters als Mittelpunkt zu verwenden, um eine Kugel mit einem bestimmten Radius zu bilden. Mit dieser Kugel wird die Schattenkarte erstellt und generiert.Bei der Berechnung von Screen-Space-Schatten wird ein Genauigkeitsvergleich durchgeführt, wobei die Schattenkarte mit der höheren Genauigkeit – entweder diese Schattenkarte oder die aus den kaskadierten Schatten – als Shadow Map für diesen Pixel verwendet wird.

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

Wie in der folgenden Animation zu sehen ist, befand sich die Figur ursprünglich vollständig im Schatten des Hauptlichts; beim Aktivieren des Nahaufnahme-Schattens wird sie nun vom Hauptlicht normal beleuchtet, da der Nahaufnahme-Schatten die nahe Schnittfläche verändert hat;Das bedeutet, dass unsere Nahaufnahme-Schatten eine separate, parametrisch anpassbare Schattenkarte sind. Zu den konkreten Parametern gehören die vordere und hintere Begrenzungsebene, die maximale Entfernung sowie die Auswahl der zu verwendenden Lichtquelle und der oft besonders knifflige „Bias“-Wert.

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

3. Hautdetails

Was die Haut betrifft, konzentrieren wir uns auf einige Detaildarstellungen, konkret am Beispiel von Erröten und Schweißausbrüchen.

  • Errötungseffekt

Im Allgemeinen verläuft das Erröten schrittweise, wobei die Intensität der Rötung je nach Körperbereich unterschiedlich ist. So fangen bei den meisten Menschen beim Erröten zunächst die Ohren an, rot zu werden, gefolgt von den Wangen; gelegentlich rötet sich das gesamte Gesicht.

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

Um diesen Vorgang zu simulieren, haben wir folgende Maßnahmen ergriffen, um das Bild lebendiger und realistischer zu gestalten:

  • Handgezeichnete Maske: Steuerung der Bereiche mit Errötung, der Farbverläufe und der Intensität anhand einer Masken-Textur;
  • Mehrkanal-Unabhängigkeit: Der Rote-Augen-Effekt lässt sich für verschiedene Bereiche wie Gesicht, Ohren und Nase separat einstellen;
  • Verlauf der Vorab-Speicherung: Der Übergangsprozess des Errötens wird kanalweise in den entsprechenden Kurven aufgezeichnet, um einen natürlichen Ausdruck von Emotionen zu erzielen.
  • Schweiß-Effekt

In unserem Spiel gibt es eine Funktion, bei der der männliche Hauptcharakter den Spieler beim Sport begleitet. Da er dabei das Training begleitet, müssen entsprechende Schweißeffekte dargestellt werden. Die konkrete Umsetzung erfolgt hauptsächlich über die folgenden drei Aspekte:

  • Kombination von Materialien und Partikeln: Der Material-Shader simuliert den Glanz und die Feuchtigkeit der Hautoberfläche; für den Schweißtropfen-Effekt stehen sowohl eine Material- als auch eine VFX-Umsetzung zur Auswahl, die auf der Haut haften;
  • Masken zur Steuerung der Schweißbereiche: Verwenden Sie Maskenbilder, um die Schweißbereiche des Materials zu definieren und so die Realitätsnähe und den künstlerischen Charakter des Schweißeffekts zu verstärken;
  • Automatische Datenübertragung: Änderungen an den Hauptsteuerungsparametern wirken sich automatisch auf die Material- und Partikelparameter aus.

Die folgende Abbildung zeigt einige konkrete Berechnungsmethoden und eine Darstellung des Endergebnisses.

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

△ Berechnung der Position der Schweißtropfen und Anpassung der Rauheit ihrer Position

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

△ Erzeugung von Zufallszahlen mithilfe der UV-Gitter-ID

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

△ Simulation der Flugbahn fallender Schweißtropfen

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

△ Schweißausbruch durch das Sportbegleitsystem

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

Die Vorstellung der physikalischen Effekte konzentriert sich hauptsächlich auf vier Aspekte: die Umsetzung der Stoffsimulation, die Steuerung in Echtzeit, die Entwicklung auf Basis von Unity DOTS sowie das Modul zur Kollisionserkennung.

1. Umsetzung der Stoffsimulation

Um einige spezifische Probleme im Rahmen des Projekts zu lösen, haben wir intern ein eigenes System zur Stoffsimulation entwickelt.

Ein auf Skeletten basierendes System zur Stoffsimulation: StrayCloth

StrayCloth verwendet einen Simulationsansatz, bei dem XPBD mit Substeps kombiniert wird. Im Vergleich zu PBD hat XPBD den Vorteil, dass es nicht von der Anzahl der Iterationen und der Zeitschritte abhängig ist; in Kombination mit Substeps lässt sich die Konvergenz der Lösung deutlich verbessern.

Das Besondere daran ist, dass wir Skelette als Simulationspartikel verwenden, das heißt, jedes Partikel enthält neben seiner Position auch Rotationsdaten.

Bei der konkreten Umsetzung der Subschritte verwenden wir je nach Leistungsanforderung dynamische Subschrittzeiten zwischen 1/200 und 1/300. Zudem führen wir eine Bewegungsinterpolation für die sich bewegenden Objekte in der Szene durch, wodurch die Kollisionseffekte stabiler werden.Tatsächlich ist der Leistungsaufwand für die Bewegungsinterpolation zwar nicht besonders hoch, doch aufgrund der Vielzahl der Typen – wie beispielsweise statische Partikel, Kollisionskörper und Windfelder – ist die Umsetzung in der Praxis dennoch sehr aufwendig.

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

Warum werden Skelette anstelle von Proxy-Meshes verwendet? Hauptsächlich aus den folgenden drei Gründen:

  • „Love and Deep Space“ stellt komplexe Anforderungen an die Darstellung in den Bereichen Handlung, Kampf und Kostümwechsel; das Skelett-Konzept ermöglicht einen reibungslosen Übergang bei Animationen und der Berechnung;
  • Angesichts der Anforderungen an die Steuerbarkeit und der Leistungsbeschränkungen auf Mobilgeräten bieten Skelett-Lösungen den Grafikdesignern mehr Spielraum für die Gestaltung;
  • Mit Skeletten und Beschränkungen lassen sich meshähnliche Strukturen erstellen, um einen ähnlichen Effekt zu erzielen.

Skelettale Fixierungsstrategie

Bei den bestehenden Skelett-Clothing-Lösungen werden Skelettbeschränkungen häufig auf der Grundlage lokaler und globaler Formbeschränkungen umgesetzt. Dies ist zwar einfach und schnell, hat jedoch auch einen offensichtlichen Nachteil: Bei der Anwendung in Clothing-Simulationen wirkt das Ergebnis eher im Cartoon-Stil, was nicht dem von „Love and Deep Space“ angestrebten realistischen 3D-Stil entspricht;Zudem ist die Parametereinstellung nicht intuitiv, da zwei Biegefestigkeitsparameter – „global“ und „local“ – vorhanden sind, was die Anpassung durch die Grafiker sowie die Abstimmung der Effekte in verschiedenen Szenen erschwert.

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

Daher haben wir uns bei den Skelettbeschränkungen für eine auf dem Cosserat-Stab basierende Skelettbeschränkung entschieden. Zu deren Vorteilen zählen:

  • Das Ergebnis wirkt natürlicher und passt besser zum realistischen Grafikstil von „Love and Deep Space“ insgesamt.
  • Die Parametereinstellung ist intuitiver, und die Festigkeiten der drei Achsen sind voneinander getrennt. In bestimmten Fällen, beispielsweise bei der Simulation eines Rocks, lässt sich durch die anisotrope Biegefestigkeit der Effekt eines Reifrocks annähernd nachbilden.
  • Da dies in der Haarsimulation direkt wiederverwendet werden kann, können wir für Haare und Kleidung denselben Satz an Beschränkungen verwenden.

Die konkreten Ergebnisse lassen sich anhand der Leistung der aktuellen Tageskarte ablesen:

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

Verbindung zwischen Stoff und Figur

Die Verbindung zwischen Stoff und Figur erfolgt hauptsächlich auf zwei Arten:

  • Hierarchie: Das statische Skelett wird direkt von der Skelettanimation des Charakters beeinflusst und bewegt sich entsprechend der Hierarchie.
  • Diese Methode ist relativ einfach und eignet sich gut für Verbindungsstellen, die eher starr sind. An komplexeren Stellen jedoch, an denen mehrere Knochen aneinandergrenzen oder eine gewisse Dehnung und Kontraktion auftritt – wie beispielsweise am Ellbogen, an der Schulter oder im Lendenbereich –, kommt es leicht dazu, dass sich der Stoff vom Charakter löst.
  • Adhäsion: Statische Partikel werden durch die Verankerungsdreiecke des Charaktermodells gesteuert. Das Mesh wird parallel gebacken und anhand der Koordinaten des Schwerpunkts bei jedem Frame neu berechnet.

In besonderen Fällen, in denen das Dreieck degeneriert ist, verwenden wir die Transformationen der Skin-Skelette der Dreiecksscheitelpunkte, um die Transformation der statischen Partikel durch gewichtete Ausgleichsberechnungen zu aktualisieren.

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

Kollisionsszenario

Was das Kollisionsverfahren betrifft, verwenden wir einen dynamischen BVH zur Verwaltung der Broad Phase der Szenenkollisionen, wobei jedes Charakterobjekt als Unterbaum fungiert, der seine internen Kollisionskörper als Unterbaumknoten enthält.

Gleichzeitig verwalten wir die gemeinsamen Kollisionsregeln für verschiedene Rollen und Komponenten anhand der drei Kriterien: Rollen-ID, Sichtbarkeit und Komponententyp.

In der „narrow phase“ erzeugen wir keine Kontakte direkt, sondern speichern Kollisionskörperpaare im Cache, um sie dann im Subschritt konkret zu lösen. Dank der Vorteile des von uns verwendeten Subschritts lassen sich in den meisten Fällen durch die direkte Verwendung von DCD Durchdringungsprobleme bei schnellen Bewegungen vermeiden, sodass keine Operationen wie CCD oder „predictive contact“ erforderlich sind.

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!
  • Implementierung des Mesh-Colliders

Bei parametrischen Geometrie-Kollisionskörpern wie „plane“, „capsule“ und „box“ lassen sich Kollisionen mit Partikeln und Kanten relativ einfach lösen. An komplexen Stellen wie Schultern, Nacken, Brust und Rücken ist es jedoch schwierig, die Form des Charaktermodells mit parametrischen Geometrien genau darzustellen, was leicht zu Durchdringungen führt. Daher verwenden wir an diesen Stellen in großem Umfang Mesh-Kollisionskörper.

Da der Mesh-Collider jedoch ein unregelmäßiger Hohlkörper ist, kann er manchmal auch nicht geschlossen sein,was es im Vergleich zu parametrisierten Geometrien relativ schwierig macht, präzise Kollisionseffekte zu erzielen, insbesondere auf mobilen Geräten. Daher verwenden wir einen Hash-Algorithmus als grobe Suchmethode für Dreiecke und kombinieren dies mit den im Cache gespeicherten Ergebnissen benachbarter Dreiecke, um vor Beginn der Iteration einmalig Partikel-Dreieck-Kollisionspaare zu generieren.In den nachfolgenden Iterationen wird geprüft, ob sich das Partikel innerhalb des Dreiecksbereichs befindet. Wenn es diesen Bereich überschreitet, wird anhand der Dreiecksnachbarbeziehungen des Modells eine Dreieckssuche mit begrenztem Schrittweite durchgeführt, um das nächstgelegene Dreieck zu ermitteln, und das Ergebnis wird für die nächste Iteration zwischengespeichert.

Die untenstehende Animation zeigt einige konkrete Beispiele für die Darstellung im Projekt; wie man sieht, ist die Darstellung relativ stabil.

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

Face Collider

Ein Gesichts-Kollisionskörper kann als spezieller Mesh-Kollisionskörper betrachtet werden. Im Vergleich zu einem einfachen Mesh-Kollisionskörper weist er eine relativ feste Form auf und ist glatter; ausgehend vom Mittelpunkt des Modells gibt es im Grunde keine überlappenden Dreiecke. Daher verwenden wir eine 16×16-CubeMap, um die Dreiecke in alle Richtungen vorab zu berechnen,sodass bei der Kollisionsberechnung benachbarte Dreiecke schnell gefunden werden können.

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

Kollision zwischen Schichten

Die Simulation von Selbstkollisionen bei Stoffen ist der schwierigste Aspekt im Spiel. Aus Leistungsgründen haben wir folgende Lösung vorgeschlagen:

  • Verwendung von Spatial Hashing als Struktur zur Beschleunigung von Suchvorgängen
  • Durch die vorläufige Einteilung in Ebenen durch die Grafik werden nur Kollisionen zwischen Partikeln und Dreiecken innerhalb derselben Ebene berücksichtigt.
  • Um ein Feststecken zwischen den Ebenen zu vermeiden, werden nur Kollisionen zwischen Partikeln und Dreiecken in Richtung der einzelnen Normalen berechnet.

Die Grafikeinheit führt vorab eine Schichtung der Stofflagen durch, wobei ausschließlich Kollisionen zwischen diesen Lagen berücksichtigt werden. Zur Beschleunigung der Suche wird ein Hash-Verfahren verwendet. Um ein „Hängenbleiben“ zwischen den Lagen zu vermeiden, werden nur Kollisionen in einer einzigen Normalenrichtung berücksichtigt; bereits durchdrungene Bereiche werden übersprungen und in einem späteren Schritt korrigiert.

In der Praxis verwenden wir die Partikelpositionen des letzten Subschritts, um Kollisionen mit den aktuellen Partikelpositionen zu berechnen. Auf diese Weise lassen sich die Daten auf einfache Weise entkoppeln und Abhängigkeiten vermeiden.

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

Trennung durch Durchdringung der Schichten

Für die Bereiche, in denen die Kollision die Schichten bereits durchdrungen hat, haben wir uns an der Methode des „Untangling Cloth“ orientiert und eine einfache Lösung verwendet: Durch die Schichtung des Stoffes werden ausgehend von den Befestigungspunkten des Stoffes die Schnittpunkte der Kanten und Dreiecke der verschiedenen Schichten berechnet, da unsere Asset-Struktur zwangsläufig aus einem einheitlichen Gitter besteht.können die Dreiecke anderer Partikel relativ einfach anhand der Gitterknotenpunkte abgeleitet werden. Schließlich wird das Durchdringen durch die Anwendung von Federbeschränkungen auf die durchdringenden Partikel-Dreieck-Paare behoben. In der Praxis ist die Wahrscheinlichkeit einer Durchdringung aufgrund der Substeps relativ gering, weshalb wir eine framenweise und blockweise Ausführung verwenden, um die Leistungsbelastung zu verringern.

2. Steuerung von Live-Auftritten

Ein Großteil der physikalischen Darstellungen in der Handlung von „Love and the Deep Space“ basiert auf der Steuerung und Anpassung verschiedener physikalischer Effekte mithilfe von Zwischensequenzen. Unsere Kollegen aus der Tool-Entwicklung haben eine äußerst leistungsfähige Zwischensequenz-Toolsuite entwickelt und gepflegt; auf dieser Grundlage haben wir verschiedene Funktionsspuren entwickelt, um die physikalischen Effekte gezielt zu steuern.

Hier sehen Sie ein Beispiel für einen „Cutscene Physics Track“ in unserem Spiel. Da unsere Grafiker sehr viel Wert auf die Bilddarstellung legen, ist die Konfiguration der gesamten Physikbahn, wie Sie sehen können, recht komplex.

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

△Beispiel für den „Cutscene Physcs Track“

SmoothBlendPose-Track

Ein sehr häufiges Problem bei der Darstellung ist das physikalische Ruckeln, das durch plötzliche Schnittwechsel verursacht wird; dies tritt sowohl bei der Darstellung von Handlungsszenen als auch beim Kostümwechsel häufig auf.

Wir haben einen relativ universellen Ansatz entwickelt, bei dem die anfängliche physikalische Pose aufgezeichnet wird und beim Wechsel eine Interpolation zwischen der anfänglichen und der aktuellen Pose berechnet wird. Auf diese Weise lassen sich Flackern erheblich reduzieren.Natürlich ist damit ein gewisser Zeitaufwand verbunden, der in der Regel bei einigen Dutzend Millisekunden liegt, was in den meisten Fällen akzeptabel ist. Es stehen einige Parameter zur Verfügung, wie beispielsweise die Anzahl der Interpolationen und die Schrittweite der Interpolation, damit die Grafiker diese je nach Bedarf anpassen können.

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

Posenverfolgung

Natürlich hat SmoothBlendPose seine Grenzen und kann keine vollkommen flüssige Übergänge garantieren, insbesondere bei komplexen Schnittsequenzen in dramatischen Szenen.Wir bieten außerdem eine relativ einfache Lösung an: Die physikalischen Eigenschaften eines bestimmten Zeitrahmens werden offline gespeichert und bei der Wiedergabe direkt auf den Stoff angewendet. Auf diese Weise lassen sich die durch Schnittwechsel verursachten Probleme vollständig vermeiden.

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

Param-Spur bearbeiten

Ein einzelnes physikalisches Objekt reicht kaum aus, um die unterschiedlichen Anforderungen der verschiedenen Szenen in der Handlung zu erfüllen – beispielsweise wenn der Stoff mal weicher, mal härter oder die Dämpfung mal stärker, mal schwächer sein soll.Wir bieten eine Spur zur Bearbeitung der Parameter an, über die diese in Echtzeit angepasst werden können. Die meisten Parameter lassen sich überschreiben, sodass Änderungen ganz einfach für einen kurzen Zeitabschnitt vorgenommen werden können. Diese Parameteranpassung kann auch zur Erzeugung spezieller Effekte genutzt werden, wie zum Beispiel den Bruch-Effekt in Animationen, der durch die Bearbeitung der Einschränkungsparameter erzielt wird.

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

Animationsreihe

Reine Physikeffekte reichen in der Praxis nicht aus, um alle Aspekte der Darstellung im gesamten Bild abzudecken. Oftmals ist eine Kombination aus Animation und Physik erforderlich, um Interaktionen zu erzeugen. Wir nutzen Animationsspuren, um die Verbindung und Verschmelzung von Animation und Physik zu realisieren und die Darstellung in verschiedenen Zeitrahmen präzise zu steuern.Im tatsächlichen Produktionsablauf bestehen erhebliche Unterschiede zwischen der Darstellung der Bewegungen in der DCC-Umgebung und der endgültigen Darstellung in der Engine. Dazu gehört auch, dass es nach Änderungen am Echtzeit-Rig-System der Engine zu Überschneidungen zwischen der Animation und anderen Elementen kommen kann. Daher können wir auf der Grundlage der Animationsintegration physikalische Kollisionseffekte überlagern, um solche Überschneidungen zu vermeiden.

Das GIF zeigt die Interaktion zwischen Physik und Animation bei einer Halskette, einschließlich des Übergangs vom Physikmodus in den Animationsmodus sowie des Wechsels zwischen verschiedenen Animationen.

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

Kollisionsspur & Windspur

Mit „Collider Track“ und „Wind Track“ lassen sich in Zwischensequenzen Kollisionskörper und Windfelder dynamisch erstellen und löschen. Je nach den Anforderungen der jeweiligen Szene können der Status der Kollisionskörper und der Windfelder flexibel angepasst werden. Durch die Gruppierung von Charakteren, Bauteiltypen und Stoffebenen lässt sich der Umfang der betroffenen Objekte detailliert steuern.

Zudem lassen sich für die meisten Parameter der Kollisionskörper und der Windfeldbahnen Animationsframes festlegen, darunter Form und Größe der Kollisionskörper sowie Richtung, Reichweite, Stärke und Turbulenz des Windfeldes. Dies erleichtert es den Grafikdesignern, die physikalischen Effekte zu steuern und Veränderungen präzise zu kontrollieren.

Das animierte Bild zeigt einige Beispiele für die Darstellung von Bahnkapseln und Windfeldern.

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

3. Entwicklung auf Basis von Unity DOTS

Berufe + Burst + Mathematik

DOTS ist ein äußerst leistungsstarkes Tool, mit dem sich bereits auf der C#-Ebene eine hochperformante Multithread-Entwicklung realisieren lässt. Unser Physiksystem basiert vollständig auf DOTS auf der C#-Ebene, was die Funktionsiteration und das Debugging erheblich vereinfacht. Derzeit können wir Simulationen mit mehr als 2.000 Skelettpartikeln unterstützen.

Natürlich haben wir im Rahmen des Projekts auch gezielt einige Optimierungen vorgenommen, um die Leistung weiter zu steigern.

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

Job-Cache

Da die Anzahl der Jobs und ihre Abhängigkeiten in der Simulation feststehen und sich die Jobdaten nicht häufig ändern, wird innerhalb eines Frames in der Regel eine Gruppe von Jobs mit derselben Anzahl und denselben Abhängigkeiten mehrfach zyklisch ausgeführt. Unity Jobs müssen bei jeder Aufgabenauslösung neu erstellt werden. Zwar lässt sich dies durch eine vorzeitige Aufgabenauslösung abmildern, doch wird der Hauptthread dadurch weiterhin blockiert. Zudem müssen die Jobs nach Abschluss der Ausführung noch gelöscht werden.Aufgrund dieser Beobachtungen haben wir eine Lösung namens „Cache Job“ entwickelt, bei der die Jobdaten im Voraus erstellt und bei jeder Ausführung wiederverwendet werden, um den durch die wiederholte Neuerstellung von Jobs verursachten Leistungsaufwand zu vermeiden.

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

Die Umsetzung ist relativ einfach, da es sich um eine spezielle Struktur handelt, die nur bestimmte Anwendungsszenarien berücksichtigt. Es wurde eine zusätzliche Atomic Queue hinzugefügt, um Cache-Jobs zu speichern, wobei die einzelnen Jobs mithilfe von „Fetch and Add Array“ abgelegt werden. Rechts ist ein schematisches Diagramm des Ablaufs dargestellt, wie ein Worker einen Cache-Job ausführt.

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

Neon-Intrinsics

Burst generiert hochleistungsfähigen SIMD-Code für verschiedene Plattformen, der sich im Burst Inspector sehr bequem einsehen lässt. Nach Überprüfung im Burst Inspector und Tests auf dem Zielgerät lässt sich die Leistung in bestimmten Fällen durch manuell geschriebenen ARM-Neon-Intrinsics-Code weiter steigern.

Hier wird ein Beispiel für die Umsetzung einer Überprüfung gegeben, ob ein Vektor Elemente größer als 0 enthält.

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!
Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

Dot(float4)

Für die Punktmultiplikation habe ich hier drei Methoden aufgeführt. Im Vergleich zu „mathematics“ lässt sich durch die Verwendung von NEON-Intrinsics in den Testfällen eine Leistungssteigerung von etwa 30 % erzielen.Sofern das Zielgerät ARMv8.2 unterstützt, kann die Leistung durch den Einsatz der neuen normierten Additionsbefehle weiter gesteigert werden. Im Allgemeinen unterstützen die meisten gängigen Geräte auf dem Markt ARMv8.2.

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

Transpose(float4x4)

Bei Transponierungsberechnungen lässt sich feststellen, dass der von „mathematics“ generierte Assemblercode offenbar eine sehr geringe Leistung aufweist; durch das manuelle Schreiben von NEON-Intrinsics lässt sich jedoch eine enorme Leistungssteigerung erzielen.

Wenn es sich lediglich um eine reine Transponierung handelt, kann man direkt die verschachtelte Lesefolge verwenden. Ich habe es hier so umgesetzt, da ich in der Praxis üblicherweise die Punktmultiplikation durch Transponierung von vier `float4`-Werten in eine Vektormultiplikation umwandle.

Hier werden lediglich die gängigsten Beispiele aus diesen beiden Projekten aufgeführt. Da der Code in „mathematics“ in der Regel inline eingebunden ist, müssen bei der konkreten Optimierung die Maßnahmen entsprechend dem Kontext des Codes angepasst werden. Für detaillierte Leistungstests können der Burst Inspector und Tests auf einem realen Gerät kombiniert werden.

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

4. Modul zur Kollisionserkennung

Warum sollte man das ausgereifte Physikmodul von Unity verlassen und eine eigene Lösung entwickeln?

Unity verfügt von Haus aus über eine ausgereifte, auf PhysX basierende Physik-Engine. Die Entscheidung, diese ausgereifte Physik-Engine von Unity zu verlassen und eine eigene zu entwickeln, beruht hauptsächlich auf folgenden Überlegungen:

  • „Love and Deep Space“ bietet eine ganze Reihe verschiedener Spielmodi, deren Layer-Einstellungen relativ unabhängig voneinander sind. Ich würde mir sehr wünschen, dass für jeden Modus eigene Layer-Einstellungen gepflegt werden können.
  • Bestimmte Module, wie beispielsweise der Kampf, erfordern spezielle Mechanismen zum Auslösen und Beenden, die bereits auf der Basisebene unterstützt werden sollten; außerdem wünschen wir uns eine flexiblere Steuerung des Ausführungsablaufs.
  • Abschließend haben wir noch einige Überlegungen zur Leistungsoptimierung: Lässt sich die Leistung mithilfe von DOTS verbessern, wenn lediglich eine Kollisionserkennung erforderlich ist?
Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

Zu den Implementierungen in „Love and Deep Space“ gehören:

  • Nahezu alle nativen Funktionen zur Kollisionsabfrage wurden implementiert.
  • Maßgeschneiderte Update- und Trigger-Logik
  • Thread-sichere Abfrageschnittstelle, die von übergeordneten Ebenen problemlos aufgerufen werden kann
  • In Verbindung mit der leichtgewichtigen Konstruktion von DOTS lässt sich in Leistungstests eine Steigerung von bis zu ~15 % erzielen.

Beispiel für einen Abfrageablauf

Da die tatsächliche Anzahl der Threads auf dem Zielsystem fest auf 4 festgelegt ist, kann der Speicherzuordner die Speicherplätze im Voraus entsprechend der Anzahl der Threads zuweisen, sodass sie bei der Zuweisung direkt anhand des aktuellen Thread-Index abgerufen werden können.

Unter Verwendung eines SAH-basierten dynamischen BVH als Beschleunigungsstruktur für die Broadphase wird bei Einfügungen, Löschungen und Bewegungen außerhalb des Bereichs eine Rotationsausgleichung für die benachbarten Knoten mehrerer Ebenen des aktuellen Operationsknotens durchgeführt.

Da die Funktionsziele der Kollisionserkennung relativ allgemein gehalten sind und keine allzu hohen Anforderungen an die Genauigkeit stellen, haben wir zugunsten der Leistung einige Kompromisse bei der Genauigkeit eingegangen und die Algorithmen zur Kollisionserkennung entsprechend vereinfacht.

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

Beispiel für einen Auslöseprozess

Um den Anforderungen des Kampfmoduls gerecht zu werden, haben wir eine spezielle Trigger-Logik entwickelt. Die Trigger „Enter“ und „Exit“ müssen paarweise auftreten. Wie in der folgenden Ablaufskizze zu sehen ist, werden nach dem Entfernen von b aus der Funktion, in der a b auslöst, alle Collider ausgelöst, die eine Überlappung mit b aufweisen.Dies unterscheidet sich von der nativen Unity-Logik – in der nativen Unity-Trigger-Logik löst das Entfernen von b keine Trigger anderer Kollisionskörper aus. Schließlich kennzeichnen wir die Version der Kollisionskörper mithilfe eines History-Zählers, um potenzielle Probleme zu beheben, die durch die Wiederverwendung von Logik entstehen könnten.

Rückblick auf den ersten ausführlichen technischen Erfahrungsaustausch zu „Love and Deep Space“!

Willkommen bei uns,

Lasst uns gemeinsam mit Technologie mehr Gutes schaffen!

原创文章,作者:游茶妹儿,禁止转载:https://youxichaguan.com/de/archives/213132

Like (0)
游茶妹儿
Als JJ Lin auf „Honor of Kings“ traf, gerieten die Kultur- und Tourismusbehörden überall aus der Fassung
Previous 15 hours ago
Auf Non-Consensus gesetzt, bereits über 10 Millionen Nutzer – „KI + Gaming“ lässt sich auch so gestalten
Next 15 hours ago

相关推荐