He tenido muchos problemas para intentar que una función de mezcla OpenGL funcione como lo esperaba con lo que esperaría (o de cualquier programa de edición de imagen sensible). Como ejemplo, usaré estas dos imágenes para mezclar (un poco difícil de ver en fondos blancos para que los colores estén etiquetados):
Imágenes para mezclar
Esto es lo que espero que suceda (y lo que sucede en paint.net):
Resultado Esperado
Obviamente, la función de mezcla predeterminada de opengl hace que se vea así (muy mal):
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
Después de un montón de pruebas, esto es lo más cerca que puedo llegar a crear una función de mezcla "buena":
glBlendFuncSeparate (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA)
Sin embargo, al mirar el resultado esperado original, notará que algunos de los colores son un poco más tenues de lo que deberían ser (la parte central izquierda). Específicamente, se premultiplican a la mitad de su valor de color (debido al .5 alfa), y parece que no puedo hacer una función que no haga esto (sin causar problemas de mezcla extraños con la parte transparente roja apenas visible).
¿Alguien sabe una solución a este problema? Una de las que tuve fue usar alfa premultiplicado en las fuentes (aunque no quiero hacer esto porque requiere un trabajo adicional para convertir cada color que uso en mi juego para premultiplicar o simplemente escribir algunas cosas en cada sombreador) y hacerlo así :
glBlendFuncSeparate (GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA) (Sin premultiplicación)
Obviamente, eso también está mal, pero este es el único resultado correcto que he obtenido hasta ahora:
glBlendFuncSeparate (GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA) (entradas premultiplicadas)
El único problema es, ¿cómo me desharía de la premultiplicación para mostrarla en la pantalla? Probablemente requerirá un ciclo de renderizado adicional para cada cosa que mezcle y eso parece demasiado complejo para este problema, por lo que todavía estoy buscando una respuesta (es interesante que no pueda encontrar nada sobre esto, porque OpenGL es tan ampliamente utilizado que Me imagino que alguien más se topó con este problema).
Fuentes:
Prueba de función de mezcla en línea: http://www.andersriggelsen.dk/glblendfunc.php
Imagen de la capa inferior: http://i.stack.imgur.com/XjLNW.png
Imagen de la capa superior : http: //i.stack.imgur .com / 9CN6w.png
fuente
Respuestas:
En su primer ejemplo (
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
), le está diciendo que mezcle colores basados en el alfa de la imagen que se dibuja en la parte superior (el "Primer plano"). Entonces, cuando combina 50% entre el color de primer plano(38,50,168)
y el color de fondo(38,50,168)
en la esquina inferior izquierda de la imagen, como era de esperar, obtiene exactamente el mismo color(38,50,168)
. Porque esa combinación cruzada es exactamente lo que le dijiste a OpenGL que hiciera.A juzgar por su imagen "esperada", no desea hacer una mezcla cruzada entre los dos colores: desea superponer el color de primer plano sobre el color de fondo de alta intensidad, no mezclar entre ellos.
Para hacer eso, desea usar glBlendFuncSeparate (ya que trata el color del fondo de manera diferente que su alfa), y especifica que durante la operación de fusión, el valor alfa del fondo debe tratarse como 100%.
Para facilitar la visualización, aquí se muestran los valores RGB finales:
Y aquí se muestran los valores finales de transparencia.
(Ese área de transparencia '100%' debería etiquetarse como 99.7% transparente, y el área inferior derecha '50% 'debería etiquetarse como 49.7% transparente, ambos debido al bit teñido de rojo en el lado derecho de la primera imagen. Además, perdón por enumerar números usando "transparencia" en lugar de opacidad; posiblemente un poco confuso).
Si hay problemas específicos, indique el área de la imagen RGB o alfa que están produciendo valores incorrectos.
fuente
GL_ONE
? De lo contrario, la ecuación alfa se ve asídest_alpha = src_alpha * src_alpha + 1 * dest_alpha
(0,0,0)
. Por supuesto , es más oscuro que cuando lo mezclas al 50% en contra(255,255,255)
.finalAlpha = srcAlpha + destAlpha * (1 - srcAlpha)
yfinalColor = ((srcColor * srcAlpha) + (destColor * destAlpha * (1 - srcAlpha))) / finalAlpha
ElColor * Alpha
bit en el cálculo del color se puede hacer en los sombreadores al generar valores premultiplicados, pero no hay forma de dividirlo por el Alfa final para deshacerse de la multiplicación previa. (Tomado de aquí )Tuve exactamente el mismo problema cuando quería renderizar un conjunto de objetos superpuestos framebuffer como capas. El problema es que las funciones de fusión de Open GL son lineales y funciona cuando el fondo de destino es opaco. Pero la ecuación de fusión real para mezclar dos capas transparentes no es una ecuación lineal y contiene una parte de división.
out_color = {src_color * src_alpha + dest_color * dest_alpha * (1-src_alpha)} / out_alpha
No creo que sea posible hacer esto con las implementaciones actuales de OpengGL. Entonces, como solución, tuve que usar un sombreador de píxeles para calcular manualmente la salida de cada píxel.
fuente