Updateperformance bei komplexen 3D Animationen



  • Hallo zusammen,

    nachdem ich mich etwas intensiver mit OpenGL beschäftigt habe, komme ich recht schnell an die Grenzen der Hardware und möchte mal die allgemeine Vorgehensweise für eine 3D Animation hinterfragen.

    Nehmen wir mal ein einfaches Beispiel, bei dem ein Buggy durch den Sand fährt.
    Der Strand besteht dabei aus einem Vertexmesh.
    Hintergrundobjekte wie Häuser, Bäume etc. sind erstmal Nebensache.
    Für die ersten Tests lasse ich einen Zylinder über das Mesh rollen und die Spuren werden je nach Gewicht (später auch nach Dynamik) entsprechend im Mesh dynamisch geändert (Z-Position und Normalenberechnung).
    (Reelle Games sehen sicherlich anders aus, geht hier nur um Machbarkeit).

    Die "Lehrmethode" ist es, die Scene komplett im Backbuffer zu zeichnen und dann zu swappen.
    Bei grober Auflösung des Meshes wird die Scene recht schnell aktualisiert.
    Bei zunehmender Komplexität und Geschwindigkeit des Objekts ist das komplette Zeichnen aber schlichtweg zu (zeit)aufwendig.
    Vertexarray, VBOs etc. helfen zwar, aber nur bis zu einer gewissen Grenze.

    Das Update der Scene muss also entsprechend minimiert werden, wozu ich mehrere Ansätze (verworfen) habe:

    glScissor und Stencil Buffer würden nicht viel bringen, da die Scene ja erst nach dem Zeichnen gefiltert wird.
    > mehr statt weniger Berechnungen

    Strandbereich ohne Auto und ohne glClear im Backbuffer, Auto in den Frontbuffer
    -> flic flack 🤡

    Objekt und Mesh in der alten und neuen Position löschen und neu zeichnen.
    -> wie bestimmt man den notwendigen Bereich der Bewegung abhängig vom Blickwinkel?

    Danke im Voraus für eine Erleuchtung 😉



  • LOD und ähnliche Algorithmen sind schonmal etwas recht gutes um die komplexität und damit den Aufwand der Szenen zu verringern.

    Dann kann man natürlich noch verinfachte techniken benutzen. Anstatt die Reifenspuren direkt in das mesh zu "drücken" wäre es auch möglich einfach eine Textur auf den Strand zu projezieren.
    Das wäre wieder ein ziemlicher Geschindigkeitsvorteil denke ich. Alleine schon, weil ddurch die geometrische Komplexität der Szene nicht mit der bewegung ansteigt!



  • makkurona schrieb:

    LOD und ähnliche Algorithmen sind schonmal etwas recht gutes um die komplexität und damit den Aufwand der Szenen zu verringern.

    Dann kann man natürlich noch verinfachte techniken benutzen. Anstatt die Reifenspuren direkt in das mesh zu "drücken" wäre es auch möglich einfach eine Textur auf den Strand zu projezieren.
    Das wäre wieder ein ziemlicher Geschindigkeitsvorteil denke ich. Alleine schon, weil ddurch die geometrische Komplexität der Szene nicht mit der bewegung ansteigt!

    LOD wäre eine Möglichkeit, werde mir dazu mal Gedanken machen.
    Erinnert mich an die Idee von Quadtrees.

    Texturen sind von einem statischen Blickwinkel sicherlich besser, aber beim reinzoomen und freien drehen der Scene ist ein Mesh einfach praktischer.
    Ganz zu schweigen von der Berechnung, wieviel Sand nachher aufgefüllt werden muss 🕶


  • Mod

    modestia schrieb:

    Bei grober Auflösung des Meshes wird die Scene recht schnell aktualisiert.
    Bei zunehmender Komplexität und Geschwindigkeit des Objekts ist das komplette Zeichnen aber schlichtweg zu (zeit)aufwendig.
    Vertexarray, VBOs etc. helfen zwar, aber nur bis zu einer gewissen Grenze.

    ab einer gewissen groesse ist die vertexleistung nicht ausreichend, deswegen nutzt kaum jemand so hochtesselierte meshes, es macht dann auch keinen sinn.

    glScissor und Stencil Buffer würden nicht viel bringen, da die Scene ja erst nach dem Zeichnen gefiltert wird.
    > mehr statt weniger Berechnungen

    glscissor und stencilbuffer sind pixelshaderoptimierungen, bringt nichts fuer die vertexleistung

    Strandbereich ohne Auto und ohne glClear im Backbuffer, Auto in den Frontbuffer
    -> flic flack 🤡

    du willst jeweils nur eine sache anzeigen? das wird unglaublich flimmern.

    Objekt und Mesh in der alten und neuen Position löschen und neu zeichnen.
    -> wie bestimmt man den notwendigen Bereich der Bewegung abhängig vom Blickwinkel?

    loeschen in welcher weise? woher weisst du wo das objekt ist? die vertexleistung verbesserst du damit aber nicht, da du das objekt zeichnest, es wird also vermutlich langsammer.



  • rapso schrieb:

    loeschen in welcher weise? woher weisst du wo das objekt ist?

    Richtig, woher soll ich wissen, welche Mesh-Bereiche unter/hinter dem Auto neu gezeichnet werden müssen?
    Dort, wo die Reifen eintauchen, weiss ich es.
    Aber das Auto verdeckt ja darüber noch eine Menge mehr.

    Das Löschen in der alten Position bedeutet soviel wie das Zeichnen des Terrains in diesem Bereich OHNE Auto.
    Dazu müsste glClear aber zu beschränken sein (ähnlich wie InvalidateRect).
    Ansonsten sieht man ja nach dem Swappen nur diesen Teilbereich, aber nicht mehr das Drumherum 🤡
    Im 2. Schritt das Mesh aktualisieren und die geänderten Vertices und das Auto in der neuen Position zeichnen.

    Hatte schon überlegt, den Buffer vor dem Zeichnen des Autos mit glReadPixels auszulesen und vor dem nächsten Schritt wieder auszuspucken.
    Klappt vielleicht bei 320x240 Pixeln 😃


  • Mod

    modestia schrieb:

    rapso schrieb:

    loeschen in welcher weise? woher weisst du wo das objekt ist?

    Richtig, woher soll ich wissen, welche Mesh-Bereiche unter/hinter dem Auto neu gezeichnet werden müssen?
    Dort, wo die Reifen eintauchen, weiss ich es.

    rauszufinden welche polys bzw vertices das sind dauert vermutlich laenger als sie einfach zeichnen zu lassen.

    Das Löschen in der alten Position bedeutet soviel wie das Zeichnen des Terrains in diesem Bereich OHNE Auto.
    Dazu müsste glClear aber zu beschränken sein (ähnlich wie InvalidateRect).

    tja, leider laeuft das so nicht, fullscreen clear dauert nichts, normalerweise. jedes selektive clear dauernt hingegen viel zeit, da du es irgendwie emulieren musst.

    Ansonsten sieht man ja nach dem Swappen nur diesen Teilbereich, aber nicht mehr das Drumherum 🤡
    Im 2. Schritt das Mesh aktualisieren und die geänderten Vertices und das Auto in der neuen Position zeichnen.

    und nochmals das rauspicken von vertices aus einem mesh -> sehr langsam.

    Hatte schon überlegt, den Buffer vor dem Zeichnen des Autos mit glReadPixels auszulesen und vor dem nächsten Schritt wieder auszuspucken

    glreadpixels wird vermutlich eine langsame cpu emulation anschmeissen. waehrend der zeit kannst du zig mal ein hochaufloesendes mesch zeichnen.

    ich glaube du weisst nicht genug ueber die hardware und ueber performance charasteriken bescheid um das zu optimieren, du bewirkst am ende das gegenteil.

    mach ein paar benchmarks, und versuch irgendwie geometrie selektiv zur gpu zu schicken und zu zeichnen, du wirst sehen, dass du selbst bei nur 5% der geometrie langsammer sein wirst, als die 100% mittels eines VBOs zu zeichnen.

    weiter kannst du mal nach impostern suchen, die koennten im weitesten sinne deiner idee nah kommen.

    greets
    rapso



  • Hi Rapso,

    bei den Hardware-Interna muss ich in der Tat passen.
    VBOs hatte ich bislang noch nicht realisiert, da ich den Nutzwert nicht abschätzen konnte.
    Und Imposter kannte ich bislang gar nicht, werde mich da mal einlesen.

    Danke Dir erstmal!



  • also ich würde auf jeden fall einen LOD-Algorithmus verwenden, z.B. "Chunked-LOD", das ist ein recht guter. Die Spuren kannst du dann mit sog. Parallax-Mapping verwirklichen, da wird nicht die Geometrie selbst verändert, sondern eine Textur aufgelegt, die sich je nach Blickwinkel verändert, sodass sie immer einen dreidimensionalen eindruck gibt. Beschrieben ist Parallax-Mapping z.B. hier



  • Heinzelotto schrieb:

    da wird nicht die Geometrie selbst verändert, sondern eine Textur aufgelegt, die sich je nach Blickwinkel verändert, sodass sie immer einen dreidimensionalen eindruck gibt. Beschrieben ist Parallax-Mapping z.B. hier

    Die Textur verändert sich nicht, sondern die Texturkoordinaten in Abhängigkeit vom View-Vektor und Höhenwert in der Textur;)
    Anonsten stimme ich zu, dass so etwas niedrig frequentes wie Reifenspuren ein perfekter Kandidat für Parallax-Mapping sind. Wobei jedoch die Erstellung so einer Parallax-Textur nicht ganz trivial ist.



  • Die Reifenspuren waren nur ein Beispiel, bei dem man mit Texturen sicherlich den besseren Weg gehen würde.
    Besser vorstellbar wäre ein Bagger, der etwas aushebt.
    Bei Ballerspielen wären es vielleicht Bombenkrater....what ever.
    Es geht also eher um die physikalische Änderung der Umgebung als um die simple Darstellung von Bildern.

    Bei dem genannten Chunked-LOD wird der fortlaufende Rechenaufwand vermutlich höher sein, als die Szene plump zu zeichnen.
    Denke, dies macht eher bei statischen Terrains Sinn, wo die Detailgenauigkeit nach Entfernung ab/zunimmt, das Mesh an sich aber wenig Änderung erfährt.

    Was passiert eigentlich bei VBOs, wenn der Grafikspeicher ausgenutzt ist?
    Wird das Kopieren dann einfach "abgeschnitten" oder läuft man dabei auf Fehler?
    Eine Quadro mit 512MB bietet genug Spielraum, aber alte 64MB Karten...
    Klar kann man den Speicher vorher prüfen, ist daher nur eine Frage aus Interesse 🙂



  • Soweit ich weiß wird dann auf langsamere Register ausgeladert, dann dauerts halt länger. (:

    Was man auch machen könnte:
    Eine Textur auf die Stellen projezieren und sie mit einer Bumpmap blenden, die vorher mit einer Lightmap (Nach Blickwinkel generiert) multipliziert wird.

    Dadurch kriegt man Blickwinkelspetifische "3Dimensionale" und sogar Beleuchtete Effekte ohne sonderlich viel CPU und Grafikkarten aufwand.
    Das dürfte doch eigentlich recht flott ghen, doer liege ich falsch?



  • makkurona schrieb:

    Eine Textur auf die Stellen projezieren und sie mit einer Bumpmap blenden, die vorher mit einer Lightmap (Nach Blickwinkel generiert) multipliziert wird.

    ???
    1. Hat er doch gesagt, dass er keinen Textur-Ansatz will
    2. Was soll das bringen? Die Bumpmap ist ja gerade für eine "bessere" Beleuchtung da, wozu also nochmal eine Lightmap?



  • ich würde auf jedem fall mal VBOs versuchen. z.b. könntest du "kacheln" machen, und jede kachel des terrains in einen eigenen VBO packen. diese kacheln kannst du dann einzeln updaten. dadurch müssen nicht so viele vertexdaten zwischen video- und system-ram hin und her geschaufelt werden.
    diese kacheln kannst du dann auch gleich für's LOD verwenden - weit entfernte kacheln werden eben nicht mit voller auflösung gezeichnet.
    dazu müsstest du dann z.b. für jede kachel "mip-maps" rechnen, also kleinere versionen mit weniger vertices (gröberes mash).



  • this->that schrieb:

    makkurona schrieb:

    Eine Textur auf die Stellen projezieren und sie mit einer Bumpmap blenden, die vorher mit einer Lightmap (Nach Blickwinkel generiert) multipliziert wird.

    ???
    1. Hat er doch gesagt, dass er keinen Textur-Ansatz will
    2. Was soll das bringen? Die Bumpmap ist ja gerade für eine "bessere" Beleuchtung da, wozu also nochmal eine Lightmap?

    Er sagte, er will sie nicht, weil sie bei verschiedenen Ansichten zu Problemen führen können.

    Was die Bumpmaps angeht scheine ich Mist gelabert zu haben. Ich habe mich noch ein zweites mal auf ein paar Seiten kundig gemacht und ja, ich hatte ein falsches Prinzip im Kopf.
    Wobei das an sich doch auch mal eine nette Kombination wäre.. unötig, aber nett.^^



  • Was soll das bringen? Die Bumpmap ist ja gerade für eine "bessere" Beleuchtung da, wozu also nochmal eine Lightmap

    Bump-Mapping und Light-Mapping sind komplementäre Techniken, können also ohne Weiteres sinnvoll kombiniert werden.

    Mit Bump-Mapping kannst du "dellen" auf ein Objekt draufzaubern, also eine gewisse Richtungs- oder Winkelabhängigkeit der Beleuchtung erreichen. Das geht mit Light-Mapping nicht.

    Mit Light-Mapping dagegen kannst du gewisse Stellen eines Objektes heller oder dunkler erscheinen lassen ("mehr oder weniger beleuchten"), also eine Positionsabhängigkeit der Beleuchtung erreichen. Das wiederum geht mit Bump-Mapping nicht.

    Anders gesagt: beim Bump-Mapping geht es um "Richtungen", beim Light-Mapping dagegen um Positionen.

    BTW: heutzutage nimmt man eher Normal-Mapping statt Bump-Mapping, da man mit Normal-Mapping einfach schönere Bilder bekommt ("korrekter" ist es auch, aber das wäre ja relativ egal), und die Grafikkarten mittlerweile halbwegs schnell genug sind.


  • Mod

    hustbaer schrieb:

    Was soll das bringen? Die Bumpmap ist ja gerade für eine "bessere" Beleuchtung da, wozu also nochmal eine Lightmap

    Bump-Mapping und Light-Mapping sind komplementäre Techniken, können also ohne Weiteres sinnvoll kombiniert werden.

    Nein, normale lightmaps koennen schlecht mit bumpmapping kombiniert werden. aus diesem grund nutzt z.b. HL2 radiosity normalmapping bei dem mehrere lightmaps richtungsabhaengig vorliegen.

    Mit Bump-Mapping kannst du "dellen" auf ein Objekt draufzaubern, also eine gewisse Richtungs- oder Winkelabhängigkeit der Beleuchtung erreichen. Das geht mit Light-Mapping nicht.

    natuerlich geht das mit lightmaps, mittels bumpmap bekommst du helligkeiten fuer eine normale an einem bestimmten punkt in abhaengikeit von der position+helligkeit der lichtquelle, bei einer lightmap ist diese helligkeit schon abgelegt.

    Mit Light-Mapping dagegen kannst du gewisse Stellen eines Objektes heller oder dunkler erscheinen lassen ("mehr oder weniger beleuchten"), also eine Positionsabhängigkeit der Beleuchtung erreichen. Das wiederum geht mit Bump-Mapping nicht.

    eine lightmap enthaellt natuerlich auch die helligkeit abhaengig von einem winkel wenn man das moechte und ist von dem lightstrahl zwischen lichtquellen-position und zu pixel im worldspace ausgerechnet.

    Anders gesagt: beim Bump-Mapping geht es um "Richtungen", beim Light-Mapping dagegen um Positionen.

    Bei bumpmap geht es darum lokale dynamische beleuchtung zu erzeugen. bei lightmaps hat man statische beleuchtung in die man globale ding (wie z.b. radiositaet) einfliessen lassen kann.

    BTW: heutzutage nimmt man eher Normal-Mapping statt Bump-Mapping, da man mit Normal-Mapping einfach schönere Bilder bekommt ("korrekter" ist es auch, aber das wäre ja relativ egal), und die Grafikkarten mittlerweile halbwegs schnell genug sind.

    normalmaps sind eine art des inputs fuer bumpmapping.



  • So ziemlich genau das was rapso geschrieben hat, wollte ich auch gerade schreiben. Aber seinem Posting ist eigentlich nichts mehr hinzuzufügen. ^^



  • Hm. Ok, wenn rapso das sagt glaube ich es erstmal. Sorry dass ich hier Blödsinn geschrieben habe! Sieht so aus als müsste ich mich mit dem Thema nochmal näher beschäftigen.


  • Mod

    hustbaer schrieb:

    Hm. Ok, wenn rapso das sagt glaube ich es erstmal. Sorry dass ich hier Blödsinn geschrieben habe! Sieht so aus als müsste ich mich mit dem Thema nochmal näher beschäftigen.

    da empfehle ich sehr
    http://www2.ati.com/developer/gdc/D3DTutorial10_Half-Life2_Shading.pdf



  • Habe nun einige Varianten probiert und möchte Euch die Ergebnisse nicht vorenthalten.
    Allerdings bin ich mir nicht sicher, ob ich den erzielten Framerates ohne weiteres glauben darf 🙄

    Meine Meshdaten liegen erstmal als Punktewolke vor, um die einzelnen Vertices dynamisch ändern zu können.
    Getestet wurden 60501 Vertices auf einer Radeon 9250.

    Die Vertice-Struktur sah so aus:

    struct {
    float nv[3];
    float p[3];
    char color;
    } vertice;
    

    Das Member "color" ist ein Index auf ein Farbfeld, was zum Testen aber nicht genutzt wurde.
    Die Buffer wurden mit glInterleavedArrays definiert, da man sich hiermit einige Aufrufe erspart:

    glInterleavedArrays(GL_N3F_V3F, sizeof(vertice), 0);
    

    glDrawElements ist bei Verwendung von VBOs ab count=42000 gnadenlos in die Knie gegangen, ein Update dauerte dann schlagartig bis zu 5 sec!
    40000 dagegen liefen problemlos.
    Die Abfrage von GL_MAX_ELEMENTS_INDICES ergab 65535, GL_MAX_ELEMENTS_VERTICES liegt bei 2147483647.
    Ohne Verwendung der VBOs gab es überhaupt keine Probleme.
    Nach langem Suchen habe ich eben dieses zusätzliche char-Element als Verursacher herausgefunden...😮
    Konnte jedoch nichts konkretes im Web finden, warum die Graka dies nicht mochte.

    Habe dann das char-Member und sämtliche Dynamik rausgenommen und nur das reine Darstellen der Daten gemessen.
    Zum Vergleich habe ich mehrere Varianten erstellt.
    Bei der "billigsten" sucht man sich per Pointer jeweils 4 nebeneinanderliegende Vertices und erzeugt einen Quad.
    Für glDrawElements wird zusätzlich ein Index-Array erstellt und für glDrawArrays habe ich ein Array erstellt, das die Quads in fortlaufender Reihenfolge enthält.
    Der Nachteil von glDrawArrays ist einerseits, dass jedes (innere) Vertice 4x im Array gelistet wird, andererseits geht damit auch die Flexibilität beim Ändern der einzelnen Vertices verloren.

    Hier nun die Ergebnisse:

    glBegin(GL_QUADS)/glEnd: 31 fps
    glDrawElements mit Indices-Array: 32 fps
    glDrawArrays mit Quad-Array: 41 fps
    glDrawArrays mit Quad-Array und VBOs: 41 fps
    glDrawElements mit Indices-Array und VBOs: 46 fps
    glBegin(GL_QUAD_STRIP)/glEnd: 51 fps
    glBegin(GL_QUAD_STRIP)/glEnd, ohne glClear: 59 fps
    glDrawElements mit VBOs, ohne glClear: 73 fps

    glClear verschlingt somit einiges, da außerhalb der GPU aufgerufen.
    Das Bit GL_DEPTH_BUFFER_BIT nimmt 16 Frames, GL_COLOR_BUFFER_BIT weitere 11.
    Es machte bei der Menge zudem keinen Unterschied, ob ich die Indices mit einem Standard-Pointer oder mit einem STL-Vector übergab.

    Sind die Ergebnisse realistisch oder bin ich auf dem Holzweg?
    Für die theoretische Videofrequenz von 25 fps würde ja schon die langsamste Methode reichen...


Anmelden zum Antworten