A transparência pode não parecer um tópico interessante. O formato GIF, que permitiu alguns pixels brilharem no fundo, foi publicado há mais de 30 anos. Quase todos os aplicativos de design gráfico lançados nas últimas duas décadas suportam a criação de conteúdo translúcido. Esses conceitos deixaram de ser algo novo.
No meu artigo, quero mostrar que, de fato, a transparência nas imagens digitais é muito mais interessante do que parece - no que damos como certo, há profundidade e beleza invisíveis.
Opacidade
Se você já viu através de óculos cor de rosa, pode ver algo semelhante ao mostrado na figura abaixo. [No artigo original, muitas imagens são interativas.] Tente mover os óculos para ver como eles influenciam o que é visível através deles:
Esses óculos funcionam da seguinte maneira: eles perdem muito vermelho, uma quantidade decente de azul e muito pouco verde. A matemática desses pontos pode ser escrita em um conjunto de três equações. A letra
R indica o resultado da operação e a letra
D descreve o ponto que estamos vendo. Os índices RGB indicam componentes vermelho, verde e azul:
R R = D R × 1.0
R G = D G × 0.7
R B = D B × 0.9
Esse vitral transmite componentes vermelhos, verdes e azuis do fundo com diferentes pontos fortes. Em outras palavras, a
transparência dos óculos cor
de rosa depende da cor da luz incidente. Em geral, a transparência pode
variar dependendo do comprimento de onda da luz , mas neste exemplo simplificado, estamos interessados apenas em como os óculos afetam os componentes RGB clássicos.
Simular o comportamento de óculos de sol comuns é muito mais simples, eles geralmente atenuam a luz incidente em alguma quantidade:
Esses óculos permitem que apenas 30% da luz passe por eles. Seu comportamento pode ser descrito pelas seguintes equações:
R R = D R × 0.3
R G = D G × 0.3
R B = D B × 0.3
Todos os três componentes de cor são reduzidos pelo mesmo valor - a absorção da luz incidente é a mesma. Podemos dizer que os óculos escuros são 30% transparentes (opacos) ou 70% opacos.
A opacidade de um objeto determina quanta cor ele bloqueia. Em computação gráfica, geralmente lidamos com um modelo simplificado no qual apenas um valor é necessário para descrever essa propriedade. A opacidade pode variar espacialmente. como, por exemplo, uma coluna de fumaça que está se tornando mais alta e mais transparente.
No mundo real, objetos com uma opacidade de 100% são simplesmente opacos e não transmitem luz. O mundo das imagens digitais é um pouco diferente. Existem casos limítrofes quando até objetos opacos sólidos passam uma certa quantidade de luz.
Cobertura
Os gráficos vetoriais lidam com descrições claras e infinitamente precisas de formas definidas usando pontos, segmentos de linha, curvas de Bezier e outras primitivas matemáticas. Quando você precisa exibir figuras na tela do computador, essas entidades impecáveis precisam ser rasterizadas em um bitmap:
Rasterização de uma forma vetorial para um bitmapA maneira mais primitiva de rasterizar é verificar onde a amostra de pixels está localizada - dentro ou fora da forma vetorial. Nos exemplos abaixo, você pode arrastar o triângulo, em uma visão ampliada, os movimentos serão mais precisos. Um contorno azul indica a geometria original do vetor. Como você pode ver, a escada nas bordas do triângulo parece feia e tremula bastante ao mover a geometria:
A desvantagem dessa abordagem é que realizamos apenas uma verificação para cada pixel exibido, e os resultados são discretizados para um dos dois valores possíveis - dentro ou fora.
Você pode experimentar a geometria vetorial várias vezes por pixel para obter uma grande gradação de etapas e decidir que alguns pixels estão apenas
parcialmente fechados. Uma solução possível é usar quatro pontos de amostragem para representar cinco níveis de cobertura: 0,
1⁄4 ,
2⁄4 ,
3⁄4 e 1:
A qualidade das arestas do triângulo melhorou, mas apenas cinco níveis possíveis de cobertura geralmente não são suficientes e podemos facilmente obter um resultado muito melhor. Embora a visualização de um pixel como um pequeno quadrado no mundo do processamento de sinais seja
desaprovada , em alguns contextos é um modelo útil que nos permite calcular a cobertura exata de um pixel por geometria vetorial. A interseção de uma linha e um quadrado sempre pode ser decomposta em um
trapézio e um
retângulo :
Um segmento de linha divide um quadrado em um trapézio e um retânguloVocê pode calcular facilmente a área de ambas as partes, e sua soma dividida pela área do quadrado determina a porcentagem de cobertura de pixels. Assim, a cobertura é calculada como um número exato com precisão arbitrária. Na demonstração mostrada abaixo, esse método é usado para renderizar arestas muito melhores que permanecem lisas, mesmo ao arrastar um triângulo:
Quando se trata de formas mais complexas, como elipses ou
Beziers , elas geralmente são divididas em segmentos de linha reta simples que permitem calcular a cobertura com a precisão correta.
O conceito de cobertura parcial é fundamental para a renderização de alta qualidade de gráficos vetoriais e, mais importante, para renderizar texto. Se você capturar uma captura de tela deste artigo e considerá-la com cuidado, notará que quase todas as arestas dos glifos cobrem pixels apenas parcialmente:
A cobertura parcial é usada ativamente na renderização de textoTendo a opacidade do objeto e cobrindo-o com pixels individuais, você pode combiná-los em um valor.
Alpha
O produto da opacidade de um objeto e sua cobertura de pixel é chamado
alfa :
= ×
Um objeto com 60% de opacidade, cobrindo 30% da área de pixel, tem um valor alfa de 18% nesse pixel. Naturalmente, quando o objeto é transparente ou não cobre completamente o pixel, o valor alfa desse pixel é 0. Após a multiplicação, as diferenças entre opacidade e revestimento desaparecem, o que justifica, de certo modo, o fato de que os conceitos de "alfa" e "opacidade" são usados como sinônimos.
Alfa é frequentemente representado como um quarto canal de uma imagem de bitmap. Os valores usuais de vermelho, verde e azul são complementados por um valor alfa, formando quatro valores RGBA.
Quando se trata de armazenar valores alfa na memória, existe uma tentação de usar apenas alguns bits para isso. No caso de cobrir os pixels das bordas de objetos opacos, parece que 4 ou até 3 bits serão suficientes, dependendo da densidade de pixels da tela:
No entanto, a opacidade também afeta o valor alfa; portanto, a baixa profundidade de bits pode ser catastrófica em alguns casos de alteração suave da transparência. A imagem abaixo mostra um gradiente de preto opaco a branco, o que demonstra que a baixa profundidade de bits resulta em variações de cores muito fortes:
Obviamente, quanto mais bits, melhor e mais frequentemente o alpha 8 usa uma profundidade de 8 para corresponder à precisão dos componentes de cores, razão pela qual muitos buffers RGBA ocupam 32 bits por pixel. Também é importante notar que, diferentemente dos componentes de cores, que geralmente são codificados usando transformação não linear, o alfa é armazenado linearmente - o valor codificado de 0,5 corresponde a um valor alfa de 0,5.
Falando em alfa, ignoramos completamente todos os outros componentes de cores, mas, além de bloquear a cor de fundo, o próprio pixel pode adicionar um pouco de cor. A idéia é bastante simples - um objeto rosa translúcido bloqueia parte da iluminação de fundo recebida e emite ou reflete um pouco de luz rosa:
Observe que ele não
se comporta como vitral. O vidro simplesmente bloqueia parte da iluminação de fundo com brilho diferente. Se você olhar para um objeto completamente preto através de vidro rosa, sua escuridão permanecerá, porque o objeto preto não emite e não reflete nenhuma luz. No entanto, o objeto rosa translúcido
adiciona sua própria luz. Se você o colocar em cima de um objeto preto, o resultado será rosado. Um bom análogo desse comportamento é o material fino suspenso no ar, como neblina, fumaça, névoa ou algum pó colorido.
Renderizar um canal alfa é um pouco mais difícil - um objeto perfeitamente transparente é invisível por definição; portanto, para distinguir entre objetos, precisamos usar dois truques. Um fundo quadriculado mostra quais partes da imagem são transparentes; Esse padrão é usado em muitas aplicações gráficas:
Padrão de xadrez mostra peças transparentes.Os quatro pequenos quadrados abaixo da imagem nos dizem que vemos os componentes vermelho, verde, azul e alfa da imagem. Em alguns casos, é útil ver diretamente os valores dos canais alfa, e a maneira mais fácil de exibi-los é usando tons de cinza:
Exibir valores RGB e A em diferentes superfíciesQuanto mais brilhante o tom de cinza, maior o valor de alfa, ou seja, preto puro corresponde a 0% alfa e branco puro a 100% alfa. Pequenos quadrados indicam que os componentes RGB e A da imagem estão divididos em duas partes.
O componente alfa em si não é particularmente útil, mas se torna muito importante quando falamos sobre composição.
Composição simples
Muito poucos efeitos da renderização 2D podem ser implementados em uma operação e, para criar um resultado final, usamos um processo de
composição que combina várias imagens. Por exemplo, um simples botão "Cancelar" pode ser criado compondo cinco elementos separados:
Elementos de composição para o botão CancelarA composição é geralmente realizada em várias etapas, em cada uma das quais duas imagens são combinadas. A imagem em primeiro plano usada na composição é comumente chamada de
origem . A imagem de plano de fundo usada na composição, na qual a fonte é sobreposta, geralmente é chamada de
destino .
Começaremos compondo em um fundo opaco, porque esse é um caso muito comum. Tudo o que você vê na tela é sobreposto pela composição em um destino opaco.
Quando o valor alfa da fonte é 100%, a fonte é opaca e deve cobrir completamente o destino. Se o valor alfa for 0%, a origem será completamente transparente e não afetará o destino de forma alguma. Um valor alfa de 25% permite que o objeto emita 25% de sua luz e passe 75% da luz do fundo, e assim por diante:
Compondo fonte roxa com diferentes valores alfa para o destino amareloVocê já pode entender para o que tudo está indo - um caso simples de composição alfa em um fundo opaco - é apenas interpolação linear entre as cores de destino e de origem. No gráfico abaixo, o controle deslizante controla o valor alfa da fonte e os gráficos vermelho, verde e azul exibem os valores dos componentes RGB. O resultado de
R é apenas uma mistura entre a origem
S e o destino
D :
O que acontece aqui pode ser descrito pelas equações mostradas abaixo. Como antes, o índice indica o componente, ou seja, SA é o valor alfa na origem e DG é o valor verde no destino:
R R = S R × S A + D R × (1 − S A )
R G = S G × S A + D G × (1 − S A )
R B = S B × S A + D B × (1 − S A )
As equações para os componentes vermelho, verde e azul têm a mesma aparência; portanto, você pode simplesmente usar o índice
RGB e combiná-los em uma linha:
R RGB = S RGB × S A + D RGB × (1 − S A )
Além disso, como o destino é opaco e já bloqueia toda a luz de fundo, sabemos que o valor alfa do resultado é sempre 1:
R A = 1
A composição em um fundo opaco é simples, mas é bastante limitada em recursos. Em muitos casos, é necessária uma solução mais confiável.
Buffers intermediários
A imagem abaixo mostra o processo de duas etapas de composição de três camadas diferentes, rotuladas como A, B e C. O símbolo ⇨ significa "sobreposto pela composição em":
O resultado da composição em duas etapas de três camadasPrimeiro, sobrepomos B com C compondo e depois sobrepomos A para obter a imagem final. No exemplo a seguir, faremos as coisas de maneira um pouco diferente. Primeiro, conectaremos as duas camadas superiores pela composição e, em seguida, sobreporemos o resultado no último destino:
O resultado da composição em duas etapas de três camadas em uma ordem diferenteVocê provavelmente está se perguntando se essa situação surge na prática, mas na verdade é muito comum. Muitas operações de composição não triviais e efeitos de renderização, como mascaramento e desfoque, exigem a passagem por um buffer intermediário contendo apenas resultados parciais de composição. Esse conceito tem nomes diferentes: passes fora da tela, camadas de transparência ou buffers laterais, mas geralmente são baseados na mesma idéia.
O que é mais importante para nós é que quase
qualquer imagem com transparência pode ser percebida como resultado parcial de alguma renderização, que mais tarde será sobreposta pela composição no último destino:
Composição parcial de um botão em uma área de transferênciaPrecisamos entender como substituir a composição das imagens translúcidas A e B por uma imagem (A⇨B) com a mesma cor e opacidade. Vamos começar calculando o valor alfa do buffer final.
Combinando valores alfa
Pode não estar claro para você como combinar a opacidade de dois objetos, mas é mais fácil falar sobre essa tarefa se falarmos sobre transparência.
Suponha que uma certa quantidade de luz passe pelo primeiro objeto e depois pelo segundo objeto. Se a transparência do primeiro objeto for 80%, ela passará 80% da luz incidente. Da mesma forma, um segundo objeto com 60% de transparência permitirá que 60% da luz passe por ele, o que nos dá 60% × 80% = 48% da luz original. Você pode experimentar a transparência no artigo original; não esqueça que os controles deslizantes controlam a
transparência e não a opacidade dos objetos no caminho da luz:
Naturalmente, quando o primeiro ou o segundo objeto é opaco, nenhuma luz passa através deles, mesmo outro é completamente transparente.
Se o objeto D tiver transparência DT e o objeto S tiver transparência ST, a transparência geral final RT desses dois objetos será igual ao seu produto:
TR = DT × S T
No entanto, a transparência é apenas uma unidade menos alfa, portanto, a substituição nos fornece o seguinte:
1 - RA = (1 - DA) × (1 - SA)
Esta expressão pode ser expandida para:
1 - R A = 1 - D A - S A + D A × S A
E simplifique assim:
R A = D A + S A - D A × S A
Pode ser reduzido para um de dois tipos semelhantes:
R A = S A + D A × (1 - S A )
R A = D A + S A × (1 - DA)
Em breve veremos que o segundo é mais usado. Também é interessante notar que o valor alfa resultante não depende da ordem relativa dos objetos - a opacidade dos pixels resultantes é a mesma, mesmo se você trocar a origem e o destino. Isso é muito lógico. A luz que passa através de dois objetos deve desaparecer da mesma maneira, de qualquer lado da estrela, de frente ou de trás.
Combinação de cores
O cálculo do alfa não foi tão difícil, então vamos tentar entender os cálculos dos componentes RGB. A imagem de origem tem a cor S
RGB , mas sua opacidade S
A força apenas o produto desses dois valores em consideração no resultado final:
S RGB × S A
A imagem de destino tem a cor D
RGB , a opacidade faz emitir luz D
RGB × D
A ; no entanto, parte da luz é bloqueada pela opacidade da imagem S, portanto, toda a influência do destino é igual a:
D RGB × D A × (1 - S A )
A contribuição total da luz de S e D é igual à sua soma:
S RGB × S A + D RGB × D A × (1 - S A )
Da mesma forma, a contribuição das camadas mescladas é igual à sua cor vezes sua opacidade:
R RGB × R A
Queremos que esses dois valores correspondam:
R RGB × R A = S RGB × S A + D RGB × D A × (1 - S A )
O que nos dá as equações finais:
R A = S A + D A × (1 - S A )
R RGB = (S RGB × SA + D RGB × SA × (1 - SA)) / RA
Veja como a segunda equação é complicada! Observe que, para obter os valores RGB do resultado, precisamos dividir pelo valor alfa. No entanto, para o próximo estágio de composição, a multiplicação pelo valor alfa será novamente necessária, porque o resultado da operação atual se tornará a nova fonte ou destino da próxima operação. É simplesmente feio.
Vamos voltar à forma quase final do R
RGB por um segundo:
R RGB × R A = S RGB × S A + D RGB × D A × (1 - S A )
Origem, destino
e resultado são multiplicados pelos seus componentes alfa. Isso nos faz entender que a cor e o alfa do pixel “gostam” de estar juntos, portanto, precisamos dar um passo atrás e repensar a maneira como armazenamos informações sobre cores.
Alfa pré-multiplicado
Lembre-se de que falamos sobre opacidade - se o objeto for parcialmente opaco, sua contribuição para o resultado também será parcial. O conceito de alfa pré-multiplicado (“pré-multiplicação por alfa”) implementa essa ideia. Os valores dos componentes RGB, como o nome indica, são pré-multiplicados pelo componente alfa. Vamos começar com a cor sem multiplicação preliminar:
(1,00, 0,80, 0,30, 0,40)
A multiplicação preliminar por alfa nos fornece o seguinte:
(0,40, 0,32, 0,12, 0,40)
Vamos dar uma olhada em vários pixels por vez. A figura abaixo mostra como as informações de cores são armazenadas sem primeiro multiplicar o alfa:
Informações RGB e A na imagem sem multiplicação préviaObserve que as áreas onde alfa é 0 podem ter valores RGB arbitrários, como pode ser visto nas falhas verdes e azuis na imagem. No caso de multiplicação preliminar por alfa, as informações de cores também armazenam valores de opacidade de pixel:
Informações RGB e A em uma imagem pré-multiplicadaAlfa pré-multiplicado às vezes é chamado de alfa associado, e alfa não pré-multiplicado é às vezes chamado de alfa linear ou não associado.
Quando o componente alfa da cor é 0, a multiplicação preliminar redefine todos os outros componentes, independentemente de seus valores:
(0,0, 0,0, 0,0, 0,0)
No caso do alfa pré-multiplicado, existe apenas
uma cor completamente transparente, e isso é encantador.
As vantagens desse processamento de componentes de cores ficarão gradualmente claras para você, mas antes de voltarmos ao exemplo de composição, vamos ver como o alfa pré-multiplicado ajuda a resolver outros problemas de renderização.
Filtragem
O desfoque gaussiano é uma maneira popular de criar um plano de fundo desfocado interessante ou reduzir a
alta frequência da parte do plano de fundo do conteúdo de alguns elementos da interface do usuário. Como veremos, a alfa de pré-multiplicação é crítica para criar o desfoque de aparência correta.
A imagem que analisaremos é criada preenchendo o fundo com 1% de azul opaco, sobre o qual um círculo vermelho opaco é desenhado. Primeiro, vamos ver um exemplo sem multiplicação preliminar. Separei os canais RGB do canal alfa para entender o que estava acontecendo. A seta indica a operação de desfoque:
Desfocar o conteúdo sem multiplicação préviaO resultado final tem um feio halo azul. Isso aconteceu porque o fundo azul vazou para a área vermelha durante o desfoque e, somente
então , durante a composição, o peso alfa foi adicionado a ele.
Quando as cores são pré-multiplicadas por alfa, o resultado está correto:
- 1% , .
, destination — , « ». , , , - , . , , , destination:
destination, destination, — « » (nearest-neighbor interpolation), .
destination.
. ( ), , . source destination, :
, .
, :
, , , «» . ,
[
], — .
, premultiplied alpha , :
—
.
, , , . , - , .
. :
R RGB ×R A = S RGB ×S A + D RGB ×D A ×(1 − S A )
premultiplied alpha, , . :
R RGB = S RGB + D RGB ×(1 − S A )
:
R A = S A + D A × (1 − S A )
, , - , , :
R = S + D × (1 − S A )
, premultiplied alpha . , .
:
R = S + D × (1 − S A )
source-over, sover normal, , , . , -, .
source-over, ,
. . :
R = (((A⇨B)⇨C)⇨D)⇨E
R = (A⇨B)⇨(C⇨(D⇨E))
R = A⇨(B⇨(C⇨(D⇨E)))
, . , , , .
source-over, . .
-
1984
“Compositing Digital Images” . premultiplied alpha source-over, -, , .
, .
Over
, . destination «», source «». , . - . . - source-over, :
R = S + D × (1 − S A )
R = S × (1 − D A ) + D
destination-over, , «» source-over — destination source , destination source. , destination-over , ,
.
Out
source-out destination-out source destination:
R = S × (1 − D A )
R = D × (1 − S A )
Destination-out, - destination.
In
source-in destination-in :
R = S × D A
R = D × S A
.
Atop
source-atop
destination-atop
, destination:
R = S × D A + D × (1 − S A )
R = S × (1 − D A ) + D × S A
Xor
(
xor
)
source, destination, :
R = S × (1 − D A ) + D × (1 − S A )
Source, Destination, Clear
.
Source
,
copy
, source. ,
destination
source
destination
.
clear
:
R = S
R = D
R = 0
.
clear
, , . ,
source
, , source.
-
, , . , . . , , , :
, , , - , .
-, , . Source F
S destination, F
D :
R = S×F S + D×F D
F
S 0, 1, D
A 1 − D
A , F
D 0, 1, S
A 1 − S
A . source destination , , , . :
. , .
, F
S , F
D 1.
plus
,
lighter
plus-lighter
:
R = S + D
source destination:
, plus
, (cyan). — , , .
, . .
, premultiplied alpha
source-over
. :
R = S + D × (1 − S A )
source , RGB-
plus
:
, source-over
, — . , , , - , , . , ,
plus
.
. « » , -.
, :
50%, , :
, . , ,
, :
, .
. , , . , :
, , :
-, , .
, , , , . , . . ,
:
,,
, . in/out, , .
8- - 256
, 2
16 . , , .
, . , ,
.
, , , . , ; :
,. :
,, . , , . , 50% sRGB , 50%, :
sRGB source destination , . :
, . , , . 50% 73.5% sRGB.
, premultiplied alpha.
, .. . , .
Premultiplied Alpha
, , premultiplied alpha « » . — , . 8- 150, 20%.
round(150 × 0.2) = 30
151, :
round(151 × 0.2) = 30
, . , 148, 149, 150, 151 152 30, :
20% 8-, , . 256
4 ( 4,3 ) 8- RGBA 25.2%; , 2 32- .
, , . , , , . .
, . , , . , , 8- —
ponto flutuante .Leitura adicional
- Pixar
.
“Alpha and the History of Digital Compositing” “alpha”, ,
.
“Interpreting Alpha” . , .
premultiplied alpha
“GPUs prefer premultiplication” . ,
, 3D-, .
-, , -, , .
, , RGB-, . 2D-.
, , , , .