Grenzen beim 32-Bit-Rendering - und Auswege
4. Januar 2002 / von aths / Seite 1 von 2
Die Probleme
In diesem älteren Artikel wurden die Vorteile einer erhöhten Rechengenauigkeit beim Rendern von der theoretischen Seite betrachtet. Hier geht es jetzt um die Praxis. Wieviel Bits braucht man überhaupt für die neuen Features, die mit DX8 und DX9 Einzug halten werden? Welche Lösungen wären denkbar, und welche sind praktikabel?
Es gibt zwei wichtige Gründe, die eine erhöhte Rechengenauigkeit fordern: Ermöglichung von Overbright Lighting und Vermeidung von Color Banding. Doch der Reihe nach! Beim 32-Bit-Rendering werden 4 Kanäle mit je 8 Bit Auflösung verarbeitet. Das sind die drei Grundfarben der additiven Farbmischung, also Rot, Grün und Blau. Sowie der Alpha-Kanal, um halbtransparente Texturen zu erlauben. Diese 8 Bit stellen Intensitäten von 0.0 bis 1.0 dar. Also von 0 bis 100%. Das klingt ja eigentlich ausreichend. Denn der Monitor kann nicht heller als Weiß darstellen.
Das ist richtig, und aus diesem Grunde reichen für einfache Szenarien auch Intensitäten bis 1.0 aus. Es kann nun sein, dass beim Rendern eine additive Blend-Operation vorgenommen wird. Dabei werden die Intensitäten zweier Texel pro Kanal addiert. Angenommen, man hat Texel A mit der Farbe "Helles Orange", was sich als RGB-Vektor z.B. durch (1.0 0.75 0.5) darstellen lässt. Darauf wird Texel B addiert, der lila ist, was (1.0 0 1.0) entspricht. Das richtige Ergebnis wäre natürlich (2.0 0.75 1.5). Auf dem Bildschirm kann aber sowieso höchstens die Farbe (1.0 0.75 1.0) dargestellt werden:
|
Oft bekommen Texturen Lightmaps. Das läuft auf eine multiplikative Blending-Operation hinaus. In diesem Beispiel ist die Lightmap sehr dunkel. Es wird mit 0.25 multipliziert. Das Ergebnis wäre (2.0 0.75 1.5) x 0.25 = (0.5 0.1875 0.375):
|
Das kann beim 32-Bit-Rendering leider nicht gemacht werden. Kein Zwischenergebnis kann größer als 1.0 werden. Es wird also das bereits beschnittene Ergebnis mit 0.25 multipliziert. Man erhält als Ausgabe (0.25 0.1875 0.25).
|
Und das ist falsch! 32-Bit-Rendering kann bei bestimmten Blend-Operationen zu Fehlern führen. Das äußert sich in zu dunklen Farben. Hier setzt nun Overbright Lighing an: Man rechnet mit mehr Bits, die den Farbraum vergrößern. Mit 9 Bit ließen sich schon Intensitäten bis 2.0 darstellen. Ein Pixel Shader Version 2.0, der mit dem kommenden DirectX9 Standard werden wird, verlangt sogar einen Farbraum bis zu 8.0. Damit die Programmierer in ihrer Effekt-Gestaltung möglichst frei sind, ist das auch nicht übertrieben. Man benötigt dafür drei zusätzlichen Bits. Da es auch subtraktives Blending gibt, müsste man sogar noch ein Vorzeichen-Bit einfügen, was schon 4 zusätzliche Bits bedeutet.
Auch beim Alpha-Blending kann der erweiterte Darstellungraum Sinn machen. 8 + 4 = 12 Bit, pro Kanal gerechnet.
Reichen also 12 Bit pro Kanal aus? Leider nein. Denn das obige Zwischenergebnis von 0.1875 bereitet Probleme. Denn diese Zahl darzustellen, ist mit dem heutigen System ist gar nicht exakt möglich! Das Bitmuster für "47" wäre etwas kleiner als 0.1875, "48" schon etwas größer. Das Ergebnis, was die Hardware zurück liefert, kann deshalb nur ein Rundungswert sein. Dieser Fehler mag verhältnismäßig klein erscheinen. Nun muß DX8 compliant Hardware bis zu 3 Texturen pro Render-Pass (Durchlauf) auftragen können. Der Rundungsfehler erscheint auch in dieser Summe noch recht klein. Dabei ist aber bitte zu berücksichtigen, dass schon bei der bi- oder trilineare Filterung Rundungsfehler ins Spiel kommen. Die Fehler durch Blend-Operationen gehen hier noch obendrauf.
Nun mag man einwenden wollen, dass ein Filter-Ergebnis nicht genauer sein kann, als die vorgegebenen Werte. 32-Bit-Texturen bedeutet, einen Farbraum von 24 Bit zu haben. Beim Filtern die Farbe genauer zu bestimmen, als mit 24 Bit möglich, erscheint auf den ersten Blick wie Kaffeesatzleserei. Das wird jedoch widerlegt, wenn man sich den DXTC1-Algorithmus anguckt, der bei Textur-Kompression zum Einsatz kommt. Was dabei komprimiert wird, ist die Farbauflösung.
Dort sind einige Referenz-Farben mit 16 Bit Genauigkeit gespeichert. Zusätzliche Farben werden interpoliert. Ein auf 24 Bit interpoliertes Ergebnis liefert eine brauchbare Qualität. Ein auf nur 16 Bit interpoliertes Ergebnis liefert den bekannten schlechten Himmel in Quake3, wenn eine GeForce-Karte genutzt wird. Kurz, es ist sinnvoll, eine Interpolation so genau wie möglich durchzuführen. Textur-Filterung ist nichts anderes, als eine mehr oder weniger aufwändige Interpolation. Damit gelten die prinzipiellen Nachteile einer nicht erhöhten Rechengenauigkeit auch für gefilterte Texel.
Kommen sehr viele Texturen zum Einsatz oder wird gar der Pixel Shader benutzt, können diese Rechenfehler Einfluss auf die sichtbare Qualität haben. Jede Textur wird schlechtestenfalls bilinear gefiltert verwendet, und schon muss gerunden werden. Pro Blending-Operation ist mit einem zusätzlichen Fehler von einem halben Bit zu rechnen. Wie kommt nun das angesprochene Color Banding zustande?
Halbtransparent Texturen werden mittels Alpha Blending gerendert. Dazu wird der Hintergrundpixel nicht übermalt, sondern er geht mit einer gewissen Stärke in das neue Pixel mit ein.
Um Color Banding bei 16-Bit-Grafik zu reduzieren, wird das Ergebnis einer Blend-Operation oft gerastert und erst dann in den Backbuffer geschrieben. Würde einfach immer nur gerundet, reduziert das ja die Anzahl der sichtbaren Farben. Beim Raster kommen jeweils zwei Farben abwechselnd vor, womit die Farb-Reduzierung weniger schlimm ausfällt. Dabei wäre es günstig, mittels Error-Diffusion-Algorithmen zu arbeiten. Ansonsten schaukeln sich Artefakte wegen wiederholter Rasterung sehr schnell auf. Mit Error-Diffusion können die Artefakte besser, aber nicht beliebig lange kompensiert werden. Da bei der Darstellung von Rauch durchaus 10 oder mehr halbtransparente Texturen sinnvoll sind, wird eine zu geringe Farbauflösung immer zu nervenden Artefakten führen.
Um einen Pixel-Brei zu verhindern, kann man deshalb in der Regel auch einstellen, beim Alpha Blending auf 16-Bit-Rasterung zu verzichten. So dass einfach nur eine Rundung stattfindet. Das Ergebnis ist aber auch nicht gerade eine Augenweide: Statt sanfter Farbübergänge sind die übrig gebliebenen Farben nun deutlich zu unterscheiden. Das nennt man Color Banding.
32-Bit-Rendering rastert grundsätzlich nicht. Es wird also immer gerundet. Das bedeutet natürlich ebenfalls, dass die Anzahl der Farben, die im Spiel sind, durch wiederholtes Alpha Blending reduziert wird. Aus diesem Grunde wird Color Banding früher oder später auch hier auftreten. Deshalb lohnt es sich, den Farbraum feiner aufzulösen.
Der Rundungsfehler beim Filtern fällt unterschiedlich stark aus und beträgt maximal ein halbes Bit. Da mal in die eine, mal in die andere Richtung gerundet wird, fällt das merklich weniger ins Gewicht als der Verlust von Zwischenfarben beim Alpha Blending.
Mit 4 zusätzlichen Bits würde der durchschnittliche Fehler auf ein Sechszehntel seines heutigen Wertes gedrückt. Das klingt passabel. Damit wäre man bei 12 + 4 = 16 Bit angelangt. Da das für R, G, B und A gilt, läuft das auf 16 x 4 = 64-Bit-Rendering hinaus. Und das sagt man so leicht.
Denn "echtes" 64-Bit-Rendering ist heute gar nicht möglich. Oder doch?