Was ist Multisampling? Angenommen, wir haben plötzlich keine Speicherprobleme mehr, und können einfach unsere Szene auf einen viermal so großen Back-Buffer rendern (also 3840 × 2160 statt 1920 × 1080). Man nimmt jetzt „einfach“ (wie wir gleich sehen, doch nicht so einfach) vier Farbwerte, und rechnet die
irgendwie zu einem Farbwert runter. Und zwar eigentlich so, dass wenn uns aus einem Pixel
\($2 \frac{W}{\mathrm m^2}$\), aus den anderen Pixeln
\($4 \frac{W}{\mathrm m^2}$\),
\($6 \frac{W}{\mathrm m^2}$\) und
\($8 \frac{W}{\mathrm m^2}$\) entgegenstrahlen, wir einen Pixel mit
\($5 \frac{W}{\mathrm m^2}$\) herauskriegen. Klingelt etwas? Richtig, es ist physikalisch
linear.
Machen wir uns doch mal klar, was wir haben:
- Wir haben Texturen und Farben
von unseren Artists (als ob). Wir nehmen einfach mal an, sie haben kein Farbprofil dabei, sondern es sind einfach nur Zahlentripel \($(x_1, x_2, x_3)^{\mathrm T}$\). Wie nehmen by default an, dass die Zahlentripel aus dem sRGB-Farbraum stammen.
- Der tolle Master-Farbraum CIEXYZ ist physikalisch linear. In ihm kann man mit handelsüblicher Arithmetik rechnen und damit auch lineare Interpolationen durchführen.
- Wir wollen also unsere sRGB-Zahlentripel der sRGB→CIEXYZ-Transformation unterziehen,
- dann mit den CIEXYZ-Zahlentripel lustig rumrechnen,
- dann sie wieder der CIEXYZ→sRGB-Transformation unterziehen.
Wie macht man die sRGB→CIEXYZ-Transformation?
Wikipedia liefert:
\($$\operatorname{t}_{\mathrm{sRGB}\rightarrow\mathrm{CIEXYZ}}\begin{pmatrix}x_1 \\ x_2 \\ x_3\end{pmatrix} = \begin{pmatrix}0.4124&0.3576&0.1805\\0.2126&0.7152&0.0722\\0.0193&0.1192&0.9505\end{pmatrix}\begin{pmatrix}\operatorname{h}(x_1)\\ \operatorname{h}(x_2)\\ \operatorname{h}(x_3)\end{pmatrix}$$\)mit irgendeiner Hilfsfunktion
\($$\operatorname{h}(x)=\begin{cases}\frac{x}{12.92}, & x\le0.04045\\\left(\frac{x+0.055}{1.055}\right)^{2.4}, & x>0.04045\end{cases}$$\)Der letzte Teil mit der Potenz kommt doch einem irgendwie bekannt vor:
Sieht irgendwie wie eine Gamma-Korrektur aus. Richtig, man kann obige Hilfsfunktion mit
\($$\operatorname{h}(x) \approx x^{2.2}$$\)approximieren, aber es ist nur eine sehr,
sehr krude Approximation (gerade bei
\($x\approx0$\) geht's in die Hose). Die inverse Funktion
\($\operatorname{t}_{\mathrm{CIEXYZ}\rightarrow\mathrm{sRGB}}$\) sei dem geneigten Leser als Übungsaufgabe nicht empfohlen.
Aber zu diesen Zeitpunkt höre ich schon das Fluchen: Was soll der Mist, eine Matrixtransformation nur für so einen einzelnen popeligen Farbwert, nö keine Lust dadrauf, zu ineffizient, hau doch ab, etc. pp. Ja richtig, wir überlegen mal gerade: Wir brauchen nur eine Transformation in
irgendeinen linearen Farbraum; warum gerade in den CIEXYZ ist vollkommen unklar. Und das stimmt auch: Wir können einfach die Matrix oben weglassen. Warum? Weil die Matrixmultiplikation einfach nur eine lineare Transformation ist; d.h. wenn wir mit
\($\operatorname{h}$\) transformiert haben, sind wir bereits in irgendeinem linearen Farbraum (aber noch nicht in dem CIEXYZ-Farbraum).
Also sagen wir einfach:
\($$\operatorname{t}_{\mathrm{sRGB}\rightarrow\mathrm{Linear}}\begin{pmatrix}x_1 \\ x_2 \\ x_3\end{pmatrix} = \begin{pmatrix}\operatorname{h}(x_1)\\ \operatorname{h}(x_2)\\ \operatorname{h}(x_3)\end{pmatrix}$$\) und fertig ist die Laube.
Ich kann das auch noch einmal praktischer erklären. Nehmen wir einfach das Beispiel vom Anfang. Sei
\($A$\) der fette Buffer mit 3840 × 2160 Auflösung, und
\($B$\) der normale Buffer in 1920 × 1080 Auflösung. Wir können die Buffer einfach via
\($B(x, y)$\) adressieren; wir sagen einfach,
\($A(x, y)_1, ..., A(x, y)_4$\) seien die vier Pixel vom feinen Buffer, die unter der Grundfläche des Pixels
\($(x, y)$\) vom groben Buffer liegen.
Nach obiger Betrachtung kriegen wir also die Pixel korrekt in den Buffer
\($B$\), wenn wir folgendes machen:
\($$B(x, y) = \operatorname{t}_{\mathrm{Linear}\rightarrow\mathrm{sRGB}}\Big(\frac{1}{4}\sum_{i = 1}^4 \operatorname{t}_{\mathrm{sRGB}\rightarrow\mathrm{Linear}}\big(A(x, y)_i\big)\Big)$$\)Die Summenbildung und die Division durch 4 nennt man übrigens einen Downsample-Filter. Der sagt einem, was man mit den vier Pixeln aus dem feinen Buffer machen soll, um einen einzigen Pixel zu erhalten. Es sind andere Downsample-Filter denkbar. Benutzt aber niemand.
Wenn jemand das implementieren will: Exakte Werte gibt es
hier. Eine
falsche Implementierung in HLSL findet man
hier; bitte lest zuerst den ersten Link, um zu sehen, warum der zweite falsch ist. Schrott überall wo man hinschaut, aber das ist man ja gewohnt. :evil:
Korrekt wäre:
Code: Alles auswählen
float3 color;
color = (color <= 0.0404482362771082) ? color / 12.92
: pow((color + 0.055) / 1.055, 2.4)
Und schließlich: Wenn man sRGB-Texturen aus den neueren Direct3D-Versionen benutzt, macht man einfach nur:
\($$B(x, y) = \frac{1}{4}\sum_{i = 1}^4 A(x, y)_i$$\)Das sollte im großen und ganzen alles gewesen sein. Noch die schnelle Antwort:
CodingCat hat geschrieben:Verschwimmen die Pixel eines Bildschirms, von dem wir uns entfernen, nicht linear?
Genau das tun sie: Sie verschimmen linear. Damit muss man im linearen Farbraum (korrekter: in
einem linearen Farbraum) seine Berechnungen anstellen. Aber Multisampling auflösen kommt
immer zum Schluss. Immer dran denken: Mal rendert einfach nur ein übergroßes Bild (3840 × 2160) das man dann am Ende herunterrechnen will (1920 × 1080). ;)
Random Katzen-Bild damit dieser Beitrag überhaupt gelesen wird: