Ponto V!

Home OpenGL Combinando cores (blending)
Vinícius Godoy de Mendonça
Combinando cores (blending)Imprimir
Escrito por Vinícius Godoy de Mendonça

A técnica de Alpha Blending não é tão recente quanto muitos podem pensar. Ela foi formalmente introduzida por T. Porter e T. Duff, num trabalho intitulado, "Compositing Digital Images", publicado na revista Computer Graphics, págs 253-259, em 1984. Ambos eram funcionários da Silycon Graphics, trabalhando para ninguém menos que a LucasFilm, em filmes como Star Trek, The Wrath of Khan. Esse filme também introduziu conceitos como os emissores de partículas, tema que veremos num próximo artigo.

A palavra Blending pode ser traduzida literalmente para "mistura". Não é a toa que Blender, significa "liquidificador" ou "misturador". Na OpenGL, configurar o modo de blending permite-nos dizer à API como misturar as cores, com base em seu canal alpha. Isso permite a criação de efeitos como transparência, ou brilho.

Nesse artigo vamos entender como funciona essa incrível capacidade da API.

Ativando o Blending

Antes de falarmos do Blending, vamos relembrar um pouco do processo de pintura. Normalmente, a OpenGL faz a pintura carregando dados no Color Buffer (buffer de cores, que representa os pixels a serem pintados na tela). Os bits de cor de cada fragmento são colocados no Depth Buffer (Buffer de profundidade, que indica o quão "para o fundo do monitor" o pixel está).

Quando a capacidade de Depth Test está desligada, novos valores de cor simplesmente sobrescrevem os valores presentes no buffer de cor. Quando ela está ligada, esses valores só sobrescreverão caso as primitivas estejam mais perto do que os já presentes, um teste feito simplesmente olhando os valores do Depth Buffer. É por isso que ao desenharmos imagens 3D, ligamos o DEPTH_TEST e também somos obrigados a limpar o DEPTH_BUFFER no comando glClear().

Quando ligamos a capacidade de Blending, as cores não são simplesmente sobrescritas. Ao invés disso, os fragmentos são combinados uns com os outros, através de uma fórmula. A forma que fazemos essa combinação permite-nos criar uma grande gama de efeitos. Para ligar a capacidade, usamos o comando glEnagle, como de costume:

glEnable(GL_BLEND);

Note que usamos um termo até então novo: fragmento. Quando a OpenGL processa as primitivas que enviamos para ela, ela as quebra em pedacinhos do tamanho de um pixel, chamados de fragmentos. Isso ocorre na fase de rasterização do pipeline. Embora muitos textos tratem pixels e fragmentos como a mesma coisa, existe sim, uma diferença sutil: pixels são as cores finais, que serão desenhadas no monitor. Sobre os fragmentos ainda serão aplicadas as operações de teste de alfa, blending, texturização, combinação com outros fragmentos, entre outras, para só então se tornarem um pixel.

Podemos entender o fragmento, então, como um pixel ainda não totalmente processado, que está sujeito a uma mudança em sua cor final.

Combinando cores

O processo de combinação de cores se dará através de uma função matemática muito simples, escolhida por você. Cada função gera um efeito diferente. A equação padrão do OpenGL é a de adição:

Cf = (Cs * S) + (Cd * D)

Claro, não faz muito sentido só olhar para fórmula sem entender seus termos. A letra C, na fórmula, refere-se aos fragmentos de cor. Existem duas cores sendo misturadas: a primeira é a cor já presente no buffer de cor, chamada cor de destino. A segunda, é a cor que está sendo aplicada sobre esse buffer, chamada de origem (source).

A cor de destino normalmente possui os quatro canais de cor (R, G, B e A), enquanto a de origem pode ter os quatro ou apenas três (sem alfa, que nesse caso é considerado igual a 1). Como já explicamos, na OpenGL representamos cada canal com um valor de 0 até 1, que representa o fator percentual daquela cor no canal.

Na fórmula, as letras S e D representam os fatores usados no blending, do destino e da origem, respectivamente. Esses valores são geralmente calculados a partir do canal alfa. Finalmente, como você já pode deduzir, Cf representa a cor final.

Os fatores S e D, usados no blending, são configurados através da função glBlendFunc, nós a utilizamos logo nos primeiros artigos, quanto brincamos com anti-alising, lembra-se?

Os dois parâmetros exigidos pela função são os fatores da origem e do destino, respectivamente. Por padrão, o valor da origem é GL_ONE, e do destino GL_ZERO. O que significa, que toda a cor da origem é aplicada sobre a cor do destino, que será totalmente ignorada no momento da pintura. Isso equivale ao comportamento da OpenGL sem o blending.

Os demais valores possíveis são:

Função Blending
RGB
Blending
Alpha
GL_ZERO (0,0,0) 0
GL_ONE (1,1,1) 1
GL_SRC_COLOR (Rs, Gs, Bs) As
SL_ONE_MINUS_SRC_COLOR (1,1,1) - (Rs, Gs, Bs) 1 – As
GL_DST_COLOR (Rd, Gd, Bd) Ad
GL_ONE_MINUS_DST_COLOR (1,1,1) - (Rd, Gd, Bd) 1 – Ad
GL_SRC_ALPHA (As, As, As) As
SL_ONE_MINUS_SRC_ALPHA (1,1,1) - (As, As, As) 1 – As
GL_DST_ALPHA (Ad, Ad, Ad) Ad
GL_ONE_MINUS_DST_ALPHA (1,1,1) - (Ad, Ad, Ad) 1 – Ad
GL_CONSTANT_COLOR (Rc, Gc, Bc) Ac
GL_ONE_MINUS_CONSTANT_COLOR (1,1,1) - (Rc, Gc, Bc) 1 - Ac
GL_CONSTANT_ALPHA (Ac, Ac, Ac) Ac
GL_ONE_MINUS_CONSTANT_ALPHA (1,1,1) - (Ac, Ac, Ac) 1 - Ac
GL_SRC_ALPHA_SATURATE (f, f, f), onde f = min(As, 1-Ad) 1

Agora que a matemática foi apresentada, que tal estudarmos a função que utilizamos para fazer transparência?

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_ALPHA);

Essa função diz à OpenGL para pegar a cor de origem (que está sendo desenhada) e multiplica-lá pelos seus valores de alfa. Em seguida, ela adicionará o resultado a cor de destino, multiplicada por um menos o valor do alfa da cor de origem. Note que o alfa da cor de destino não é levado em consideração nesse processo. É também interessante perceber que "um menos alfa" é o mesmo que dizer para inverter o alfa, uma vez que esse valor só varia de 0 até 1.

Vamos supor que você tenha a cor verde (0.0f, 1.0f, 0.0f), já desenhada na tela (destination color), e sobre ela queira aplicar a cor azul (0.0f, 0.0f, 1.0f) que tem um alfa definido em 0.6f (source color). Vamos ver como faríamos o cálculo manualmente:

Cor de destino = Cd = (0.0f, 1.0f, 0.0f)
Cor de origem = Cs = (0.0f, 0.0f, 1.0f)
S = alfa da origem = 0.6
D = 1 – S = 1.0 – 0.6 = 0.4

Agora a equação:

Cf = (Cs * S) + (Cd * D)

Pode ser transformada em:

Cf = (Azul * 0.6f) + (Verde * 0.4f)

Resolvendo a encrenca temos:

Cf = [(0,0,1) * 0.6f] + [(0,1,0) * 0.4f] = (0,0,0.6f) + (0,0.4f,0) = (0,0.4f, 0.6f)

O que faz bastante sentido para transparência, certo? Ainda não entendeu? Então pense no que esses números significam:

Se você colocar um vidro azul que obscurece 60% da passagem da luz, na frente de um objeto verde, você verá 40% da cor verde do objeto, e os outros 60% serão o azul do vidro:

Imagem que demonstra o blending citado

Alterando a equação de mistura

Na parte anterior, apenas mostramos qual era a equação de mistura padrão. Como vimos, o que ela faz é adicionar os pixels de uma operação sobre a outra. Entretanto, essa não é a única opção disponível.

O comando glBlendEquation, permite definir qual operação queremos fazer quando estivermos realizando misturas. Como parâmetro, ela aceita o modo de blending, e seus valores possíveis são:

Modo Função
GL_FUNC_ADD (padrão) Cf = (Cs * S) + (Cd * D)
GL_FUNC_SUBTRACT Cf = (Cs * S) - (Cd * D)
GL_FUNC_REVERSE_SUBTRACT Cf = (Cd * D) - (CS * S)
GL_MIN Cf = min(Cs, Cd)
GL_MAX Cf = max(Cs, Cd)

Essa função alterará a função que combina os valores RGBA da origem e do destino. Uma função ainda mais personalizada é a glBlendFuncSeparate que permite definir a função para os componentes RGB e alfa separadamente.

Finalmente, como visto anteriormente, alguns uns parâmetros de blending usam uma cor constante (GL_CONSTANT_COLOR, GL_ONE_MINUS_CONSTANT_COLOR, GL_CONSTANT_ALPHA e GL_ONE_MINUS_CONSTANT_ALPHA). Para definir que cor é essa, usamos a função glBlendColor.

A ordem de pintura importa

Embora já tenha sido dito, é melhor não deixar esse detalhe tão importante passar despercebido. Quando você está usando transparência, a ordem de pintura importa! Como vimos anteriormente, a origem é desenhada sobre um destino e se misturará à suas cores. Mas isso implica no fato de que o destino já tem que estar lá, desenhado!

Quando desenhamos sem transparência, normalmente deixamos o Depth Buffer cuidar dos detalhes de quem vai à frente e ao fundo para nós. E isso geralmente é suficiente. Quando você usa transparência, entretanto, ocorre um problema se você desenhar o objeto transparente primeiro. Ele irá preencher o Depth Buffer, e ficará "na frente" do objeto atrás dele. Quando você desenhar o objeto de trás, o teste de Depth Buffer falhará e não haverá mistura. Na verdade, o objeto de trás sequer será desenhado!

Existem algumas formas de lidar com esse problema. A primeira é começar desenhando todos os objetos opacos da cena e, somente em seguida, desenhar os objetos transparentes. Em jogos simples, essa abordagem é geralmente suficiente, pois o número de objetos transparentes é muito pequeno. E, muitas vezes, a única área onde temos transparência é no HUD – o Campeonato Bola Gelada é um exemplo de jogo assim.

Mas… e se houver objetos transparentes no desenho, uns sobre os outros? Existem duas maneira de resolver o problema:

  1. Você pode, durante o desenho dos objetos transparentes, desabilitar a escrita no Depth Buffer. Assim, seria como se você desabilitasse o DEPTH_TEST apenas na transparência. Para isso, basta passar o valor GL_FALSE para a função glDepthMask. Dessa forma, objetos não transparentes irão cobrir os não transparentes, mas um transparente não cobrirá outro. Infelizmente, essa abordagem, apesar de simples, só funciona para a função ADD (que por sorte é a mais comum) e não permite efeitos mais elaborados.
  2. O segundo método seria ordenar os objetos baseado na distância deles em relação a câmera, e então desenhar os objetos mais distantes por primeiro. Ou seja, efetivamente desenhando na ordem que o OpenGL espera. Esse método gera os melhores e mais confiáveis resultados, mas usa mais matemática, que não será coberta nesse artigo. É importante utilizá-lo somente em objetos transparentes pois essa ordenação é feita pela CPU, que geralmente é muito mais lenta que a placa de vídeo.

Note que é esse tipo de detalhe que começa a justificar a existência de motores gráficos como a Ogre3D. Em adição a ordenar as imagens sozinho, um motor pode até mesmo evitar totalmente o desenho e o cálculo de uma forma, caso ela esteja fora do campo de visão do observador, além de implementar outras otimizações, como otimizar cargas de texturas, por exemplo.

Finalizando

Nesse artigo, você viu o que é o blending e como funciona. Viu que a aplicação de blending nada mais é do que a avaliação de uma função simples, que leva em conta as cores do pixel da origem e do destino, associada ao seu valor de alfa.

Para se aprimorar mais um pouco, é muito interessante ler o artigo "Compositing Digital Images" indicado no início do texto, pois ele dá mais exemplos de efeitos baseados nessa técnica. O capítulo de Blending do RedBook também pode fornecer outras informações importantes.

Esse artigo usou como referências os livros OpenGL SuperBible e Beginning OpenGL Game Programming, além do próprio artigo já citado. Além disso, somei alguns comentários próprios, baseados em minha experiência.

No próximo artigo, mostraremos efeitos comumente produzidos com essa técnica, sendo eles o suaviação de serrilhamento (anti-aliasing) e a neblina.


Comentários (14)
  • Danny
    avatar

    Artigo introdutório de conceito bom, mas o maior problema no ensino de opengl hoje em dia, é que são ensinadas coisas antigas atrapalhando o aprendizado de opengl moderno.

    Fixed pipeline deve ser evitado (à não ser em caso de compatibilidade com máquinas antigas ou iphone antigo ogles1).

    Mas para que meu comentário seja mais útil, aqui está algo um pouco melhor que o glBlendFunc, o glBlendFuncSeparate:

    http://www.opengl.org/sdk/docs/man/xhtml/glBlendFuncSeparate.xml

    Ele separa o blending de RGB e o blending de Alpha, dando um pouco mais de liberdade para criar efeitos variados.

  • Vinícius Godoy de Mendonça
    avatar

    É verdade. A idéia é depois de terminar o pipeline fixo, focar mais no ensino de Shaders e no pipeline programável.

    Ainda considero o pipeline fixo uma boa introdução ao assunto. Pode-se fazer muita coisa rapidamente nele, e mostrar os conceitos básicos de vértice, lado, cores, etc.

    Mas, realmente, não recomendo o pipeline fixo para se programar jogos hoje em dia. Nem mesmo as tecnologias mobile ou webgl o utilizam.

  • Rogério Kund
    avatar

    ótimo tuto será muito util para meuis próximos projetos de jogos.
    Bem eu sou programador, game designer e designer gráfico ou seja crio meus próprios jogos em c++, visual basic, small basic, blitz3D ... Acesse meu site e veja alguns de meus tutoriais.

  • Vinícius Godoy de Mendonça
    avatar

    Bacana o site. Avise-nos se tiver interesse em publicar algum artigo aqui sobre algo que conheça. ;)

  • Emerson MX
    avatar

    Muito bom os artigos Vinicius, nunca pensei que fosse tão simples fazer uma aplicação com OpenGL usando a SDL. :D

    Só que fiquei com uma duvida, você usa a OpenGL que vem com o compilador (acho que é a versão 1). Eu vi no site da OpenGL que eles tão na versão 4.1! Quais são as principais diferenças entre essas versões?

  • Vinícius Godoy de Mendonça
    avatar

    Cada versão inclui algumas funções a mais. Seja para controlar o tamanho do ponto, fazer multi-texturas, algumas opções de iluminação, etc.

    A OpenGL que vem com o compilador (se você o mantiver atualizado) é, geralmente, a última versão. O que você tem que ver é se as libs da sua máquina suportam essa versão, e isso vai depender da placa de vídeo.

    Nos artigos, uso a OpenGL 1 pois há menos chances também de um tutorial não rodar na casa de alguém.

  • Irving
    avatar

    Parabens pelos artigos!! Muito bom mesmo
    Espero qeu continue com trabalho
    abração

  • Vinícius Godoy de Mendonça
    avatar

    Obrigado! :)

  • Leandro  - Duvida sobre uso especifico
    avatar

    Parabens pelo artigo, muito bom para começar a estudar o assunto!

    Após ler me surgiu uma duvida, geralmente sprite atlas vem com fundo rosa... e me disseram que a maneira certa de tirar esse rosa de fundo e deixar transparente quando for usar o atlas com opengl seria usar blending... Mas como fazer isso??? Alguem poderia me dar uma luz para esse caso de uso?

    Não tenho nenhum ideia de como verificar se é rosa e substituir por transparente se for...

    Muito obrigado pelo artigo e espero que continue com o otimo trabalho :)

  • Vinícius Godoy de Mendonça
    avatar

    De que atlas vc está falando?

    Enfim, o que você está pedindo é chamado de "Color keying". A OpenGL não tem suporte para esse recurso. Mas existe duas formas de emula-lo:
    a) Usar um shader com a GLSL;
    b) Usar a SDL para transformar essa cor em alfa (para isso, vc terá que percorrer a imagem pixel a pixel e subtituir a cor);

    O jeito mais recomendável mesmo é você achar um editor (como o Gimp) e usá-lo para limpar o fundo e usar alpha de verdade.

  • Leandro
    avatar

    Entendo, os Atlas que falo são aquelas imagens que reune nela varios sprites, assim você vai dar bind em apenas uma imagem para desenhar todo seu cenario com tiles... é para jogos 2d =)
    Pensei que isso fosse simples já que na internet todos os conjuntos de sprites que vejo tem fundo rosa hehe...

    Como estou começando a fazer jogos para android foi ficar com a opção mais facil que é usar o photoshop para deixar transparente hehe... Não conheço muito bem ainda as limitações do OpenGL ES 1.1, nem sei se tem como utilizar shader no android hehe

    Obrigado pela resposta.

  • Bruno Crivelari Sanches
    avatar

    Na verdade você vai ter que converter o rosa para um canal alpha, dai basta usar os parâmetros corretos (veja a tabela que tem no artigo) para especificar que deve considerar o canal alpha da textura.

    T+

  • Francisco  - Blending
    avatar

    Estou criando uma engine para games 2D com intuito acadêmico porém ao criar a classe de sprites me deparei com algumas dúvidas.
    ---------------------------------------------------------------------- --------------------------
    0)O que é o "Color keying" ?
    ---------------------------------------------------------------------- --------------------------
    1)Como implementar a técnica do "Color keying" citado acima por Vinícius Godoy de Mendonça, utilizando GLSL 3.2 ou 3.3 ou até mesmo uma pseudo linguagem?
    ---------------------------------------------------------------------- --------------------------
    2)"Color keying" é diferente de "Alpha channel transparency"? Se possível me explique.
    ---------------------------------------------------------------------- --------------------------
    3)Observei que as fontes na internet que falam sobre sprites tendem a utilizar formatos .gif.Qual a vantagem? Conhece algum tutorial que explique de forma resumida o formato .gif?
    ---------------------------------------------------------------------- --------------------------

    Vinícius Godoy ótimo trabalho.Sua didática para ensino é muito boa.Espero que o site poste bastantes tutoriais sobre pipeline programável e introduza a linguagem GLSL. Parabéns!

  • ViniGodoy
    avatar

    0) É quando você opta por uma cor, que se transformará em transparente, ao invés de dedicar um canal inteiro de cor para isso. Isso explica a cor de fundo de imagens como essa: http://www.spacearkstudios.com/wp-content/uploads/2011/01/game-starlig ht-sprite.jpg. rgb(1,0,1)

    1) Você só irá desenhar a textura quando a cor for diferente da do color key em questão. Nos demais casos, deixe alpha=1.

    2) A transparência com Alpha Channel dedica um canal inteiro de cor para regular a quantidade de blending que será feito. É exatamente o que expliquei no artigo. O color key usa uma única cor, ou seja, ou você tem 100% de transparência naquela cor, ou não tem nada. A vantagem de trabalhar com color keying é que você permite transparência até em imagens sem canal alfa, o que pode poupar espaço. Por isso, a maior parte das engines tem suporte tanto a alpha blending, quanto a keying.

    3) Nenhuma vantagem. Eu geralmente recomendo o PNG, que possui compressão sem perdas e não existem royalties associados.

Escrever um comentário
Your Contact Details:
Gravatar enabled
Comentário:
[b] [i] [u] [url] [quote] [code] [img]   
:angry::0:confused::cheer:B):evil::silly::dry::lol::kiss::D:pinch::(:shock:
:X:side::):P:unsure::woohoo::huh::whistle:;):S:!::?::idea::arrow:
Security
Por favor coloque o código anti-spam que você lê na imagem.
LAST_UPDATED2  

Busca

Linguagens

Twitter