Ansatz zum Terrain-Rendering - noch Probleme beim Textur-Blending



  • Ich plane, die Terrain-Vertize in einem einzigen fetten Vertex-Buffer unterzubringen. Natürlich soll nur der sichtbare Bereich gerendert werden. Dazu sollen IndexBuffer verschiedene, sich ganz leicht überlappende Regionen beschreiben (um zu vermeiden, dass man die Nahtstellen sieht). Ziel soll sein, das ganze sichtbare Gelände mit wenigen DrawIndexedPrimitives-Aufrufen zu zeichnen.

    Ich wollte die Texturierung in etwa so ablaufen lassen, dass der Boden für verschiedene Gelände-Typen jeweils einmal gerendert wird. Dabei wird die aktuelle Textur unendlich wiederholt. Überblenden wollte ich mit den Farbwerten der Vertizes, also den Alpha-Wert des Vertex mit der Texturfarbe modulieren und der Rasterizer sollte mit Gouraud-Shading die Alpha-Werte schön interpolieren.

    Jetzt ist meine Frage, ob dieser Ansatz ok ist. Ich bin auch auf einen anderen Ansatz gestoßen (http://www.gamedev.net/reference/programming/features/texturingheightmaps/page2.asp), der mir aber nicht gefällt, da jede Kachel einzeln gerendert wird. Das verursacht Nahtstellen und ich stelle es mir unperformant vor.
    Falls der Ansatz von mir so ok ist, weiß ich noch nicht sicher, wie ich die Alpha-Werte speichere. Soll ich mir ein eigenes Vertex-Format schreiben, dass so viele Alpha-Verte pro Vertex haben kann, wie ich brauche (für jeden Geländetyp einen)? Dann weiß ich aber auch nicht, wie ich Direct3D sagen kann "und jetzt zählt dieser Alpha-Wert". Außerdem kann ich dann nicht einfach einen Gelände-Typ hinzufügen, ohne das Vertex-Format zu ändern.
    Eine andere Idee von mir wäre, für jeden Geländetyp einen eigenen VertexBuffer zu haben, bei dem sich die Vertize aber nur in ihrer Farbe unterscheiden würden.

    Bitte um Input. 🙂


  • Mod

    nein, ein einziger großer vertexbuffer ist schlecht, das dürfte zuviel speicher verschwenden, zuviel cachemissen verursachen und zum großteil eh die ganze zeit brach liegen und so den platz für nützlicherere dinge verschwenden. dafür sollte man wie beim texturecaching einen guten cachealgo verwenden.



  • Optimizer schrieb:

    Jetzt ist meine Frage, ob dieser Ansatz ok ist. Ich bin auch auf einen anderen Ansatz gestoßen (http://www.gamedev.net/reference/programming/features/texturingheightmaps/page2.asp), der mir aber nicht gefällt, da jede Kachel einzeln gerendert wird. Das verursacht Nahtstellen und ich stelle es mir unperformant vor.

    Bei den Bildern im Artikel kann ich keine Nahtstellen entdecken. Man muss natürlich beim Kacheln kachelfähige Texturen verwenden, aber das weißt du doch.

    Und teste doch erstmal wie performant es tatsächlich ist, würde mich da nicht auf mein Gefühl verlassen.



  • Das habe ich schon getestet. Jedes Dreieck einzeln über den Bus zu schicken scheint nicht geil zu sein, die Framerate fällt für 256 Kacheln schon auf < 100. Wie aussagekräftig das ist, sei dahingestellt, aber nen wirklich billigen Boden nur mit 100 fps rendern zu können ist hart.

    User-- schrieb:

    Bei den Bildern im Artikel kann ich keine Nahtstellen entdecken.

    Bei diesem Verfahren werden die Dreiecke einzeln gezeichnet, d.h. der Rasterizer füllt sie unabhängig voneinander aus. Dadurch kann es auf Grund der arithmetischen Fehler dazu kommen, dass man zwischen zwei Dreiecken den Hintergrund oder die Clear-Farbe durscheinen sieht. Das Problem kann man auch bei meinem aktuellen Spiel in seltenen Fällen beobachten. Es ist nicht katastrophal, aber unnötig, außerdem würden die Vertize bei diesem Verfahren unnötigerweise mehrmals transformiert und beleuchtet werden. IMHO muss das alles nicht sein.

    rapso schrieb:

    nein, ein einziger großer vertexbuffer ist schlecht, das dürfte zuviel speicher verschwenden, zuviel cachemissen verursachen und zum großteil eh die ganze zeit brach liegen und so den platz für nützlicherere dinge verschwenden. dafür sollte man wie beim texturecaching einen guten cachealgo verwenden.

    Ok danke schon mal für diesen Hinweis. Kannst du mir zu der Texturierung noch was sagen (mittlerer Absatz)? Ich würde gerne wissen, wie es gescheit ist, die Alpha-Werte für verschiedene Gelände-Texturen pro Vertex zu speichern.



  • Ja heut sollt man sowieso nicht mehr mit einzelnen Dreiecken arbeiten. Glaub am besten ist immer noch das gute alte Splatting...


  • Mod

    Optimizer schrieb:

    Ich würde gerne wissen, wie es gescheit ist, die Alpha-Werte für verschiedene Gelände-Texturen pro Vertex zu speichern.

    das hängt von terrain ab, meistens macht man sowas aber mit blendmaps (statt vertexcolor), weil sonst das lodding des terrains zu sehr zu sehen wäre. ansonsten kannst du das so machen;)



  • Ein Ansatz den ich einmal verwendet habe: Das Terrain besteht aus lauter 32x32-Blöcken. Jeder diese Blöcke hat einen eigenen Vertex und Index Buffer. Zusätzlich kann jeder Block bis zu 4 verschiedene Texturen haben (also "Geländetypen"). Wie stark jeder dieser Typen auf einem Vertex vertreten ist habe ich einfach in das 2. Texturkoordinatenset geschrieben (alle Werte zusammen ergeben natürlich 1). Dann habe ich die einzelnen Texturen einfach in einem Fragment Shader gewichtet kombiniert. Das ist um einiges schneller als normales Blending, gerade wenn man mehrere Passes bräuchte. Anschließend habe ich noch einen Detail-Texture-Pass drüberlaufen lassen, der in der Nähe der Kamera das Terrain mit einer Detailtexture überzieht (dabei gibt es wieder unterschiedliche Detail Textures ja nach Geländetyp). Das habe ich zwar mit Blending gemacht, aber dieser Pass muss normalerweise nur für 1 oder maximal 2 Blöcke ausgeführt werden.
    Ein weiterer Vorteil von Shadern ist dass du theoretisch die Gewichtungen der einzelnen Geländetypen dynamisch aus Werten wie Höhe oder Steigung berechnen kannst.

    Nahtstellen sieht man keine, die Grafikkarte muss ja identische Vertexpositionen identisch transformieren. Die einzelnen Blöcke schließen daher auch nahtlos aneinander an (außer du hast T-Vertices, weil du static LOD benutzt. Aber ich glaube LOD braucht man bei der heutigen Transformationsrate bei Terrain eh kaum noch).

    Für Visibility Culling habe ich einfach einen Quadtree benutzt.


  • Mod

    man sollte bedenken, dass man mit d3d maximal ca 1000 bis 2000 renderaufrufe pro frame machen darf, damit es noch flüssig läuft (quelle: nvidia ). falls man nen größeren teil der 1024blöcke sieht(mit nur einem pass), wird es schon enger... sicherlich möchte man da noch mehr als das terrain rendern.



  • rapso schrieb:

    man sollte bedenken, dass man mit d3d maximal ca 1000 bis 2000 renderaufrufe pro frame machen darf, damit es noch flüssig läuft (quelle: nvidia ). falls man nen größeren teil der 1024blöcke sieht(mit nur einem pass), wird es schon enger... sicherlich möchte man da noch mehr als das terrain rendern.

    Stimmt, das könnte eng werden. Bei mir war das damals kein Problem, da waren meistens um die 30 Blöcke sichtbar (Sichtweite war glaube ich 600m bei 1x1m pro Zelle). Aber man kann die einzelnen Blöcke sicher auch größer machen. Wenige, größere Blöcke werden wohl schneller bearbeitet als viele kleine, man hat ja um einiges weniger CPU-Overhead, sowohl bei den Draw-Calls wie auch beim Culling. Sowas kann man aber eh nur empirisch ausprobieren.

    Mein Spiel (OpenGL) ist eigentlich auch mit Vegetation und animierten Charakteren meistens auf 60 FPS gelaufen, wenn man mitten in hohem Gras gestanden ist ists auf 30 runtergegangen (mit VSync, ansonsten auf 40-50). Ich habe auch vor dem ersten sichtbaren Terrain-Rendering-Pass noch einen reinen Depth-Pass gemacht in dem ich den Depth-Buffer befüllt habe damit ich mit early-Z möglichst wenige Fragmente mit den verschiedenen Geländetypen rendern muss. Das hat allerdings kaum Performance gebracht, vielleicht 10%.

    Was für Ideen hast denn du für LOD? Ich habe einmal static LOD probiert, abgesehen von T-Vertices (die kann man ganz gut verstecken) bekommt man ziemlich häßliches Popping wenn ein Block auf eine nächsthöhere Detailstufe umspringt. Dynamic LOD (ROAM etc) bringt auf heutigen Grafikkarten wohl so gut wie gar nichts, viel zu viel CPU-Overhead. Eventuell könnte man Geomipmapping oder so etwas versuchen? Oder man rendert die Szene in mehreren "Slices" entlang der Tiefen-Achse (mit unterschiedlichen LOD-Stufen) und kombiniert die Slices dann in einem Pixel Shader. Damit bekommt man zumindest das Popping weg, obs schön ist ist natürlich immer noch eine andere Frage.



  • Kustl schrieb:

    Was für Ideen hast denn du für LOD? Ich habe einmal static LOD probiert, abgesehen von T-Vertices (die kann man ganz gut verstecken) bekommt man ziemlich häßliches Popping wenn ein Block auf eine nächsthöhere Detailstufe umspringt. Dynamic LOD (ROAM etc) bringt auf heutigen Grafikkarten wohl so gut wie gar nichts, viel zu viel CPU-Overhead. Eventuell könnte man Geomipmapping oder so etwas versuchen? Oder man rendert die Szene in mehreren "Slices" entlang der Tiefen-Achse (mit unterschiedlichen LOD-Stufen) und kombiniert die Slices dann in einem Pixel Shader. Damit bekommt man zumindest das Popping weg, obs schön ist ist natürlich immer noch eine andere Frage.

    Es gibt auch neuere "dynamic" LOD algorithmen, die nicht soviel cpu speed verbraten.. Und LOD-algorithmen brauchste immer dann, wenn die terrains größer werden. Nehmen wir z.B. ein 4096x4096 Terrain, dass du mit Bruteforce rendern willst. Die Kamera befindet sich genau in der Mitte mit 45° sichtwinkel. Dann musste mit einem Bruteforcealgorithmus schon mindestens 4096*4096/8 = 2097152 Vertices pro frame nur für das Terrain rendern..

    Gegen poping helfen dann geomorphing algorithmen, die die neu hinzukommenden Vertices einfach "langsam" an die richtige position schieben..



  • life schrieb:

    Es gibt auch neuere "dynamic" LOD algorithmen, die nicht soviel cpu speed verbraten.. Und LOD-algorithmen brauchste immer dann, wenn die terrains größer werden. Nehmen wir z.B. ein 4096x4096 Terrain, dass du mit Bruteforce rendern willst. Die Kamera befindet sich genau in der Mitte mit 45° sichtwinkel. Dann musste mit einem Bruteforcealgorithmus schon mindestens 4096*4096/8 = 2097152 Vertices pro frame nur für das Terrain rendern..

    Gegen poping helfen dann geomorphing algorithmen, die die neu hinzukommenden Vertices einfach "langsam" an die richtige position schieben..

    Hab deine Homepage gesehen, offenbar hast du dich wirklich schon genauer damit beschäftigt 🙂
    Ich habe mir einmal dieser SOAR-Paper aus 2002 und das Video dazu angesehen, sieht wirklich nicht schlecht aus. Ich nehme an dein Algorithmus läuft hauptsächlich auf der GPU? Vertex oder Index Buffers dynamisch zu ändern kann ja nicht wirklich effizient sein.

    Daher war auch meine Idee zwar statische LOD-Stufen zu verwenden, diese aber in unterschiedliche nach Tiefe sortierte Slices in Offscreen Rendertargets zu rendern und die dann im Framebuffer mit einem Shader mit sanften Übergängen zwischen den einzelnen LOD-Stufen zusammenzusetzen (je nach Tiefenwert des aktuellen Pixels müsste das relativ leicht machbar sein).

    Noch etwas aus meiner persönlichen Erfahrung: Am wichtigsten für die visuelle Qualität eines Terrains sind die Texturen. Ohne gute Texturen oder mit sichtbaren Texturwiederholungen sieht ein Terrain einfach mies aus. Ich habe damals echt lang gesucht bis ich brauchbare Texturen gefunden habe, die sind auch alle entsprechend groß (1024x1024) und wiederholen sich glaube ich alle 16m. In Kombination mit einer zur Textur passenden Detail Map hat das dann ok ausgesehen (ohne Detail Map natürlich zu verwaschen).



  • Kustl schrieb:

    Hab deine Homepage gesehen, offenbar hast du dich wirklich schon genauer damit beschäftigt 🙂
    Ich habe mir einmal dieser SOAR-Paper aus 2002 und das Video dazu angesehen, sieht wirklich nicht schlecht aus. Ich nehme an dein Algorithmus läuft hauptsächlich auf der GPU? Vertex oder Index Buffers dynamisch zu ändern kann ja nicht wirklich effizient sein.

    Eigentlich änder ich wirklich den Vertex und IB dynamisch.. Will aber nicht behaupten, dass das nicht effizienter geht 😉
    Und die Tesslierung läuft bei SOAR auch komplett auf CPU ab, wobei hier die meiste Kriterien schon vorberechnet werden (statisches terrain) und somit die Runtimetesslierung relativ günstig ist..

    Hatte aber auch schon andere Algorithmen getestet, wo auch vieles auf die GPU verlagert wurde (z.B. auch das Geomorphing) und die VB und IB nur minimal geändert wurden pro frame (wenn überhaupt).. Allerdings wurd das ganze sehr schnell sehr kompliziert und den Geschwindigkeithit brachte es auch nicht, da die GPU ja auch noch andere Aufgaben wie z.B. Belichtung ausführen soll und der LOD Algorithmus selber auch ziemlich einfach war..
    Wenn man aber natürlich das ganze in ein Spiel oder so eingebettet hat, wo die CPU eh schon total ausgelastet ist, wird das ganze natürlich interesanter..

    Noch etwas aus meiner persönlichen Erfahrung: Am wichtigsten für die visuelle Qualität eines Terrains sind die Texturen. Ohne gute Texturen oder mit sichtbaren Texturwiederholungen sieht ein Terrain einfach mies aus. Ich habe damals echt lang gesucht bis ich brauchbare Texturen gefunden habe, die sind auch alle entsprechend groß (1024x1024) und wiederholen sich glaube ich alle 16m. In Kombination mit einer zur Textur passenden Detail Map hat das dann ok ausgesehen (ohne Detail Map natürlich zu verwaschen).

    Wenn du so tolle Texturen gefunden hast, könnteste die mir auch gleich mal schicken. 😃


  • Mod

    mit der cpu buffer zu locken ist zwar manchmal unumgänglich, aber das kann sehr viel performance kosten. auf durchschnitts-gpus schafft man heutzutage locker zwei millionen polys pro frame flüssig durchtransformieren zu lassen, falls man buffer lockt, kann das auf 2millionen polys pro sekunde herabgehen. bei solchen benchmarks sollte man dann aber genau wissen wo das bottleneck ist, denn wenn man zuviel overdraw hat, einen zu langen pixelshader oder texturetrasching, dann braucht man das natürlich nicht zu benchen.



  • 2 millionen Vertices extra pro frame zu zeichen kann unter umständen auch sehr viel performance kosten 😉



  • life schrieb:

    Und die Tesslierung läuft bei SOAR auch komplett auf CPU ab, wobei hier die meiste Kriterien schon vorberechnet werden (statisches terrain) und somit die Runtimetesslierung relativ günstig ist..

    Ich denke Geometry Shader werden da in Zukunft sehr viel bringen. Die sind ja schließlich genau dafür da 🙂


  • Mod

    life schrieb:

    2 millionen Vertices extra pro frame zu zeichen kann unter umständen auch sehr viel performance kosten 😉

    klar, aber mit ein wenig culling und meshlods kommt man wohl besser weg als buffer per cpu zu manipulieren.



  • rapso schrieb:

    klar, aber mit ein wenig culling und meshlods kommt man wohl besser weg als buffer per cpu zu manipulieren.

    hm.. sagen wirs so: im allgemeinen nicht! Mag natürlich für einige spezialfälle gelten..


  • Mod

    life schrieb:

    rapso schrieb:

    klar, aber mit ein wenig culling und meshlods kommt man wohl besser weg als buffer per cpu zu manipulieren.

    hm.. sagen wirs so: im allgemeinen nicht! Mag natürlich für einige spezialfälle gelten..

    ist im allgemeinen so, kannst du auch in allen papern nachlesen die optimales arbeiten mit gpus bearbeiten. jeder lock ist sehr evil!



  • ja.. und goto ist auch evil 😉

    Musst nicht alles glauben was die leute schreiben.. Der große Performancetest mit meinen dynamic Indexbuffer mit 3 (oder doch lieber 0?) indices, der jedes mal gelocked/unlocked wird gegen dein 2millionen vertices pro frame ohne lock zeichnen, würde dann glaubich doch zu meinen gunsten gehn 😉

    Wie willste überhaupt ohne locken auskommen, wenn die meshdaten alleine sagen wir mal ~1gb groß sind?


  • Mod

    life schrieb:

    ja.. und goto ist auch evil 😉

    natürlich, ist absolut unnötig und ein überbleibsel aus lowlevel zeiten. hab ich in den letzten 10jahren mit 100%iger sicherheit nicht einmal in c/c++ benutzen müssen.

    life schrieb:

    Musst nicht alles glauben was die leute schreiben..

    da ich das selber schreibe aufgrund meiner erfahrung, darf ich mir glauben, genauso wie du es darfst.

    life schrieb:

    Der große Performancetest mit meinen dynamic Indexbuffer mit 3 (oder doch lieber 0?) indices, der jedes mal gelocked/unlocked wird gegen dein 2millionen vertices pro frame ohne lock zeichnen, würde dann glaubich doch zu meinen gunsten gehn 😉

    ich schlage dir 1000 ibuffer mit je 1000tris vor, du darfst sie dann gerne auf 1-tri stellen und jedesmal locken. dann zeichnest du deine 1k und ich die 1M. du wirst sehen, die stumpfe methode gewinnt.

    [edit]wobei wir für nen echten test dein terrain gegen die bruteforce methode antretten lassen müßte, dann wäre es kein blosser schwanzvergleich, sondern wäre lehrreich für die user hier[/edit]


Log in to reply