Jump to content
Unity Insider Forum
Quellcode

LOD System - Gedanken

Recommended Posts

Hallo Zusammen,

ich wollte zu einem zweiten Punkt nochmal eure Meinung einholen um zu prüfen ob mein Weg der richtige ist oder ob man dies noch optimieren könnte.

Da mein Spiel eine "Open World" werden soll, muss ich eine Art LOD System / Cluster Gruppen nutzen, da ich unmöglich alles live rendern lassen kann.

 

Als Test habe ich nun einfach mal einen Cube genommen und ein simples Script angehangen, welches die Distanz zum Player prüft.

Ist der Cube also in der Range, wird der MeshRenderer aktiviert, sonst deaktiviert.

Damit nicht alle potenziell möglichen Cubes gleichzeitig die Distanz berechnen, habe ich noch eine Zufallszahl zwischen 0 und 29 eingesetzt und führe damit 2x pro Sekunde (60 FPS) die Berechnung pro Cube aus.

 

Mein Test lässt mich diese Nummer sogar mit 13.000 Cubes bei über 70 FPS und 25.000 Cubes mit knapp 59/60 FPS erfolgreich durchführen.

Soweit so gut!

 

Nun sind das einige Objekte und sogar viel mehr als ich letztendlich "pro Cluster" anzeigen lassen möchte.

Mein Gedankengang war wie folgt:

- Gesamte Welt

-> Cluster (großer Bereich wie zB. eine Stadt oder ein Wald)

--> LOD Group (Stadtteil etc.)

---> LOD (Haus, Baum, Auto, ... )

 

Pro LOD Group sollten es später maximal 1.500 Objekte werden, sodass wenn der Player genau zwischen 4 LOD Groups stehen würde kurzfristig 6.000 Objekte geprüft werden.

Die Cluster, welche nicht aktiv sind, wollte ich auf der Festplatte speichern und im Ram (im Spiel) sind dann nur die LOD Groups des aktivien Cluster.

Die Cluster hätten Abstand zueinander, also ist hier immer nur einer Aktiv (dazwischen dann eine länger Straße etc.).

 

Was haltet Ihr von diesem Vorgehen? Macht dies Sinn oder kennt Jemand eine noch bessere Lösung?

Ich habe auch in Unity selbst den Component "LOD Group" gefunden und werde gleich im Anschluss danach googlen, da ich oben von meinen eigenen LOD Groups spreche.

Dieses Video sehe ich mir nun noch an, wegen der Speicherung: https://unity3d.com/de/learn/tutorials/topics/scripting/persistence-saving-and-loading-data

 

Aber vielleicht habt ihr noch einen schlauen Link für mich oder ein passendes Video.

 

Danke im Voraus für euren Input, eure Ideen oder Kritik :)

Share this post


Link to post
Share on other sites

Ich kann nur davon abraten ein Skript zu verwenden, welches die Distanz berechnet, daß ist viel zu langsam für eine große Gruppe an Objekten. Hier solltest du besser die bereits vorhandene Komponente von Unity verwenden:
https://docs.unity3d.com/Manual/LevelOfDetail.html

Über diese kannst du Objekte auch komplett ausblenden.

Ein komplett anderes Thema ist allerdings, Objekte in der Szene nachzuladen, was man bei OpenWorld-Szenarien sehr wichtig wird.

Share this post


Link to post
Share on other sites

Danke für den Link, schaue ich mir an!

Die Frage wird dann sein, ob die Unity interne Funktion mit meiner Cluster Idee harmoniert.

Aber dürfte eigtl kein Problem sein, wenn ich die Objekte innerhalb der "LOD Group" durch die Unity Funktion ein bzw ausblenden lasse...

Share this post


Link to post
Share on other sites

Es gibt auch noch eine erweiterte LOD-API (so nenne ich es mal) wo es aber nicht nur um die Visibility geht (also nicht nur darum, ob ein Objekt gerendert wird oder nicht), sondern darüber kann man auch Objekte steuern die in eine LOD-Gruppe Eintreten oder diese Verlassen, also beispielsweise kann man darüber Gameobjekte komplett deaktivieren und damit Skripte ausschalten die nicht benötigt werden, da sie zu weit von der Kamera entfernt sind. Vielleicht passt diese API auch besser zu deinem Clusteransatz, ein Cluster wäre hier eine Culling Group:
https://docs.unity3d.com/Manual/CullingGroupAPI.html'
 

Zitat

Die Cluster, welche nicht aktiv sind, wollte ich auf der Festplatte speichern und im Ram (im Spiel) sind dann nur die LOD Groups des aktivien Cluster.

Noch ein Tipp dazu, wenn du ein Cluster in eine Unityszene legst, dann kannst du zur Laufzeit im Hintergrund diesen Cluster laden und anzeigen wenn der Spieler den Cluster betritt (siehe Culling Group). Das Ganze ist natürlich recht komplex, da der Cluster bereits vorgeladen werden muss, wenn der Spieler den Cluster noch nicht erreicht hat. In Unity kann man aber mittlerweile Szenen im Hintergrund laden und dann additiv in der Szene anzeigen wenn sie benötigt werden ...

Hier ist ein Asset welches diese Techniken auf die Spitze getrieben hat, da kann man sich auch ein paar Ideen "klauen":
https://assetstore.unity.com/packages/tools/utilities/world-streamer-36486

Share this post


Link to post
Share on other sites

Update

 

Hallo Zusammen,

bevor ich einen neuen Thread eröffne, wollte ich hier einmal den aktuellen Stand beschreiben und eure Meinung einholen.

 

Ich habe mich mit dem Unity eigenen LOD Group System beschäftigt, leider erfüllt es nicht meine genauen Vorstellungen.

Nehmen wir als Beispiel einen Innen-Stadt Szene. Hier sind viele Gebäude vorhanden, welche aber teilweise verdeckt werden.

Würde ich ein Haus bei sagen wir 100 Meter Entfernung von LOD3 auf LOD2 umstellen, wäre dies in der Innenstadt unnötig, da es dennoch verdeckt ist und nicht besser zu sehen sein muss. Hier reicht mit das LOD3 Mesh auch bis 30 Meter...

Nun dachte ich an die einfachen Box Collider, welche ich als Trigger Stufen nutzen möchte.

 

Es gibt also pro Gebäude 3 Collider, welche einen gewissen - aber vor allem immer individuellen - Abstand zum Player darstellen.

Jetzt kann ich dies im Leveldesign ausnutzen um Berge, andere Gebäude etc. auch zu berücksichtigen.

 

Das Script ist absolut simpel (ausgehend davon, dass der Player nicht zu nah an einem Gebäude startet):

	public GameObject[] LOD = new GameObject[4];
	private int state = 3;


	void Start()
	{ 
		for( int i = 0; i < 4; i++ )
		{
			LOD[i].active = false;
		}
		LOD[state].active = true;
	}
 

	void OnTriggerEnter( Collider col )
	{
		if( col.tag == "Player" ){
			Change(-1);
		}
	}


	void OnTriggerExit( Collider col )
	{
		if( col.tag == "Player" ){
			Change(1);
		}
	}


	void Change( int direction )
	{
		LOD[state].active = false;
		state = state + direction;

		if( state > 3 ){ state = 3; }
		if( state < 0 ){ state = 0; }

		LOD[state].active = true;
	}

 

Ich habe dies soeben mit einem Test von 110 Gebäuden = 330 Trigger in einer Szene und einem verdammt flinken Player ohne FPS Einbußen genießen können.

Dennoch frage ich mich, gibt es vielleicht eine noch bessere Methode?

Mir geht es immer darum das best mögliche und die optimierteste Variante zu nehmen, da im zunehmenden Projektverlauf noch viele Stellen kommen werden, die Rechenleistung benötigen. Da sollten dann die "Basics" einwandfrei laufen.

 

Wie löst Ihr dieses Problem, was gibt es noch für Alternativen?

Zum Unity eigenen System: Es ist gut, aber je nachdem wie die FBX aufgebaut ist (mehrere Meshes) ist allein die Steuerung im Editor "ruckelig", außerdem kann ich nur die Entfernung zur Kamera nehmen und nicht wie oben beschrieben, individuelle Levelteile beachten.

 

Danke im Voraus für euren Input!

Share this post


Link to post
Share on other sites

Ich hab noch nie mein eigenes LOD-System geschrieben(zumindest nicht für polygon Meshs) aber was du da machst klingt so, als würdest du eher das Gegenteil erreichen.
Dass du bei 330 Collidern keine Framedrops hast, bestätigt kein gutes Ergebnis. So eine moderne Engine sollte locker ein paar zehntausend collider ohne probleme handhaben können.

Das heißt aber auch nicht, dass was du da machst effektiv ist. Du hast schon allein dadurch einen riesen Overhead, dass jeder dieser Trigger OnTriggerExit und OnTriggerEnter implementiert.
Auch in der Innenstadt würde ich mal vermuten, dass der Fall, dass ein Gebäude nicht ganz aber soweit verdeckt ist, dass du ein geringeres Detaillevel anzeigen willst, ziemlich selten ist.

Ich gehe davon aus, dass deine Methode nicht wirklich nach oben skaliert. Dein CPU Overhead gleicht vermutlich wieder die gesparte GPU Zeit aus / übersteigt sie.
Aber wenn du es genau wissen willst, schalt den Profiler an ¯\_(ツ)_/¯

Zitat

Mir geht es immer darum das best mögliche und die optimierteste Variante zu nehmen, da im zunehmenden Projektverlauf noch viele Stellen kommen werden, die Rechenleistung benötigen. Da sollten dann die "Basics" einwandfrei laufen.

Klingt übrigens nach dem typischen Indie-Problem. Statt das tatsächliche Spiel zu entwickeln, arbeitet man an Zeug drumherum, das man gar nicht braucht.

Share this post


Link to post
Share on other sites
vor 1 Stunde schrieb Life Is Good:

Ich hab noch nie mein eigenes LOD-System geschrieben(zumindest nicht für polygon Meshs) aber was du da machst klingt so, als würdest du eher das Gegenteil erreichen.
Dass du bei 330 Collidern keine Framedrops hast, bestätigt kein gutes Ergebnis. So eine moderne Engine sollte locker ein paar zehntausend collider ohne probleme handhaben können.

Das heißt aber auch nicht, dass was du da machst effektiv ist. Du hast schon allein dadurch einen riesen Overhead, dass jeder dieser Trigger OnTriggerExit und OnTriggerEnter implementiert.
Auch in der Innenstadt würde ich mal vermuten, dass der Fall, dass ein Gebäude nicht ganz aber soweit verdeckt ist, dass du ein geringeres Detaillevel anzeigen willst, ziemlich selten ist.

Ich gehe davon aus, dass deine Methode nicht wirklich nach oben skaliert. Dein CPU Overhead gleicht vermutlich wieder die gesparte GPU Zeit aus / übersteigt sie.
Aber wenn du es genau wissen willst, schalt den Profiler an ¯\_(ツ)_/¯

Klingt übrigens nach dem typischen Indie-Problem. Statt das tatsächliche Spiel zu entwickeln, arbeitet man an Zeug drumherum, das man gar nicht braucht.

Den Profiler muss ich mir wirklich langsam aber sicher genauer ansehen, das ist noch Neuland für mich.

 

Das "typische Indie-Problem" ist aber hier nicht der Fall, ich weiß ja wo es "enden wird" und plane gerne voraus. Dies ist einer der Wichtigsten Punkte, denn wenn man schon hier eine Bremse hat, wird das Projekt scheitern sobald man auf halber Strecke merkt, dass ein Hauptteil des Systems "für den Popo" war ...

 

Gibt es sonst eine Alternative?

Du nutzt also immer das Unity eigene System?

Share this post


Link to post
Share on other sites
vor 10 Stunden schrieb Quellcode:

Gibt es sonst eine Alternative?

Klar, jede Menge. Die meisten Studios, die ihr eigenes LOD System implementieren, machen das halt passend zu deren Anforderungen.

Ich hab leider weder zu Unity, noch zu UE4 was genaues gefunden. Aber wenn du willst, könntest du ja selber nachschauen wie UE4 das löst, der Code ist schließlich open source.

Soweit ich das sehe, gibt es 2 große Ansatzpunkte:
- Du hast eine Mesh Funktion, die beim Rendern direkt die Distanz zur Kamera zurückliefert (könnte gut sein, dass Unity das intern macht)
- Du nutzt eine Datenstruktur, mit der du deine Welt in Bereiche einteilst, die du dann komprimieren kannst

Ersteres ist vermutlich raus, wenn du nicht gerade deine eigene Renderpipeline schreiben willst.
Datenstrukturen gibt es jede Menge für sowas. Das Stichwort hier ist: Spatial Data Structures
Ein paar Beispiele:
- Octree
- KD-Tree
- B-Tree
- Bounding Volume hierarchy
etc.

vor 10 Stunden schrieb Quellcode:

Du nutzt also immer das Unity eigene System?

Für normale Meshs würde ich das vermutlich nutzen, ja. Um eine andere Lösung würde ich mich erst kümmern, wenn ich damit an die Grenzen komme.

 

Hier eine gute Präsentation, wie die solche Probleme in Infamous: Second Son angegangen sind.
https://archive.org/details/GDC2014Bentley

  • Like 1

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×