hab da mal paar fragen zu Spezialeffekte in 3D Engines.
Wie kriegen Engines wie Doom3/Frustum.org/RealityEngine es hin das ne scene mit viel PerPixel Licht/Schatten und Specialeffekten (HDR, Partikel, etc) angenehm flüssig läuft, sprich Drawcalls < 500 per frame (ist so laut nvidia in modernen 3d engines nen guter wert)
Ich zerbrech mir momentan, den kopf wie ich Welt und Licht gescheit optimieren kann.
Hab mir dazu einige gedanken gemacht und auch einiges umgesetzt, meine Welt ist ein Dynamischer Octree, welche ich via Frustum Nodes und Objekte rausoptimiere und etliches an fps sparen kann.
Lichter sind gewöhnliche Spheres, mit Position, Radius und werden auch so via Frustum rausoptimiert.
Noch dazu, lass ich Objekte welche nicht von licht "berührt" werden via "AABB - Sphere" intersection ebenso rausoptimieren was auch einiges an fps spart.
Im GLSL shader selbst hab ich auch noch ne optimierung drin, wo Vertexe die das Licht "Backfacen" die Gewichtung auf 0 gesetzt wird.
Habe sogar an Material optimierung gedacht, und lass Materialen sortieren, und mach nur State changes wenns wirklich notwendig ist.
Trotzdem reichts nicht aus, desto mehr lichter ich in ner scene habe, desto übler ruckelts.
Faktoren sind anzahl und grösse der Lichter.
Kann es vielleicht daran liegen, das ich zuviele kleine VBO´s habe, weil ich hab Pro Objekt, pro Material (Objekte sind nach Materialien sortiert) eine VBO. Die Grunddaten wie Vertices/Normalen/Tangenten/BiNormalen etc. sind dann ja natürlich auch pro Objekt und Material.
Ich verzweifel noch, da ich ja noch vor habe HDR einzubauen, Volumen Licht, Spot Licht, Glass, Wasser und andere nette effekte.
Stencil Shadow Volumes kommen auch noch dazu, sofern mal alles mit Licht flüssig läuft.
Ich hab hier mal nen Screenshot und ne Binary der aktuellen Engine, der das mal mit eigenen augen sehen will :p
Wichtig: Falls es nicht läuft, einfach die log.txt wo generiert wird mal durchgucken, sofern es keine Speicherverletzung sollten gute fehler drin stehen ^^
Mindestvorausetzung ist ne Graka mit GLSL und VBO hardware support !
Was gibts denn alles noch für möglichkeiten das Licht rauszuoptimieren ?
Mir bekannte sind noch:
- Scissor rects
- Occlusion culling
aber wie das genau funzt keine ahnung.
Wenn ihr mich aufklären könnt in der sache, oder vorschläge habt.
Postet was die tasten hergeben ^^
Falls wen es interessiert wie die engine aufgebaut ist, kann gern mal bissel was dazu posten, ist bestimmt interessant für den ein oder anderen
Sag dann mal nen danke im voraus und freu mich auf antworten,
Wäre auf jedemfall echt genial, wenn ich das problem in den nächsten tagen in griff kriegen würde.
Zuerst solltest du prüfen, ob dein Programm Vertex- oder Füllratenlimitiert ist. Mit Optimierungsverfahren wie Culling sparst du Vertex-Berechnungen, aber wenn die Fragmentshader der Flaschenhals sind, gibt es kaum eine nennenswerte Performancesteigerung.
Vertexlimitation zeichnet sich dadurch aus, das die Framerate mit sinkender Vertexzahl stark ansteigt. Da du aber bereits sehr viel cullst, ohne das ein Performancegewinn eintritt, kann man davon ausgehen dass das nicht der Fall ist. Trotzdem könntest du den Vertexdurchsatz noch erhöhen, wenn du Indexbuffer statt glDrawElements einsetzt. Allerdings sollte das bei kleinen VBOs keinen großen Unterschied machen.
Viel eher ist deine Anwendung aber durch den Fragmentshader limitiert. Das kannst du ganz einfach testen, indem du die Auflösung verringerst. Wenn sich die FPS nahezu proportional erhöhen, musst du entweder die Anzahl der gezeichneten Fragmente reduzieren oder die Fragmentoperationen vereinfachen.
Eine gute Methode ist da der so genannte Z-Prepass. Dabei zeichnest du die Szene zwei mal, einmal mit abgeschalteten Color-Write und einmal mit abgeschaltetem Depth-Write. Dadurch erzeugst du beim ersten Durchlauf eine Z-Maske, die verhindert dass aufwendig Fragmente berechnet werden, die im selben Frame ohnehin noch überschrieben werden. Durch den zweimaligen Durchlauf verdoppelst du praktisch die Last der Vertexeinheit, was aber bei dir ein wünschenswerter Ausgleich ist.
Die bedingten Optimierungen im Shader würde ich aber sein lassen. Zum einen unterstützen ältere Grafikkarten (alles vor NVIDIAs 6000er Generation) ein Early-Out, d.h. es in jedem Falle alle Berechnungen durchgeführt und das Ergebnis der if-abfragen im Nachhinein ermittelt. Zum anderen sind alle GPUs auf Vektorberechnung optimiert, die Auswertung einer Bedingung kann unter Umständen länger brauchen als z.B. eine Vektoroperation..
Zuerst solltest du prüfen, ob dein Programm Vertex- oder Füllratenlimitiert ist. Mit Optimierungsverfahren wie Culling sparst du Vertex-Berechnungen, aber wenn die Fragmentshader der Flaschenhals sind, gibt es kaum eine nennenswerte Performancesteigerung.
Naja es gibt ja noch mehr Culling methoden, grad speciell fürs licht... Licht optimierung hab ich nur die AABB-Sphere intersection. PVS fürs octree fehlt auch noch, allerdings sollte das in echtzeit sein und nicht vorberechnet, da Objekte bei mir in der Welt dynamisch sind und sich der Octree dynamisch anpasst.
yonibear hat geschrieben:
Vertexlimitation zeichnet sich dadurch aus, das die Framerate mit sinkender Vertexzahl stark ansteigt. Da du aber bereits sehr viel cullst, ohne das ein Performancegewinn eintritt, kann man davon ausgehen dass das nicht der Fall ist. Trotzdem könntest du den Vertexdurchsatz noch erhöhen, wenn du Indexbuffer statt glDrawElements einsetzt. Allerdings sollte das bei kleinen VBOs keinen großen Unterschied machen.
Was sind denn Indexbuffers ? mit glDrawElements, greif ich ja schon auf mein indice-array zu. Versteh das nich ganz, bitte mal näher erklären.
yonibear hat geschrieben:
Viel eher ist deine Anwendung aber durch den Fragmentshader limitiert. Das kannst du ganz einfach testen, indem du die Auflösung verringerst. Wenn sich die FPS nahezu proportional erhöhen, musst du entweder die Anzahl der gezeichneten Fragmente reduzieren oder die Fragmentoperationen vereinfachen.
Jop, das ist korrekt aber selbst minimalste Shader operationen, wie einfach nur Licht Attenuation ohne Specular, ohne bump, ohne alles bringt gerade mal 1 fps ersparrniss wenn alle Lichter im frustum sind.
Stimmt zwar das Grafikkarten Fillraten begrenzt sind, auch vom Treiber abhängig, aber das er nur an den shader operationen liegen kann kann ich mir nicht vorstellen.
Wenn man sich den Screenshot anschaut, oder die demo selbst sieht man ja, das er einfach zu viele "Drawcalls" macht. Man sollte in einer modernen 3d engine nicht mehr als 500 draw calls per scene machen. Siehe: "Modern-Graphics-Engine-Design.pdf" von NVIDIA.
yonibear hat geschrieben:
Eine gute Methode ist da der so genannte Z-Prepass. Dabei zeichnest du die Szene zwei mal, einmal mit abgeschalteten Color-Write und einmal mit abgeschaltetem Depth-Write. Dadurch erzeugst du beim ersten Durchlauf eine Z-Maske, die verhindert dass aufwendig Fragmente berechnet werden, die im selben Frame ohnehin noch überschrieben werden. Durch den zweimaligen Durchlauf verdoppelst du praktisch die Last der Vertexeinheit, was aber bei dir ein wünschenswerter Ausgleich ist.
Das klingt logisch und werde auch gleich mal testen.
yonibear hat geschrieben:
Die bedingten Optimierungen im Shader würde ich aber sein lassen. Zum einen unterstützen ältere Grafikkarten (alles vor NVIDIAs 6000er Generation) ein Early-Out, d.h. es in jedem Falle alle Berechnungen durchgeführt und das Ergebnis der if-abfragen im Nachhinein ermittelt. Zum anderen sind alle GPUs auf Vektorberechnung optimiert, die Auswertung einer Bedingung kann unter Umständen länger brauchen als z.B. eine Vektoroperation..
ich hab jeweils 1 if abfrage im vertex und fragment shader drin, zum prüfen ob Parallax aktiv ist.
Kannst ja mal angucken, im GLSL ordner sind alle shader drin.
Benutze für fast alle materials den parallax shader.
In so einer Umgebung sind einige per Hand gesetzte Portale sehr gut. Man braucht dann auch nicht die Polygone an den Portalen schneiden, sondern es reicht aus wenn man dann ganz normales Frustum Culling nur eben durch das Portal macht. Portale sind besser als PVS weil sie genauer sind und wenn man nur grob testet auch nicht viel CPU Zeit kosten. PVS bezieht sich ja immer auf eine gewisse Umgebung. Die kann man auch noch an die Türen binden, so dass sie beim Schließen von Türen deaktiviert werden. Innerhalb eines Sektors kann man dann ja einen beliebigen Baum verwenden.
Einen ZPass am Anfang, wie schon gesagt, auf jeden Fall verwenden.
Um bei Lichtern gut zu cullen, kann man auch von der Lichtquelle aus in alle Richtungen rendern und dann nochmal alle Polygone mit Occlusion Culling auf Sichtbarkeit testen. So braucht man nichts rendern, was im Schatten liegt. Das geht sehr schnell und lohnt sich sobald ein Licht einige Frames an einem Ort bleibt.
IF Abfragen im Fragment Shader sind sehr langsam. Da sollte man besser #ifdefs verwenden.
Das hat alles bei mir auf einer GF4 sehr gut funktioniert, aber die Portale haben am meisten gebracht.
Allerdings sollte man überlegen, ob man es mittlerweile nicht besser machen kann. Mehrere Lichter pro Pass sind mit modernen Shadern auf jeden Fall drin. Falls sie alle Schatten brauchen kann man eine Schattenmaske für 4 Lichter in einer RGBA Texture speichern. Dann muß man zwar auch 4 Mal den Schatten rendern, aber nur einmal pro 4 Lichter. Weichzeichnen kann man den Schatten dann zusätzlich noch billig.
ich hab jeweils 1 if abfrage im vertex und fragment shader drin, zum prüfen ob Parallax aktiv ist.
Das ist genau das was du auf keinen Fall machen solltest. Je nach Grafikkarte kann es sein, dass alle Berechnungen durchgeführt werden und bei abgeschaltetem Parallax hinterher wieder verworfen werden. Besser ist es, für jede Anwendung einen eigenen Shader zu schreiben. Falls das zu aufwändig ist, kann man auch Shader aus einzelnen Code-Teilen dynamisch zusammensetzen.
Finalspace hat geschrieben:
Was sind denn Indexbuffers ? mit glDrawElements, greif ich ja schon auf mein indice-array zu. Versteh das nich ganz, bitte mal näher erklären.
Du hast bei Indices genauso mit mit Vertices die möglichkeit, sie über ein VBO auf der Grafikkarte abzulegen.Mit glDrawElements lädst du die Indices jedes mal aufs neue hoch. Dabei generierst du genauso wie bei Vertex Buffers ein VBO, bindest es aber auf GL_ELEMENT_ARRAY_BUFFER. Mit glBufferData lädst du dann die Daten hoch (immer noch mit GL_ELEMENT_ARRAY_BUFFER als Ziel). Um aus dem Index Buffer zu zeichnen, bindest du wieder den Indexbuffer und rufst dann glDrawElements auf, allerdings mit dem Datenpointer auf 0.
Wenn du der Ansicht bist, dass du zu viele Drawcalls hast, kann das schon etwas bringen.
Ok ich denke nun, ich weiss wo der hund begraben liegt.
Es liegt definitiv an den Drawcalls, meine struktur ist bissel für die katz.
Bei 35 Sichtbaren Objekten, mit 43 lichter im frustum wären das 1505 drawcalls + 35*3 für den pre-Z und ambient pass.
Noch dazu hab ich eindeutig zuviele VBOs.
Werde mich mal in die Index buffer methode reinhängen, und pro material und licht neuen index buffer erstellen und dann nur noch ein Drawcall pro licht
Betreffend IFDEF in shader, hatte da was gelesen, das es nur NVIDIA karten können.
Werd mir betreffend da was einfallen lassen und sone art Detail lvl in meine Shaderscripts einbauen.
Registriert: Di Mai 18, 2004 16:45 Beiträge: 2623 Wohnort: Berlin
Programmiersprache: Go, C/C++
Zum thema optimierung der Szene will ich auch noch was sagen.
Ich hab mit Oc2k1 folgende Idee gehabt aber ich hab sie noch nicht umgesetzt.
Teil deine Umgebung mit Octree ein, alle faces in einem node werden gelistet und nach textur sortiert, dann werden diese wiederrum nach stripes sortiert. Nun packst du alle daten in ein einziges vbo und erstellst für jedes node eine displaylist in der du dann mit draw array und passenden texture,shader bindings dein node erhältst. Nun kannst du noch einen Visible Tree erstellen den du über occlusion culling vorher schnell ermitteln kannst. Nun kannst du zur laufzeit das node raus suchen wo die kamera ist und die display listen die im Visible Tree stehen aufrufen.
Dynamische Objekte kommen ja nicht mit rein drum müssen die erst per Frustumculling entfernt werden und dann über ein Occlusion culling perfekt raus gecullt werden. Gerade bei viel Pixelshader einsatz kann jedes Objekt zählen und ein Occlusion Culling ist auf niederer auflösung sehr flink.
Der Vorteil der VBO-Displaylist variante ist, dass du alle Daten im VRam hast und ledeglich nur ne handvoll calls machen musst, die über den datenbus müssen. Das mehrfache sortieren der Daten ist auch sehr hilfreich für die Performance. Es werden weniger Texturwechsel gebraucht und Stripes brauchen weniger Speicher und sind schneller. Einziger Nachteil ist, du brauchst ne menge displaylisten aber ich denke, es sind immer noch weniger als du momentan an vbos nutzt.
_________________ "Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren" Benjamin Franklin
Deswegen kann die App aber trotzdem in einem funktionierenden Zustand ausgeliefert werden. Auf 2 Testrechner hab ich jeweils eine E/A Exception erhalten.
Registriert: Sa Okt 22, 2005 20:24 Beiträge: 291 Wohnort: Frauenfeld/CH
jap, ich wollte nur darauf hinweisen, da teilweise dann plötzlich andere leute wieder irgendwelche informationen bringen, bzw sich bemühen zu helfen, wobei das eigentliche problem schon behoben ist. War keinesfalls ne aussage wie "ihr dürft da nix mehr kommentieren"
Mitglieder in diesem Forum: 0 Mitglieder und 11 Gäste
Du darfst keine neuen Themen in diesem Forum erstellen. Du darfst keine Antworten zu Themen in diesem Forum erstellen. Du darfst deine Beiträge in diesem Forum nicht ändern. Du darfst deine Beiträge in diesem Forum nicht löschen. Du darfst keine Dateianhänge in diesem Forum erstellen.