Modo de fusión "normal" con problemas de OpenGL

8

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

Imágenes de prueba

Esto es lo que espero que suceda (y lo que sucede en paint.net):

Resultado Esperado

Prueba de imagen Paint.Net

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)

Prueba de mezcla normal OpenGL

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)

Prueba de mezcla personalizada de OpenGL

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)

Prueba de mezcla personalizada de OpenGL

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)

Prueba de mezcla personalizada de OpenGL

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

Gota de limon
fuente
Todas las capturas de pantalla se han tomado de la herramienta en línea Visual glBlendFunc + glBlendEquation de Anders Riggelsen . En el futuro, sería útil mencionar ese tipo de cosas al pedir ayuda.
Trevor Powell
@TrevorPowell Dang Sabía que olvidé decir algo (realmente estaba planeando poner eso en la parte inferior). De cualquier manera, es justo lo que estaba usando para probar fácilmente los modos de mezcla, debería ser estándar para cualquier otra cosa OpenGL (aunque puedo ver a otras personas que quieran usarlo).
Lemon Drop

Respuestas:

5

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%.

trabajando

Para facilitar la visualización, aquí se muestran los valores RGB finales: RGB

Y aquí se muestran los valores finales de transparencia. alfa

(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.

Trevor Powell
fuente
¿No debería ser también el coeficiente alfa fuente GL_ONE? De lo contrario, la ecuación alfa se ve asídest_alpha = src_alpha * src_alpha + 1 * dest_alpha
bcrist el
Bueno, si lo miras, la pieza del centro de la izquierda sigue siendo mucho más oscura de lo que debería ser, también voltear las capas hace que se vea así: i.imgur.com/rigBNz6.png (ese rojo apenas visible está ahí para probar cosas como eso
Lemon Drop
Lo estás mezclando al 50% en contra (0,0,0). Por supuesto , es más oscuro que cuando lo mezclas al 50% en contra (255,255,255).
Trevor Powell
@TrevorPowell ¿No debería importar eso si el alfa es 0? Al igual que las ecuaciones que estoy buscando, son como: finalAlpha = srcAlpha + destAlpha * (1 - srcAlpha)y finalColor = ((srcColor * srcAlpha) + (destColor * destAlpha * (1 - srcAlpha))) / finalAlphaEl Color * Alphabit 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í )
Lemon Drop
0

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.

Supun Gothama
fuente
Sí, lo que hice fue pre-multiplicar el alfa en las texturas en lugar de hacer algunas soluciones, es fácil hacer eso con algunas bibliotecas (simplemente texturizarán previamente en carga)
Lemon Drop