OpenGL Render zu Textur mit partieller Transparenz (Transluzenz) und dann Rendern das auf den Bildschirm

8

Ich habe ein paar Stellen gefunden, an denen das gefragt wurde, aber ich habe noch keine gute Antwort gefunden.

Das Problem: Ich möchte Textur rendern, und dann möchte ich diese gerenderte Textur auf den Bildschirm IDENTICALLY zeichnen, wie es aussehen würde, wenn ich den Render-to-Texture-Schritt überspringe und nur direkt bin Rendern auf dem Bildschirm. Ich verwende derzeit einen Mischmodus glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA). Ich habe glBlendFuncSeparate, um auch mit zu spielen.

Ich möchte in der Lage sein, teilweise transparente überlappende Elemente zu dieser Textur zu rendern. Ich weiß, dass die Blend-Funktion derzeit die RGB-Werte auf der Alpha-Ebene durcheinander bringt. Ich habe einige vage Vorschläge zur Verwendung von "vorgemerztem Alpha" gesehen, aber die Beschreibung ist schlecht, was das bedeutet. Ich mache Png-Dateien in Photoshop, ich weiß, dass sie ein Transluzenz-Bit haben und Sie können den Alpha-Kanal nicht einfach unabhängig bearbeiten, wie Sie mit TGA können. Bei Bedarf kann ich zu TGA wechseln, obwohl PNG bequemer ist.

Nehmen wir an, dass wir für diese Frage keine Bilder verwenden, sondern nur Vollfarben-Quads mit Alpha.

Sobald ich meine Szene in die Textur gerendert habe, muss ich diese Textur in eine andere Szene rendern, und ich muss die Textur BLENDEN, wobei ich wieder Teiltransparenz annahm. Hier fallen die Dinge auseinander. In den vorherigen Überblendungsschritten ändere ich deutlich die RGB-Werte basierend auf Alpha, was wiederum funktioniert - okay, wenn Alpha 0 oder 1 ist, aber wenn es dazwischen ist, ist das Ergebnis eine weitere Verdunkelung dieser teilweise durchscheinenden Pixel / p>

Wenn ich mit den Mischmodi spiele, hatte ich sehr wenig Glück. Das Beste, was ich tun kann, ist die Texturierung mit:

glBlendFuncSeparate (GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);

Ich habe festgestellt, dass das Rendern mehrere Male mit glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) die richtige Farbe approximiert (sofern sich die Dinge nicht überschneiden). Aber das ist nicht genau perfekt (wie Sie in der folgenden Abbildung sehen können, werden die Teile, wo die grünen / roten / blauen Kästchen überlappen, dunkler, oder akkumuliert Alpha. (EDIT: Wenn ich die Mehrfachzeichnungen nur in den Render-Bildschirm-Teil mache) Rendern Sie einmal Textur, das Alpha-Akkumulationsproblem verschwindet und es funktioniert, aber warum ?! Ich möchte nicht die gleiche Textur hunderte Male auf dem Bildschirm rendern müssen, um es richtig anzusammeln)

Hier sind einige Bilder, die das Problem darstellen (die Render-Durchgänge sind mehrere mit Basic Blending (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA), und sie werden im Textur-Rendering-Schritt mehrfach gerendert. Die 3 Felder auf der rechten Seite werden zu 100% rot, grün gerendert oder blau (0-255), aber bei Alpha-Werten von 50% für Blau, 25% für Rot und 75% für Grün:

Also, eine Aufschlüsselung dessen, was ich wissen möchte:

  1. Ich setze den Mischmodus auf X ?
  2. Ich rende meine Szene auf eine Textur. (Vielleicht muss ich mit ein paar Mischmodi oder mehrmals rendern?)
  3. Ich setze meinen Mischmodus auf Y ?
  4. Ich rende meine Textur auf dem Bildschirm über eine bestehende Szene. (Vielleicht brauche ich einen anderen Shader? Vielleicht muss ich die Textur ein paar Mal rendern?)

Gewünschtes Verhalten ist, dass am Ende dieses Schrittes das endgültige Pixelergebnis identisch ist mit dem, wenn ich das einfach machen würde:

  1. Ich setze meinen Mischmodus auf: (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
  2. Ich rende meine Szene auf den Bildschirm.

Und zur Vollständigkeit, hier ist ein Teil meines Codes mit meinem ursprünglichen naiven Versuch (nur normales Mischen):

%Vor%

Hier ist der Code, den ich verwende, um die Szene einzurichten:

%Vor%

Und der Code zum Rendern der Szene:

%Vor%

Und hier ist mein Bildschirm zu zeichnen:

%Vor%

* BEARBEITEN: FRAMEBUFFER SETUP / TEADOWN CODE:

%Vor%

Und mein klarer Bildschirmcode:

%Vor%     
M2tM 21.06.2014, 22:54
quelle

4 Antworten

12

Basierend auf einigen Berechnungen und Simulationen, die ich ausgeführt habe, habe ich zwei ziemlich ähnliche Lösungen gefunden, die den Trick zu machen scheinen. Man verwendet vormultiplizierte Farben in Kombination mit einer einzelnen (separaten) Überblendungsfunktion, die andere arbeitet ohne vormultiplizierte Farben, erfordert aber ein paar Mal die Änderung der Überblendungsfunktion.

Option 1: Single-Blend-Funktion, Vorvermehrung

Dieser Ansatz funktioniert mit einer einzigen Überblendfunktion durch den gesamten Prozess. Die Überblendungsfunktion ist:

%Vor%

Es erfordert vormultiplizierten Farben, was bedeutet, dass, wenn Ihre Eingabefarbe normalerweise (r, g, b, a) wäre, stattdessen (r * a, g * a, b * a, a) verwendet wird. Sie können die Vormultiplikation im Fragment-Shader durchführen.

Die Reihenfolge ist:

  1. Legen Sie die Mischfunktion auf (GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_ONE) fest.
  2. Setze das Renderziel auf FBO.
  3. Rendern Sie Layer, die Sie auf FBO rendern möchten, mit vormultiplizierten Farben.
  4. Setzen Sie das Renderziel auf den Standard-Framebuffer.
  5. Rendern Sie Ebenen, die unterhalb des FBO-Inhalts liegen sollen, indem Sie vormultiplizierte Farben verwenden.
  6. FBO-Anhang rendern, ohne wendet eine Vormultiplikation an, da die Farben im FBO bereits vormultipliziert wurden.
  7. Rendern Sie die gewünschten Layer über den FBO-Inhalt, indem Sie vormultiplizierte Farben verwenden.

Option 2: Mischfunktionen ohne Vorvermehrung

Dieser Ansatz erfordert keine Vormultiplikation der Farben für irgendeinen Schritt. Der Nachteil ist, dass die Mischfunktion während des Prozesses einige Male umgeschaltet werden muss.

  1. Legen Sie die Mischfunktion auf (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_ONE) fest.
  2. Setze das Renderziel auf FBO.
  3. Rendere Ebenen, die auf FBO gerendert werden sollen.
  4. Setzen Sie das Renderziel auf den Standard-Framebuffer.
  5. (optional) Setzen Sie die Überblendungsfunktion auf (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) .
  6. Rendern Sie die gewünschten Layer unter dem FBO-Inhalt.
  7. Legen Sie die Mischfunktion auf (GL_ONE, GL_ONE_MINUS_SRC_ALPHA) fest.
  8. FBO-Anhang rendern.
  9. Legen Sie die Mischfunktion auf (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) fest.
  10. Rendern Sie die gewünschten Layer über den FBO-Inhalt.

Erklärung und Beweis

Ich denke, Option 1 ist netter und möglicherweise effizienter, weil beim Rendern keine Mischfunktionen benötigt werden. Die detaillierte Erklärung ist also für Option 1. Die Mathematik für Option 2 ist jedoch ziemlich gleich. Der einzige wirkliche Unterschied ist, dass Option 2 GL_SOURCE_ALPHA für den ersten Ausdruck der Überblendungsfunktion verwendet, um die Vormultiplikation bei Bedarf durchzuführen, wobei Option 1 erwartet, dass vormultiplizierte Farben in die Überblendungsfunktion gelangen.

Um zu zeigen, dass das funktioniert, gehen wir durch ein Beispiel, in dem 3 Ebenen gerendert werden. Ich mache alle Berechnungen für die Komponenten r und a . Die Berechnungen für g und b entsprechen denen für r . Wir werden drei Schichten in der folgenden Reihenfolge rendern:

%Vor%

Für die Referenzberechnung mischen wir diese 3 Ebenen mit der standardmäßigen GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA Blend-Funktion. Wir müssen das resultierende Alpha hier nicht verfolgen, da DST_ALPHA nicht in der Überblendungsfunktion verwendet wird und wir die vormultiplizierten Farben noch nicht verwenden:

%Vor%

Der letzte Begriff ist also unser Ziel für das Endergebnis. Jetzt rendern wir die Layer 2 und 3 zu einem FBO. Später werden wir Schicht 1 in den Bildpuffer rendern und dann den FBO darüber mischen. Das Ziel ist es, das gleiche Ergebnis zu erzielen.

Ab jetzt werden wir die am Anfang aufgelistete Blend-Funktion anwenden und vormultiplizierten Farben verwenden. Wir müssen auch die Alphas berechnen, da DST_ALPHA in der Blend-Funktion verwendet wird. Zuerst rendern wir die Layer 2 und 3 in den FBO:

%Vor%

Jetzt rendern wir zum primären Framebuffer. Da uns das resultierende Alpha nicht interessiert, berechne ich nur die r -Komponente erneut:

%Vor%

Jetzt mischen wir den Inhalt des FBO darüber. Was wir also für "after layer 3" in der FBO berechnet haben, ist unsere Quellfarbe / alpha, a1 * r1 ist die Zielfarbe und GL_ONE, GL_ONE_MINUS_SRC_ALPHA ist immer noch die Mischfunktion. Die Farben im FBO sind bereits vormultipliziert, so dass es beim Mischen des FBO-Inhalts keine Vormultiplikation im Shader geben wird:

%Vor%

Vergleichen Sie den letzten Term mit dem Referenzwert, den wir oben für den Standard-Mischfall berechnet haben, und Sie können sehen, dass es genau derselbe ist.

Diese Antwort auf eine ähnliche Frage enthält einige weitere Hintergrundinformationen zum GL_ONE_MINUS_DST_ALPHA, GL_ONE -Teil der Mischfunktion: OpenGL ReadPixels (Screenshot) Alpha .

    
Reto Koradi 24.06.2014, 06:57
quelle
2

Ich habe mein Ziel erreicht. Nun, lass mich diese Informationen mit dem Internet teilen, da es nirgends gibt, die ich sonst finden könnte.

  1. Erstelle deinen Framebuffer (Blindbildpuffer usw.)
  2. Löschen Sie den Framebuffer auf 0, 0, 0, 0
  3. Richten Sie Ihr Ansichtsfenster richtig ein. Das sind alles grundlegende Dinge, die ich in der Frage für selbstverständlich gehalten habe, aber hier einbeziehen möchte.
  4. Rendern Sie Ihre Szene nun normalerweise mit glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) zum Framebuffer. Stellen Sie sicher, dass die Szene sortiert ist (genau wie normalerweise.)
  5. Binden Sie jetzt den enthaltenen Fragment-Shader. Dadurch wird der mit den Bildfarbenwerten verursachte Schaden über die Überblendungsfunktion rückgängig gemacht.
  6. Rendern Sie die Textur mit glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
  7. auf Ihrem Bildschirm
  8. Gehe zurück zum normalen Rendering mit einem regulären Shader.

Der Code, den ich in die Frage einfüge, bleibt grundsätzlich unberührt, außer dass ich sicherstelle, dass ich den Shader, den ich unten lese, binge, wenn ich meine "preDraw" -Funktion mache, die spezifisch für mein eigenes kleines Framework ist, aber im Grunde die "draw um nach "rufe nach meiner gerenderten Textur" zu suchen.

Ich nenne dies den "Unblend" -Shader.

%Vor%

Warum mache ich textureColor / = sqrt (textureColor.a)? Weil die ursprüngliche Farbe wie folgt dargestellt wird:

ErgebnisR = r * a, ErgebnisG = g * a, ErgebnisB = b * a, ErgebnisA = a * a

Nun, wenn wir das rückgängig machen wollen, müssen wir herausfinden, was ein ist. Der einfachste Weg zu finden ist, hier nach "a" zu suchen:

resultA = a * a

Wenn a beim ursprünglichen Rendern .25 ist, haben wir:

resultA = .25 * .25

Oder:

resultA = 0,0625

Wenn die Textur jedoch auf den Bildschirm gezeichnet wird, haben wir kein "a" mehr. Wir wissen, welches Ergebnis A ist, es ist der Alphakanal der Textur. Also können wir sqrt (resultA) .25 zurück bekommen. Jetzt mit diesem Wert können wir teilen, um die Multiplikation rückgängig zu machen:

textureColor / = sqrt (textureColor.a);

Und das behebt alles, um die Überblendung rückgängig zu machen!

* EDIT: Nun ... Irgendwie. Da ist eine Taschenspieler-Ungenauigkeit, in diesem Fall kann ich es zeigen, indem ich über eine klare Farbe rendere, die nicht mit der Farbe des Framebuffers identisch ist. Einige Alpha-Informationen scheinen verloren zu sein, wahrscheinlich in den RGB-Kanälen. Dies ist immer noch gut genug für mich, aber ich wollte mit dem Screenshot, der die Ungenauigkeit zeigt, bevor ich mich abmelde. Wenn jemand eine Lösung hat, bitte geben Sie es an!

Ich habe eine Prämie geöffnet, um diese Antwort auf eine kanonische 100% korrekte Lösung zu bringen. Gerade jetzt, wenn ich mehr transparente Objekte über die vorhandene Transparenz rendere, wird die Transparenz anders akkumuliert als rechts, was zu einer Aufhellung der endgültigen Textur über das hinaus führt, was rechts gezeigt wird. Wenn die Darstellung über einen nicht schwarzen Hintergrund erfolgt, ist es ebenfalls klar, dass die Ergebnisse der vorhandenen Lösung sich wie oben gezeigt geringfügig unterscheiden.

Eine richtige Lösung wäre in allen Fällen identisch. Meine existierende Lösung kann die Zielmischung in der Shader-Korrektur nicht berücksichtigen, nur das Quell-Alpha.

    
M2tM 22.06.2014 03:12
quelle
1

Um dies in einem einzigen Durchgang zu tun, benötigen Sie Unterstützung für separate Farbe & amp; Alpha-Mischfunktionen. Zuerst rendert man die Textur, deren Vordergrundbeitrag im Alphakanal gespeichert ist (d. H. 1 = vollständig undurchsichtig, 0 = vollständig transparent) und den vormultiplizierten Quellfarbwert im RGB-Farbkanal. Um diese Textur zu erstellen, machen Sie folgende Operationen:

  1. Löschen Sie die Textur zu RGBA = [0, 0, 0, 0]
  2. stellt die Farbkanalmischung auf src_color * src_alpha + dst_color * (1-src_alpha)
  3. ein
  4. setzt die Alpha-Kanal-Mischung auf src_alpha * (1-dst_alpha) + dst_alpha
  5. rendert die Szene an die Textur

Um den durch 2) und 3) spezifizierten Modus einzustellen, können Sie Folgendes tun: glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_ONE) und glBlendEquation(GL_FUNC_ADD)

Rendern Sie als Nächstes diese Textur für die Szene, indem Sie die Farbmischung auf Folgendes einstellen: src_color + dst_color * (1-src_alpha), d.h. glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA) und glBlendEquation(GL_FUNC_ADD)

    
JarkkoL 25.06.2014 03:10
quelle
0

Ihr Problem ist älter als OpenGL oder Personal Computer oder tatsächlich ein lebender Mensch. Sie versuchen, zwei Bilder miteinander zu verschmelzen und es so aussehen zu lassen, als seien sie überhaupt nicht gemischt worden. Druckmaschinen stehen vor genau diesem Problem. Wenn Tinte auf Papier aufgetragen wird, ergibt sich eine Mischung zwischen der Tintenfarbe und der Papierfarbe.

Die Lösung ist in Papier genauso wie in OpenGL. Sie müssen Ihr Quellbild ändern, um Ihr Endergebnis zu kontrollieren. Dies ist leicht genug, um herauszufinden, ob Sie die Mathe zum Mischen untersuchen.

Für jede von R, G, B ist die resultierende Farbe (old * (1-opacity)) + (new * opacity) . Das Basisszenario, und dasjenige, das Sie emulieren möchten, zeichnet eine Farbe direkt auf den letzten Backpuffer bei Opazität A.

Zum Beispiel ist die Deckkraft 50% und dein grüner Kanal hat 0xFF . Das Ergebnis sollte 0x7F auf schwarzem Hintergrund sein (einschließlich unvermeidbarer Rundungsfehler). Sie können wahrscheinlich nicht davon ausgehen, dass der Hintergrund schwarz ist, also erwarten Sie, dass der grüne Kanal zwischen 0x7F und 0xFF variiert.

Sie möchten wissen, wie Sie dieses Ergebnis emulieren können, wenn Sie wirklich zu einer Textur rendern und dann die Textur an den Back-Buffer rendern. Es stellt sich heraus, dass die "vagen Vorschläge zu verwenden '' waren korrekt. Während Ihre Lösung darin besteht, im letzten Schritt einen vorherigen Blendvorgang mit einem Shader zu löschen, besteht die Standardlösung darin, die Farben Ihrer ursprünglichen Quelltextur mit dem Alpha-Kanal (auch als Alpha-Multiplikat) zu multiplizieren. Wenn die Zwischentextur kompostiert wird, werden die RGB-Kanäle gemischt, ohne mit Alpha zu multiplizieren. Wenn die Textur in den Back-Puffer gerendert wird, werden die RGB-Kanäle gemischt, ohne mit Alpha zu multiplizieren. So vermeiden Sie das Mehrfachmultiplikationsproblem sauber.

Bitte konsultieren these Ressourcen für ein besseres Verständnis. Ich und die meisten anderen sind mit dieser Technik in DirectX vertrauter, daher müssen Sie möglicherweise nach den entsprechenden OGL-Flags suchen.

    
Sophit 24.06.2014 01:14
quelle