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
C'est ce que j'attends (et ce qui se passe dans paint.net):
résultat attendu
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)
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)
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)
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)
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
la source
Réponses:
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%.
Pour faciliter la visualisation, voici les valeurs RVB finales en sortie:
Et voici les valeurs finales de transparence en cours de sortie.
(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.
la source
GL_ONE
également? Sinon, l'équation alpha ressemble àdest_alpha = src_alpha * src_alpha + 1 * dest_alpha
(0,0,0)
. Bien sûr, il est plus sombre que lorsque vous le mélangez à 50%(255,255,255)
.finalAlpha = srcAlpha + destAlpha * (1 - srcAlpha)
etfinalColor = ((srcColor * srcAlpha) + (destColor * destAlpha * (1 - srcAlpha))) / finalAlpha
leColor * Alpha
bit 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 )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.
la source