Mode de mélange «normal» avec problème OpenGL

8

J'ai eu beaucoup de mal à essayer de faire fonctionner une fonction de mélange OpenGL comme je m'y attendais avec ce que j'attendais (ou de tout programme d'édition d'image sensé). À titre d'exemple, je vais utiliser ces deux images pour mélanger (peu difficile à voir sur fond blanc, donc les couleurs sont étiquetées):

Images à mélanger

Images de test

C'est ce que j'attends (et ce qui se passe dans paint.net):

résultat attendu

Test d'image Paint.Net

De toute évidence, la fonction de fusion par défaut d'Opengl donne l'aspect suivant (très faux):

glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

Test de mélange normal OpenGL

Après une tonne de tests, c'est le plus proche que j'ai pu obtenir pour créer une "bonne" fonction de mélange:

glBlendFuncSeparate (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA)

Test de mélange personnalisé OpenGL

En regardant le résultat original attendu, vous remarquerez que certaines couleurs sont un peu plus sombres qu'elles ne devraient l'être (la partie centrale gauche). Plus précisément, ils sont prémultipliés à la moitié de leur valeur de couleur (en raison de l'alpha 0,5) et je n'arrive pas à créer une fonction qui ne le fasse pas (sans causer de problèmes de mélange étranges avec la partie transparente rouge à peine visible).

Quelqu'un connaît-il une solution à ce problème? L'une que j'avais était d'utiliser l'alpha prémultiplié dans les sources (même si je ne veux pas le faire car cela nécessite un travail supplémentaire pour convertir chaque couleur que j'utilise dans mon jeu en prémultiplié ou simplement écrire des trucs dans chaque shader) et le faire comme ça :

glBlendFuncSeparate (GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA) (pas de prémultiplication)

Test de mélange personnalisé OpenGL

C'est évidemment faux aussi, mais c'est en fait le seul résultat correct que j'ai obtenu jusqu'à présent:

glBlendFuncSeparate (GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA) (entrées prémultipliées)

Test de mélange personnalisé OpenGL

Le seul problème est, comment pourrais-je me débarrasser de la prémultiplication pour l'afficher à l'écran? Cela nécessiterait probablement un cycle de rendu supplémentaire pour chaque chose que je mélange et cela semble beaucoup trop complexe pour ce problème, donc je cherche toujours une réponse (c'est intéressant que je ne trouve rien à ce sujet, car OpenGL est si largement utilisé que J'imagine que quelqu'un d'autre a rencontré ce problème).

Sources:

Test de fonction de mélange en ligne - http://www.andersriggelsen.dk/glblendfunc.php
Image de la couche inférieure - http://i.stack.imgur.com/XjLNW.png
Image de la couche supérieure - http: //i.stack.imgur .com / 9CN6w.png

Goutte de citron
la source
Toutes les captures d'écran ont été prises à partir de l'outil en ligne Visual glBlendFunc + glBlendEquation d'Anders Riggelsen . À l'avenir, ce genre de chose serait utile de mentionner lors de la demande d'aide.
Trevor Powell
@TrevorPowell Dang Je savais que j'avais oublié de dire quelque chose (j'avais vraiment l'intention de mettre ça au fond). Quoi qu'il en soit, c'est exactement ce que j'utilisais pour tester facilement les modes de mélange, il devrait être standard pour tout autre type d'OpenGL (même si je peux voir d'autres personnes qui souhaitent l'utiliser).
Lemon Drop

Réponses:

5

Dans votre premier exemple ( glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)), vous lui dites de mélanger les couleurs en fonction de l'alpha de l'image qui est dessinée en haut (celle du "premier plan"). Ainsi, lorsque vous mélangez 50% entre la couleur de premier plan (38,50,168)et la couleur d'arrière-plan (38,50,168)dans le coin inférieur gauche de l'image, vous obtenez sans surprise exactement la même couleur (38,50,168). Parce que ce mélange croisé est exactement ce que vous avez dit à OpenGL de faire.

A en juger par votre image "attendue", vous ne voulez pas faire un mélange croisé entre les deux couleurs - vous voulez superposer la couleur de premier plan sur la couleur d'arrière-plan pleine force, pas un mélange croisé entre elles.

Pour ce faire, vous souhaitez utiliser glBlendFuncSeparate (puisque vous traitez la couleur de l'arrière-plan différemment de son alpha) et spécifiez que pendant l'opération de fusion, la valeur alpha de l'arrière-plan doit être traitée à 100%.

travail

Pour faciliter la visualisation, voici les valeurs RVB finales en sortie: RVB

Et voici les valeurs finales de transparence en cours de sortie. alpha

(Cette zone de transparence `` 100% '' devrait en fait être étiquetée comme 99,7% transparente, et la zone en bas à droite `` 50% '' devrait être étiquetée comme 49,7% transparente, à la fois en raison du bit teinté de rouge sur le côté droit de la première image. Aussi, désolé de lister les numéros en utilisant la "transparence" plutôt que l'opacité; peut-être un peu déroutant.)

S'il y a des problèmes spécifiques, veuillez indiquer la zone de l'image RVB ou alpha qui produit des valeurs incorrectes.

Trevor Powell
la source
Le coefficient alpha source ne devrait-il pas l'être GL_ONEégalement? Sinon, l'équation alpha ressemble àdest_alpha = src_alpha * src_alpha + 1 * dest_alpha
bcrist
Eh bien, si vous le regardez, la pièce du milieu gauche est encore beaucoup plus sombre qu'elle ne devrait l'être, le fait de retourner les calques donne également l'impression: i.imgur.com/rigBNz6.png (ce rouge à peine visible est là pour tester des choses comme que
Lemon Drop
Vous le mélangez à 50% contre (0,0,0). Bien sûr, il est plus sombre que lorsque vous le mélangez à 50% (255,255,255).
Trevor Powell
@TrevorPowell Cela ne devrait-il pas avoir d'importance du tout si l'alpha est 0? Comme les équations que je cherche sont comme: finalAlpha = srcAlpha + destAlpha * (1 - srcAlpha)et finalColor = ((srcColor * srcAlpha) + (destColor * destAlpha * (1 - srcAlpha))) / finalAlphale Color * Alphabit dans le calcul des couleurs peut être fait dans les shaders en sortant des valeurs prémultipliées, mais il n'y a aucun moyen de diviser par le finalAlpha pour se débarrasser de la prémultiplication. (Tiré d' ici )
Lemon Drop
0

J'ai eu exactement le même problème lorsque je voulais rendre un ensemble d'objets Framebuffer qui se chevauchent comme des calques. Le problème est que les fonctions de fusion Open GL sont linéaires et fonctionnent lorsque l'arrière-plan de destination est opaque. Mais l'équation de fusion réelle pour mélanger deux couches transparentes n'est pas une équation linéaire et contient une partie de division.

out_color = {src_color * src_alpha + dest_color * dest_alpha * (1-src_alpha)} / out_alpha

Je ne pense pas qu'il soit possible de le faire avec les implémentations actuelles d'OpengGL. Donc, pour contourner le problème, j'ai dû utiliser un pixel shader pour calculer manuellement la sortie de chaque pixel.

Supun Gothama
la source
Ouais, ce que j'ai fait, c'est juste de pré-multiplier l'alpha sur les textures plutôt que de faire quelques contournements, c'est facile à faire avec certaines bibliothèques (elles vont juste prémultiplier les textures à la charge)
Lemon Drop