Registriert: Mi Jan 31, 2007 18:32 Beiträge: 150
Programmiersprache: Pascal
Ich habe angefangen einen SceneGraph zu implementieren....
1 : Welche Nodes sollten unbedingt implementiert werden??
2 : Welche "Funktionen" sollten zusammengefasst welche in seperaten Nodes verarbeitet werden(z.B Trasformationen)??
3 : Wie realiesiert man mehrere Passes (für Refelection/Stencil Schatten)??
Nodes werden bisher registriert, dabei wird für jeden Type ein Index vergeben. Anhand dem Index wird dan beim laden der Node eine Klasse initlialisiert der wiederum der Offset der Daten über ein Header-Record vermittelt wird...
mfG FrenK
PS. : Ich finde ein Artikel über einen groben Aufbau eines SceneGraphs wäre recht nützlich(wies aussieht basteln viele an einer eigenn engine/toolchain etc.)
Schon mal in Java3D reingeschaut oder den PGL Performer (oder so ähnlich).
Was ist der Switch bei dir? Gruppenknoten fällt mir noch so ein. Und das ganze Aussehen. Halt so ein bissl VRML-mäßig
_________________ __________
"C++ is the best language for garbage collection principally because it creates less garbage." Bjarne Stroustrup
Registriert: Mi Jan 31, 2007 18:32 Beiträge: 150
Programmiersprache: Pascal
switch = eine art schalter der Nodes je nach State übergiebt
Zitat:
Aussehen
Mit solchen antworten hatte ich eigentlich nicht gerechnet tut mir leid wenn du mich nicht ganz verstanden hast mir geht es um etwas detaiertere Antworten...
Unter Aussehen kann mann ne Menge Nodes erstellem(Material , Texture, Shader usw.) mir geht es aber darum was in eine Node Klasse kommt um ensprechend Aussehen zu erreichen (Entschuldigung für dieses ... Deutsch war jetzt gerade ne schnelle Antwort)
Registriert: Di Mai 18, 2004 16:45 Beiträge: 2623 Wohnort: Berlin
Programmiersprache: Go, C/C++
Einzelne Nodes für Transformation,Rotation und so ist übelste verschwendung an Ressourcen, gerade für Spiele.
Mein letzter SG hat eine 4x4 Matrix in jeden Node gehabt, die hat man einfach multipliziert.
Im Prinzip gibt es ein Urklasse Node, davon 2 Nodes abgeleitet, Darstellbare und nicht Darstellbare Nodes.
Matrix und nen Funktionsset für transformation,rotation,skalieren und so weiter packst du einfach mit in die Darstellbare Node rein.
Du kannst die ganze Sache rekursiv durchgehen, was aber absoluter Overkill ist, dass hab ich noch so gemacht(hatte trotzdem noch über 1k FPS ^^). Benutze Callbacks, die sind um längen schneller, also ein Update und Draw Callback.
Der Drawcallback ist für sichtbare Objekte und Update haben alle Nodes, wird also schon bei Urklasse implementiert.
Update macht positionsupdates, status updates,logik und so weiter.
Wie läuft das ganze dann ab ?
Dein SG hat Listen für Update- und Drawcallbacks, wenn du ein neues Node erstellst, dann packst du den Updatecallback in die Liste und wenn der Callback durch ist und keine Updates mehr braucht kannst du den Callback entfernen sonnst prüfst du z.B. bei Zeichenbaren Obj ob er im Sichtbereich ist und registrierst noch den Draw callback. Wenn deine Physik z.B. ein Objekt bewegt, dann schaut es nach was in der nähe liegt und prüft diese mit Kollision, kollidiert es mit einem Objekt, dann wird das Objekt aufgefordert sich in der Updateliste wieder zu registrieren, wenn es nicht drin ist. Das System braucht mehr Quellcode, wegen den ganzen registrieren und unregister aber ist deutlich schneller und so kannst du z.B. dein Speicher bis zum anschlag mit Objekten vollpacken und es läuft trotzdem problemlos.
Ich hatte noch ein bischen mit Instancing, Datenbanken, Hashes,Caches und so gearbeitet aber das ist zum Anfang eher nicht so wichtig.
_________________ "Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren" Benjamin Franklin
Registriert: Mi Jan 31, 2007 18:32 Beiträge: 150
Programmiersprache: Pascal
Ich dachte jetzt nicht an einzelne Rotations Nodes usw. sondern zB. :
Ob Ich einen Node nehme der beim Aufruf von Draw einen Matrix mit der aktuellen multipliziert und dann Draw bei allen children aufruft oder ob ich das mit in die children implementieren sollte
Wie hast du dann aufwendiegere Sachen(mehrere passes) Reflectionen usw implementiert oder wie würdest du dies tun??
Eine weitere Frage wie hast mann deinen SceneGraph "zusammengebaut" sprich die einzelnen Nodes hinzugefüt eigenschaften gesetzt usw.??
Callbacks : das mache ich atm bei meiner GUI so von daher solte es keinen Probleme geben so etwas zu registrieren
Instancing : schon vorgesehen
Sichtbarkeit : Die frage ist wie feststellen ..Ich meine es wäre schon unsinnig jedesmal alle Nodes durchzugehen und auf Sichtbarkeit zu prüfen von daher hast du das über Octree realisiert ?? wenn ja wie hast du entschieden welche nodes überhaupt überprüft werden müssen??
Registriert: Di Mai 18, 2004 16:45 Beiträge: 2623 Wohnort: Berlin
Programmiersprache: Go, C/C++
Der Folgende Text ist ein bischen Inkonsistent, wenn es um die Tiefe der Erklärungen geht.
Am besten sind wirklich sehr explizite Fragen, sonnst schweife ich aus und dann können einige Punkte auch mal unter gehen.
Da gibt es kein Patentrezept, wie man ein SG aufbauen kann, ich hab mitlerweile 4 oder 5 verschiedene SG aufgebaut, die allesammt unterschiedliche stärken udn schwächen hatten. Mein letzer basierte auf Views, also wie bei einer Datenbank wurden der SceneGraph durch Views zerlegt und konnte dann effektiv durchwandert werden ohne Callback zu verwenden.
Bei den Views hab ich es z.B. so gemacht, dass es ein OctreeView gab, welches die Nodes intern in ein Octree zerlegt hat dann kam ein Frustum Culling noch mit rein und wenn ich dann die View abgefragt habe, bekam ich nur die Nodes zurück die aktuell Sichtbar waren.
Es gab auch eine Physik View, wo nur dynamische Objekte drin waren.
Wenn ich nun ein Callback basiertes schreiben würde, dann würde ich wieder Views verwenden Also der array sind die Obersten Knoten(z.B. Mensch, Auto, Himmel, Baum,...) Wenn also der Callback durchläuft, dann macht er gleich alle seine childs rekursiv mit, da diese ja sowieso ausgeführt werden.
So braucht man die Callbackliste nicht vollstopfen. Bei Draw z.B. sind alle Childs sichtbar, wenn Parent sichtbar ist und wenn Parent nicht sichtbar ist sind alle childs nicht sichtbar. BoundingVolumes sind dein Freund, ich habe diese überall implementiert, weil bei mir auch nicht Sichtbare Objekte eine Position haben(Sound hat dann die BoundingSphere mit Radius Soundreichweite, Lichter können 0 für immer sichtbar(Sonne) oder >0 sein für alle anderen Lichter, Trigger sind bereichsbezogen Eigenschaften wie Himmeltextur oder so habe ich als Property mit an die Node gepackt).
Das war aber mehr faulheit und hat keinen speziellen Grund, dass ich alles in ein Node verpackt hab.
Der Code ist in entsprechendem SVN zu finden, ein webview gibs hier.
Draw besitzt bei mir ein Parameter Pass, darüber wird der Zeichenpass festgelegt, diesen habe ich auch in meine Materials mit übernommen.
Ich hatte ein Z-Pass und ein RenderPass eingebaut, der TransparentPass hab ich ned mehr fertig gemacht.
Also hab ich in meiner mainloop SG.Update(); ... SG.Draw(ZPASS); ... SG.Draw(DRAWPASS); ... gehabt.
Instancing ist relativ easy zu lösen, bau eine GetInstance(....) routine in deinen SG ein und der SG kümmert sich dann um das laden der Ressourcen, also delegiert die aufrufe weiter. So kann der SG erstmal schauen, ob er per hashtable das Objekt schon hat und entsprechend in einer SharedNode Struktur den counter erhöhen, ein lightweight Node ertellen und die SharedNode Struktur da mit ranhängen(pointer) und das ganze zurück liefern.
Wenn du in mein Code guckst, solltest du ziemlich schnell die entsprechenden Klassen und records sehen. Software Instancing ist ja nicht nur für den Speicher nützlich sondern auch ziemlich performant für Grafik rendering. Darum habe ich für den RenderPass Parameter von Draw noch entsprechende Konstanten festgelegt gehabt. Zeichne normal, beginne Stack, zeichne Stack, beende Stack, wobei beginne und ende halt bind und unbind von shadern und vbo macht und zeichne nur der zeichenaufruf ist.
Wenn du das Instancing so realisierst, wie bei mir, also alle Daten liegen in einer seperaten Struktur , dann bekommst du auch noch HotAsset loading kostenlos dazu . Wenn es dir nichts sagt, HotAsset loading ist eine Technik, die dein Entwicklerverzeichnis überwacht und bei änderung von Datein, die neuen Daten ein liest und über netzwerk an die laufende Demo schickt, wo dann nur noch das Datenrecord ausgewechselt werden muss und somit on the fly du änderungen von Models, Levels sehen und testen kannst. In verbindung mit Serialisierung für das Dateiformat machst du das ganze noch um ein vielfaches leichter und sparst ne menge Code. Ich habe mein Fileformat nicht Serialisierbar aufgebaut und schleife für jedes Objekt immer ein passenden Loadcode mit rum, der viel Zeit kostet. Sowas wird schon in einer Hand voll Gameengines für den ganzen Grafischen Kontent verwendet.
Sichtbarkeit ist ein schönes Thema, da hab ich mich dafür entschieden, es über eine Octree-FrustumCulling als Basis und ein größentest als erweiterung zu verwenden. Das bedeutet, ich habe in dieser View alle Nodes in View.Source hinterlegt und dann Update aufgerufen. Damit wurden dann alle Nodes in Source in ein Octree, zur Laufzeit, eingegliedert und bei View.Destination lagen dann die für die Aktuelle Kamera sichtbaren Nodes Vor.
Die ganze geschichte ist ziemlich kurzsichtig durchdacht gewesen, da jeden Frame man das Octree neu generiert hat.
Ich weiss nicht mehr, ob ich schon es in den Code schon drin ist, dass Update das Octree immer generiert, wenn es aufgerufen wird und Draw, anhand des aktuell übergebenen Kameraobjektes, Destination updated. So wird das Octree nur bei einen Update von Source neu aufgerufen und nicht jeden Frame.
Das Octree war auch ne anpassung, es ist Größenbasiert also es hat eine minimale Kantenlänge von 5Einheiten.
Dies hat den Vorteil, dass man mit einer Formel das Node im Baum errechnen kann, in welchen man gerade suchen will und muss nicht erst den Baum durchwandern und testen. Hab ihn aber trotzdem als normales dynamisches Octree verwendet.
Das Problem wäre der Speicherverbrauch bei meiner Lösung.
Also bei einem 4Dimmensionalen Dynamischen array, wo 1.Dimmension die Ebene ist, 2,3 und 4 XYZ auf der Ebene sind.
Ein Zugriff auf den Root vom Octree wäre Nodes[0,0,0,0], Node[1,0,0,0] wäre Root in 8 Würfel unterteilt und dabei der obere linke hintere.
Also 1. Dimension gibt die Ebene, Iteration der Unterteilung an.
2-4 sind die X,Y,Z Indices der Nodes.
Ein Objekt bei 19,42,6 wäre im 3,9,2 in Iteration it=Width/5;
Aber wie man sehen kann würde der Speicherverbrauch ziemlich hoch sein, obwohl man davon nicht viel verwendet.
Deswegen hatte ich die Implementierung erstmal verworfen gehabt.
Eine Idee wäre vieleicht Hashtabellen oder ähnliches, das würde den Verbrauch drastisch reduzieren.
Wie schon erwähnt habe ich allen Nodes eine BoundingSphere verpasst und die habe ich auch verwendet, um festzustellen, wieviele Pixel noch ein Objekt groß ist und wenn es einen Schwellenwert überschritt, dann wurde es einfach nicht mehr gezeichnet. Über den errechneten Wert kann man dann auch LOD Stufen prima verwalten, da man ganz Große Objekte erst wesentlich später in den LOD Stufen runter schalten sollte als ganz kleine.
Sascha hatte mir da mal als Letzte LOD Stufe Billboard vorgeschlagen, also das Objekt nochmal in ein FBO reinpacken und dann nur noch ein Quad zeichnen.
Die Idee ist sehr gut aber ich hab sie nicht mehr Implementiert gehabt.
Ich hatte auch parallel dazu angefangen ein PDF zu schreiben http://karmarama.linuxprofessionals.org/upload/scenegraph.pdf, dass ist aber nie fertig geworden, da schon beim schreiben sich einiges in der Implementierung verändert hatte, weil sich einfach viel bessere Möglichkeiten auftaten.
Ein Screenshot vom letzten Stand kannst du hier sehen.
Danach bin ich auf C++ umgestiegen und seit dem hatte ich keine Zeit mehr zum weiter entwickeln.
_________________ "Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren" Benjamin Franklin
Registriert: Mi Jan 31, 2007 18:32 Beiträge: 150
Programmiersprache: Pascal
Da ich sowieso schon wrapper für einzelne Dateinformate Plattformen usw. verwende hatte ich vor jedem NodeType wie Mesh entsprechened einen Zeiger auf einen record der die Notwendigen Daten enthält zu benutzen somit wäre Instancing recht einfach hinzuzufügen
Das einzige Problem was ich noch sehe, ist die Sichtbarkeitsüberprüfung auch wenn schon optimiert (Octree) könnte das überprüfen ordentlich Zeit kosten wenn mann das nicht weiter beschleunigt sprich Sortierung...hast Irgenwer sich schon mal mit so was beschäftigt??
Registriert: Di Mai 18, 2004 16:45 Beiträge: 2623 Wohnort: Berlin
Programmiersprache: Go, C/C++
Du sortierst in ein Octree ja deine Nodes ein und damit kannst du schon beim Prüfen auf eines OctreeNode mit positiven erfolg alle Subnodes mit allen daran hängenden Zeichenbaren Nodes in die, muss gezeichnet werden Kategorie verfrachten. Das ist ja der tolle Vorteil von Octree, wenn ein Test weit Oben schon positiv ausfällt sind alle Test tiefer drin automatisch auch positiv udn man kann das ganze Frustumculling auf BoundingSpheres einfach überspringen.
Für meine Szenen brauchte ich nie mehr Power als das ein Octree nicht völlg gereicht hätte.
Wenn du wirklich performance Probleme bekommen solltst, dann reden wir nochmal.
_________________ "Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren" Benjamin Franklin
Mitglieder in diesem Forum: 0 Mitglieder und 15 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.