Verzögerte Alpha-Mischung

In diesem Artikel möchte ich über Methoden zum Mischen von gerasterter Geometrie sprechen. Die klassischen Mischmodelle von durchscheinenden Objekten - Alpha, Additiv, Multiplikativ - werden nach demselben Zeichnungsprinzip vereint: Zeichnen Sie nacheinander ein Grundelement nach dem anderen und mischen Sie die am Ausgang des Fragment-Shaders empfangenen Pixel mit dem, was sich im aktuellen Puffer befindet. Jedes neue Grundelement aktualisiert den Bereich des Puffers, in den es gezeichnet wird. Im Fall von Alpha-Blending werden die Objekte, die höher sind, zuvor gezeichnete Objekte in den Schatten gestellt. Aber was ist, wenn Sie etwas mit einer Gruppe von Objekten tun möchten, die über der Szene gezeichnet sind - z. B. durch Maske zuschneiden oder hervorheben? Zwei Entscheidungen fallen sofort ein: Nehmen Sie entweder Änderungen an ihrem Material vor (d. H. Ändern Sie den Shader, erweitern Sie den Satz von Texturen), indem Sie beispielsweise eine Projektion einer anderen Textur hinzufügendie für die Transparenzmaske verantwortlich sein wird. Wenn wir jedoch viele fleckige Objekte haben, ist das Ändern jedes einzelnen Materials unpraktisch und mit Fehlern behaftet. Die zweite Möglichkeit besteht darin, alle für uns interessanten Objekte in ein separates Vollbildziel zu zeichnen und es bereits in der endgültigen Szene zu zeichnen. Hier können wir mit dem Inhalt machen, was wir wollen, aber dies erfordert die Zuweisung von zusätzlichem Speicher und, was am unangenehmsten ist, das Wechseln des Ziel-Renderings. Dies ist nicht der „billigste“ Vorgang auf Mobilgeräten, der zweimal ausgeführt werden muss. Und wenn Sie mit mehreren Ebenen wie diesen arbeiten möchten?Hier können wir mit dem Inhalt machen, was wir wollen, aber dies erfordert die Zuweisung von zusätzlichem Speicher und, was am unangenehmsten ist, das Wechseln des Ziel-Renderings. Dies ist nicht der „billigste“ Vorgang auf Mobilgeräten, der zweimal ausgeführt werden muss. Und wenn Sie mit mehreren Ebenen wie diesen arbeiten möchten?Hier können wir mit dem Inhalt machen, was wir wollen, aber dies erfordert die Zuweisung von zusätzlichem Speicher und, was am unangenehmsten ist, das Wechseln des Ziel-Renderings. Dies ist nicht der „billigste“ Vorgang auf Mobilgeräten, der zweimal ausgeführt werden muss. Und wenn Sie mit mehreren Ebenen wie diesen arbeiten möchten?



Es gibt eine andere, einfachere und elegantere Möglichkeit, diese Probleme zu lösen. Wir können die Szene umgekehrt malen!

Ein kleiner Exkurs, der Sie daran erinnert, wie die klassische Rendermethode funktioniert
- Alpha Blending , , , . 4 RGBA, RGB — A(Alpha) — ( ). . :


ColorSrc — RGB , ( , ), ColorDst — , , Color_Result — , , , . Variable1 Variable2, ? , ( , ). , : , ... 

:


AlphaSrc — - (), OneMinusAlphaSrc, , 1.0 — AlphaSrc. : 1 * + 2 * (1 — ). alpha (). = 1, , = 0, . OpenGL .

OpenGL ES 2.0 — .

So entsteht das Bild in Schritten: Zuerst zeichnen wir einen Hintergrund, dann zeichnen wir alle Objekte nacheinander in Ebenen. Was zuletzt gerendert wird, überschreibt die vorherigen Pixel: 





Was ist der Trick? 


Das Wesen der Technologie des umgekehrten Renderns oder, wie es auch genannt werden kann, des verzögerten Mischens ist wie folgt. Wir zeichnen die Szene mit einer anderen Mischformel rückwärts. Darüber hinaus bleibt das endgültige Bild genau das gleiche wie beim klassischen Ansatz.

Wie es funktioniert?


Die Methode zum Mischen durch den Transparenzkanal des von uns gezeichneten Bildes wurde oben beschrieben. Jetzt werden wir es umgekehrt drehen: Wir werden die Transparenz bereits gezeichneter Pixel verwenden (oder vielmehr die gezeichnete Transparenz mit den bereits gezeichneten mischen). Das heißt, anstelle von AlphaSrc verwenden wir AlphaSaturate und anstelle von OneMinusAlphaSrc - One. Es stellt sich heraus, dass, wenn sich bereits etwas mit Transparenz = 1 im Puffer befindet, der Beitrag Null ist und sich die Farbe eines solchen Pixels nicht ändert. Wenn es keine Transparenz gab, addieren wir beide Farben (dazu müssen wir den Bildspeicher mit Nullen oder Schwarz mit Null Transparenz löschen). Mit diesem Zusatz entspricht die resultierende Farbe der gezeichneten Farbe. Die endgültige Formel sieht folgendermaßen aus:
 

(ca. AlphaSaturate = min (AlphaSrc, 1 - AlphaDst))

Die Transparenzwerte müssen hinzugefügt werden: Sie müssen sich Schicht für Schicht ansammeln, dh wir haben Eins und Eins in den Mischvariablen für den Alphakanal. Warum ändern wir ColorDst nicht und löschen den Puffer mit Nullen? Dies ist für das additive Mischen erforderlich. AdditiveBlending unterscheidet sich nur darin, dass die AlphaSrc-Variable Null enthält. Es sollte nicht die Transparenz ändern, sondern nur die Farbe.

Aus Gründen der Übersichtlichkeit sieht das umgekehrte Rendering-Schema folgendermaßen aus: 

Zuerst löschen wir den Frame-Puffer. Dann stellen wir die oben angegebene Mischfunktion ein und beginnen mit dem Zeichnen von den obersten Objekten (im klassischen Ansatz würden sie zuletzt gezeichnet), wobei wir zu den untersten Objekten gehen. Das Hintergrundbild wird zuletzt gezeichnet.


Wie kann das genutzt werden?


Ich werde einige Aufgaben beschreiben, die mit dieser Methode gelöst wurden, wobei unser Projekt als Beispiel dient:

  1. Objekte durch Maske mit Transparenz beschneiden. Reibungsloses Abschneiden des Spielzimmers:


    Nach dem Zeichnen des Spielfelds reicht es aus, die Transparenz an den Stellen des Bildes zu löschen, die wir ausblenden möchten. Dies erfolgt unter Verwendung einer Mischformel, bei der das gezeichnete Maskenobjekt Farbe und Transparenz umgekehrt mit seiner eigenen Transparenz überschreibt und der Reinigungsgrad kontinuierlich angepasst werden kann. In diesem Fall wird die folgende Geometrie zum Abschneiden verwendet:


    Es ändert seine Form, wenn sich die Kamera zwischen Räumen bewegt. Die Mischformel zur Reinigung lautet wie folgt:

    ColorSrc = GL_ZERO,
    ColorDst = GL_ONE_MINUS_SRC_ALPHA,
    AlphaSrc = GL_ZERO,
    AlphaDst = GL_ONE_MINUS_SRC_ALPHA

    Sie können eine beliebige Geometrie mit beliebigen Texturen verwenden und die Reinigung von der Ebene aus starten, von der Sie Folgendes benötigen:

  2. Das reibungslose Verschwinden des Feldes erfolgt auf ähnliche Weise. Der Ausgabepreis beträgt ein DrawCall.
  3. :


    , UI, . , «» , , « », . , : , . :

    ColorSrc = GL_SRC_ALPHA,
    ColorDst = GL_ONE_MINUS_SRC_ALPHA,
    AlphaSrc = GL_ZERO,
    AlphaDst = GL_ONE
  4. , :


  5. «»:



    . , . — 2 DrawCalls.



Ambient occlusion





Es gibt einen Nachteil oder eher Einschränkungen: Nicht alle Mischungen können für eine solche Technik wiederholt werden. Alpha-Mischen und Additiv sind definitiv möglich, aber Sie müssen Ihre eigenen speziellen Mischungen anpassen oder nicht verwenden. Es gibt jedoch einen Ausweg: Sie können die Phasen des Renderns der Szene trennen. Ein Teil davon erfolgt nach der umgekehrten Methode, ein Teil nach der üblichen Methode. Wir haben dies für Spezialeffekte auf dem Feld und nach dem Prozess durchgeführt.

Ein wichtiger Punkt bei additiven und gemischten Rendering-Techniken: Wenn es VOR dem umgekehrten Rendering-Durchgang gezeichnet wird und die Textur keine Transparenzinformationen enthält (Textur wie „weißer Fleck auf schwarzem Hintergrund“), überschreibt ein solches Objekt die Transparenz. In der Passage „Zurück“ gehen Informationen zu diesem Abschnitt verloren, und visuell sieht es aus wie ein „dunkles Quadrat“ oder ein schwarzer Rand um einen hellen Zusatzpunkt:


Dies kann überwunden werden, indem das additive Mischen hinsichtlich des Mischens des Alphakanals modifiziert wird:

AlphaSrc = GL_ONE_MINUS_DST_ALPHA
AlphaDst = GL_ONE

Dies ist jedoch nicht für alle Arten des Mischens geeignet, und es ist zuverlässiger, die Textur selbst zu modifizieren. Was ist gemeint:

Wenn es Texturen der Form gibt: 


Dann müssen Sie einen von ihnen machen:


Das heißt, die Helligkeit der Farbkanäle muss in Transparenz umgewandelt werden und die Farben umgekehrt zur Transparenz strecken. Die resultierende und alte Textur sollte auf einem schwarzen Hintergrund gleich aussehen. Manuell ist es unwahrscheinlich, dass dies gelingt. Es ist sinnvoll, einen automatischen Konverter herzustellen. In diesem Fall sieht der Pseudocode für die Kanalkonvertierung folgendermaßen aus:

RGB_old = Texel_in.rgb
A_old = Texel_in.a
A_middle = 1.0 / ((RGB_old) / 3.0) * A_old // linear color space
RGB_new = RGB_old * A_middle;
A_shift = minimum( 1.0 / RGB_new.r, 1.0)
A_shift = minimum( 1.0 / RGB_new.g, A_shift)
A_shift = minimum( 1.0 / RGB_new.b, A_shift)
RGB_result = RGB_new * A_shift; 
A_result = (RGB_result) / 3.0)
Texel_out = Vector4(RGB_result, A_result)



Hier werde ich die Schritte zum Rendern der Szene unseres Projekts durchgehen
 
  1. . , -.

  2. , , , «» :


  3. UI:

  4. , , , :

  5. :

  6. :

  7. , .




Fazit


Die Methode ermöglicht es, ganz einfach und kostengünstig mit Ebenen der gezeichneten Szene zu arbeiten, wobei der Alphakanal als Maske verwendet wird. Es ist relativ einfach, es in einem bereits funktionierenden Projekt zu implementieren: Es erfordert keine tiefgreifende Änderung des Codes des Grafiksubsystems, es reicht aus, die Renderreihenfolge und die Mischformel zu ändern. An manchen Stellen kann die Leistung erheblich gesenkt werden. Es gibt Einschränkungen, aber in den meisten Fällen können Sie sich damit abfinden.

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


All Articles