[OGL] Konzeptionelle Vertexbuffer Frage
-
Hi,
ich schreibe momentan eine kleine 3D Engine welche auf ein Open-World Szenario ausgelegt ist und daher Octrees zum rendern der Welt benutzt.
Momentan besitzt jedes "Leaf" des Octrees eine Display-List welche die Geometrie und entsprechende Textur-Bindings enthält.
Da Display-Lists inzwischen auslaufen und auch nicht sonderlich flexibel sind möchte ich gerne auf Vertexbuffer(VBO) umsteigen. Meine Frage ist nun, ob bzw. wie ich mit einem Vertexbuffer-Object solche Sachen wie Textur-Bindings, Tangent-Vectors, Shader-Variablen etc. übergeben kann. Außerdem brauche ich zwei Rendermodes, einer für das Rendern "in Farbe" mit allen Texturen usw. und einer nur zum Rendern in eine Depth-Textur (fürs Shadow-Mapping).
Ist so etwas mit Vertexbuffern möglich? Und wenn ja, könnte mir jemand eine kleine konzeptionelle Hilfestellung geben wie ich so etwas erreichen kann?
-
Ein VBO ist nichts anderes als ein Buffer der nach Möglichkeit im VRAM der Grafikkarte abgelegt sein kann wenn der Treiber es als Sinnvoll erachtet. Wie der Name schon sagt dient dieser Buffer dazu deine Vertexdaten aufzunehmen. Du kannst also Position, Texcoords, Tangent-Vektoren, alles an Vertexattributen in einem VBO ablegen. Das wars. Ein VBO ist kein direkter Ersatz für Display Lists und speichert keinen State. Ein VBO ist nur ein Stück Grafikspeicher und sonst nichts. Um Texturebindings, Shader und Shaderparameter, etc. musst du dich selbst kümmern.
-
Du musst deine Daten so sortieren, dass du den VBO oder so viele zusammenhängende Stückchen davon wie möglich auf einmal zeichnen kannst.
Das ist eine Aufgabe für sich, aber es lohnt sich durchaus.
-
Das heißt ich muss für jede Gruppe an Triangles welche die selbe Textur benutzen einen eigenen VBO erstellen damit ich zwischen dem Rendern der VBOs das Textur-Binding verändern kann?
Die Frage ist ob mir der VBO dann noch was bringt da ich in meinem Octree-Leaf momentan maximal 512 Triangles habe. Und wenn ich da noch nach Texturen sortiere habe ich vielleicht VBOs mit weniger als 50 Triangles.
-
RedPuma schrieb:
Das heißt ich muss für jede Gruppe an Triangles welche die selbe Textur benutzen einen eigenen VBO erstellen damit ich zwischen dem Rendern der VBOs das Textur-Binding verändern kann?
Nein. Aber wenn du die Textur wechseln willst musst du eben mindestens für jede Textur einen Drawcall absetzen. Du kannst trotzdem alle Dreiecke in einem VBO haben und bei jedem Drawcall einfach nur einen bestimmten Bereich des VBO zeichnen (z.B. Vertices 123 bis 321). Üblicherweise verwendet man ein VBO pro Modell für alle Vertices und ein zweites als Indexbuffer. Sinnvollerweise wird man die Vertices/Indices so im Buffer ablegen dass man alle Dreiecke die das selben Material (Textur/Shader) haben auf einmal zeichnen kann um mit möglichst wenigen Textur/Shader switches auszukommen.
-
Richtig, du musst die Textur Bindings ändern, bevor du zeichnest.
Du kannst schon alles in einen VBO stecken, nur dann musst du entweder nur partiell zeichnen oder einen Texturatlas aka Spritesheet für jedes Model verwenden. Was ich eher empfehlen würde. Das spart einiges an nötiger Performance. Die Textur-Coords musst halt entsprechend anpassen.Selbes Spiel mit eventuellen Shadern.
Meine Empfehlung:
Sortier nach Texturatlas, steck alle Models die diesen Atlas nutzen in ein VBO und Render das ganze in einem Rutsch. Wäre der einfachste Weg den ich auch gewählt habe. Allerdings zeichne _ich_ nur 2D.
-
Ich habe also zwei Möglichkeiten:
1:
Ich packe alle Vertices meiner "Welt" in einen VBO. Dann erstelle ich für jedes Octree-Leaf mehrere Indexbuffer welche die Indices der zum Leaf gehörenden Triangles mit der selben Textur speichern.
Wenn ich das Octree-Leaf dann render binde ich die zum 1. Indexbuffer gehörende Textur, render den 1. Indexbuffer, binde die 2. Textur, render den 2. Indexbuffer usw.Vorteil: Nur ein rekursiver Aufruf meines Octrees.
Nachteil: Mehr Textur-Bindings.2:
Selbe struktur wie oben, nur dass ich den gesamten Octree in mehreren Phasen render, eine Phase für jede Textur. In den Phasen render ich immer alle Leafs, allerdings innerhalb der Leafs nur den zur entsprechenden Textur gehörenden Indexbuffer.Vorteil: Anzahl Textur-Bindings entspricht nur der Gesamtanzahl der Texturen meiner "Welt", ist also minimal.
Nachteil: Mehrere rekursive Aufrufe der Octree-Struktur. Außerdem muss ich mein Frustum-Culling umstellen. Anstatt in der Renderfunktion selber die Sichtbarkeit eines Knotens zu ermitteln müsste ich das vorher einmalig erledigen und die Sichtbarkeit in den Knoten speichern.Jetzt noch drei Fragen:
1: Habe ich das jetzt richtig zusammengefasst?
2: Haben Indexbuffer auch eine empfohlene Größe? Vertexbuffer sollen ja laut NVidia 1-4MB groß sein.
3: Welche Methode meint ihr ist schneller? Wiegt die häufigere Rekursion mehr oder die häufigeren Textur-Bindings?
-
Naja da hier niemand mehr antwortet stelle ich das Problem erst mal zurück und kümmere mich erstmal um andere Features. Zu frühe Optimierung ist bekanntlich die Wurzel allen Übels, und solang die Displaylisten auf meiner Mobility HD 5730 immer noch mit 200 FPS laufen bin ich ganz zufrieden.
Btw noch was aktuelles: Mir ist aufgefallen das die Textur-Compression zur Runtime (via internalformat = GL_COMPRESSED_RGBA_ARB) auf meiner ATi Karte einen geschätzten Faktor 100 langsamer ist als auf meiner Nvidia GTX260. Was aber nur wieder belegt dass die ATI-OpenGL-Treiber nicht so das gelbe vom Ei sind.
-
Hier gehts weniger um Optimierung imho.
Das ist schon eher ne Versionsfrage.
Display-Listen gehören, schlag mich, aber ich denke zu OpenGL 1.2 oder 1.1 sogar.
Vertex Buffer Objects sind seit OpenGL3 glaube ich im Core und waren davor nur als extension verfügbar.
Ich weiss es grade nicht ohne nachzuschlagen.Imho solltest du DisplayListen schnell loswerden, denn die sind wirklich sowas von deprecated :D. Aber gut, nicht jeder will sich auf OpenGL3 (=DX10 Karten) binden und/oder Shader verwenden.
Man kann halt mit VBO mehr Vertices auf einmal rendern. Grade bei 3D wo es um einige millionen vertices gehen kann ein unschlagbarer Vorteil.
Optimierung in OpenGL ist halt eine Sache für sich. Wie ich bereits schrieb, du kannst viel Performance durch Textur Atlase und den damit verbundenen fehlenden Texturwechsel sparen. Weniger Texturwechsel, mehr Vertices auf einmal gerendert.Leider kann ich deine Fragen oben nicht direkt beantworten, da ich mit Index Buffers noch nicht gearbeitet habe.
Imho solltest du aber so früh wie möglich entscheiden, wie du mit OpenGL arbeiten willst. Eine nachträgliche Portierung auf OpenGL3 kann der Schmerz im Arsch sein wie der Engländer sagt :D. Und das sage ich dir aus Erfahrung. Die Umstellung fixed -> programmable Pipeline ist ne sache für sich.
Auf der anderen Seite ist die Anwendung damit zukunftsicherer, falls NVIDIA oder ATI doch die fixed functions iwann aus den Treibern werfen.
(was eigentlich nicht sooo schnell passieren sollte)
-
Mir ist aufgefallen das die Textur-Compression zur Runtime (via internalformat = GL_COMPRESSED_RGBA_ARB) auf meiner ATi Karte einen geschätzten Faktor 100 langsamer ist als auf meiner Nvidia GTX260.
Was aber nur wieder belegt dass die ATI-OpenGL-Treiber nicht so das gelbe vom Ei sind.Vergleiche doch auch mal die qualitativen Ergebnisse.
Es ist halt ein Unterschied, ob der Treiber versucht bloss irgendeine Loesung zu finden oder den nach psychovisuellen Gesichtspunkten vertretbarsten Mittelweg.
-
Ich habe keinen Unterschied bei der Qualität gemerkt. Es liegt wohl daran dass der ATI-Treiber die Textur auf der CPU komprimiert, während nvidia dies auf der GPU macht.
Ist aber nur eine Vermutung welche allerdings hierdurch bekräftig wird:
http://developer.nvidia.com/object/texture_tools.html