Leitfaden performancefreundlicher Fahrzeugbau

Aus OMSIWiki
Wechseln zu:Navigation, Suche

Wie der Name schon sagt, soll dieser Leitfaden Möglichkeiten und Denkanstöße bieten, OMSI-Fahrzeuge möglichst performance-freundlich zu gestalten.

Ein OMSI-Fahrzeug besteht aus 3D-Modellen, Texturen, Sound und Script – und in allen vier Bereichen sollte man mehr oder weniger intensiv auf eine Optimierung hinsichtlich der Performance achten!

Aber eines gilt immer: Auch wenn man nicht mehr so geizen muss wie früher: Verschwenden sollte man keinesfalls! - Also sollte man bei jedem Teil, welches man modelliert, bei jedem Sound und bei jeder Textur ein wenig im Hinterkopf haben, was sinnvoll und nötig ist oder was eventuell mehr ist, als man braucht. Dass das Geschmacks- und Ansichtssache ist, versteht sich natürlich von selbst.

3D-Modelle

Theorie

Zunächst muss der Begriff 'Drawcall' definiert werden:

Zwar wird der Bus (wie alles andere) aus einzelnen Dreiecken gezeichnet, die Dreiecke ihrerseits sind aber zusammengefasst zu Meshs mit gleichen Materialeigenschaften, Texturen und so weiter. Einen solchen zusammengefassten Zeichenvorgang bezeichnet man als Drawcall. Intern wird dieser zunächst von OMSI vorbereitet, dann werden alle Parameter der Grafikkarte übergeben und schließlich arbeitet die Grafikkarte "eigenständig" den Drawcall mit allen zugehörigen Dreiecken ab.

Dieser Zusammenhang erklärt auch, warum ein Drawcall mit 100.000 Vertices wesentlich schneller abgearbeitet wird, als 1000 Drawcalls mit jeweils 100 Vertices.

Nicht zuviele o3d-Dateien und nicht zuviele einzelne Texturen

Jede o3d-Datei verursacht mindestens einen Drawcall pro Textur, die die o3d-Datei verwendet. In bestimmten Fällen und insbesondere auch bei x-Dateien können es sogar mehrere Drawcalls pro Textur und o3d-Datei sein.

Hieraus ergeben sich folgende Regeln:

Verwende so wenig o3d-Dateien wie möglich pro Bus.

Unumgänglich ist eine Separierung bei Animationen und oft bei transparenten Dingen, bei denen über die Separierung die Renderreihenfolge festgelegt wird. Daran kann man nicht rütteln.

Es ist aber ohne weiteres möglich, das Fahrzeug nur aus sehr wenigen nicht-animierten o3d-Dateien zusammen zu setzen, z.B. (Reihenfolge = Renderreihenfolge):

  1. Innenmesh inkl. Fahrerarbeitsplatz ohne Leuchtmelder, Schalter usw.
  2. Transparenzen im Innenraum
  3. Außenmesh
  4. Fensterscheiben von innen
  5. Fensterscheiben von außen

Innerhalb einer o3d-Datei so wenig unterschiedliche Texturen wie möglich.

Mit der obigen Erklärung wohl einfach zu verstehen: Wenn mein komplettes Außenmesh nur eine Textur hat, dann ist es auch nur ein Drawcall. Das ist besser als wenn mein Außenmesh zwei Texturen benötigt, sodass das Außenmesh in zwei Drawcalls gerendert werden muss.

Wird ein Bauteil ohnehin immer animiert (typisches Beispiel: Räder), dann ist es weniger tragisch, wenn deren Textur separat ist. Allerdings sollten auch die LODs berücksichtigt werden: Da unsere Fahrzeuge in größerer Entfernung keine animierten Räder haben, haben wir die Radtextur auch in die Haupttextur integriert.

Verwendung von reduzierter Darstellung für die Entfernung und für KI-Fahrzeuge

Mit Hilfe von LODs ist es möglich, dass das Fahrzeug auf größerer Entfernung einfacher gezeichnet wird. Und auch, wenn der User es nicht selbst verwendet, sollte es einfacher werden.

Jedes Fahrzeug sollte mindestens über zwei LOD-Stufen verfügen, sodass es ab unter etwa 10% Bildschirmgröße zu einer starken Vereinfachung kommt.

Das sehr einfache LOD-Mesh für die größere Entfernung des SD77. Zwar sehr einfach, aber für größere Entfernungen völlig ausreichend und sehr sparsam. Einziges Manko: Aufgrund der Repaint-Möglichkeiten muss trotzdem die große 1024er-Textur für die Außenhaut geladen werden, aber abgesehen von der Reflexionstextur ist das alles!

In dieser Größenordnung kann bereits auf den Innenraum komplett verzichtet werden, die äußere Hülle sollte nur noch ein sehr einfaches Mesh sein (20-50 Vertices). Wir haben außerdem auf Nachttexturen und Lichteffekte in dieser Entfernung komplett verzichtet. Man muss ja nur noch sehen, "dass da ein Bus fährt".

Jedes Fahrzeug sollte als KI-Fahrzeug wesentlich vereinfacht werden. (Obgleich unpräzise, ist mit "KI-Fahrzeug" auch ein "verlassenes" Userfahrzeug gemeint. Sämtliche Flags, wie die folgende, die sich auf diese Nomenklatur beziehen, stufen verlassene Userfahrzeuge als KI-Fahrzeuge ein.)

Hierzu dient der Viewpoint-Befehl. Die darunterstehende Zahl kann folgende Werte annehmen:

  • 0 = immer sichtbar
  • 1 = am aktuellen Userfahrzeug nur von außen sichtbar
  • 2 = am aktuellen Userfahrzeug nur von innen sichtbar
  • 3 = 1 + 2, also am aktuellen Userfahrzeug von innen und außen sichtbar
  • 4 = nur an KI-Fahrzeugen sichtbar.
  • 5 = 4 + 1, also an KI-Fahrzeugen sichtbar oder am Userfahrzeug nur von außen
  • 6 = 2 + 4, also an KI-Fahrzeugen sichtbar oder am Userfahrzeug nur von innen
  • 7 = 1 + 2 + 4, also immer sichtbar, dann kann man auch die 0 nehmen! :)

Zu beachten ist: Ein KI-Fahrzeug sieht man sich nur von außen an, aber meist nicht so genau. Es kann aber sehr dicht stehen und man guckt oft auch mal von hinten rein (an der Ampel oder Haltestelle).

  • KI-Fahrzeuge sollten daher das komplette, detaillierte Außenmesh haben und die groben Dinge des Innenmeshs, wie Verkleidungen, Sitze usw. Hierbei sind vor allem die Dinge wichtig, die man von hinten sieht.
  • KI-Fahrzeuge brauchen nicht über ein animiertes Cockpit verfügen (animierter Sitz, Lenkrad) und Haltestangen kann man auch wesentlich vereinfachen. Theoretisch gilt das auch für den eigenen Bus in der Außenansicht, aber hier muss man nicht so sparsam sein, da der eigene Bus ja nur einmal sichtbar ist - KI-Fahrzeuge dagegen oft in großer Zahl.
  • Sehr viel Potenzial bieten wieder Drawcalls: Typischerweise gibt es bei aller Optimierung mehrere Texturen für den Innenraum, oft aber nur eine für das Außenmesh. Sinnvollerweise also sollte man soviele Texturen des Innenraums wie möglich einsparen. Das statische Dashboard, das Lenkrad, die übriggebliebenen Haltestangen usw. benutzen oft separate Texturen; dies alles sollte im KI-Mesh über einen kleinen Bereich einer weiterhin notwendigen Textur gemappt werden, z.B. eine simple dunkelgraue, leicht "angerauhte" Fläche. Aber Achtung: Es bringt erst dann was, wenn eine oder mehrere komplette Texturen eingespart werden.

Das KI-Modell des SD77 als Beispiel

Das KI-Innenmesh des SD77 benötigt lediglich die Texturen SD77_02.bmp, _03.bmp und _04.bmp. Eingespart werden die Texturen Fahrersitz, Panel, Thermometer und Zahltisch gegenüber dem User-Innenraum:

2012 0716 03 Performance.jpg

Unterschiede KI- und User-Mesh beim SD77. In der Ansicht von Hinten erkennt man die nur geringen Unterschiede, die vor allem Mesh-Vereinfachungen mit größerem Poly-Reduktionspotential besitzen: Haltestangen, Haltewunschknöpfe, Griffe an den Sitzen. Wichtige Details wie die Sitze, Treppenwangen oder auch Leuchten müssen beibehalten werden:

2012 0716 04 Performance.jpg

Nicht übermäßig viele Vertices verwenden - Texturen können auch sinnvolle Dienste leisten

Damals™, als wir im Flusi mit dem Modellbau angefangen haben, mussten wir noch das Mesh per Hand auf Millimeterpapier zeichnen und die Koordinaten selbst ausmessen und maßstäblich umrechnen. Dann haben wir uns an die Lochkarten-Stanzmaschine gesetzt und wehe, wenn wir einen Fehler gemacht haben... ok, genug der angestaubten Geschichten von früher™! :-D (Wobei der Anfang noch stimmt, die Design-Programme für den Flusi kamen erst mit den Versionen 2000/2002 mit FSDS und Gmax so richtig in Schwung...)

Was ich mit der kleinen Geschichte von "damals als wir nix hatten" sagen will: Schon aus Egoismus heraus war man mit Polys und Vertices sparsam. Das bleibt so drinn - während wir heute uns den Freeware-NL202 ansehen und den 3D-Löwen sowie den 3D-Schriftzug bestaunen - und ebenso erstaunt sind, dass das OMSI frisst...

Selbstverständlich ist der NL202 ein gelungenes und natürlich sehr beliebtes OMSI-Addon. Trotzdem bin ich mal so frech und stelle ihn neben unseren in Entwicklung befindlichen NG272:

2012 0716 07 Performance.jpg

Was fällt auf? Der NL202 verfügt über wesentlich mehr Polygone als der NG272. Selbstverständlich lassen sich bestimmte Dinge nur darstellen mit entsprechend detaillierten Modellierungen, z.B. die Schrägstellung der Lüftungsschlitze oder das Profil des Fenstergummileisten. Aber man muss sich beim Fahrzeugbau ständig fragen: Brauch ich das wirklich? Lohnt sich einerseits der Aufwand und andererseits der Preis der höheren Polygonzahl? Natürlich sieht es sehr schick aus, aber wann ist man schonmal da hinten am Motorraum?

Denn wenn man von weitem schaut, dann reicht eine passende fotoreale Textur im Allgemeinen aus. Natürlich glänzen unsere Lüftungsschlitze dafür nicht so schön in der Sonne! :)

Vertices nur da einbauen, wo es auch sinnvoll ist

Noch ein Beispiel zum Thema Vertex-Einsparung:

2012 0716 06 Performance.jpg

Natürlich ist das mittlere Rohr das schönste! :) Aber dieses hat auch die meisten Polygone. Die Frage ist nun - wie genau spart man Polygone ein und wieviel optische Einbuße bringt es?

In diesem Fall kann man entweder in Längsrichtung oder in Umfangrichtung sparen. Was bringt mehr? Wenn man ungefähr gleiche Vertex- und Face-Zahlen erhalten möchte, dann sehen die Rohre wie gezeigt aus. Links wird in Längsrichtung kein einziges Mal mehr gebrochen, dafür hat der Umfang immer noch 32 Kanten. Rechts dagegen hat das Rohr nur 8 Kanten, dafür konnten alle sieben Brüche (entspricht ebenfalls 32 Ecken entlang des Vollkreises!) beibehalten werden. Offensichtlich die bessere Lösung: Denn den Querschnitt nimmt man nur wahr, wo das Profil endet (Auspuff) oder auf eine andersfarbige Fläche trifft bzw. selbst eine andere Farbe annimmt, während man die Längsrichtung oft sehr direkt sieht, wie auch in diesem Beispiel.

Typische Ähnlichkeit hierzu haben Haltestangen, die bei heutigen Bussen oft schwungvoll gestaltet sind: Auch dort sollte der "Schwung" ausreichend rund sein - dafür kann das Profil ruhig etwas eckiger sein (Acht- oder nur Sechseck).

Um also zum Anfang zurück zu kommen: 1995 hatte der FlightShop-Jumbo-Jet für den Flight Simulator 95 einen achteckigen (!!!) Rumpfquerschnitt! Das sollte man immer bedenken, wenn man 32-eckige Haltestangenprofile konstruiert! :D

Texturen

Ein wichtiger Punkt wurde schon genannt:

Wenige große Texturen sind im Allgemeinen besser als viele kleine

..., insbesondere dann, wenn sie zusammen auf einer o3d-Datei zur Anwendung kommen. Natürlich gibt es Grenzen: Einerseits werden die Texturen ab einer bestimmten Auflösung irgendwann etwas "unhandlich", weshalb wir meistens nur bis 1024x1024 gehen, obgleich diese Grenze sicherlich auch nicht mehr ganz zeitgemäß ist.

Regelmäßige Auflösungen verwenden!

Was früher jeder Objektbauer im Flugsimulator wusste, ist heute nicht mehr allgemein bekannt: Texturen sollten Pixel-Maße haben, die der Zwei-Hoch-Reihe folgen:

1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, (2048, 4096, ...)

Die eingeklammerten Werte sind jenseits dessen, was wir üblicherweise nutzen und ehrlich gesagt weiß ich nicht, welche Einschränkungen da eventuell auch heute noch warten. Bis 1024 ist aber keinesfalls ein Problem.

Rechtecke sind auch ok, solange die obige Reihe eingehalten wird. 256x256 war früher im Flusi der ultimative Standard für alle Texturen, deshalb findet man bei "alten Hasen" oft heute noch dieses Maß. Aber 1024x128 ist ebenso ok wie 512x512 oder 4x128. Aber man sollte alle krummen Maße vermeiden (200x300 oder aber auch 256x77), weil die Verarbeitung tendentiell schlechter wird. Inwieweit das auf aktuellen Grafikkarten noch der Fall ist, weiß ich nicht - aber hinsichtlich der Kompatibilität zu älteren Karten kann es nicht schaden, sich an die Regel zu halten.

Insbesondere wird in solchen "krummen" Fällen ohnehin aufgerundet zum nächsten "zulässigen" Maß. Dann kann man auch gleich selbst aufrunden und die übrigen Bereiche noch sinnvoll nutzen anstatt verfallen zu lassen.

Dateiformate

DirectX lädt relativ viele Formate. Das traditionelle Format ist Bitmap (*.bmp). Da dieses Format nicht komprimiert ist, ist mancher geneigt, JPG zu verwenden.

Ob die Ladezeit schneller wird oder langsamer, vermag ich nicht zu sagen und das hängt sicherlich auch vom Aussehen der Textur und von einer Vielzahl anderer Faktoren ab. Allerdings sollte klar sein, dass (soweit ich weiß) es aber nichts bringt, um Grafikspeicher zu sparen, da das JPEG-Format nicht im Grafikkartenspeicher zur Anwendung kommen kann.

Möchte man also Grafikkartenspeicher sparen, dann sollte man eher auf das DDS-Format zurückgreifen, welches eine Komprimierung anbietet, die sich - soweit ich richtig informiert bin - auch in den Grafikkartenspeicher überträgt.

Sound

Beim Sound kann man eigentlich gar nicht so viel falsch machen. Grundsätzlich ist OMSI da recht genügsam und auch dafür ausgelegt, dass Fahrzeuge viiiele Sounds haben, damit man richtig viel Spaß hat und normalerweise sind auch etwas längere Sample von 48kHz kein Thema.

Allerdings hat OMSI aus Performance-Gründen einen Soundzähler und eine Sortierfunktion, welche dafür sorgen, dass eine gewisse Höchstanzahl an Sounds nicht überschritten werden. Dies betrifft aber nicht nur aktuell hörbare sondern auch "theoretisch hörbare" Sounds, die also ständig geprüft werden müssen, ob sie demnächst hörbar werden müssen. Damit nun keine unnötigen freien "Soundplätze" verschwendet werden, gibt es die Möglichkeit, dass KI-Fahrzeuge (und wiederum User-Fahrzeuge, die verlassen wurden) mit einem einfacheren Sound-Set ausgerüstet werden.

Dies ist relativ einfach zu realisieren:

1. Zuerst erstellt man die normale Sound.cfg wie gewohnt und stellt sie komplett fertig.

2. Nun kopiert man sie und benennt sie um, praktischerweise z.B. in Sound_AI.cfg.

3. Nun löscht man alle Sound-Einträge in dieser Datei, die bei Fahrzeugen, die nicht der User selbt in der Hand hat, unnötig sind - als da wären:

  • Anlasser und Abschaltsound
  • Soundeffekte, die vor allem innen hörbar sind, wie z.B. der Luftpresser oder das Klappern im Stand beim SD
  • Cockpit- und ähnliche Systemsounds wie Schalter, Warnungen, IBIS, Rollband, Scheibenwischer usw. usw.
  • Beibehalten haben wir aber Motor, Achsplaneten, Getriebesounds, die man von außen hört, Retarder, Türen, Standheizung und Bremsenquietschen.

4. Zuletzt muss man noch die *.bus/*.ovh-Datei um folgenden Eintrag ergänzen: [sound_ai] | sound\Sound_AI.cfg, sodass OMSI nun weiß, dass hier ein KI-Soundset vorliegt.

Man stellt schnell fest, wieviel man hier einsparen kann und wieviel mehr Fahrzeuge dann noch in der Umgebung "klingen" im Gegensatz zu vorher.

Script

Allgemein

Ähnlich wie beim Sound gibt es die Möglichkeit, ein vereinfachtes Script ablaufen zu lassen. Wie das geht, wird im Artikel Scriptsystem beschrieben.

Damit man allerdings jederzeit die Möglichkeit hat, das Fahrzeug zu übernehmen, werden stets alle normalen Variablen vorgehalten. Dies ist allerdings normalerweise kein größeres Problem.

Abgesehen davon ist OMSI auch hinsichtlich des Scripts genügsam bzw. gibt es kein größeres Optimierungspotential. Ganz allgemein kann man dem Script-Entwickler nur ganz allgemein auf den Weg geben: Sie wenig wie möglich, so viel wie nötig. Geizen muss man nicht, Verschwenden sollte man nicht und wenn man sich zwischen zwei Varianten entscheiden kann, sollte man versuchen, die mit besserer Leistung zu nehmen.

Wenn übrigens mehrfach genutzte Script-Sequenzen in ein gemeinsames Macro ausgelagert werden, dann spart das Ladezeit und Speicher. Zwar vermutlich nur mikroskopisch wenig, aber immerhin! ;-)

Spezielle Performance-Probleme

Was man beim Script aber beachten sollte, sind eventuelle Engpässe. Das Script selbst ist relativ unkritisch. Die System-Makros allerdings erfordern bisweilen einige Verarbeitung durch OMSI, z.B. wenn irgendwelche Hofdateien durchsucht werden. Es ist hier also sinnvoll, möglichst sparsam mit System-Makros umzugehen.

Außerdem erlaubt die Variable System- und vordefinierte lokalen Variablen ein gezieltes Aktualisieren von OMSI-Fonts am Fahrzeug (oder Szenerieobjekt): Dies sollte auch nur dann geschehen, wenn sich auch wirklich etwas geändert hat. Beim eigenen Fahrzeug ist das noch unkritisch, aber KI-Fahrzeuge sollten ihre Strings nur dann aktualisieren, wenn sich wirklich etwas ändert - z.B. die Matrix-Anzeige.