Grafikoptimierung. Interessanter konkaver Rumpf

Während der Entwicklung des Spiels war ich einmal mit dem Problem der Leistung auf modernen PCs konfrontiert. Unser Modellbauer hat einen ziemlich leistungsstarken modernen roten Montagecomputer. Aber er hatte unser Projekt furchtbar langsam und lud einen Prozessorkern.

Der Grund ist einfach: Die neuen Prozessoren haben viele Kerne, sind jedoch in Single-Thread-Anwendungen weniger effizient. Zu dieser Zeit hatte ich ein Rendering in einem Stream. Tatsächlich waren die Gründe dafür nicht so sehr. Um das Problem zu finden, habe ich mich entschlossen zu berechnen, wie viele Polygone in der Szene vorhanden sind:



Auf der durchschnittlichen Spielkarte, mit der maximalen Entfernung und mit einer großen Gruppe von Palmen - 15 824 756 Dreiecke! Fast 16 Millionen! Eine riesige Anzahl.

Nachdem ich ein wenig mit dem Kartengenerator gespielt hatte, gelang es mir, einen Platz mit 16,75 Millionen zu finden:



Obwohl hier ein ähnlicher Ort mit Weihnachtsbäumen nur 8,5 Millionen Dreiecke gab:



Die durchschnittliche Szene bestand aus ~ 4 Millionen:



Im Allgemeinen war ich froh, dass mein Render mit einer so großen Anzahl von Dreiecken fertig wurde, aber ihre Anzahl war zu hoch. Die Lösung war an der Oberfläche:

  1. Optimieren Sie die Anzahl der Polygone in Modellen.
  2. Optimieren Sie das polygonale Netz der Landschaft.
  3. Die Implementierung von Multithread-Rendering.

Für diejenigen, die möglicherweise nicht am ersten Absatz unseres Optimierungsprogramms interessiert sind, z. B. unerfahrene Spezialisten, empfehle ich, sicher zum zweiten Teil überzugehen.

1. Optimierung der Anzahl der Polygone in Modellen


In unserem Motor wird die Vegetation in „Packungen“ gezeichnet, die gesamte Landschaft ist in Kacheln und Unterkacheln unterteilt, die Mindestpackung besteht aus einer Unterkachel.



Ein Pack ist ein Mesh, da durch die Reduzierung der Anzahl der Meshes die Anzahl der CPU-> GPU-Aufrufe erheblich reduziert wird.



Anfangs bestanden unsere Bäume aus Kegelstümpfen, die sich zu vollen Kegeln bewegten. Es gelang uns, ein paar zusätzliche Dreiecke zu entfernen:



Die Kirsche auf dem Kuchen war die Entscheidung, die Stämme von den Bäumen zu entfernen, weil sie mit unserem Kamerawinkel einfach nicht sichtbar waren.

Infolgedessen konnten wir die Anzahl der Polygone auf einer Packung Weihnachtsbäume um durchschnittlich 40% reduzieren. Die Unterschiede sind fast unsichtbar:



Bei Palmen war es schwieriger, aber Packungen mit 5000 - 6000 Polygonen mussten repariert werden. Wie erreicht man Massivität und Dichte des Dschungels? Aufgrund der großen Anzahl von Palmen wurde eine hohe Dichte des Dschungels erreicht:



Wir beschlossen, die Palmen zu vereinfachen und eine zweite Vegetationsstufe einzuführen, die es uns ermöglichte, die sichtbare Dichte beizubehalten und die gewünschten 600 - 700 Polygone pro Packung zu erreichen.



Die 10-fache Reduzierung der Anzahl der Polygone ist ein hervorragendes Ergebnis.



2. Optimierung der polygonalen Netzlandschaft



Ursprünglich war das Landschaftsraster:



Der Screenshot zeigt einfache Teile der Landschaft, dies sind Fliesen von Wiesen, Ebenen, wenn auch anderen glatten Oberflächen. Ich beschloss, kleine Unregelmäßigkeiten in der Landschaft zu beseitigen. So vergrößerte er die Fläche der gleich hohen Fliesen. Ich konnte dieses Ergebnis nicht erzielen, indem ich alle Eckpunkte der Kacheln und Unterkacheln geschickt auf Höhengleichheit überprüfte:



Es gab immer noch flache Stellen, die optimiert werden konnten, und ich begann, Polygone aus diesen Dreiecken mit derselben Höhe zu bauen. Eine Kachel wurde genommen und alle ihre Dreiecke wurden in eine Anordnung von Dreiecken mit ungleicher Höhe und eine Liste von Anordnungen sortiert, die aus gleich hohen und benachbarten Dreiecken bestehen.



Im angegebenen Beispiel stellte sich heraus: 1 Array von Dreiecken, die sich nicht ändern konnten, da sie alle unterschiedliche Höhen hatten (rote Dreiecke) und eine Liste bestehend aus zwei Arrays von Dreiecken mit derselben Höhe (die Arrays sind mit Farbe gefüllt).

Nun bestand die Aufgabe darin, aus der Anordnung der Dreiecke ihre konvex-konkave Kontur (konkave Hülle) zu finden, und viele Dreiecke konnten Löcher haben. Früher bin ich bei meiner Arbeit auf Convex Hull gestoßen, es gab keine Probleme mit ihnen, ich habe bereits den Graham-Algorithmus verwendet. Aber es gab Probleme mit dem Bau von Concave Hull ... Es stellte sich als ziemlich schwierig heraus, Informationen zu diesem Thema im Internet zu finden. Ich musste eine Implementierung von Algorithmen von Grund auf neu schreiben. Ich werde nicht lügen, wenn ich sage, dass ich ungefähr zehn verschiedene Dissertationen zu diesem Thema gelesen habe. Alle vorgeschlagenen Algorithmen ergaben jedoch ein ungefähres Ergebnis mit einigen Fehlern. Nach einer Woche voller Qualen und Schmerzen kam mir die Idee meines Algorithmus.

Ich habe versucht, eine Kontur aus der Menge der Eckpunkte der Dreiecke zu erstellen, d. H. Ich habe das Array von Dreiecken in ein Array von Eckpunkten übersetzt und bereits eine Shell darauf aufgebaut. Für meine Aufgabe war dies jedoch nicht erforderlich. Nach meinen Schlussfolgerungen war es einfacher, eine Schale direkt aus Dreiecken zu bauen, und die Genauigkeit des konkaven Rumpfes betrug 100%.

Anfangs wollte ich hier ein Patchwork des Quellcodes des Algorithmus einfügen, aber es scheint einfacher zu sein, es auf den Punkt zu bringen: Basis ist die Regel: Wenn der Scheitelpunkt eines Dreiecks in vier oder weniger Dreiecken enthalten ist, liegt eine der Kanten, die den Scheitelpunkt bilden, am Rand. Als nächstes wird eine Liste solcher Kanten gebildet, wobei das Entfernen identischer Kanten berücksichtigt wird. Wir finden die Kante mit dem kleinsten X und Y und beginnen damit die Passage / Sortierung der Kanten mit dem Hinzufügen eindeutiger Eckpunkte zur Liste. Diese Liste wird die Hülle vieler Dreiecke sein. Das einzige, was im Finale passiert, ist, kollineare Punkte aus der Liste zu entfernen.

Infolgedessen konnte ich Concave Hull von nahezu beliebiger Komplexität bauen. Dieser Algorithmus passte nicht in einen Lochsatz, aber ich habe dies umgangen, indem ich diesen Satz einfach in zwei Hälften in einem Loch geteilt habe. Dann habe ich die Schaltung bekommen und sie trianguliert:







Alles hat gut geklappt:



Aber am Ende war ich über das Ergebnis verärgert. Der von mir entwickelte Algorithmus führte zu einer spürbaren Leistungssteigerung beim Rendern einer Szene, da die Anzahl der Polygone im Durchschnitt um 60 bis 70% reduziert wurde. Gleichzeitig begann die Kartengenerierung zehnmal langsamer. Der Algorithmus erwies sich als sehr zeitaufwändig.

Es dauerte drei Tage, um eine leichtgewichtige Version des Algorithmus zur Optimierung des polygonalen Netzes der Landschaft in Betracht zu ziehen, die folgende Ergebnisse lieferte:



Jetzt sind die Datenberechnungen zur Optimierung vor dem Hintergrund der Kartengenerierung nicht mehr erkennbar, und die Anzahl der Polygone ist im Durchschnitt um 40-50% gesunken.

Dieser Artikel ist versuchsweise und oberflächlich. Wenn sich jemand für das Thema Spieleentwicklung interessiert, bin ich bereit, den Artikel mit dem Geist spezifischer Schritte, Lösungen und zukünftiger Pläne fortzusetzen und zu erweitern. Ich denke auch, dass Sie sich für das Thema des Erstellens einer in Java entwickelten Open GL-Multithread-Anwendung interessieren werden, über das ich im nächsten Artikel sprechen werde.

Source: https://habr.com/ru/post/de436040/


All Articles