So funktioniert Alpha Compositing

Bild

Transparenz scheint kein interessantes Thema zu sein. Das GIF-Format, mit dem einige Pixel durch den Hintergrund scheinen konnten, wurde vor über 30 Jahren veröffentlicht. Nahezu jede in den letzten zwei Jahrzehnten veröffentlichte Grafikdesign-Anwendung unterstützt die Erstellung durchscheinender Inhalte. Diese Konzepte sind längst nichts Neues mehr.

In meinem Artikel möchte ich zeigen, dass Transparenz in digitalen Bildern tatsächlich viel interessanter ist, als es scheint - in dem, was wir für selbstverständlich halten, gibt es eine unsichtbare Tiefe und Schönheit.

Deckkraft


Wenn Sie jemals durch eine rosa Brille geschaut haben, können Sie etwas Ähnliches wie in der folgenden Abbildung sehen. [Im Originalartikel sind viele Bilder interaktiv.] Versuchen Sie, die Brille zu bewegen, um zu sehen, wie sie sich auf das auswirkt, was durch sie sichtbar ist:


Solche Brillen funktionieren wie folgt: Sie vermissen viel Rot, eine ordentliche Menge Blau und sehr wenig Grün. Die Mathematik dieser Punkte kann in drei Gleichungen geschrieben werden. Der Buchstabe R gibt das Ergebnis der Operation an, und der Buchstabe D beschreibt den Punkt, den wir betrachten. RGB-Indizes geben rote, grüne und blaue Komponenten an:

R R = D R × 1.0
R G = D G × 0.7
R B = D B × 0.9

Dieses Buntglas überträgt rote, grüne und blaue Komponenten des Hintergrunds mit unterschiedlichen Stärken. Mit anderen Worten, die Transparenz einer rosa Brille hängt von der Farbe des einfallenden Lichts ab. Im Allgemeinen kann die Transparenz in Abhängigkeit von der Wellenlänge des Lichts variieren. In diesem vereinfachten Beispiel interessiert uns jedoch nur, wie sich Brillen auf die klassischen RGB-Komponenten auswirken.

Die Simulation des Verhaltens gewöhnlicher Sonnenbrillen ist viel einfacher. Sie dämpfen das einfallende Licht normalerweise nur um einen gewissen Betrag:


Diese Gläser lassen nur 30% des Lichts durch. Ihr Verhalten kann durch die folgenden Gleichungen beschrieben werden:

R R = D R × 0.3
R G = D G × 0.3
R B = D B × 0.3

Alle drei Farbkomponenten werden um den gleichen Wert reduziert - die Absorption des einfallenden Lichts ist gleich. Wir können sagen, dass dunkle Brillen zu 30% transparent (undurchsichtig) oder zu 70% undurchsichtig sind. Die Deckkraft eines Objekts bestimmt, wie viel Farbe es blockiert. In der Computergrafik handelt es sich normalerweise um ein vereinfachtes Modell, bei dem nur ein Wert zur Beschreibung dieser Eigenschaft benötigt wird. Die Opazität kann räumlich variieren. wie zum Beispiel eine Rauchsäule, die höher und transparenter wird.

In der realen Welt sind Objekte mit einer Deckkraft von 100% einfach undurchsichtig und lassen überhaupt kein Licht durch. Die Welt der digitalen Bilder ist etwas anders. Es gibt Grenzfälle, in denen selbst feste undurchsichtige Objekte eine bestimmte Lichtmenge passieren.

Abdeckung


Vektorgrafiken befassen sich mit klaren und unendlich genauen Beschreibungen von Formen, die mithilfe von Punkten, Liniensegmenten, Bezier-Kurven und anderen mathematischen Grundelementen definiert wurden. Wenn Sie Zahlen auf einem Computerbildschirm anzeigen müssen, müssen diese einwandfreien Entitäten in eine Bitmap gerastert werden:


Rasterisierung einer Vektorform zu einer Bitmap

Die primitivste Methode zum Rastern besteht darin, zu überprüfen, wo sich die Pixelprobe befindet - innerhalb oder außerhalb der Vektorform. In den folgenden Beispielen können Sie das Dreieck ziehen. In einer vergrößerten Ansicht sind die Bewegungen genauer. Ein blauer Umriss zeigt die ursprüngliche Vektorgeometrie an. Wie Sie sehen können, sieht die Leiter an den Rändern des Dreiecks hässlich aus und flackert beim Verschieben der Geometrie stark:


Der Nachteil dieses Ansatzes besteht darin, dass wir nur eine Prüfung für jedes angezeigte Pixel durchführen und die Ergebnisse auf einen von zwei möglichen Werten diskretisiert werden - innen oder außen.

Sie können die Vektorgeometrie mehrmals pro Pixel abtasten, um eine große Abstufung der Schritte zu erhalten und zu entscheiden, dass einige Pixel nur teilweise geschlossen sind. Eine mögliche Lösung besteht darin, vier Abtastpunkte zu verwenden, um fünf Abdeckungsgrade darzustellen: 0, 14 , 24 , 34 und 1:


Die Qualität der Kanten des Dreiecks hat sich verbessert, aber nur fünf mögliche Abdeckungsstufen reichen oft nicht aus, und wir können leicht ein viel besseres Ergebnis erzielen. Obwohl die Ansicht eines Pixels als kleines Quadrat in der Welt der Signalverarbeitung mit Missbilligung betrachtet wird , ist es in einigen Zusammenhängen ein nützliches Modell, mit dem wir die genaue Abdeckung eines Pixels anhand der Vektorgeometrie berechnen können. Der Schnittpunkt einer Linie und eines Quadrats kann immer in ein Trapez und ein Rechteck zerlegt werden:

Bild

Ein Liniensegment teilt ein Quadrat in ein Trapez und ein Rechteck

Sie können die Fläche beider Teile leicht berechnen, und ihre Summe geteilt durch die Fläche des Quadrats bestimmt den Prozentsatz der Pixelabdeckung. Somit wird die Abdeckung als exakte Zahl mit beliebiger Genauigkeit berechnet. In der unten gezeigten Demo wird diese Methode verwendet, um viel bessere Kanten zu rendern, die auch beim Ziehen eines Dreiecks glatt bleiben:


Wenn es um komplexere Formen geht, z. B. Ellipsen oder Beziers , werden diese häufig in einfache gerade Liniensegmente unterteilt, mit denen Sie die Abdeckung mit der richtigen Genauigkeit berechnen können.

Das Konzept der teilweisen Abdeckung ist entscheidend für das qualitativ hochwertige Rendern von Vektorgrafiken und vor allem für das Rendern von Text. Wenn Sie einen Screenshot dieses Artikels machen und ihn sorgfältig prüfen, werden Sie feststellen, dass fast alle Kanten der Glyphen Pixel nur teilweise abdecken:


Teilabdeckung wird beim Rendern von Text aktiv verwendet

Wenn Sie die Deckkraft des Objekts haben und es mit einzelnen Pixeln bedecken, können Sie diese zu einem Wert kombinieren.

Alpha


Das Produkt aus der Opazität eines Objekts und seiner Pixelabdeckung heißt Alpha :

= ×

Ein Objekt mit einer Deckkraft von 60%, das 30% der Pixelfläche abdeckt, hat in diesem Pixel einen Alpha-Wert von 18%. Wenn das Objekt transparent ist oder das Pixel nicht vollständig bedeckt, ist der Alpha-Wert in diesem Pixel natürlich 0. Nach der Multiplikation verschwinden die Unterschiede zwischen Opazität und Beschichtung, was in gewissem Sinne die Tatsache rechtfertigt, dass die Konzepte "Alpha" und "Opazität" als Synonyme verwendet werden.

Alpha wird oft als vierter Kanal eines Bitmap-Bildes dargestellt. Die üblichen Werte für Rot, Grün und Blau werden durch einen Alpha-Wert ergänzt, der vier RGBA-Werte bildet.

Wenn es darum geht, Alpha-Werte im Speicher zu speichern, besteht die Versuchung, nur wenige Bits dafür zu verwenden. Im Fall der Abdeckung der Pixel der Kanten von undurchsichtigen Objekten scheinen 4 oder sogar 3 Bits je nach Pixeldichte des Bildschirms völlig ausreichend zu sein:


Die Deckkraft wirkt sich jedoch auch auf den Alpha-Wert aus, sodass eine geringe Bittiefe in einigen Fällen katastrophal sein kann, wenn sich die Transparenz reibungslos ändert. Das Bild unten zeigt einen Farbverlauf von undurchsichtigem Schwarz nach Weiß, der zeigt, dass eine geringe Bittiefe zu sehr starken Farbabweichungen führt:


Je mehr Bits, desto besser und am häufigsten verwendet Alpha 8 eine Bittiefe von 8, um der Genauigkeit der Farbkomponenten zu entsprechen, weshalb viele RGBA-Puffer 32 Bit pro Pixel belegen. Es ist auch erwähnenswert, dass im Gegensatz zu Farbkomponenten, die häufig mit nichtlinearer Transformation codiert werden, Alpha linear gespeichert wird - der codierte Wert von 0,5 entspricht einem Alpha-Wert von 0,5.

In Bezug auf Alpha haben wir alle anderen Farbkomponenten vollständig ignoriert, aber zusätzlich zum Blockieren der Hintergrundfarbe kann das Pixel selbst ein wenig Farbe hinzufügen. Die Idee ist ganz einfach: Ein durchscheinendes rosa Objekt blockiert einen Teil der einfallenden Hintergrundbeleuchtung und sendet oder reflektiert ein wenig rosa Licht:


Beachten Sie, dass es sich nicht wie Buntglas verhält. Glas blockiert einfach einen Teil der Hintergrundbeleuchtung mit unterschiedlicher Helligkeit. Wenn Sie ein vollständig schwarzes Objekt durch rosa Glas betrachten, bleibt seine Schwärze erhalten, da das schwarze Objekt kein Licht emittiert und reflektiert. Das durchscheinende rosa Objekt fügt jedoch sein eigenes Licht hinzu. Wenn Sie es auf ein schwarzes Objekt legen, ist das Ergebnis rosa. Ein gutes Analogon zu diesem Verhalten ist feines Material, das in der Luft schwebt, wie Dunst, Rauch, Nebel oder etwas farbiges Pulver.

Das Rendern eines Alphakanals ist etwas schwieriger - ein perfekt transparentes Objekt ist per Definition unsichtbar. Um zwischen Objekten zu unterscheiden, müssen zwei Tricks angewendet werden. Ein Schachbretthintergrund zeigt an, welche Teile des Bildes transparent sind. Dieses Muster wird in vielen grafischen Anwendungen verwendet:


Schachmuster zeigt transparente Figuren.

Die vier kleinen Quadrate unter dem Bild zeigen an, dass wir die roten, grünen, blauen und Alpha-Komponenten des Bildes sehen. In einigen Fällen ist es nützlich, die Alphakanalwerte direkt anzuzeigen, und die einfachste Möglichkeit, sie anzuzeigen, ist die Verwendung von Graustufen:


Zeigen Sie RGB- und A-Werte auf verschiedenen Oberflächen an

Je heller der Grauton ist, desto höher ist der Alpha-Wert, dh reines Schwarz entspricht 0% Alpha und reines Weiß 100% Alpha. Kleine Quadrate zeigen an, dass die RGB- und A-Komponenten des Bildes in zwei Teile unterteilt sind.

Die Alpha-Komponente selbst ist nicht besonders nützlich, aber sie wird sehr wichtig, wenn wir über Compositing sprechen.

Einfaches Compositing


In einem Arbeitsgang können nur sehr wenige Effekte des 2D-Renderings implementiert werden. Um ein fertiges Ergebnis zu erzielen, verwenden wir einen Compositing- Prozess, bei dem verschiedene Bilder kombiniert werden. Zum Beispiel kann eine einfache Schaltfläche "Abbrechen" erstellt werden, indem fünf separate Elemente zusammengesetzt werden:


Zusammensetzen von Elementen für die Schaltfläche Abbrechen

Das Zusammensetzen erfolgt häufig in mehreren Schritten, in denen jeweils zwei Bilder kombiniert werden. Das beim Zusammensetzen verwendete Vordergrundbild wird üblicherweise als Quelle bezeichnet . Das beim Compositing verwendete Hintergrundbild, dem die Quelle überlagert ist, wird normalerweise als Ziel bezeichnet .

Wir beginnen mit dem Compositing auf einem undurchsichtigen Hintergrund, da dies ein sehr häufiger Fall ist. Alles, was Sie auf dem Bildschirm sehen, wird letztendlich durch Compositing an einem undurchsichtigen Ziel überlagert.

Wenn der Alpha-Wert der Quelle 100% beträgt, ist die Quelle undurchsichtig und sollte das Ziel vollständig abdecken. Wenn der Alpha-Wert 0% beträgt, ist die Quelle vollständig transparent und hat keinerlei Einfluss auf das Ziel. Ein Alpha-Wert von 25% ermöglicht es dem Objekt, 25% seines Lichts zu emittieren und 75% des Lichts vom Hintergrund zu leiten, und so weiter:


Zusammenstellen einer lila Quelle mit unterschiedlichen Alpha-Werten zum gelben Ziel

Sie können bereits verstehen, was alles passieren wird - ein einfacher Fall von Alpha-Compositing auf einem undurchsichtigen Hintergrund - es ist nur eine lineare Interpolation zwischen den Ziel- und Quellfarben. In der folgenden Grafik steuert der Schieberegler den Alpha-Wert der Quelle, und in den roten, grünen und blauen Grafiken werden die Werte der RGB-Komponenten angezeigt. Das Ergebnis von R ist nur eine Mischung zwischen Quelle S und Ziel D :


Was hier passiert, kann durch die unten gezeigten Gleichungen beschrieben werden. Wie zuvor bezeichnet der Index die Komponente, dh SA ist der Alpha-Wert in der Quelle und D G ist der grüne Wert im Ziel:

R R = S R × S A + D R × (1 − S A )

R G = S G × S A + D G × (1 − S A )

R B = S B × S A + D B × (1 − S A )

Die Gleichungen für die roten, grünen und blauen Komponenten sehen gleich aus. Sie können also einfach den RGB- Index verwenden und sie in einer Zeile kombinieren:

R RGB = S RGB × S A + D RGB × (1 − S A )

Da das Ziel undurchsichtig ist und bereits das gesamte Hintergrundlicht blockiert, wissen wir außerdem, dass der Alpha-Wert des Ergebnisses immer 1 ist:

R A = 1

Das Zusammensetzen auf einem undurchsichtigen Hintergrund ist einfach, aber in seinen Fähigkeiten ziemlich begrenzt. In vielen Fällen ist eine zuverlässigere Lösung erforderlich.

Zwischenpuffer


Das folgende Bild zeigt den zweistufigen Prozess des Zusammensetzens von drei verschiedenen Schichten mit den Bezeichnungen A, B und C. Das Symbol ⇨ bedeutet "durch Zusammensetzen überlagert":


Das Ergebnis einer zweistufigen Zusammenstellung von drei Schichten

Zuerst überlagern wir B mit C, indem wir komponieren, und überlagern dann A mit ihnen, um das fertige Bild zu erhalten. Im folgenden Beispiel werden wir die Dinge etwas anders machen. Zuerst verbinden wir die beiden obersten Ebenen durch Zusammensetzen und überlagern dann das Ergebnis mit dem letzten Ziel:


Das Ergebnis einer zweistufigen Zusammenstellung von drei Schichten in unterschiedlicher Reihenfolge

Sie fragen sich wahrscheinlich, ob eine solche Situation in der Praxis auftritt, aber tatsächlich ist sie sehr häufig. Viele nicht triviale Compositing-Operationen und Rendering-Effekte, wie z. B. Maskieren und Verwischen, erfordern das Durchlaufen eines Zwischenpuffers, der nur teilweise Compositing-Ergebnisse enthält. Dieses Konzept hat unterschiedliche Namen: Offscreen-Durchgänge, Transparenzebenen oder Seitenpuffer, aber normalerweise basieren sie auf derselben Idee.

Was für uns wichtiger ist, ist, dass fast jedes Bild mit Transparenz als Teilergebnis eines Renderings wahrgenommen werden kann, das später durch Zusammensetzen des letzten Ziels überlagert wird:


Teilweise Zusammenstellung einer Schaltfläche in einer Zwischenablage

Wir müssen verstehen, wie das Zusammensetzen der durchscheinenden Bilder A und B durch ein Bild (A⇨B) mit derselben Farbe und Deckkraft ersetzt werden kann. Beginnen wir mit der Berechnung des Alpha-Werts des endgültigen Puffers.

Alpha-Werte kombinieren


Es ist Ihnen vielleicht nicht klar, wie Sie die Deckkraft zweier Objekte kombinieren sollen, aber es ist einfacher, über diese Aufgabe zu sprechen, wenn wir stattdessen über Transparenz sprechen.

Angenommen, eine bestimmte Lichtmenge tritt durch das erste Objekt und dann durch das zweite Objekt. Wenn die Transparenz des ersten Objekts 80% beträgt, werden 80% des einfallenden Lichts durchgelassen. In ähnlicher Weise lässt ein zweites Objekt mit 60% Transparenz 60% des Lichts durch, was 60% × 80% = 48% des ursprünglichen Lichts ergibt. Sie können im Originalartikel mit Transparenz experimentieren. Vergessen Sie nicht, dass die Schieberegler die Transparenz und nicht die Deckkraft von Objekten im Lichtweg steuern:


Wenn entweder das erste oder das zweite Objekt undurchsichtig ist, tritt natürlich kein Licht durch sie hindurch, selbst ein anderes ist vollständig transparent.

Wenn Objekt D Transparenz D T hat und Objekt S Transparenz S T hat , ist die endgültige allgemeine Transparenz R T dieser beiden Objekte gleich ihrem Produkt:

R T = D T × S T.

Transparenz ist jedoch nur eine Einheit minus Alpha, sodass wir durch Substitution Folgendes erhalten:

1 - R A = (1 - D A ) × (1 - S A )

Dieser Ausdruck kann erweitert werden in:

1 - R A = 1 - D A - S A + D A × S A.

Und vereinfachen Sie es so:

R A = DA + SA - DA × SA

Es kann auf einen von zwei ähnlichen Typen reduziert werden:

R A = SA + DA × (1 - SA)

R A = DA + SA × (1 - DA)

Bald werden wir sehen, dass die zweite häufiger verwendet wird. Es ist auch interessant festzustellen, dass der resultierende Alpha-Wert nicht von der relativen Reihenfolge der Objekte abhängt - die Deckkraft der resultierenden Pixel ist dieselbe, selbst wenn Sie die Quelle und das Ziel vertauschen. Das ist sehr logisch. Das Licht, das durch zwei Objekte fällt, sollte auf dieselbe Weise von jeder Seite des Sterns, von vorne oder von hinten, ausgeblendet werden.

Farbkombination


Die Berechnung von Alpha war nicht so schwierig. Versuchen wir also, die Berechnungen von RGB-Komponenten zu verstehen. Das Quellbild hat die Farbe S RGB , aber seine Deckkraft S A zwingt nur das Produkt dieser beiden Werte in das Endergebnis:

S RGB × S A.

Das Zielbild hat die Farbe D RGB , die Opazität lässt es Licht emittieren D RGB × D A , jedoch wird ein Teil des Lichts durch die Opazität des Bildes S blockiert, so dass der gesamte Einfluss des Ziels gleich ist:

D RGB × DA × (1 - SA)

Der Gesamtbeitrag des Lichts von S und D entspricht ihrer Summe:

S RGB × SA + D RGB × DA × (1 - SA)

In ähnlicher Weise entspricht der Beitrag der zusammengeführten Ebenen ihrer Farbe mal ihrer Opazität:

R RGB × R A.

Wir möchten, dass diese beiden Werte übereinstimmen:

R RGB × R A = S RGB × S A + D RGB × DA × (1 - SA)

Was gibt uns die endgültigen Gleichungen:

R A = SA + DA × (1 - SA)

R RGB = (S RGB × S A + D RGB × D A × (1 - S A )) / R A.

Sehen Sie, wie kompliziert die zweite Gleichung ist! Beachten Sie, dass wir durch den Alpha-Wert dividieren müssen, um die RGB-Werte des Ergebnisses zu erhalten. Für die nächste Stufe des Zusammensetzens ist jedoch erneut eine Multiplikation mit dem Alpha-Wert erforderlich, da das Ergebnis der aktuellen Operation zur neuen Quelle oder zum neuen Ziel der nächsten Operation wird. Es ist einfach hässlich.

Kehren wir für eine Sekunde zur fast endgültigen Form von R RGB zurück :

R RGB × R A = S RGB × S A + D RGB × DA × (1 - SA)

Quelle, Ziel und Ergebnis werden mit ihren Alpha-Komponenten multipliziert. Dies lässt uns verstehen, dass die Farbe und das Alpha des Pixels „zusammen“ sein sollen. Daher müssen wir einen Schritt zurücktreten und die Art und Weise, wie wir Farbinformationen speichern, überdenken.

Vormultipliziertes Alpha


Denken Sie daran, dass wir über Opazität gesprochen haben - wenn das Objekt teilweise undurchsichtig ist, ist sein Beitrag zum Ergebnis auch teilweise. Das Konzept des vormultiplizierten Alphas („Vormultiplikation mit Alpha“) setzt diese Idee um. Die Werte der RGB-Komponenten werden, wie der Name schon sagt, mit der Alpha-Komponente vormultipliziert. Beginnen wir mit Farbe ohne vorläufige Multiplikation:

(1,00, 0,80, 0,30, 0,40)

Die vorläufige Multiplikation mit Alpha ergibt Folgendes:

(0,40, 0,32, 0,12, 0,40)

Schauen wir uns mehrere Pixel gleichzeitig an. Die folgende Abbildung zeigt, wie Farbinformationen gespeichert werden, ohne zuvor Alpha zu multiplizieren:


RGB- und A-Informationen im Bild ohne vorherige Multiplikation

Beachten Sie, dass Bereiche, in denen Alpha 0 ist, beliebige RGB-Werte haben können, wie aus den grünen und blauen Störungen im Bild ersichtlich ist. Bei der vorläufigen Multiplikation mit Alpha werden in den Farbinformationen auch Pixelopazitätswerte gespeichert:


RGB- und A-Informationen in einem vormultiplizierten Bild

Vormultipliziertes Alpha wird manchmal als assoziiertes Alpha bezeichnet, und nicht vormultipliziertes Alpha wird manchmal als gerades oder nicht assoziiertes Alpha bezeichnet.

Wenn die Alpha-Komponente der Farbe 0 ist, setzt die vorläufige Multiplikation alle anderen Komponenten zurück, unabhängig von ihren Werten:

(0,0, 0,0, 0,0, 0,0)

Im Fall von vormultipliziertem Alpha gibt es nur eine vollständig transparente Farbe, und dies ist charmant.

Die Vorteile dieser Verarbeitung von Farbkomponenten werden Ihnen nach und nach klar. Bevor wir jedoch zum Compositing-Beispiel zurückkehren, wollen wir uns ansehen, wie vormultipliziertes Alpha zur Lösung einiger anderer Rendering-Probleme beiträgt.

Filtern


Gaußsche Unschärfe ist eine beliebte Methode, um einen interessanten defokussierten Hintergrund zu erstellen oder die hohe Frequenz des Hintergrundteils des Inhalts einiger UI-Elemente zu reduzieren. Wie wir sehen werden, ist das Vormultiplizieren von Alpha entscheidend, um die richtige Unschärfe zu erzielen.

Das Bild, das wir analysieren, wird erstellt, indem der Hintergrund mit 1% undurchsichtigem Blau gefüllt wird, über das ein undurchsichtiger roter Kreis gezogen wird. Schauen wir uns zunächst ein Beispiel ohne vorläufige Multiplikation an. Ich habe die RGB-Kanäle vom Alpha-Kanal getrennt, um zu verstehen, was los war. Der Pfeil zeigt die Unschärfeoperation an:


Verwischen Sie den Inhalt ohne vorherige Multiplikation

Das fertige Ergebnis hat einen hässlichen blauen Heiligenschein. Dies geschah, weil der blaue Hintergrund während der Unschärfe auf den roten Bereich sickerte und erst dann während des Zusammensetzens das Alpha-Gewicht hinzugefügt wurde.

Wenn die Farben mit Alpha vormultipliziert werden, ist das Ergebnis korrekt:


Verwischen von vormultiplizierten Inhalten

Aufgrund der Vormultiplikation wird die blaue Farbe des Bildes auf 1% seiner ursprünglichen Stärke reduziert, sodass die Auswirkungen auf die Farben des unscharfen Kreises äußerst gering sind.

Interpolation


Das Rendern eines Bildes, dessen Pixel perfekt zum Ziel passen, ist eine einfache Aufgabe, da wir eine einfache Eins-zu-Eins-Zuordnung zwischen Samples durchführen müssen. Ein Problem tritt auf, wenn es keine einfache Zuordnung gibt, beispielsweise aufgrund von Drehung, Skalierung oder Silbentrennung. Die folgende Abbildung zeigt, dass die durch den roten Umriss angezeigten Pixel des gedrehten Bildes nicht mehr mit dem Ziel übereinstimmen:


Relative Bildorientierung und Zielpixel vor und nach der Drehung

Es gibt viele Möglichkeiten, eine Farbe aus dem Bild auszuwählen, das in das Zielpixel geschrieben werden soll, und die einfachste davon ist die sogenannte Interpolation zum nächsten Nachbarn, bei der Als letztes Pixel wird einfach die nächstgelegene Probe in der Textur ausgewählt.

In der folgenden Demonstration zeigt der rote Umriss die Bildposition im Ziel. Die rechte Seite zeigt die Positionen der Proben aus Sicht des Bildes . Durch Ziehen des Schiebereglers (im Originalartikel) können Sie das Viereck drehen und beobachten, wie die Samples Farben aus der Bitmap auswählen. Ich habe ein Pixel in Quelle und Ziel hervorgehoben, damit ihre Beziehung klarer wird:


Diese Lösung ist sehr funktional und die Pixel haben eine ganzheitliche Farbe, aber die Qualität ist nicht akzeptabel. Es wäre besser, eine bilineare Interpolation zu verwenden , die den gewichteten Durchschnitt der vier nächsten Pixel des abgetasteten Bildes berechnet:


Dies funktioniert besser, aber die Kanten um die Rechtecke sehen nicht richtig aus. Der Inhalt der Pixel wird ohne Multiplikation zusammengeführt, da Alpha nach der Interpolation "angewendet" wird. Manchmal ist die empfohlene Lösung zum Zusammenführen der Farbe des richtigen Inhalts, die in dem erstaunlichen Artikel von Adrian Correger [ Übersetzung auf Habré] gezeigt wird, alles andere als ideal - keine einzige Farbe in der Lücke zwischen dem roten und dem blauen Rechteck sieht richtig aus.

Mal sehen, wie alles im Bild mit vormultipliziertem Alpha und Compositing mit einer erweiterten Formel aussehen wird, die wir bald ableiten werden:


Einfach perfekt - wir haben alle Farbfusionen beseitigt und die Zähne sind nirgends zu sehen .

Letztendlich sind die mit Unschärfe und Interpolation verbundenen Probleme eng miteinander verbunden. Jede Operation, die eine Kombination von durchscheinenden Farben erfordert, ohne zuvor die Farben mit Alpha zu multiplizieren, führt wahrscheinlich zu falschen Ergebnissen.

Das richtige Compositing


Kommen wir zurück zum Compositing. Wir haben uns für eine fast abgeleitete Gleichung entschieden:

R RGB × R A = S RGB × S A + D RGB × D A × (1 - S A )

Wenn Sie sich Farben mit vormultipliziertem Alpha vorstellen, verschwinden alle diese unangenehmen Multiplikationen, da Alpha bereits Teil der Farbwerte ist. Dann bekommen wir folgendes:

R RGB = S RGB + D RGB × (1 - S A )

Schauen wir uns die Alpha-Gleichung an:

R A = S A + D A × (1 - S A )

Die Koeffizienten für die Kanäle Rot, Grün, Blau und Alpha sind gleich, sodass wir den gesamten Ausdruck mit einer Gleichung ausdrücken können und uns daran erinnern, dass jede Komponente dieselbe Operation durchläuft:

R = S + D × (1 - S A )

Sehen Sie, wie vormultipliziertes Alpha die Dinge einfach machte. Wenn wir die Komponenten der Gleichung analysieren, sind sie alle vorhanden. Die Operation maskiert einen Teil des Hintergrundlichts und fügt ein neues Licht hinzu:

R = S + D × (1 - S A )

Dieser Mischvorgang wird als Source-Over, Souver oder einfach als normal bezeichnet und ist ohne Zweifel der häufigste Compositing-Modus. Fast alles, was Sie auf meiner Website sehen, wird in diesem Modus gemischt.

Assoziativität


Eine wichtige Source-Over-Eigenschaft, die für vor Alpha-multiplizierte Farben ausgeführt wird, ist die Assoziativität dieser Operation. Dank ihm können wir in der komplexen Mischungsgleichung die Klammern völlig willkürlich platzieren. Alle unten gezeigten Zusammensetzungen sind äquivalent:

R = (((A⇨B) ⇨C) ⇨D) ⇨E

R = (A⇨B) ⇨ (C⇨ (D⇨E))

R = A⇨ (B⇨ (C⇨ (D⇨E))) )

Der Beweis dafür ist einfach genug, aber ich werde Sie nicht mit algebraischen Manipulationen belasten. In der Praxis bedeutet dies, dass wir teilweise komplexe Zeichnungen rendern können, ohne befürchten zu müssen, dass die endgültige Komposition falsch aussieht.

In den allermeisten Fällen wird Alpha nur für das Compositing mithilfe von Source-Over verwendet, aber seine Vorteile enden hier nicht. Alpha-Werte können auch für andere nützliche Rendering-Vorgänge verwendet werden.

Porter-Duff-Compositing


1984 “Compositing Digital Images” . premultiplied alpha source-over, -, , . , .

Over


In zukünftigen Beispielen werden wir interaktive Demos verwenden, die die Funktionsweise verschiedener Mischmodi zeigen. Das Zielbild ist das schwarze Symbol „Club“ und das Quellbild ist das rote Symbol „Würmer“. Sie können das Herz über das Bild ziehen und beobachten, wie sich die überlappenden Formen unter verschiedenen Compositing-Operatoren verhalten. Achten Sie auf die kleine Minikarte in der Ecke. Einige Mischmodi sind sehr destruktiv und leicht zu verwechseln. Die Minikarte zeigt immer das Ergebnis eines einfachen Source-Over-Compositing, was das Verständnis vereinfacht:


R = S + D × (1 - S A )

R = S × (1 - D A ) + D

Wenn Sie zum Ziel-Over wechseln, werden Sie sofort feststellen, dass es einfach das Source-Over „umdreht“ - Ziel und Quelle werden in der Gleichung vertauscht, und das Ergebnis entspricht dem, was wir als Ziel als Quellbild betrachten. Obwohl es redundant erscheint, ist der Ziel-Over-Operator äußerst nützlich, da Sie damit Objekte erstellen können, die sich unter vorhandenen Inhalten befinden.

Raus


Die Source-Out- und Destination-Out-Anweisungen eignen sich hervorragend zum Stanzen von Löchern in Quelle oder Ziel:


R = S × (1 - D A )

R = D × (1 - S A )

Von diesen beiden Operatoren ist Destination-Out bequemer, da der Alphakanal zum Stanzen von Löchern in der Zielform verwendet wird.

In


Die Source-In- und Ziel-In-Operatoren sind im Wesentlichen Maskierungsoperatoren:


= S × R D A

= D × R S A

Sie machen es ziemlich einfach, komplexe Schnittpunkte nichttrivialer Geometrie zu erstellen, ohne die relativ schwierig zu berechnenden Schnittpunkte von Vektorkonturen aufzulösen.

Oben


Die Operatoren source-atopund destination-atopermöglichen es Ihnen, neue Inhalte mit vorhandenen zu überlagern, während Sie diese entlang des Zielpfads maskieren:


= S × R D A + D × (1 - S A )

R = S × (1 - D A ) + D × S A

Xor


Der exklusive OR-Operator ( xor) speichert entweder Quelle oder Ziel, und die entsprechenden Bereiche verschwinden:


R = S × (1 − D A ) + D × (1 − S A )

Source, Destination, Clear


. Source , copy , source. , destination source destination . clear :


R = S

R = D

R = 0

. clear , , . , source , , source.

-


Nachdem wir uns mit einzelnen Operatoren befasst haben, wollen wir sehen, wie Sie sie kombinieren können. Im folgenden Beispiel zeichnen wir ein Marine-Logo ohne Maskierung oder komplexe geometrische Formen. Die blauen Umrisse zeigen die einfache Geometrie, die erstellt wird. Sie können durch die Schritte gehen, indem Sie auf die rechte Seite des Bildes klicken, und zurückgehen, indem Sie auf die linke Seite klicken:


Natürlich sind wir keineswegs verpflichtet, Masken aufzugeben und Konturen zu schneiden, aber wir vergessen oft ein Werkzeug wie den Porter-Duff-Compositing-Modus, obwohl es viel einfacher ist, mit ihrer Hilfe einige visuelle Effekte zu erstellen.

Betreiber


-, , . Source F S destination, F D :

R = S×F S + D×F D

F S 0, 1, D A 1 − D A , F D 0, 1, S A 1 − S A . source destination , , , . :

01D A1 − D A
0klarsourcesource-insource-out
1destinationdestination-over
S Adestination-indestination-atop
1 − S Adestination-outsource-oversource-atopxor

. , .


, F S , F D 1. plus , lighter plus-lighter :

R = S + D

source destination:


Mit dem Bediener implementierte additive Beleuchtungplus

Grün und Rot bilden korrekt Gelb, während Grün und Blau Cyan bilden. Schwarz ist das Fehlen einer Operation, es ändert die Farbwerte in keiner Weise, da das Hinzufügen von Null zu einer Zahl nichts ändert.

Die drei verbleibenden Operatoren erhielten keine Namen, da sie nicht besonders nützlich sind. Sie sind nur eine Kombination aus Maskieren und Mischen.

Es ist auch erwähnenswert, dass vormultipliziertes Alpha es uns ermöglicht, den Operator auf source-overunerwartete Weise zu verwenden. Schauen wir uns die Gleichung noch einmal an:

R = S + D × (1 - S A )

Wenn es uns gelingt, den Alpha-Wert in der Quelle gleich Null zu machen, können wir, wenn die RGB-Kanäle Werte ungleich Null enthalten, eine additive Beleuchtung erzielen, ohne den Operator zu verwenden plus:


Additive Beleuchtung mit dem Operator implementiertsource-over

Beachten Sie, dass Sie hier vorsichtig sein müssen - die Werte werden nicht mehr korrekt mit Alpha multipliziert. In einigen Programmen kann es eine Optimierung geben, die das Mischen von Farben mit null Alpha vollständig vermeidet, während andere Programme die Vormultiplikation mit Alpha-Werten umkehren, einige Farboperationen ausführen und dann erneut mit Alpha multiplizieren können, wodurch die Farbkanäle vollständig zerstört werden. Es kann auch schwierig sein, Ressourcen in diesem Format zu exportieren. Wenn Sie also nicht die vollständige Kontrolle über die Rendering-Pipeline haben, sollten Sie sich an den Operator halten plus.

Alle Elemente, die wir diskutieren, wurden bisher gut kombiniert. Nehmen wir nun unsere rosa Brille ab und diskutieren einige Probleme, die bei der Arbeit mit Alpha-Compositing berücksichtigt werden müssen.

Gruppenopazität


Werfen wir einen Blick auf diese einfache Pillenzeichnung, die nur aus sechs Grundelementen besteht:


Zeichnen einer Pille mit einfachen Formen

Wenn wir gebeten würden, eine Pille mit einer Deckkraft von 50% zu rendern, könnten wir versucht sein, die Deckkraft einfach in die Hälfte jeder Zeichenoperation aufzuteilen, aber dies würde sich als fehlerhafte Entscheidung herausstellen:


Unerwartetes Ergebnis beim Rendern einer Pille mit halber Deckkraft.

Um das richtige Ergebnis zu erzielen, können wir die Deckkraft eines Objekts nicht einfach auf jede seiner einzelnen Komponenten verteilen. Wir müssen zuerst ein Objekt erstellen, es in eine Bitmap rendern und erst dann die Deckkraft der Bitmap ändern und schließlich das Compositing durchführen:


Erwartetes Ergebnis beim Rendern einer Pille mit halber Deckkraft

Dies ist ein weiterer Fall, der die Nützlichkeit des Konzepts des Renderns in einen Seitenpuffer demonstriert.

Compositing-Abdeckung


Das Konvertieren einer geometrischen Abdeckung in einen einzelnen Alpha-Wert hat unangenehme Folgen. Betrachten Sie den Fall, in dem zwei ideal übereinstimmende Kanten von Vektorgeometriefiguren, die in der folgenden Abbildung mit orangefarbenen und blauen Konturen dargestellt sind, in eine Bitmap gerendert werden. In einer idealen Welt sollten die Ergebnisse ungefähr so ​​aussehen, da jedes Pixel vollständig geschlossen ist:


Ein ideales Rendering-Ergebnis mit der richtigen Abdeckung.

Wenn wir jedoch zuerst die orange Geometrie und dann die blaue Geometrie rendern, leckt im endgültigen Bild immer noch ein kleiner weißer Hintergrund in die Randpixel:


Das Ergebnis des zweistufigen Compositing

Sobald die Beschichtung im Alpha-Kanal gespeichert ist, gehen alle geometrischen Informationen verloren und wir können sie in keiner Weise wiederherstellen. Die blaue Geometrie mischt sich einfach mit einem Teil des Inhalts des Puffers, weiß jedoch nicht, dass die durch die rötlichen Pixel dargestellte Geometrie mit dieser übereinstimmen muss. Dieses Problem macht sich insbesondere dann bemerkbar, wenn sich Geometrien präzise überlagern. Im Bild unten wird ein weißer Kreis über einen schwarzen gezeichnet. Dunkle Kanten sind erkennbar, obwohl beide Kreise genau den gleichen Radius und die gleiche Position haben:


Ein weißer Kreis über einem schwarzen Kreis

Eine Möglichkeit, dieses Problem zu beheben, besteht darin , die Teilabdeckung der Pixel nicht zu berechnen und erheblich größere Puffer zu verwenden. Durch Rastern der Vektorgeometrie mit einer einfachen In / Out-Beschichtung und anschließendes Reduzieren der Skalierung des Ergebnisses auf die Größe des Originalbilds können Sie das erwartete Ergebnis erzielen.

Für einen perfekten Vergleich der Renderqualität der Kanten des 8-Bit-Alpha-Kanals sollten die Puffer in beiden Richtungen 256-mal größer sein, dh die Anzahl der Pixel sollte um 2 bis 16 erhöht werdenmal. Wie wir oben gesehen haben, können Sie zwar die Bittiefe für Abdeckungswerte verringern, aber dennoch zufriedenstellende Ergebnisse erzielen, sodass Sie in der Praxis einen kleineren Maßstab verwenden können.

Es ist auch erwähnenswert, dass solche Probleme oft ohne die Verwendung großer Bitmaps relativ leicht vermieden werden können. Anstatt beispielsweise zwei übereinanderliegende Kreise zu zeichnen, können Sie einfach zwei Quadrate übereinander zeichnen und das Ergebnis dann maskieren, um einen Kreis zu bilden.

Lineare Werte


Wenn Sie Ihr Wissen über Farbräume aktualisiert haben , können Sie sich daran erinnern, dass die meisten von ihnen Farbwerte nicht linear codieren und eine vorläufige Linearisierung erforderlich ist, um die korrekten mathematischen Operationen auszuführen. Wenn diese Phase abgeschlossen ist, ist das Ergebnis des Zusammensetzens wie folgt; Achten Sie auf die schöne gelbliche Färbung der übereinanderliegenden Teile:


Unscharfe rote Kreise, die durch Zusammensetzen auf einem grünen Hintergrund unter Verwendung linearer Werte überlagert werden.

In den meisten Fällen ist das Zusammensetzen jedoch nicht der Fall . Die Standardmethode für das Web und die meisten Grafikprogramme besteht darin, nichtlineare Werte direkt zu mischen:


Unscharfe rote Kreise, die von einem Komponisten auf einem grünen Hintergrund mit nichtlinearen Werten

überlagert werden. Beachten Sie, dass die Bereiche, in denen sich Rot auf Grün überlagert, viel dunkler sind. Sie sind alles andere als ideal, aber in einigen Fällen sind falsche Operationen tief verwurzelt im Verständnis, wie wir Farbe wahrnehmen. Zum Beispiel sieht 50% undurchsichtiges Grau aus dem sRGB-Bereich genauso aus wie reines Schwarz mit 50% Deckkraft gemischt mit einem weißen Hintergrund:


Zusammensetzen von zwei Farben auf weißem Hintergrund ohne Linearisierung

In der folgenden Abbildung werden die sRGB-Farben der Quell- und Zielbilder linearisiert und dann zur Anzeige wieder in eine nichtlineare Codierung konvertiert. So sollten diese Farben eigentlich aussehen:


Zusammensetzung von zwei Farben auf weißem Hintergrund mit Linearisierung.

Wir haben eine Diskrepanz, die nicht unseren Erwartungen entspricht. Die einzige Möglichkeit, mit dieser Methode eine visuelle Einheitlichkeit zu erzielen, besteht darin, alle Farben mit linearen Werten auszuwählen. Dies unterscheidet sich jedoch stark von dem, was jeder gewohnt ist. 50% Grau mit linearen Werten sieht auf 73,5% des sRGB-Speicherplatzes wie Grau aus.

Darüber hinaus müssen Sie besonders vorsichtig sein, wenn Sie mit vormultipliziertem Alpha arbeiten. Die Vormultiplikation muss mit linearen Werten durchgeführt werden, d.h. vor dem Codieren auf nichtlinear. Aus diesem Grund endet der Linearisierungsschritt korrekt mit den korrekten linearen Werten, die zuvor mit Alpha multipliziert wurden.

Vorvervielfachte Alpha- und Bittiefe


Trotz seiner großen Nützlichkeit für Compositing, Filterung und Interpolation ist vormultipliziertes Alpha keine „Silberkugel“ und hat seine Nachteile. Am schwerwiegendsten ist die Verringerung der Bittiefe der Farben, die man sich vorstellen kann. Stellen Sie sich eine 8-Bit-Codierung mit einem Wert von 150 vor, die mit Alpha 20% vormultipliziert wird. Nach vorläufiger Multiplikation mit Alpha erhalten wir

rund (150 × 0,2) = 30

Wenn wir das gleiche Verfahren mit einem Wert von 151 wiederholen, erhalten wir:

rund (151 × 0,2) = 30

Der codierte Wert bleibt trotz der unterschiedlichen Anfangswerte gleich. Tatsächlich werden nach dem Multiplizieren mit Alpha die Werte von 148, 149, 150, 151 und 152 in 30 codiert, und der ursprüngliche Unterschied zwischen diesen fünf eindeutigen Farben geht verloren:


Die Vormultiplikation mit Alpha von 20% reduziert die verschiedenen 8-Bit-Werte auf eins.

Je kleiner das Alpha ist, desto zerstörerischer ist natürlich seine Wirkung. Von dem möglichen Bereich von 256 4 (ungefähr 4,3 Milliarden) verschiedener Kombinationen von 8-Bit-RGBA-Werten behalten nach vorläufiger Multiplikation mit Alpha nur 25,2% eine eindeutige Darstellung; Tatsächlich verlieren wir fast 2 Bits aus dem 32-Bit-Bereich.

Um Farben zwischen verschiedenen Farbräumen umzuwandeln, ist es manchmal erforderlich, die vorläufige Multiplikation umzukehren, dh die Werte durch die Alpha-Komponente zu teilen, um die ursprüngliche Farbhelligkeit zu erhalten. Dieser Schritt ist erforderlich, da, wie oben erwähnt, die Codierung nicht linear durchgeführt wird. Das Vorhandensein einer Vormultiplikation verringert die Genauigkeit der Farbdarstellung, und die Konvertierungen zwischen Farbräumen können unvollständig sein.

In der Praxis ist die Reduzierung der Bittiefe selten wichtig, insbesondere beim Compositing. Je niedriger der Alpha-Wert, desto weniger sichtbar ist die Farbe und desto weniger Einfluss hat sie auf das Compositing. Wenn Sie pedantisch genaue Farboperationen anstreben, werden Sie letztendlich nicht deren 8-Bit-Darstellung verwenden - für diesen Zweck sind Formate viel besser geeignetGleitkomma .

Zusätzliche Lektüre


Das Konzept des Alpha-Kanals wurde von den Pixar-Studio-Mitbegründern Elvy Smith und Ed Catmell entwickelt . Smiths Artikel „Alpha und die Geschichte des digitalen Compositing“ beschreibt die Geschichte der Erfindung und die Quellen des Namens „Alpha“ sowie die Entwicklung dieser Konzepte und die schrittweise Ersetzung des Konzepts der Masken in der Filmproduktion .

Um die Bedeutung von Alpha zu verstehen, empfehle ich Ihnen dringend, Andrew Glassners „Interpreting Alpha“ zu lesen . Dieser Artikel bietet eine strenge, aber sehr leicht zugängliche mathematische Ableitung von Alpha als Maß für die Wechselwirkung zwischen Opazität und Abdeckung.

Eine ausführliche Diskussion des vormultiplizierten Alphas kann in untersucht werden"GPUs bevorzugen Vormultiplikation" von Eric Haines. Der Artikel bietet nicht nur einen hervorragenden Überblick über die Probleme, die durch das Fehlen einer vorläufigen Multiplikation, insbesondere beim 3D-Rendering, verursacht werden , sondern enthält auch Links zu vielen anderen Artikeln zu diesem Thema.

Abschließend


Ursprünglich war dieser Artikel als Erklärung der Porter-Duff-Compositing-Operatoren gedacht, aber alle anderen Konzepte im Zusammenhang mit Alpha-Compositing erwiesen sich als so interessant, dass ich sie nicht übersehen konnte.

Was mir an Alpha am besten gefällt, ist, dass es nur eine zusätzliche Zahl ist, die RGB-Komponenten begleitet, aber gleichzeitig viele einzigartige Rendering-Funktionen bietet. Alpha hat buchstäblich eine neue Chance in der langweiligen alten Welt des Compositing und 2D-Renderings geschaffen.

Wenn Sie das nächste Mal die glatten Kanten von Vektorformen sehen oder eine dunkle Überlagerung bemerken, die einige Teile der Benutzeroberfläche verdeckt, denken Sie an eine kleine, aber leistungsstarke Komponente, die alles möglich gemacht hat.

Source: https://habr.com/ru/post/de468067/


All Articles