Culling-Techniken zum Rendern vieler Würfel

Lesezeit: 8 Minuten

Ich arbeite an einem persönlichen Lernprojekt, um a Minecraft Klon. Es funktioniert sehr gut, abgesehen von einer Sache. Ähnlich wie bei Minecraft hat mein Terrain viele Würfel auf dem Y gestapelt, damit Sie nach unten graben können. Obwohl ich Frustum Culling mache, bedeutet dies immer noch, dass ich nutzlos alle Schichten von Würfeln unter mir zeichne. Die Würfel sind X, Y und Z geordnet (allerdings nur in 1 Richtung, also ist es technisch gesehen nicht Z zur Kamera geordnet). Ich füge im Grunde von der Position des Spielers aus nur Zeiger zu Würfeln um den Spieler hinzu. Gegen diese führe ich dann Frustum-Culling durch. Ich mache keine Oct-Tree-Unterteilung. Ich dachte daran, die Ebenen unter dem Player einfach nicht zu rendern, außer dass dies nicht funktioniert, wenn der Player in ein Loch schaut. Wie könnte ich angesichts dessen vermeiden, Würfel unter mir zu rendern, die ich nicht sehen kann, oder auch Würfel, die von anderen Würfeln verdeckt werden?

Vielen Dank

void CCubeGame::SetPlayerPosition()
{
PlayerPosition.x = Camera.x / 3;
PlayerPosition.y = ((Camera.y - 2.9) / 3) - 1;
PlayerPosition.z = Camera.z / 3;
}

void CCubeGame::SetCollids()
{

SetPlayerPosition();

int xamount = 70;
int zamount = 70;
int yamount = 17;

int xamountd = xamount * 2;
int zamountd = zamount * 2;
int yamountd = yamount * 2;
PlayerPosition.x -= xamount;

PlayerPosition.y -= yamount;

PlayerPosition.z -= zamount;


collids.clear();
CBox* tmp;

    for(int i = 0; i < xamountd; ++i)
    {
        for(int j = yamountd; j > 0; --j)
        {
            for(int k = zamountd; k > 0; --k)
            {

                tmp = GetCube(PlayerPosition.x + i, PlayerPosition.y + j, PlayerPosition.z + k);



                if(tmp != 0)
                {
                    if(frustum.sphereInFrustum(tmp->center,25) != NULL)
                    {
                        collids.push_back(tmp);
                    }
                }

            }
        }

}

  • Zumindest beim Rendern sortieren Sie von vorne nach hinten, sodass Sie alle unteren Würfel schnell verwerfen. Aber ein Oct-Tree wäre eine gute Idee.

    – GManNickG

    12. September 2010 um 1:50 Uhr

  • @GMan Wie rendere ich von vorne nach hinten, wenn mein Winkel etwa 65 Grad auf x, 70 auf y beträgt, wie könnte ich das in diesem Fall tun, ohne eine Entfernungsprüfung von Kamera zu Player durchzuführen?

    – jmasterx

    12. September 2010 um 1:55 Uhr


  • Klonen eines Klons. Ich bin ziemlich froh, dass ich mich entschieden habe, nicht zu versuchen, meine eigenen zu machen.

    – Jon Purdy

    12. September 2010 um 2:02 Uhr

  • Verwenden Sie zuerst einen Octree, um die Anzahl der zu zeichnenden Würfel zu minimieren. Sortieren Sie dann nach Entfernung im Quadrat. (“Okklusionsabfrage” ist eine Sache, die Sie vielleicht auch nachschlagen möchten.)

    – GManNickG

    12. September 2010 um 3:03 Uhr


Folgendes habe ich beim Schreiben meines eigenen Klons gelernt:

  1. Legen Sie nicht einfach jeden Cube in OpenGL ab, sondern sorgen Sie sich auch nicht darum, die gesamte Sichtbarkeit selbst zu bereinigen. Überprüfen Sie, wie in einer anderen Antwort angegeben, alle 6 Flächen, um festzustellen, ob sie vollständig von einem angrenzenden Block verdeckt sind. Rendern Sie nur Gesichter, die sichtbar sein könnten. Dies reduziert Ihre Flächenzahl grob von einem kubischen Begriff (ein Volumen von Würfeln n * n * n) auf einen quadratischen Begriff (Oberfläche von nur etwa n * n).
  2. OpenGL kann Frustrum Culling viel schneller durchführen als Sie. Sobald Sie alle Ihre Oberflächenflächen in eine Anzeigeliste oder VBO gerendert haben, senden Sie einfach den gesamten Blob an OpenGL. Wenn Sie Ihre Geometrie in Slices (oder was Minecraft Chunks nennt) aufteilen, vermeiden Sie möglicherweise, die Chunks zu zeichnen, die Sie können leicht bestimmen, sind hinter der Kamera.
  3. Rendern Sie Ihre gesamte Geometrie in eine Anzeigeliste (oder Listen) und zeichnen Sie diese jedes Mal neu. Dies ist ein einfacher Schritt, wenn Sie den Sofortmodus verwenden, da Sie einfach Ihren vorhandenen Code in glNewList/glEndList umschließen und mit glCallList neu zeichnen. Das Reduzieren der Anzahl der OpenGL-Aufrufe (pro Frame) hat eine wesentlich größere Auswirkung als das Reduzieren des Gesamtvolumens der zu rendernden Polygone.
  4. Sobald Sie sehen, wie viel länger es dauert, die Anzeigelisten zu generieren, als sie zu zeichnen, werden Sie anfangen, darüber nachzudenken, wie Sie die Aktualisierungen in einen Thread einfügen können. Hier zahlt sich die Konvertierung in VBOs aus: Der Thread rendert in einfache alte Arrays (z. B. Hinzufügen von 3 Floats zu einem Array, anstatt glVertex3f aufzurufen) und der GL-Thread muss diese dann nur noch mit glBufferSubData in die Karte laden. Sie gewinnen doppelt: Der Code kann in einem Thread laufen und einen Punkt mit 3 Array-Schreibvorgängen anstelle von 3 Funktionsaufrufen “zeichnen”.

Andere Dinge, die mir aufgefallen sind:

VBOs und Anzeigelisten haben eine sehr ähnliche Leistung. Es ist durchaus möglich, dass eine bestimmte OpenGL-Implementierung intern ein VBO verwendet, um eine Anzeigeliste zu speichern. Ich habe Vertex-Arrays (eine Art clientseitiges VBO) direkt übersprungen, daher bin ich mir bei diesen nicht sicher. Verwenden Sie die ARB-Erweiterungsversion von VBOs anstelle des GL 1.5-Standards, da die Intel-Treiber nur die Erweiterung implementieren (obwohl sie behaupten, 1.5 zu unterstützen) und die nvidia- und ATI-Treiber sich nicht darum kümmern.

Regel für Texturatlanten. Wenn Sie eine Textur pro Fläche verwenden, sehen Sie sich an, wie Atlanten funktionieren.

Wenn Sie meinen Code sehen möchten, finden Sie mich auf github.

  • Danke für die Ideenliste, du bekommst das Kopfgeld.

    – Adam Davis

    20. Oktober 2010 um 3:52 Uhr

Benutzer-Avatar
Jakow Galka

Von vorne nach hinten rendern. Dazu brauchen Sie keine Sortierung, verwenden Sie Octrees. Die Blätter sind keine einzelnen Würfel, sondern größere Gruppen davon.

Ein Mesh für jedes solche Blatt sollte in einem Vertex-Puffer zwischengespeichert werden. Wenn Sie dieses Netz erzeugen unterlassen Sie Generieren Sie alle Würfel in einer Brute-Force-Weise. Überprüfen Sie stattdessen für jede Würfelfläche, ob sie einen undurchsichtigen Nachbarn innerhalb desselben Blattes hat, wenn ja, müssen Sie diese Fläche überhaupt nicht erzeugen. Sie rendern also nur die Fläche zwischen massiven Würfeln und leerem Raum. Sie können auch benachbarte Flächen mit demselben Material zu einem einzigen langen Rechteck vereinen. Sie können das Netz auch in sechs Sätze aufteilen, einen Satz für jede Hauptrichtung: +/-XYZ-Flächen. Zeichnen Sie nur die Sätze von Gesichtern, die der Kamera zugewandt sein können.

Das Rendern von vorne nach hinten hilft nicht so sehr. Sie können jedoch verwenden Okklusionskeulung die moderne Hardware bietet, um von dieser Ordnung zu profitieren. Überprüfen Sie vor dem Rendern eines Octree-Blatts, ob dessen Bbox die Okklusionsabfrage besteht. Wenn es nicht besteht, müssen Sie es überhaupt nicht zeichnen.

Ein alternativer Ansatz zur Okklusionsabfrage kann Raytracing sein. Raytracing ist gut zum Rendern einer solchen Umgebung. Sie können einen spärlichen Satz von Strahlen werfen, um ungefähr zu sehen, welche Blätter sichtbar sind, und nur diese Blätter zeichnen. Dies wird jedoch die eingestellte Sichtbarkeit unterschätzen.

Wie andere habe ich mit Ogre mit einer Blockwelt-“Engine” herumgespielt und währenddessen einige Artikel geschrieben (siehe Weltartikel blockieren). Der grundlegende Ansatz, den ich verfolgt habe, ist:

  • Erstellen Sie nur die sichtbaren Flächen von Blöcken (keine Flächen zwischen Blöcken).
  • Welt in kleinere Chunks aufteilen (nur notwendig für schnellere Aktualisierung einzelner Blöcke).
  • Kombinieren Sie Blocktexturen in einer Texturdatei (Texturatlas).

Wenn Sie diese verwenden, können Sie auf großen einfachen Blockwelten (z. B. 1024 x 1024 x 1024 auf anständiger Hardware) eine sehr gute Leistung erzielen.

Ich arbeite derzeit an einem Minecraft-Klon in Python/Pyglet, nur aus Neugier.

Ich zerlege die Daten wie in Minecraft in Chunks und erstelle dann für jeden Chunk eine OpenGL-Anzeigeliste basierend auf der Cube-Sichtbarkeit. Ich führe dann ein einfaches 2D-Frustum-Culling an diesen Chunks durch und rufe jede Anzeigeliste innerhalb einer bestimmten Entfernung des Players auf.

Beim Hinzufügen/Entfernen von Würfeln erstelle ich die Anzeigeliste für den Chunk neu.

Es gibt kein Okklusions-Culling außer bei Würfeln, die vollständig von anderen Würfeln umgeben sind.

Bei einfachen Szenen kann dies auf einer bescheidenen Grafikkarte mit einer Sichtweite von etwa 200 Würfeln über 600 fps erreichen.

Octtree usw. wird sicher funktionieren, aber eine andere Möglichkeit, Ihr spezifisches Problem zu lösen, könnte darin bestehen, zu speichern, um eine hinzuzufügen usigned char visible zu jedem Würfelobjekt. Der Wert der visible Feld wird wie folgt berechnet:

  • wenn die rechte Seite (Blick entlang der x-Achse) keinen Nachbarn hat, dann wird das erste Bit (1) gesetzt.
  • wenn die linke Seite (Blick entlang der negativen x-Achse) keinen Nachbarn hat, dann wird das 2. Bit (2) gesetzt.
  • wenn die Vorderseite (Blick entlang der z-Achse) keinen Nachbarn hat, dann wird das 3. Bit (4) gesetzt
  • … und so weiter, sodass Sie für jede der 6 Seiten eines Würfels 1 Bit haben

Immer wenn der Spieler einen Würfel weggräbt, müssen Sie den aktualisieren visible Feld aller Nachbarwürfel.

Also, aber wie wird das helfen? Wenn ein Würfel a hat visible Wert 0, dann ist es einfach – der Würfel wird nie angezeigt. Aber nehmen wir an, der Würfel hat a visible Wert 1. Dann ist der Würfel (evtl.) nur sichtbar, wenn Xplayer < Xcube. Die anderen Seiten funktionieren ähnlich, und ich denke, dass eine solche Funktion, die entscheidet, ob ein Würfel sichtbar sein könnte, ziemlich schnell sein wird und viele versteckte Würfel überspringen kann.

Der Nachteil dabei ist, dass dieser Test nur ein Pro-Cube-Test ist und man damit keine ganzen Gruppen überspringen kann. Also vielleicht ein Octtree (zum Überspringen ganzer Regionen) und so ein sichtbares Feld wie hier beschrieben, um die hohe Anzahl versteckter Würfel zu überspringen (Da solche Würfel gestapelt sind, ist die Anzahl der versteckten Würfel viel höher als die Anzahl der sichtbaren Einsen) innerhalb dieser Region könnte eine gute Lösung sein.

Benutzer-Avatar
Nekrolis

Sie können verwenden PVS (potenziell sichtbarer Satz) Dafür gelten, obwohl es im Allgemeinen für Gelände gilt, die gleichen Prinzipien, keulen, was nicht sichtbar ist. Gamedev.net hat auch einen Terrain-Morphing-Artikel, der sich damit befasst.

Falls nur das Zeichnen das Problem ist (und nicht die Drehung nicht verwendeter Scheitelpunkte), könnte so etwas nicht sein c-Puffer nützlich? Ich habe es mit ziemlichem Erfolg verwendet, es erfordert sortierte Polygone (zB nach dem Algorithmus des Malers) und fast null Speicher (im Gegensatz zu z-buffer).

1303350cookie-checkCulling-Techniken zum Rendern vieler Würfel

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy