Modo de mistura "normal" com problemas no OpenGL

8

Eu tenho tido muitos problemas ao tentar fazer com que uma função de mistura do OpenGL funcionasse como eu esperava, como o que eu esperaria (ou de qualquer programa de edição de imagem sensível). Como exemplo, usarei essas duas imagens para mesclar (um pouco difícil de ver em fundos brancos para que as cores sejam rotuladas):

Imagens a serem mescladas

Imagens de teste

É isso que espero que aconteça (e o que acontece no paint.net):

resultado esperado

Teste de Imagem Paint.Net

Obviamente, a função de mistura padrão do opengl faz com que seja assim (muito errado):

glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

Teste de mistura normal do OpenGL

Após uma tonelada de testes, este é o mais próximo que pude de criar uma função de mistura "boa":

glBlendFuncSeparate (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA)

Teste de mistura personalizada do OpenGL

Porém, olhando para o resultado original esperado, você notará que algumas das cores são um pouco mais escuras do que deveriam (a parte central esquerda). Especificamente, eles são pré-multiplicados à metade de seu valor de cor (por causa do 0,5 alfa), e não consigo criar uma função que não faça isso (sem causar problemas de mistura estranhos com a parte transparente vermelha quase invisível).

Alguém conhece uma solução para esse problema? Uma que eu tinha era usar alfa pré-multiplicado nas fontes (embora eu não queira fazer isso porque exige trabalho extra para converter todas as cores que eu uso no meu jogo para pré-multiplicar ou apenas escrever algumas coisas em cada sombreador) e fazer assim :

glBlendFuncSeparate (GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA) (sem pré-multiplicação )

Teste de mistura personalizada do OpenGL

Obviamente, isso também está errado, mas este é realmente o único resultado correto que obtive até agora:

glBlendFuncSeparate (GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA) (entradas pré-multiplicadas)

Teste de mistura personalizada do OpenGL

O único problema é: como eu me livraria da pré-multiplicação para exibi-la na tela? Provavelmente, exigiria um ciclo de renderização adicional para cada coisa que eu misturar e isso parece muito complexo para esse problema, então ainda estou procurando uma resposta (é interessante que eu não consiga encontrar nada sobre isso, porque o OpenGL é tão amplamente usado que Eu imagino que alguém tenha encontrado esse problema).

Fontes:

Teste on-line da função de mistura - http://www.andersriggelsen.dk/glblendfunc.php
Imagem da camada inferior - http://i.stack.imgur.com/XjLNW.png
Imagem da camada superior - http: //i.stack.imgur .com / 9CN6w.png

Gota de limão
fonte
Todas as capturas de tela foram tiradas da ferramenta Visual glBlendFunc + glBlendEquation online de Anders Riggelsen . No futuro, esse tipo de coisa seria útil mencionar ao pedir ajuda.
Trevor Powell
@TrevorPowell Dang Eu sabia que tinha esquecido de dizer algo (eu estava realmente planejando colocar isso no fundo). De qualquer maneira, é exatamente o que eu estava usando para testar facilmente os modos de mesclagem, deve ser padrão para qualquer outro tipo de coisa OpenGL (mesmo que eu possa ver outras pessoas querendo usá-lo).
Drop Drop

Respostas:

5

No seu primeiro exemplo ( glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)), você está dizendo para ele misturar cores com base no alfa da imagem que é desenhada na parte superior (o "Primeiro plano"). Portanto, quando você mescla 50% entre a cor do primeiro plano (38,50,168)e a cor do plano de fundo (38,50,168)no canto inferior esquerdo da imagem, obtém, sem surpresa, exatamente a mesma cor (38,50,168). Porque essa mistura cruzada é exatamente o que você disse ao OpenGL para fazer.

A julgar pela sua imagem "esperada", você não deseja fazer uma mistura cruzada entre as duas cores - você deseja sobrepor a cor do primeiro plano à cor de fundo com força total, e não a mistura cruzada entre elas.

Para fazer isso, você deseja usar glBlendFuncSeparate (já que está tratando a cor do plano de fundo de maneira diferente da alfa) e especifique que, durante a operação de mesclagem, o valor alfa do plano de fundo seja tratado como 100%.

trabalhando

Para facilitar a visualização, eis os valores finais de RGB sendo produzidos: RGB

E aqui estão os valores finais de transparência sendo produzidos. alfa

(Essa área de transparência '100%' deve realmente ser rotulada como 99,7% transparente, e a área '50% 'inferior direita deve ser rotulada como 49,7% transparente, ambas devido ao bit vermelho no lado direito da primeira imagem. Além disso, lamento listar números usando "transparência" em vez de opacidade; possivelmente um pouco confuso.)

Se houver problemas específicos, aponte a área da imagem RGB ou alfa que está produzindo valores incorretos.

Trevor Powell
fonte
O coeficiente alfa de origem também não deveria ser GL_ONE? Caso contrário, as equações alfa parece comdest_alpha = src_alpha * src_alpha + 1 * dest_alpha
bcrist
Bem, se você observar que a peça do meio à esquerda ainda está muito mais escura do que deveria, também virar as camadas faz com que pareça: i.imgur.com/rigBNz6.png (esse vermelho quase invisível está lá para testar coisas como Isso
Lemon Drop
Você está misturando 50% contra (0,0,0). De curso é mais escuro do que quando você misturá-la em 50% contra (255,255,255).
Trevor Powell
@TrevorPowell Isso não deveria importar se o alfa é 0? Como as equações que eu estou procurando, são como: finalAlpha = srcAlpha + destAlpha * (1 - srcAlpha)e finalColor = ((srcColor * srcAlpha) + (destColor * destAlpha * (1 - srcAlpha))) / finalAlphaO Color * Alphabit no cálculo de cores pode ser feito nos shaders emitindo valores pré-multiplicados, mas não há como dividir pelo finalAlpha para se livrar da pré-multiplicação. (Retirado daqui )
Lemon Drop
0

Eu tive exatamente o mesmo problema quando queria renderizar um conjunto de objetos de buffer de estrutura sobrepostos, como camadas. O problema é que as funções de mesclagem Open GL são lineares e funcionam quando o fundo de destino é opaco. Mas a equação de mesclagem real para mesclar duas camadas transparentes não é uma equação linear e contém uma parte de divisão.

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

Eu não acho que é possível fazer isso com as implementações atuais do OpengGL. Portanto, como solução alternativa, tive que usar um sombreador de pixels para calcular manualmente a saída de cada pixel.

Supun Gothama
fonte
Sim o que eu fiz foi apenas premultiply o alfa nas texturas em vez de fazer algumas soluções, é fácil de fazer isso com algumas bibliotecas (eles texturas apenas premultiply sobre a carga)
gota de limão