# Aufbau einer JOGL Anwendung



## AllquantorX (5. Feb 2013)

Hallo Forum,

ich habe eine Frage zum Design einer Applikation, die auf 3D mittels Jogle umgestellt werden soll. Hier gibt es einige Rahmenbedingungen, die zu beachten sind. Dabei soll nur 'neues' OpenGL verwendet werden, d.h. VBOs / VBAs und keine fixe Render-Pipeline (sofern dieser Ansatz geeignet ist).

Die Applikation besitzt zur Zeit einen 2D Viewport, der jetzt auf 3D umgestellt werden sollen. Ein solches Modell (das angezeigt wird) besteht lediglich aus einer Liste von Objekten, die alle von AnimationObject erben. Davon gibt es in der Regel mehere Hundert oder Tausend (auch mal bis  100.000). Jedes AnimationObject kann völlig unterscheidliche Eigenschaften haben, teilweise werden erst zur Laufzeit neue Klassen nachgeladen, die Applikation kennt die Implementierungen also nicht, sondern nur die Methoden der Basisklasse AnimationObject. 

Diese AnimationObjects besitzen eine Methode (deren Signatur noch festgelegt werden kann), die in konkreten Unterklassen überschreiben werden muss, um festzulegen, wie genau das Objekt darzustellen ist. Relativ zu einem zentralen Ursprung. Das können z.B. ein Würfel eine Kugel oder andere ganz wilde Polygon-Haufen sein. 

Nun sollen das Modell (d.h. alle AnimationObjects) schnell in 3D gezeigt werden. Es handelt sich um eine Echtzeit-Animation. Die Objekte können sich während dieser Animation bewegen und es werden recht häufig auch neue Objekte hinzugefügt und alte Objekte gelöscht (einfach durch add oder remove auf dieser Liste). Alles kein Problem, wenn es mit der alten Pipeline umgesetzt werden soll. Dort kann jedes Objekt einfach eine Display-List erzeugen und die Applikation kann von jedem Objekt einfach eine render-Method aufrufen. Hier ist es kein Problem, dass die Objekte-Liste sich ständig ändert oder die Positionen der Objekte sich in der Animation verändern da mit jedem render Vorgang die Daten neu in die Pipeline gejagd werden.

Wie löst man dies allgemein mit VBAs am Besten? Muss man die Vertex-Daten bei jeder Änderung in der Objekt-Liste (was häufig vorkommt) komplett neu an die Graphikkarte übertragen? Ist das nicht kostpielig? Würde man für jedes Objekt einen eigenen Vertex-Buffer anlegen? Wie geht man mit Bewegung von Animation am Besten um?

Vielen Dank


----------



## Kr0e (5. Feb 2013)

Grundsätzlich:


Ein VBO enthält die Row-Vertex Daten. Da es bei dir offenbar durchaus ne ganze Menge an Objekten geben wird, würde ich vorschlagen, mehrere Objekte in einem VBO zusammen zufassen.

Bewegungsänderungen von Objekten werden mittels Vertex-Shadern gemacht. Der Shader bekommt dann die Rohdaten als Stream von einem VBO und kann Transformieren (Matrix ModelView/Projektion).

Danach muss der Fragment-Shader noch Farbwerte für die Fragmente (Pixel) berechnen. Du brauchst also definitiv Shader um in der neuen Welt von OpenGL auch nur iwas zu zeichnen.


Wenn sich ein Objekte strukturell verändert (also wenn die Position der Vertices sich zueinander ändert) kannst du die VBOs updaten. Es gibt beim Erstellen dafür ein Flag. Ich meine es heißt GL_DYNAMIC_DRAW  oder so ähnlich. Damit kannst du den Treiber anweisen, diesen VBO für häufige Änderungen zu optimieren.


PS: Zur einfacheren Handhabung würde ich dann noch VAOs (Vertex Array Objects) einsetzen. Damit kann das Binden von Shadern/Attributen/VBOs auf der Grafikkarte als Befehlforlge gespeichert werden.


PSS: Falls du kannst, würde ich ggf. eine Library/Engine einsetzen um diesen ganzen LowLevel Kram managen zu lassen. Spart enorm Zeit. Z.b. jME3 oder sogar Java3D.


----------



## AllquatorX (5. Feb 2013)

Hallo,

vielen Dank schon einmal für die Antwort.

Wie sieht es mit der häufigen Änderungen der Objekte aus (ich meine Zufügen und Löschen aus der Liste der zuzeichnenden Objekte)? Werden dann üblicherweise alle Vertex-Daten neu übertragen oder kann man reletiv günstig ganze Blöcke dort entferenen und hinzufügen?

PS: Gibt es generell ein Pattern wann Java3D als High-Level Lösung dem JOGL Layer zu bevorzugen wäre?


----------



## Kr0e (5. Feb 2013)

Hi,

nun Java3D oder jME3 bietet einem natürlich sehr viel. Ich würde deine Frage so beantworten:
Wenn du ein umfangreiches 3D Problem hast, was mit Jogl und LWJGL viel Handarbeit erfordert, so würde ich eine der fertigen Lösungen vorschlagen.

Man kann jME3 z.b. auch einbetten in ein AWL Panel soweit ist weiß.


Nun zu dem Entfernen: Man kann den Buffer stets neu anlegen. Ich glaube es ist aber per Standard nicht unbedingt gewährleistet dass danach Daten drin sind, bzw korrekt drin sind. Insofern würde manuelles verkleinern/vergrößern ein Rewrite der Daten erfordern (Lasse mich hier aber gern korrigieren, z.b. von Guest2 aka Fancy)

Ich würde es einfach anders machen:

Du nimmst z.b. immer einen VBO für z.B. 100 Objekte (Vorausgesetzt die Objekte übersteigen nicht ein gewisses Maximum an Vertices) Wenn nun ein Objekt rausfliegt, können die Daten ruhig drin bleiben. Ich würde einfach den Platz als frei deklarieren. Diese Lösung wäre natürlich ideal wenn deine Objekte alle die gleiche Verticesanzahl hätten (in dem Fall wäre es perfekt ).


Aber falls das nicht so ist, würde ich es mit einem Rewrite der Daten probieren, sooo langsam sind VBOs nicht. Was du aber defintiv machen kannst, ist dass du mehrer VBOs benutzt und immer nur den neuschreibst, wo sich was geändert hat.

So kannst du die Anzahl der neuzuschreibenden Objekte stark verringern.

Vlt. hat ja noch wer anders eine bessere Idee.


----------



## schalentier (5. Feb 2013)

Hey, ich kenn ja die genauen Anforderungen/die genaue Aufgabe nicht, aber du hast von Display-Listen geschrieben. 

Evtl. waere es eine Moeglichkeit, in einem ersten Versuch mal alles auf normale OpenGL-Display-Listen umzustellen. Vermutlich waeren die notwendigen Aenderungen bei weitem nicht so aufwendig, wie die Umstellung auf das von Kr0e Beschriebene.

In meinem letzten OpenGL Experiment hab ich eine feste Anzahl von VBO mit fester Groesse benutzt und dort einfach Vertex-Daten reingeschrieben. Sobald ein Buffer voll ist, wird der in die Graka geladen und gerendert. Auf einem hoeheren Level hab ich Teile der Szene, die sich nicht aendern, in DisplayListen "gecached". Is halt schoen einfach zu implementieren.

Wenns zu langsam ist, kann man das immer noch umbauen und die Buffer zwischenspeichern, aber ich hatte da furchtbare Problem mit unterschiedlich grossen Buffern... Und am Ende haste zumindest belastbare FPS-Raten, die es mit der Alternative zu schlagen gilt ;-)

Falls es interessiert, hier der Code:

BufferedRenderer
Display-Listen-Cache


----------



## Kr0e (5. Feb 2013)

```
for( int x = 0; x < OctreeNode.CHUNK_SIZE; x++ ) {
    for( int y = 0; y < OctreeNode.CHUNK_SIZE; y++ ) {
         for( int z = 0; z < OctreeNode.CHUNK_SIZE; z++ ) {
```


Du machst bestimmt iwas MineCraft mäßiges, oder ?


----------



## AllquantorX (5. Feb 2013)

Hallo,

zu Display-Listen: Wie schon gesagt, Vorgabe ist, keine deprecated OpenGL Funktionen zu verwenden und ich denke dass Display-Listen dazu gehören (bitte um Korrektur, falls ich mich irre).

zu VBO: Ja, ich könnte die Objekte in Gruppen anordnen, allerdings gibt es im Vorfeld keinen Hinweis darauf, welche Objekte bald zu löschen sind und wo neue Objekte am Besten einzuodnen wären... Ist das wirklich ein geeignter Ansatz?


----------



## Kr0e (5. Feb 2013)

AllquantorX hat gesagt.:


> Hallo,
> 
> zu Display-Listen: Wie schon gesagt, Vorgabe ist, keine deprecated OpenGL Funktionen zu verwenden und ich denke dass Display-Listen dazu gehören (bitte um Korrektur, falls ich mich irre).



Das ist auch mein letzter Stand.




AllquantorX hat gesagt.:


> zu VBO: Ja, ich könnte die Objekte in Gruppen anordnen, allerdings gibt es im Vorfeld keinen Hinweis darauf, welche Objekte bald zu löschen sind und wo neue Objekte am Besten einzuodnen wären... Ist das wirklich ein geeignter Ansatz?




Hm, nungut ich kenne natürlich nicht deine Anforderungen. Ich denke aber, dass ein solches Modell gehen müsste, auch wenn alle Objekte in keinem Zusammenhang stehen, also wenn es iwo passieren könnte.

Damit wollte ich nur verhindern, dass halt der GESAMTE VBO neu geschrieben werden muss.


----------



## schalentier (5. Feb 2013)

Kr0e: Jupp, deswegen passt es nicht ganz zum Thema, aber das Prinzip ist ja ueberall gleich.

AllquantorX: Dann waere ein erster Versuch, einfach alles in Buffer zu schreiben und wenn voll, zu rendern. Dann kannst mit verschieden grossen Buffern experimentieren, Objekte ohne Aenderungen in speziellen Buffern zu merken, etc. 

Ich glaube, es ist recht schwierig, auf solche Frage pauschale Antworten zu geben, ohne konkretere Zahlen zu haben.


----------



## Guest2 (6. Feb 2013)

Moin,

bei aktuellem OpenGL sind die Grundlagen immer gleich: Vertex Specification, Vertex Rendering, Primitive.
Und hier wahrscheinlich noch: Buffer Object Streaming

Bei VBOs gilt erstmal größer ist besser. Wenn die Daten aber tatsächlich oft geändert werden sollen, dann ist etwa 4 MB ein Anhaltspunkt. Größer wäre besser, kann jedoch dazu führen das der Grafikkartenspeicher fragmentiert, was langfristig dann wieder bremsen kann. Wie viele Vertices das konkret sind, hängt davon ab wie viele Vertexattribute an einem Vertex hängen.

Grundsätzlich sollte man es aber vermeiden Dinge wie Sichtbarkeitsberechnungen oder Animationen auf der CPU berechnen zu wollen und das Ergebnis dann auf die Grafikkarte streamen zu wollen. Die GPU kann das alleine besser. Das dürfte hier aber vom [c]AnimationObject[/c] abhängen.

Was aber klar ist, bei 100.000 Objekten kann nicht 100.000-Mal der Shader gewechselt werden oder 100.000-Mal ein glDrawElements aufgerufen werden, dann bricht die Framerate ein. Die Objekte müssen also sinnvoll zusammengefasst werden.

Viele Grüße,
Fancy


----------



## AllquantorX (6. Feb 2013)

Ersteinmal vielen Dank für die Antworten und die Denkanstöße in die richtige Richtung. Ein/Zwei Fragen kommen mir so spontan noch auf:

Wie ist es genau gemeint, Animationen im Hintegrund laufen zu lassen? In diesem konkreten Fall geht es um eine Applikation, die durch eine ereignis-diskrete Simulation gesteuert wird, dass heißt es werden durch Ereignisse Dinge bewegt, gestoppt (genauer die Bewegung anggetoßen und das Ende der Bewegung markiert, aber auch plötzliche Stopps sind möglich durch unerwartete Events von Außen). Ereignis-Verarbeiter arbeitet in einem speziellen Thread, der mit einem bestimmeten Faktor synchron zur Systemzeit geschaltet wird (d.h. künstlich ausgebremst wird). Zwischen-Ergeignise führen zu Aktualiserung der Position von Objekten durch Interpolationen. Die Objekte kennen den Pfad auf dem sie sich bewegen und bei der Aktualisierung wird berechnet (unter Berücksichtigung von Geschwindigkeit, Beschleunigung und anderen Dinge) wie viel Prozent das Objekt auf seinem Pfad schon zurückgelegt hat. 

Wenn ich es richtig verstehe würde man also ein Vertex-Shader laden, dem man dann, immer die aktuellen Pfad-Positionen berechnet wurden, Informationen / Parameter übergibt, die dann im Programm auswertet werden um die Vertex-Daten entsprechend zu translatieren und rotieren. Müsste es dann aber nicht für jedes Objekt eine solche Variablen geben oder wie kann man für jedes Objekt dem Shader mitteilen, wo sich das Objekt nun genau befindet?


----------



## Guest2 (7. Feb 2013)

Idealerweise würde die Simulation selbst auf der Grafikkarte laufen, dann müssten keine Daten ausgetauscht werden und es gäbe auch keine Synchronisationsprobleme. Hier scheint aber die Simulation selbst schon zu existieren, sodass es nur ums Anzeigen geht. Das kann langsamer sein, als wenn sich alles auf der GPU abspielt. Hängt aber davon ab, ob und wo sich später ein Bottleneck bilden könnte.

So wie ich das bisher verstanden habe, würde ich das [c]AnimationObject[/c] so bauen, das dort zum einen eine Liste der normalen Vertexattribute (z.B. x, y, z, nx, ny, nz, s, t) zurückgegeben wird als auch eine Liste der Indices. Diese Listen sollten konstant sein. Die aktuelle Position und Rotation könnten als Transformationsmatrix zurückgegeben werden. 

Im OpenGL Teil müssten mehrere [c]AnimationObject[/c] zusammengefasst werden, so das eine sinnvolle Größe entsteht. Dabei müssten entsprechende VAO/VBO/IBO gebaut werden. Ich würde dabei die Vertexattribute um ein zusätzliches Attribut ergänzen in dem die aktuelle ObjektID untergebracht wird. Die zugehörigen Tranzformationsmatrizen könnten über ein UBO an den Shader übergeben werden. In GLSL kann darauf wie auf ein Array zugegriffen werden, so das sich für jedes Vertex aus der zugehörigen ObjektID auch die zugehörige Transformatinsmatrix bestimmen lässt.

Allerdings garantiert die OpenGL Spec UBOs nur bis zu einer Mindestgröße von 16 kB. Realistisch dürften maximal 64 kB sein, was immerhin für 1000 Objekte reichen würde. Bei maximal 100.000 Objekten müsste das dann also in 100 Durchläufe mit je 1000 Objekten aufgeteilt werden. Das wäre dann vielleicht auch eine sinnvolle Maximalgröße für ein VBO. Eine Alternative dürften noch SSBOs sein, die sind wesentlich größer, jedoch erst ab OpenGL 4.3 verfügbar. Oder klassisch geht auch noch eine Buffer Texture.

Als Tipp würde ich Dir empfehlen mal das eine oder andere kleine OpenGL Programm zu schreiben um etwas Gefühl dafür zu bekommen.

Viele Grüße
Fancy


----------



## AllquantorX (7. Feb 2013)

Vielen Dank!

ich werde mit diesen Infos eines ersten Designansatz probieren und das ganze einmal ausprobieren.


----------

