Home

Populationen optimal begrenzen

Sonntag, 21. Oktober 2007 | Autor:

Ein wesentliches Problem mit Populationen ist deren Ressourcenbedarf. Hat man mehrere in einem Bild, werden oft hunderte Megabyte, gerne auch mal ein paar Gigabyte an Speicher benötigt. Bevor Vista veröffentlicht wurde, wurden typische PCs von der Stange eher sparsam mit RAM bestückt. Dabei kommt man mit 512MB nicht mehr sehr weit. Darüber hinaus dauert das Populieren vor dem eigentlichen Render mitunter recht lange.
Wie müssen Populationen also aussehen, um schnell und sparsam generiert werden zu können?

Probleme und technischer Hintergrund

Wichtigstes Problem bei Populationen auf älteren Rechnern ist zu hoher Speicherbedarf. Eher früher als später steht TG2 nicht mehr genug davon zur Verfügung, um alle seine Daten abzulegen. Das kann aus verschiedenen Gründen auch passieren, wenn noch genug Speicher frei ist.

Eine technische Lösung des Problems sind beispielsweise 64bit-Systeme mit 64bit-Betriebssystemen. Dort ist der ansprechbare Speichervorrat etwa 4 Milliarden Mal größer als in 32bit-Systemen. Aber auch hier treten Probleme auf, wenn wesentlich mehr Speicher verwendet wird, als installiert ist – Speicherseiten werden dann auf die Festplatte ausgelagert und nur bei Bedarf in den Arbeitsspeicher geladen. Da aber der Zugriff auf eine Festplatte typischerweise 100 Mal länger dauert als der Zugriff auf RAM, wird die Arbeitsgeschwindigkeit durch zu häufiges Auslagern drastisch reduziert.

Angesichts solcher Einschränkungen ist die lange Wartezeit beim Generieren der Population noch das geringste Problem.

Auswirkungen in der Praxis

Als Experiment habe ich eine 10*10km große Fläche mit einer Baum-Population bestückt, wobei der Abstand zwischen den Instanzen 2m beträgt. Das ergibt 25 Millionen Instanzen.
Nachdem die Population fertig generiert ist, stürzt TG2 auch prompt ab. Ein Blick in den Taskmanager bestätigt: Vor dem Absturz erreicht der Speicherverbrauch einen Maximalwert von 2,3GB. XP Home hätte an diesem Punkt längst aufgegeben, aber auch auf einem XP Pro mit /3G-Switch, der das System bis zu 3GB Speicher nutzen lässt, findet TG2 nicht mehr ausreichend Speicherplatz.

Lösungen

Object Spacing

Eine unmittelbar einleuchtende Lösung ist natürlich, den Abstand zwischen den Objekten zu erhöhen. 2m zwischen zwei Bäumen ist wenig. Hier lohnt es sich, den Abstand großzügiger zu bemessen. Bereits eine Verdopplung bedeutet eine Reduzierung der Instanzenanzahl auf ein Viertel!

Doch mitunter möchte man das nicht, beispielsweise bei einer Grasdecke, die geschlossen sein soll. Außerdem reicht die Maßnahme in unserem Test-Beispiel leider nicht aus.

Maske für den Sichtbereich

Der Bereich, den TG2 mit einer Population bevölkert ist rechteckig. Das macht sich sehr einfach bei Platzierung und Dimensionierung einer Population, hat aber auch Nachteile. Zum einen ist der Bereich, in dem die Population tatsächlich existiert, entlang einer scharfen Kante begrenzt – was unrealistisch aussieht.

Zum anderen werden Areale bevölkert, die später im Bild gar nicht auftauchen – hier wird unnötig Speicherplatz und Zeit beim Generieren der Population verschenkt. Im Einzelbild mag das noch nicht ins Gewicht fallen, will man jedoch eine Animation rendern, wollen hunderte Quadratkilometer mit Pflanzen und Steinen bevölkert werden, von denen in einem Einzelbild der Animation jedoch nur ein Bruchteil zu sehen ist – der Rest der Population blockiert sinnlos Speicher.

Als Lösung bietet sich an, die Population mit einer Maske einzuschränken. Für einen Render brauchen wir tatsächlich nur die Bäume, die sich im Sichtbereich der Kamera befinden. Diese Maske erhalten wir aber problemlos, indem wir eine weiße Textur durch die Renderkamera projizieren. Gesetzt den Fall, die Population wird bereits durch einen Distribution Shader oder einen sonstigen Shader als Density Shader begrenzt, sieht das entsprechende Netz nachher so aus:

Der Image Map Shader, in dem noch die Render Camera als Projection Camera eingestellt werden muss, lädt unsere Textur (ein 1x1px großes, rein weißes Bild reicht aus), der Merge Shader mit Mix to A auf 1, Merge Color aktiviert und Colour Merge Mode auf Multiply (egal welches) maskiert den Distribution Shader.

Das Ergebnis dieses kleinen Tricks kann sich durchaus sehen lassen! Die Instanzen sind nun nur noch genau dort verteilt, wo die Kamera auch hinsieht. Damit lassen sich bis zu 50% Instanzen sparen, wenn man annimmt, dass das Rechteck schon vorher optimal war und ein Einzelbild gerendert werden soll.

Im Grunde dient das Rechteck nun lediglich noch dazu, die Entfernung festzulegen, ab der die Population aufhört. Falls wir für die Entfernung zur Kamera auch automatisch eine Maske generieren lassen könnten, wären wir in der Lage, das Rechteck beliebig groß machen zu dürfen, ohne dass der RAM-Verbrauch explodiert. Wir könnten den gesamten Planeten durch das Rechteck erfassen, unsere Masken würden dennoch automatisch nur dort Populationen erstellen, wo es nötig ist!

Weitsicht eingrenzen für Animationen

Es ist unmittelbar klar, dass es ab einer bestimmten Entfernung keinen Sinn mehr macht, Objekte zu verwenden. Ab einem gewissen Punkt ist es ohnehin nicht mehr möglich, zu entscheiden, ob eine Pixelgruppe tatsächlich ein Baum ist oder nicht. Dort kann man dann anfangen, sich Objekte zu sparen und beispielsweise Wälder einfach mit grüner Farbe zu simulieren.

Das wirft natürlich eine Frage auf: Wie groß ist diese Distanz? Wann genau macht es keinen Sinn mehr einzelne Objekte zu rendern? Man kann sich dazu auf den Standpunkt stellen, dass Objekte nicht mehr als solche zu erkennen sind, sobald sie im fertigen Bild nur noch einen Pixel einnehmen, denn anhand eines einzigen Bildpunktes zu erkennen, dass dieser einen Baum darstellt und nicht zum Beispiel ein Stück Wiese, ist unmöglich. In einigen speziellen Fällen mag das nicht gelten, in anderen sind auch zwei oder drei Pixel noch unerkennbar. Ich nehme hier einfach einen Wert von einem Pixel als Grenze und nenne die kritische Entfernung, ab der ein Objekt nur noch einen Pixel groß ist, „1px-Entfernung“.

Der Bildwinkel (Field of View) der Kamera kann in TG2 direkt eingestellt werden (Horizontal FoV). Die Scheinbare Größe eines Objekts wird ebenfalls als Winkel angegeben. Wenn man davon ausgeht, dass ein Baum mindestens einen Pixel groß sein muss, um das Recht zu erwerben als Objekt gerendert zu werden, dann darf die Scheinbare Größe des Baums bei einer gegebenen Renderauflösung einen bestimmten Wert nicht unterschreiten. Ohne jetzt noch weiter auf die Mathematik eingehen zu wollen, hier das Resultat als Graph:

x-Achse: Winkel in Grad/°
y-Achse: Entfernung in km
(Plot erstellt mit dem MAFA Funktionsplotter)

Die Funktionen ordnen einem bestimmten Bildwinkel (FoV) den nötigen Mindestabstand von der Kamera zu, ab dem ein 10m breites Objekt im Render weniger als einen Pixel groß erscheint. Die Kurven gelten (v.l.n.r) für 800px, 1024px, 1280px, 1600px.
Ein Beispiel: Bei einem Render in einer Auflösung von 1024px und einem FoV von 60° (voreingestellter Wert) ist ab einer Entfernung von 10km ein Baum nur noch 1px groß. Erhöht man die Größe des Objektes oder die Rendergröße um einen bestimmten Faktor, muss auch die 1px-Entfernung um diesen Faktor erhöht werden.
Ein Beispiel: Ist das fragliche Objekt im obigen Beispiel statt 10m nur 1m groß, beträgt die kritische Entfernung nicht 10km, sondern nur 1km.

Die Kurven sollen im Wesentlichen als Orientierung und theoretisches Fundament dienen. Im Alltag wird man diese Entfernung nur grob schätzen. Außerdem ist zu beachten: Viele Objekte sind um einen Faktor höher als breit. Die 1px-Entfernung muss dann ebenfalls mit diesem Faktor multipliziert werden.

Die Realisierung in TG2 sieht folgendermaßen aus:

Die Konfiguration des Distance-Shaders für 10km ist links zu sehen. Er färbt einen Punkt weiß, wenn dessen Abstand zur Kamera näher als die Near Distance ist, schwarz, wenn er größer als die Far Distance ist. Dazwischen findet ein sanfter Übergang statt.

Mit einer Kopie des Merge-Shaders multiplizieren wir unsere neue Maske mit der alten und nehmen das Resultat als Density-Shader.

Damit haben wir unser Ziel erreicht! Unabhängig von der vorgegebenen Population-Area berechnet uns TG2 selbst, wo es Objekte hin pflanzt und wo nicht. Dennoch sollte die Area für die Population nicht allzu übermütig vergrößert werden, denn dann verbraucht der Generierungsprozess unverhältnismäßig viel Zeit.

Das war’s auch schon wieder! Kritik und Fragen sind, wie immer, willkommen 🙂

 

Tags » «

Trackback: