Scene Rendering
Verfasst: 26.01.2012, 10:40
Ich möchte demnächst das Grafikgrundgerüst meines Spiels umstellen, da mir der bisherige Ansatz zu langsam vorkommt. Gedacht ist das ganze erstmal für einen Egoshooter, aber auch Spiele aus der TopDown Ansicht könnten in nächster Zeit interessant werden.
Aktuell besteht meine Szene prinzipiell aus einem Haufen Objekten, die sich auf einem Gelände (bzw. Boden) befinden. Dieses Entitysystem finde ich ansich ganz nett, man kann einfache statische Objekte durch verschiedene Komponenten erweitern, die dann jeweils auf Ereignisse in der Spielwelt (oder auch Nutzereingaben) reagieren können, wodurch man eigentlich die Spiellogik ganz gut umsetzen kann.
Jedes dieser Entitys hat jetzt ein Modell-Objekt. Das kann ein 3D Modell mit zugehörigem Material laden und rendern. Im Hintergrund sind dabei ein paar Manager die sich darum kümmern, dass Ressourcen nur einmal geladen werden müssen, aber prinzipiell kann sich jedes Modell eigenständig rendern. Die nötigen Shader werden dazu aus den materialeigenschaften generiert, was hässlich wird, sobald mal 'globale' Effekte, wie Lichtquellen oder Schatten hat. Dafür hat im Moment jedes Material eine Referenz auf ein 'LightSetup' um die entsprechenden Lichtquellen in den Shader berücksichtigen zu können.
Nun, der Ansatz funktioniert und war auch nicht sonderlich komplex umzusetzen, aber es ist langsam. Da jedes Modell eigenständig ist, wird für jedes Entity quasi alles neu gesetzt. Ein paar Test in gDebugger ergaben, dass das ganze die CPU scheinbar echt zum schwitzen bringt. ShadowMapping scheint die Sache weder durch die hohe Füllrate noch durch den komplexen Shader auszubremsen, sondern schlicht deshalb, weil alle Objekte zweimal gezeichnet werden müssen. Viele Statechanges halt.
Ein paar Optimierungen sind natürlich schon drin: Ich habe ein ViewFrustum Culling für die Objekte, die in einem Quadtree (da das Spielfeld sehr flach ist) eingebaut, was die Sache auch deutlich beschleunigt. Dieser Quadtree wird derzeit auch für Kollisionsabfrage benutzt.
Jetzt will ich das ganze dahingehend umbauen, dass ich eine Art Scenengraph (ob es wirklich ein Graph sein wird, weiß ich noch nicht) programmieren will, indem sich die Entitys einfach mit ihrem Modell registrieren und der dann sämtliche Renderarbeiten übernimmt. Das sollte eine ganze Reihe an Optimierungen zulassen und das Rendern deutlich beschleunigen. Jetzt ist meine Frage, wie würde ich das ganze aufbauen?
Man möchte ja vermutlich Objekte mit dem selben Material irgendwie gruppieren um diese dann hintereinander Zeichnen zu können, um StateChanges zu sparen. Wonach sortiert man da so? Spontan würde ich sagen, dass Shader neu setzen aufwändiger ist, als eine Textur zu ändern. Wie geht man das Problem an, dass der Shader nicht nur die Materialeigenschaften sondern auch die Beleuchtungsinformationen (Lichtquellen, Schatten, usw.) berücksichtigen muss? Irgendwie muss man den doch generieren, oder legt man sich da auf 1-5 hardkodierte Shader fest? Wie gesagt, bislang hab ich die Shader im Code generiert, aber das ist ziemlich fummelig zu programmieren, vielleicht gibt es da ja bessere Möglichkeiten.
Und wie sieht es mit verbessertem Culling aus? Ich denke, für Outdoorszenen ist ein Baum schon ganz cool, aber was ist mit Objekten, die genau auf der Grenze liegen (bisher hatte ich die dann einfach in einem höheren Node liegen). Macht es vielleicht sogar Sinn, erst den Baum zu durchlaufen und alle Objekte in eine Temporäre Liste zu speichern, die dann nochmal nach Material oder was auch immer sortiert wird?
Früher oder später hätte ich auch gerne Indoor Szenen. Da sieht man ja meistens nur einen winzigen Teil des Levels, wie cullt man da am besten? Man hört ja von Portalen, wo das Level in mehrere Bereiche aufgeteilt, und man zeichnet nur den Bereich, in dem sich die Kamera befindet und die, die man durch Portale sehen kann. Da man das Frustum auf diese Portale beschneiden kann, wird man so wohl recht effektiv Dinge wegschneiden können. Aber was passiert, wenn man den selben Knoten durch unterschiedliche Portale sehen kann? Wenn 2 Fenster nebeneinander liegen und man nach draußen guckt, wird die komplette Außenwelt dann doppelt gerendert, oder wird gespeichert, welche Bereiche schonmal gerendert wurden (was natürlich das Beschneiden des ViewFrustums schwierig macht).
Hier sollten ja eigentlich einige rumspringen, die sich mit derlei Fragen schon beschäftigt haben, aber vielleicht hat ja auch jemand ein paar ganz gute Artikel zu dem Thema. Ich habe zwar immer mal wieder etwas in die Richtung gelesen, aber Texte von vor 5 Jahren müssen ja heute nicht mehr unbedingt empfehlenswert sein.
Aktuell besteht meine Szene prinzipiell aus einem Haufen Objekten, die sich auf einem Gelände (bzw. Boden) befinden. Dieses Entitysystem finde ich ansich ganz nett, man kann einfache statische Objekte durch verschiedene Komponenten erweitern, die dann jeweils auf Ereignisse in der Spielwelt (oder auch Nutzereingaben) reagieren können, wodurch man eigentlich die Spiellogik ganz gut umsetzen kann.
Jedes dieser Entitys hat jetzt ein Modell-Objekt. Das kann ein 3D Modell mit zugehörigem Material laden und rendern. Im Hintergrund sind dabei ein paar Manager die sich darum kümmern, dass Ressourcen nur einmal geladen werden müssen, aber prinzipiell kann sich jedes Modell eigenständig rendern. Die nötigen Shader werden dazu aus den materialeigenschaften generiert, was hässlich wird, sobald mal 'globale' Effekte, wie Lichtquellen oder Schatten hat. Dafür hat im Moment jedes Material eine Referenz auf ein 'LightSetup' um die entsprechenden Lichtquellen in den Shader berücksichtigen zu können.
Nun, der Ansatz funktioniert und war auch nicht sonderlich komplex umzusetzen, aber es ist langsam. Da jedes Modell eigenständig ist, wird für jedes Entity quasi alles neu gesetzt. Ein paar Test in gDebugger ergaben, dass das ganze die CPU scheinbar echt zum schwitzen bringt. ShadowMapping scheint die Sache weder durch die hohe Füllrate noch durch den komplexen Shader auszubremsen, sondern schlicht deshalb, weil alle Objekte zweimal gezeichnet werden müssen. Viele Statechanges halt.
Ein paar Optimierungen sind natürlich schon drin: Ich habe ein ViewFrustum Culling für die Objekte, die in einem Quadtree (da das Spielfeld sehr flach ist) eingebaut, was die Sache auch deutlich beschleunigt. Dieser Quadtree wird derzeit auch für Kollisionsabfrage benutzt.
Jetzt will ich das ganze dahingehend umbauen, dass ich eine Art Scenengraph (ob es wirklich ein Graph sein wird, weiß ich noch nicht) programmieren will, indem sich die Entitys einfach mit ihrem Modell registrieren und der dann sämtliche Renderarbeiten übernimmt. Das sollte eine ganze Reihe an Optimierungen zulassen und das Rendern deutlich beschleunigen. Jetzt ist meine Frage, wie würde ich das ganze aufbauen?
Man möchte ja vermutlich Objekte mit dem selben Material irgendwie gruppieren um diese dann hintereinander Zeichnen zu können, um StateChanges zu sparen. Wonach sortiert man da so? Spontan würde ich sagen, dass Shader neu setzen aufwändiger ist, als eine Textur zu ändern. Wie geht man das Problem an, dass der Shader nicht nur die Materialeigenschaften sondern auch die Beleuchtungsinformationen (Lichtquellen, Schatten, usw.) berücksichtigen muss? Irgendwie muss man den doch generieren, oder legt man sich da auf 1-5 hardkodierte Shader fest? Wie gesagt, bislang hab ich die Shader im Code generiert, aber das ist ziemlich fummelig zu programmieren, vielleicht gibt es da ja bessere Möglichkeiten.
Und wie sieht es mit verbessertem Culling aus? Ich denke, für Outdoorszenen ist ein Baum schon ganz cool, aber was ist mit Objekten, die genau auf der Grenze liegen (bisher hatte ich die dann einfach in einem höheren Node liegen). Macht es vielleicht sogar Sinn, erst den Baum zu durchlaufen und alle Objekte in eine Temporäre Liste zu speichern, die dann nochmal nach Material oder was auch immer sortiert wird?
Früher oder später hätte ich auch gerne Indoor Szenen. Da sieht man ja meistens nur einen winzigen Teil des Levels, wie cullt man da am besten? Man hört ja von Portalen, wo das Level in mehrere Bereiche aufgeteilt, und man zeichnet nur den Bereich, in dem sich die Kamera befindet und die, die man durch Portale sehen kann. Da man das Frustum auf diese Portale beschneiden kann, wird man so wohl recht effektiv Dinge wegschneiden können. Aber was passiert, wenn man den selben Knoten durch unterschiedliche Portale sehen kann? Wenn 2 Fenster nebeneinander liegen und man nach draußen guckt, wird die komplette Außenwelt dann doppelt gerendert, oder wird gespeichert, welche Bereiche schonmal gerendert wurden (was natürlich das Beschneiden des ViewFrustums schwierig macht).
Hier sollten ja eigentlich einige rumspringen, die sich mit derlei Fragen schon beschäftigt haben, aber vielleicht hat ja auch jemand ein paar ganz gute Artikel zu dem Thema. Ich habe zwar immer mal wieder etwas in die Richtung gelesen, aber Texte von vor 5 Jahren müssen ja heute nicht mehr unbedingt empfehlenswert sein.