Zusammenführen von Geometrie / Netz ohne Verlust von Vorteilen

11

In three.js können wir einfach die Geometrie zusammenführen, um die Anzahl der Draw-Aufrufe zu begrenzen und damit die Leistung zu steigern. In einem einfachen Test mit einem Material konnte ich 50.000 Cubes + Shadows @ 60fps auf meiner GTX660-GPU zeichnen. Ohne das Zusammenführen der Geometrie verursachten bereits 5.000 Würfel ein Problem.

Ich frage mich, wie ich die Vorteile des Renderns jedes Würfelnetzes für sich behalten kann. Wie wählt man beispielsweise ein Würfelnetz aus, wenn alles zu einer Geometrie zusammengeführt wird? Standardmäßig ist das natürlich nicht möglich.

Gibt es eine übliche Technik für dieses Problem? Immerhin habe ich auch nach dem Zusammenführen alle nicht zusammengeführten Netzobjekte. Es muss also eine Möglichkeit geben, sie für die Kommissionierung zu verwenden?

Was ich auf den Punkt bringen möchte

  • SimCity-ähnliches Spiel für Lernzwecke
  • Jedes Haus ist ein Würfelnetz
  • Sie möchten 50.000 Häuser rendern und Häuser hinzufügen und entfernen können
  • Die Hausauswahl per Mauszeiger (Picking) muss möglich sein
user990827
quelle
Ich bin mir nicht sicher, ob dies für Sie von Nutzen ist, aber ich erwähne es der Vollständigkeit halber. Simplygon verfügt über ein lizenzgebührenbasiertes Preismodell für Indie-Entwickler und kann einen Großteil dieses Netzes zur Entwurfszeit zusammenführen und partitionieren.
steeveeet

Antworten:

11

OK ich habe es. Nachdem ich die gesamte Geometrie zusammengeführt habe, habe ich immer noch die einzelnen Netze in einem Array. Ich kann diese Netze also einfach für das Raycasting verwenden, obwohl sie nicht einmal gerendert werden. Ich habe eine Weile gebraucht, um das zu realisieren.

Geben Sie hier die Bildbeschreibung ein

Für die Auswahl verwende ich diese Octree-Implementierung: http://threejs.org/examples/#webgl_octree_raycasting

Dadurch werden die Schnittpunkttests pro Aktualisierung von 50.000 auf ~ 500 gesenkt. Ohne den Octree werden die fps stark abnehmen.

Der orangefarbene Picking-Rumpf, den Sie sehen, ist das jetzt gerenderte Netz (+1 Draw Call) mit geändertem Material und geänderter Größe.

Geben Sie hier die Bildbeschreibung ein

Ich denke, der nächste Schritt besteht darin, eine Art Kartenpartitionierung zu implementieren. Teilen Sie die zusammengeführte Geometrie in mehrere Teile auf. Der Grund dafür ist, dass die zusammengeführte Geometrie eine große Anzahl von Eckpunkten aufweist. Das heißt, wenn ich die Karte zu 99% vom Bildschirm verschiebe, muss die Grafikkarte immer noch alle Scheitelpunkte verarbeiten, da die Geometrie immer noch sichtbar ist, mindestens 1% davon. Wenn es also aufgebrochen ist, müssen nur die sichtbaren Teile gerendert werden.

user990827
quelle
Vielen Dank für diesen Einblick. Ich habe versucht, auch einen Weg zu finden, und ich denke, Ihre Lösung hier ist hervorragend! Kurze Frage: Welche Eigenschaften sind für Ihre lokale Liste von Objekten (dh Three.Object3Ds) erforderlich, damit rayCaster.intersectObjects () funktioniert?
AlvinfromDiaspar
Sehr schön gemacht.
ClassicThunder
Ich habe ein ähnliches Problem, kann aber das Raycasting mit r70 irgendwie nicht zum Laufen bringen. Haben Sie beim Erstellen und Zusammenführen der Boxen etwas Besonderes getan? Ich verwende den folgenden Code ( pastebin.com/PStaAF3P ), um die Knoten zu erstellen und zusammenzuführen, aber möglicherweise fehlt etwas, um den Raycaster zum Laufen zu bringen?
fhahn
Kurze Frage: Ich mache etwas sehr Ähnliches wie Sie (eine 3D-Karte basierend auf GeoJSON-Daten). Müssen Sie bei Ihrer Methode bei jeder Drehung der Kamera auch die bereits vorhandenen Gebäudenetze drehen? Oder fügen Sie der Szene einfach die einzelnen Felder hinzu, rendern sie aber nicht?
Spencer
@Spencer Nein, muss nicht gedreht werden. Ich behalte einzelne Felder in einem globalen Array und füge der Szene nur zusammengeführte Geometrie hinzu. Sie können dann mit dem Objekt in einem globalen Array Raycasting durchführen, da Raycaster von der Kameramatrix und der Position der einzelnen Boxen abhängt. Höchstwahrscheinlich nicht der beste Weg, dies war nur ein Proof of Concept für mich. Zu diesem Zeitpunkt keine drei mehr.
user990827
1

Zum Auswählen können Sie auch IDs für jeden Cube auf ein anderes Renderziel rendern und einfach überprüfen, wie sich der ID-Wert am Cursor befindet. Der Vorteil ist, dass die Auswahl pixelgenau ist und auch bei komplexeren Geometrien effizient funktioniert.

Wenn alle Objekte dieselbe Geometrie haben, können Sie instanziiertes Rendern verwenden. Ein Stream definiert die Geometrie, während ein anderer Eigenschaften pro Instanz definiert (z. B. Transformation). Für das Kegelstumpf-Culling müssten Sie den Instanz-Stream für jeden Frame basierend auf dem Sichtbarkeitstest erstellen. Wenn Sie jedoch eine große Anzahl von Objekten haben, möchten Sie diese Objekte möglicherweise in einem losen Octree oder etwas anderem platzieren, um das Keulen zu beschleunigen.

JarkkoL
quelle
0

Ich bin mir über die Einzelheiten von three.js nicht sicher, aber im Allgemeinen fallen mir zwei mögliche Leistungsprobleme ein: OpenGL:

  1. Haben Sie darüber nachgedacht, zu instanziieren? Sie benötigen ebenfalls nur einen Draw Call und verwenden weniger VRAM.
  2. Haben Sie sich ernsthaft mit dem Kommissionieralgorithmus befasst? Wenn Ihre Cubes beispielsweise Begrenzungsvolumina in einer Liste anstelle eines Baums haben, würde dies einen Unterschied dieser Größenordnung erklären.
bogglez
quelle
0

Ein anderer Ansatz, den Sie wählen können, besteht darin, ein Scheitelpunktattribut in Ihre Geometrie zu integrieren und die Hervorhebungslogik in Ihren Fragment-Shader einzufügen. Dies ist äußerst nützlich, wenn Sie nicht zwei Kopien von Daten im Speicher haben möchten und mehr Kontrolle darüber haben, wie die Hervorhebung implementiert wird.

HaoCS
quelle