Cómo funciona la composición alfa

imagen

La transparencia puede no parecer un tema interesante. El formato GIF, que permitió que algunos píxeles brillaran en el fondo, se publicó hace más de 30 años. Casi todas las aplicaciones de diseño gráfico lanzadas en las últimas dos décadas admiten la creación de contenido translúcido. Estos conceptos han dejado de ser algo nuevo.

En mi artículo quiero mostrar que, de hecho, la transparencia en las imágenes digitales es mucho más interesante de lo que parece: en lo que damos por sentado, hay una profundidad y belleza invisibles.

Opacidad


Si alguna vez has visto a través de lentes rosas, entonces podrías ver algo similar a lo que se muestra en la figura a continuación. [En el artículo original, muchas imágenes son interactivas.] Intente mover las gafas para ver cómo afectan lo que se ve a través de ellas:


Tales gafas funcionan de la siguiente manera: pierden mucho rojo, una cantidad decente de azul y muy poco verde. La matemática de estos puntos se puede escribir en un conjunto de tres ecuaciones. La letra R indica el resultado de la operación, y la letra D describe el punto que estamos viendo. Los índices RGB indican los componentes rojo, verde y azul:

R R = D R × 1.0
R G = D G × 0.7
R B = D B × 0.9

Este vitral transmite componentes rojos, verdes y azules del fondo con diferentes intensidades. En otras palabras, la transparencia de las gafas de color rosa depende del color de la luz incidente. En general, la transparencia puede variar dependiendo de la longitud de onda de la luz , pero en este ejemplo simplificado, solo estamos interesados ​​en cómo las gafas afectan los componentes RGB clásicos.

Simular el comportamiento de las gafas de sol normales es mucho más simple, por lo general solo atenúan la luz incidente en cierta cantidad:


Estas gafas permiten que solo el 30% de la luz pase a través de ellas. Su comportamiento se puede describir mediante las siguientes ecuaciones:

R R = D R × 0.3
R G = D G × 0.3
R B = D B × 0.3

Los tres componentes de color se reducen en el mismo valor: la absorción de la luz incidente es la misma. Podemos decir que las gafas oscuras son 30% transparentes (opacas) o 70% opacas. La opacidad de un objeto determina cuánto color bloquea. En gráficos de computadora, generalmente tratamos con un modelo simplificado en el que solo se necesita un valor para describir esta propiedad. La opacidad puede variar espacialmente. como, por ejemplo, una columna de humo que se hace más alta y más transparente.

En el mundo real, los objetos con una opacidad del 100% son simplemente opacos y no transmiten luz en absoluto. El mundo de las imágenes digitales es un poco diferente. Hay casos límite en el que incluso los objetos opacos sólidos pasan una cierta cantidad de luz.

Cobertura


Los gráficos vectoriales tratan con descripciones claras e infinitamente precisas de formas definidas utilizando puntos, segmentos de línea, curvas de Bezier y otras primitivas matemáticas. Cuando necesite mostrar figuras en la pantalla de una computadora, estas entidades impecables deben rasterizarse en un mapa de bits:


Rasterización de una forma vectorial a un mapa de bits

La forma más primitiva de rasterizar es verificar dónde se encuentra la muestra de píxeles, dentro o fuera de la forma del vector. En los ejemplos a continuación, puede arrastrar el triángulo, en una vista ampliada, los movimientos serán más precisos. Un contorno azul indica la geometría del vector original. Como puede ver, la escalera en los bordes del triángulo se ve fea y parpadea mucho al mover la geometría:


La desventaja de este enfoque es que solo realizamos una comprobación para cada píxel mostrado, y los resultados se discretizan a uno de los dos valores posibles, dentro o fuera.

Puede muestrear la geometría del vector varias veces por píxel para obtener una gran gradación de pasos y decidir que algunos píxeles solo estén parcialmente cerrados. Una posible solución es usar cuatro puntos de muestreo para representar cinco niveles de cobertura: 0, 14 , 24 , 34 y 1:


La calidad de los bordes del triángulo ha mejorado, pero a menudo solo cinco niveles posibles de cobertura no son suficientes y podemos lograr fácilmente un resultado mucho mejor. Aunque la vista de un píxel como un pequeño cuadrado en el mundo del procesamiento de señales se ve con desaprobación , en algunos contextos es un modelo útil que nos permite calcular la cobertura exacta de un píxel por geometría vectorial. La intersección de una línea y un cuadrado siempre se puede descomponer en un trapecio y un rectángulo :

imagen

Un segmento de línea divide un cuadrado en un trapecio y un rectángulo.

Puede calcular fácilmente el área de ambas partes, y su suma dividida por el área del cuadrado determina el porcentaje de cobertura de píxeles. Por lo tanto, la cobertura se calcula como un número exacto con precisión arbitraria. En la demostración que se muestra a continuación, este método se usa para renderizar bordes mucho mejores que permanecen suaves incluso al arrastrar un triángulo:


Cuando se trata de formas más complejas, por ejemplo, elipses o Beziers , a menudo se dividen en segmentos simples de línea recta que le permiten calcular la cobertura con la precisión correcta.

El concepto de cobertura parcial es crítico para la representación de alta calidad de gráficos vectoriales y, lo que es más importante, para la representación de texto. Si toma una captura de pantalla de este artículo y lo considera cuidadosamente, notará que casi todos los bordes de los glifos cubren los píxeles solo parcialmente:


La cobertura parcial se usa activamente en la representación de texto

Teniendo la opacidad del objeto y cubriéndolo con píxeles individuales, puede combinarlos en un solo valor.

Alfa


El producto de la opacidad de un objeto y su cobertura de píxeles se llama alfa :

= ×

Un objeto con una opacidad del 60%, que cubre el 30% del área de píxeles, tiene un valor alfa del 18% en este píxel. Naturalmente, cuando el objeto es transparente o no cubre completamente el píxel, entonces el valor alfa en este píxel es 0. Después de la multiplicación, las diferencias entre opacidad y recubrimiento desaparecen, lo que en cierto sentido justifica el hecho de que los conceptos de "alfa" y "opacidad" se usan como sinónimos.

Alfa a menudo se representa como un cuarto canal de una imagen de mapa de bits. Los valores habituales de rojo, verde y azul se complementan con un valor alfa, formando cuatro valores RGBA.

Cuando se trata de almacenar valores alfa en la memoria, existe la tentación de usar solo unos pocos bits para esto. En el caso de cubrir los píxeles de los bordes de los objetos opacos, parece que 4 o incluso 3 bits serán suficientes, dependiendo de la densidad de píxeles de la pantalla:


Sin embargo, la opacidad también afecta el valor alfa, por lo que una profundidad de bits baja puede ser catastrófica en algunos casos de cambios suaves de transparencia. La imagen a continuación muestra un gradiente de negro opaco a blanco, lo que demuestra que una profundidad de bits baja produce variaciones de color muy fuertes:


Obviamente, cuantos más bits, mejor, y con mayor frecuencia para alfa, se utiliza una profundidad de 8 bits para igualar la precisión de los componentes de color, razón por la cual muchos buffers RGBA ocupan 32 bits por píxel. También vale la pena señalar que, a diferencia de los componentes de color, que a menudo se codifican mediante transformación no lineal, alfa se almacena linealmente: el valor codificado de 0.5 corresponde a un valor alfa de 0.5.

Hablando de alfa, ignoramos por completo todos los demás componentes de color, pero además de bloquear el color de fondo, el píxel en sí mismo puede agregar un poco de color. La idea es bastante simple: un objeto rosa translúcido bloquea parte de la iluminación de fondo entrante y emite o refleja una pequeña luz rosa:


Tenga en cuenta que no se comporta como vidrieras. El vidrio simplemente bloquea parte de la iluminación de fondo con diferente brillo. Si miras un objeto completamente negro a través del vidrio rosa, entonces su negrura permanecerá, porque el objeto negro no emite y no refleja ninguna luz. Sin embargo, el objeto rosa translúcido agrega su propia luz. Si lo coloca encima de un objeto negro, el resultado será rosado. Un buen análogo de este comportamiento es el material fino suspendido en el aire, como neblina, humo, niebla o algún polvo de color.

Representar un canal alfa es un poco más difícil: un objeto perfectamente transparente es invisible por definición, por lo que para distinguir entre los objetos, necesitamos usar dos trucos. Un fondo de tablero de ajedrez muestra qué partes de la imagen son transparentes; Este patrón se usa en muchas aplicaciones gráficas:


El patrón de ajedrez muestra piezas transparentes.

Los cuatro cuadrados pequeños debajo de la imagen nos dicen que vemos los componentes rojo, verde, azul y alfa de la imagen. En algunos casos, es útil ver directamente los valores del canal alfa, y la forma más fácil de mostrarlos es usar tonos de gris:


Mostrar valores RGB y A en diferentes superficies

Cuanto más brillante sea el tono de gris, mayor será el valor alfa, es decir, el negro puro corresponde al 0% alfa y el blanco puro al 100% alfa. Los cuadrados pequeños indican que los componentes RGB y A de la imagen se dividen en dos partes.

El componente alfa en sí no es particularmente útil, pero se vuelve muy importante cuando hablamos de composición.

Composición simple


Se pueden implementar muy pocos efectos de renderizado 2D en una sola operación, y para crear un resultado final, utilizamos un proceso de composición que combina varias imágenes. Por ejemplo, se puede crear un simple botón "Cancelar" al componer cinco elementos separados:


Elementos de composición para el botón Cancelar

La composición a menudo se realiza en varias etapas, en cada una de las cuales se combinan dos imágenes. La imagen de primer plano utilizada en la composición se denomina comúnmente fuente . La imagen de fondo utilizada en la composición, en la que se superpone la fuente, generalmente se denomina destino .

Comenzaremos componiendo en un fondo opaco, porque este es un caso muy común. Todo lo que ves en la pantalla se superpone en última instancia al componer en un destino opaco.

Cuando el valor alfa de la fuente es 100%, la fuente es opaca y debe cubrir completamente el destino. Si el valor alfa es 0%, el origen es completamente transparente y no afecta el destino de ninguna manera. Un valor alfa del 25% permite que el objeto emita el 25% de su luz y pasa el 75% de la luz del fondo, y así sucesivamente:


Composición de fuente púrpura con diferentes valores alfa a destino amarillo

Ya puedes entender lo que va a pasar todo, un simple caso de composición alfa en un fondo opaco, es solo una interpolación lineal entre el destino y los colores de origen. En el gráfico a continuación, el control deslizante controla el valor alfa de la fuente, y los gráficos rojo, verde y azul muestran los valores de los componentes RGB. El resultado de R es solo una mezcla entre la fuente S y el destino D :


Lo que sucede aquí puede describirse mediante las ecuaciones que se muestran a continuación. Como antes, el índice denota el componente, es decir, S A es el valor alfa en la fuente y D G es el valor verde en el 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 )

Las ecuaciones para los componentes rojo, verde y azul tienen la misma apariencia, por lo que simplemente puede usar el índice RGB y combinarlos en una línea:

R RGB = S RGB × S A + D RGB × (1 − S A )

Además, dado que el destino es opaco y ya bloquea toda la luz de fondo, sabemos que el valor alfa del resultado siempre es 1:

R A = 1

La composición en un fondo opaco es simple, pero tiene capacidades bastante limitadas. En muchos casos, se requiere una solución más confiable.

Tampones intermedios


La imagen a continuación muestra el proceso de dos pasos de composición de tres capas diferentes, etiquetadas A, B y C. El símbolo ⇨ significará "superpuesto por composición en":


El resultado de la composición en dos etapas de tres capas.

Primero superponemos B con C componiendo, y luego superponemos A con ellos para obtener la imagen final. En el siguiente ejemplo, haremos las cosas un poco diferentes. Primero, conectaremos las dos capas superiores mediante la composición, y luego superpondremos el resultado en el último destino:


El resultado de la composición en dos etapas de tres capas en un orden diferente

Probablemente se esté preguntando si tal situación surge en la práctica, pero de hecho es muy común. Muchas operaciones de composición no triviales y efectos de renderizado, como el enmascaramiento y el desenfoque, requieren pasar a través de un búfer intermedio que contiene solo resultados de composición parciales. Este concepto tiene diferentes nombres: pases fuera de pantalla, capas de transparencia o buffers laterales, pero generalmente se basan en la misma idea.

Lo que es más importante para nosotros es que casi cualquier imagen con transparencia se puede percibir como un resultado parcial de alguna representación, que luego se superpondrá al componer en el último destino:


Composición parcial de un botón en un portapapeles

Necesitamos entender cómo reemplazar la composición de las imágenes translúcidas A y B con una imagen (A⇨B) que tenga el mismo color y opacidad. Comencemos calculando el valor alfa del búfer final.

Combinando valores alfa


Es posible que no tenga claro cómo combinar la opacidad de dos objetos, pero es más fácil hablar de esta tarea si hablamos de transparencia.

Supongamos que una cierta cantidad de luz pasa a través del primer objeto y luego a través del segundo objeto. Si la transparencia del primer objeto es del 80%, pasará el 80% de la luz incidente. Del mismo modo, un segundo objeto con un 60% de transparencia permitirá que el 60% de la luz pase a través de él, lo que nos da un 60% × 80% = 48% de la luz original. Puedes experimentar con la transparencia en el artículo original; no olvide que los controles deslizantes controlan la transparencia y no la opacidad de los objetos en el camino de la luz:


Naturalmente, cuando el primer o el segundo objeto es opaco, no pasa luz a través de ellos, incluso otro es completamente transparente.

Si el objeto D tiene transparencia D T y el objeto S tiene transparencia S T , entonces la transparencia general final R T de estos dos objetos es igual a su producto:

R T = D T × S T

Sin embargo, la transparencia es solo una unidad menos alfa, por lo que la sustitución nos da lo siguiente:

1 - R A = (1 - D A ) × (1 - S A )

Esta expresión se puede expandir a:

1 - R A = 1 - D A - S A + D A × S A

Y simplifícalo así:

R A = D A + S A - D A × S A

Se puede reducir a uno de dos tipos similares:

R A = S A + D A × (1 - S A )

R A = D A + S A × (1 - D A )

Pronto veremos que el segundo se usa con más frecuencia. También es interesante observar que el valor alfa resultante no depende del orden relativo de los objetos: la opacidad de los píxeles resultantes es la misma, incluso si intercambia el origen y el destino. Esto es muy lógico. La luz que pasa a través de dos objetos debe desvanecerse de la misma manera, desde cualquier lado de la estrella, desde el frente o desde la parte posterior.

Combinación de colores


Calcular alfa no fue tan difícil, así que tratemos de entender los cálculos de los componentes RGB. La imagen de origen tiene el color S RGB , pero su opacidad S A obliga solo al producto de estos dos valores a tener en cuenta en el resultado final:

S RGB × S A

La imagen de destino tiene el color D RGB , la opacidad hace que emita luz D RGB × D A , sin embargo, parte de la luz está bloqueada por la opacidad de la imagen S, por lo que toda la influencia del destino es igual a:

D RGB × D A × (1 - S A )

La contribución total de la luz de S y D es igual a su suma:

S RGB × S A + D RGB × D A × (1 - S A )

Del mismo modo, la contribución de las capas fusionadas es igual a su color multiplicado por su opacidad:

R RGB × R A

Queremos que estos dos valores coincidan:

R RGB × R A = S RGB × S A + D RGB × D A × (1 - S A )

Lo que nos da las ecuaciones finales:

R A = S A + D A × (1 - S A )

R RGB = (S RGB × S A + D RGB × D A × (1 - S A )) / R A

¡Mira lo complicada que es la segunda ecuación! Tenga en cuenta que para obtener los valores RGB del resultado, necesitamos dividir por el valor alfa. Sin embargo, para la siguiente etapa de la composición, se requerirá nuevamente la multiplicación por el valor alfa, porque el resultado de la operación actual se convertirá en la nueva fuente o destino de la próxima operación. Es simplemente feo.

Volvamos a la forma casi final de R RGB por un segundo:

R RGB × R A = S RGB × S A + D RGB × D A × (1 - S A )

El origen, el destino y el resultado se multiplican por sus componentes alfa. Esto nos hace comprender que el color y el alfa del píxel "gusta" para estar juntos, por lo que debemos dar un paso atrás y repensar la forma en que almacenamos la información del color.

Alfa premultiplicado


Recuerde que hablamos de opacidad: si el objeto es parcialmente opaco, entonces su contribución al resultado también será parcial. El concepto de alfa premultiplicado ("pre-multiplicación por alfa") implementa esta idea. Los valores de los componentes RGB, como su nombre lo indica, se multiplican previamente por el componente alfa. Comencemos con el color sin multiplicación preliminar:

(1.00, 0.80, 0.30, 0.40)

La multiplicación preliminar por alfa nos da lo siguiente:

(0.40, 0.32, 0.12, 0.40)

Echemos un vistazo a varios píxeles a la vez. La siguiente figura muestra cómo se almacena la información de color sin multiplicar primero alfa:


Información RGB y A en la imagen sin multiplicación previa

Tenga en cuenta que las áreas donde alfa es 0 pueden tener valores RGB arbitrarios, como se puede ver en las fallas verdes y azules en la imagen. En el caso de la multiplicación preliminar por alfa, la información de color también almacena valores de opacidad de píxeles:


Información RGB y A en una imagen previamente multiplicada

El alfa premultiplicado a veces se denomina alfa asociado, y el alfa no premultiplicado a veces se denomina alfa directo o no asociado.

Cuando el componente alfa del color es 0, la multiplicación preliminar restablece todos los demás componentes, independientemente de sus valores:

(0.0, 0.0, 0.0, 0.0)

En el caso del alfa premultiplicado, solo hay un color completamente transparente, y esto es encantador.

Las ventajas de este procesamiento de componentes de color se harán evidentes gradualmente, pero antes de que volvamos al ejemplo de composición, veamos cómo el alfa premultiplicado ayuda a resolver algunos otros problemas de representación.

Filtrado


El desenfoque gaussiano es una forma popular de crear un fondo desenfocado interesante o reducir la alta frecuencia de la parte de fondo del contenido de algunos elementos de la interfaz de usuario. Como veremos, la pre-multiplicación alfa es crítica para crear el desenfoque de aspecto correcto.

La imagen que analizaremos se crea rellenando el fondo con azul opaco al 1%, sobre el cual se dibuja un círculo rojo opaco. Primero, veamos un ejemplo sin multiplicación preliminar. Separé los canales RGB del canal alfa para comprender lo que estaba sucediendo. La flecha indica la operación de desenfoque:


Contenido borroso sin multiplicación previa

El resultado final tiene un halo azul feo. Sucedió porque el fondo azul se filtró sobre el área roja durante el desenfoque, y solo entonces , durante la composición, se le agregó el peso alfa.

Cuando los colores se multiplican previamente por alfa, el resultado es correcto:


Contenido

pre-multiplicado borroso Debido a la multiplicación previa, el color azul de la imagen se reduce al 1% de su fuerza original, por lo que su efecto sobre los colores del círculo borroso es extremadamente pequeño.

Interpolación


Renderizar una imagen cuyos píxeles encajan perfectamente con el destino es una tarea simple porque necesitamos realizar un mapeo trivial uno a uno entre muestras. Un problema surge cuando no hay un mapeo simple, por ejemplo, debido a la rotación, escala o separación de sílabas. La siguiente figura muestra que los píxeles de la imagen girada indicados por el contorno rojo ya no coinciden con el destino:


Orientación relativa de la imagen y píxeles de destino antes y después de la rotación

Hay muchas formas de seleccionar un color de la imagen para escribir en el píxel de destino, y la más simple de ellas es la llamada interpolación del vecino más cercano, en la que como el píxel final, simplemente se selecciona la muestra más cercana en la textura.

En la demostración a continuación, el contorno rojo muestra la posición de la imagen en el destino. El lado derecho muestra las posiciones de las muestras desde el punto de vista de la imagen . Al arrastrar el control deslizante (en el artículo original), puede girar el cuadrángulo y observar cómo las muestras seleccionan los colores del mapa de bits. Destaqué un píxel en origen y destino, para que su relación sea más clara:


Esta solución es bastante funcional y los píxeles tienen un color holístico, pero la calidad es inaceptable. Sería mejor utilizar la interpolación bilineal , que calcula el promedio ponderado de los cuatro píxeles más cercanos de la imagen muestreada:


Esto funciona mejor, pero los bordes alrededor de los rectángulos no se ven bien, el contenido de los píxeles se fusiona sin multiplicación, porque el alfa se "aplica" después de la interpolación. A veces, la solución recomendada para fusionar el color del contenido correcto, que se muestra en el sorprendente artículo de Adrian Correger [ traducción en Habré], está lejos de ser ideal: ni un solo color en la brecha entre los rectángulos rojo y azul se verá bien.

Veamos cómo se verá todo en la imagen con alfa premultiplicado y composición con una fórmula avanzada, que pronto derivaremos:


Simplemente perfecto: nos deshicimos de todas las fusiones de colores y los dientes no se ven por ningún lado .

En última instancia, los problemas asociados con el desenfoque y la interpolación están estrechamente relacionados. Es probable que cualquier operación que requiera cualquier combinación de colores translúcidos, sin multiplicar primero los colores por alfa, dé resultados incorrectos.

La composición correcta


Volvamos a la composición. Nos decidimos por una ecuación casi derivada:

R RGB × R A = S RGB × S A + D RGB × D A × (1 - S A )

Si imagina colores usando alfa premultiplicado, entonces todas estas incómodas multiplicaciones desaparecerán, porque el alfa ya será parte de los valores de color. Luego obtenemos lo siguiente:

R RGB = S RGB + D RGB × (1 - S A )

Veamos la ecuación alfa:

R A = S A + D A × (1 - S A )

Los coeficientes para los canales rojo, verde, azul y alfa son los mismos, por lo que podemos expresar la expresión completa con una ecuación y solo recordar que cada componente se somete a la misma operación:

R = S + D × (1 - S A )

Vea cómo el alfa premultiplicado facilitó las cosas. Cuando analizamos los componentes de la ecuación, todos están en su lugar. La operación enmascara parte de la luz de fondo y agrega una nueva luz:

R = S + D × (1 - S A )

Esta operación de mezcla se llama source-over, sover o simplemente normal, y es sin duda el modo de composición más común. Casi todo lo que ves en mi sitio web se mezcla en este modo.

Asociatividad


Una propiedad importante de fuente sobre realizada en colores pre-alfa multiplicados es la asociatividad de esta operación. Gracias a él, en la compleja ecuación de mezcla, podemos colocar los corchetes de forma completamente arbitraria. Todas las composiciones que se muestran a continuación son equivalentes:

R = (((A⇨B) ⇨C) ⇨D) ⇨E

R = (A⇨B) ⇨ (C⇨ (D⇨E))

R = A⇨ (B⇨ (C⇨ (D⇨E)) )

La prueba de esto es bastante simple, pero no te cargaré con manipulaciones algebraicas. En la práctica, esto significa que podemos representar parcialmente dibujos complejos sin temor a que la composición final se vea mal.

En la gran mayoría de los casos, alpha se usa solo para componer usando source-over, pero sus ventajas no terminan ahí. Los valores alfa también se pueden usar para otras operaciones de representación útiles.

Porter-Duff Compositing


En julio de 1984, Thomas Porter y Tom Duff publicaron el artículo original, "Composición de imágenes digitales" . Los autores no solo introdujeron por primera vez el concepto de alfa premultiplicado y derivaron la ecuación de composición fuente, sino que también presentaron una familia completa de operaciones de composición alfa, muchas de las cuales son poco conocidas, aunque muy útiles. Las nuevas funciones también se denominan operadores porque, como sumar o multiplicar, realizan acciones en los valores de entrada para crear un valor de salida.

Sobre


En ejemplos futuros, utilizaremos demostraciones interactivas que muestran las operaciones de varios modos de fusión. La imagen de destino será el símbolo negro "club", y la imagen de origen será el símbolo rojo "gusanos". Puede arrastrar el corazón sobre la imagen y observar cómo se comportan las formas superpuestas bajo diferentes operadores de composición. Presta atención al pequeño minimapa en la esquina. Algunos modos de fusión son muy destructivos y fáciles de confundir. El minimapa siempre muestra el resultado de una composición de fuente simple, lo que simplifica la comprensión:


R = S + D × (1 - S A )

R = S × (1 - D A ) + D

Si cambia a destino de destino, inmediatamente se dará cuenta de que simplemente "voltea" el origen de destino: el destino y el origen se intercambian en la ecuación y el resultado es equivalente a lo que consideraremos destino como la imagen de origen. Aunque parece redundante, el operador de destino sobre es extremadamente útil porque le permite componer objetos que están bajo contenido existente.

Fuera


Las declaraciones de origen y destino son excelentes para perforar agujeros en origen o destino:


R = S × (1 - D A )

R = D × (1 - S A )

De estos dos operadores, Destination-out es más conveniente porque utiliza el canal alfa para perforar agujeros en el formulario de destino.

En


Los operadores de origen y destino son esencialmente operadores de enmascaramiento:


R = S × D A

R = D × S A

Facilitan la creación de intersecciones complejas de geometría no trivial sin resolver las intersecciones relativamente difíciles de calcular contornos vectoriales.

Encima


Los operadores source-atopy le destination-atoppermiten superponer nuevo contenido en los existentes, mientras lo enmascaran a lo largo de la ruta de destino:


R = S × D A + D × (1 - S A )

R = S × (1 - D A ) + D × S A

Xor


El operador OR exclusivo ( xor) guarda el origen o el destino, y sus áreas coincidentes desaparecen:


R = S × (1 - D A ) + D × (1 - S A )

Fuente, destino, claro


Los últimos tres modos de composición clásicos son bastante aburridos. Source, también llamado copy, solo toma la fuente de color. Del mismo modo, destinationignora la fuente de color y simplemente regresa destination. El operador clearsimplemente borra todo:


R = S

R = D

R = 0

La aplicabilidad de estos modos es limitada. Al clearusarlo, puede vaciar un búfer lleno, pero esta operación se puede optimizar simplemente llenando la memoria con ceros. Además, en algunos casos source puede ser más económico en los cálculos, ya que no requiere ninguna mezcla, sino que simplemente reemplaza el contenido del búfer con la información de origen.

Porter Duff en acción


Habiendo tratado con operadores individuales, veamos cómo puede combinarlos. En el siguiente ejemplo, dibujaremos un logotipo marino sin usar enmascaramiento o formas geométricas complejas. Los contornos azules muestran la geometría simple que se está creando. Puede moverse a través de los pasos haciendo clic en el lado derecho de la imagen y regresar haciendo clic en el lado izquierdo:


Por supuesto, no estamos obligados a abandonar máscaras y recortar contornos, pero a menudo nos olvidamos de una herramienta como los modos de composición Porter-Duff, aunque es mucho más fácil crear algunos efectos visuales con su ayuda.

Operadores


Si observa de cerca a los operadores de Porter-Duff, notará que todos tienen la misma forma. La fuente siempre se multiplica por un cierto coeficiente F S y se agrega al destino multiplicado por un coeficiente F D :

R = S × F S + D × F D

F S puede tomar los valores 0, 1, D A + 1 - D A , y F D pueden ser igual a 0, 1, S A o 1 - S A . No tiene sentido multiplicar el origen o el destino por su propio alfa, porque ya están multiplicados previamente, y solo obtenemos el efecto elegante, pero no muy útil, del alfa cuadrático. Todos los operadores se pueden representar en forma de tabla:

0 01D Un1 - D A
0 0clarofuentefuentefuente
1destinodestino sobre
S Adestino endestino en la cima
1 - S Adestino de salidafuentefuente en la cimaxor

Presta atención a la simetría de los operadores en la diagonal. Faltan los cuatro elementos centrales de la tabla y sucedió porque son diferentes del resto.

Iluminación aditiva


En su artículo, Porter y Duff presentan otro operador, en el que tanto el F el S y el F D igual a 1. Se conoce bajo los nombres plus, lightery plus-lighter:

R = S + D

Esta operación esencialmente agrega iluminación de origen al destino:


La iluminación aditiva implementada con el operadorplus

Verde y rojo forman correctamente el amarillo, mientras que el verde y el azul forman cian. El negro es la ausencia de una operación; no cambia los valores de color de ninguna manera, porque agregar cero a un número no cambia nada.

Los tres operadores restantes no recibieron nombres porque no son particularmente útiles. Son solo una combinación de enmascaramiento y mezcla.

También vale la pena señalar que el alfa premultiplicado nos permite usar el operador de una source-overmanera inesperada. Echemos un vistazo a la ecuación nuevamente:

R = S + D × (1 - S A )

Si logramos que el valor alfa en la fuente sea igual a cero, entonces si hay valores distintos de cero en los canales RGB, podemos lograr una iluminación aditiva sin usar el operador plus:


Iluminación aditiva implementada utilizando el operadorsource-over

Tenga en cuenta que debe tener cuidado aquí: los valores ya no se multiplican por alfa correctamente. En algunos programas, puede haber una optimización que evite completamente mezclar colores con cero alfa, mientras que otros programas pueden revertir la multiplicación previa por valores alfa, realizar algunas operaciones de color y luego volver a multiplicar previamente por alfa, lo que destruye completamente los canales de color. También puede ser difícil exportar recursos en este formato, por lo que si no tiene control total sobre la canalización de renderizado, entonces debe quedarse con el operador plus.

Todos los elementos que estamos discutiendo hasta ahora han sido bien combinados. Ahora, quítese nuestras gafas de color rosa y analicemos algunos problemas que deben tenerse en cuenta al trabajar con la composición alfa.

Opacidad de grupo


Echemos un vistazo a este sencillo dibujo de píldora compuesto por solo seis primitivas:


Dibuje una píldora usando formas simples

Si se nos pidiera que renderizáramos una píldora con una opacidad del 50%, entonces podríamos sentir la tentación de dividir la opacidad en la mitad de cada operación de dibujo, pero esto sería un error:


Resultado inesperado de representar una píldora con media opacidad.

Para lograr el resultado correcto, no podemos simplemente distribuir la opacidad de un objeto sobre cada uno de sus componentes individuales. Primero tenemos que crear un objeto, convertirlo en un mapa de bits, y solo luego cambiar la opacidad del mapa de bits y finalmente realizar la composición:


Resultado esperado de renderizar una pastilla con media opacidad

Este es otro caso que demuestra la utilidad del concepto de renderizar en un búfer lateral.

Cobertura de composición


Convertir una cubierta geométrica en un solo valor alfa tiene consecuencias incómodas. Considere el caso cuando dos bordes idealmente coincidentes de figuras de geometría vectorial, que se muestran en la figura a continuación con contornos naranja y azul, se representan en un mapa de bits. En un mundo ideal, los resultados deberían verse así, porque cada píxel está completamente cerrado:


Un resultado de renderizado ideal con la cobertura correcta.

Sin embargo, si primero renderizamos la geometría naranja y luego la azul, en la imagen final todavía se filtrará un pequeño fondo blanco en los píxeles del borde:


El resultado de la composición en dos etapas

Tan pronto como el recubrimiento se almacena en el canal alfa, se pierde toda su información geométrica y no podemos restaurarla de ninguna manera. La geometría azul simplemente se mezcla con algunos de los contenidos del búfer, pero no sabe que la geometría representada por los píxeles rojizos debe coincidir. Este problema es especialmente notable cuando las geometrías se superponen con precisión entre sí. En la imagen a continuación, se dibuja un círculo blanco encima de uno negro. Los bordes oscuros son notables, aunque ambos círculos tienen exactamente el mismo radio y posición:


Un círculo blanco dibujado encima de un círculo negro

Una forma de solucionar este problema es no calcular la cobertura parcial de los píxeles y usar buffers significativamente más grandes. Al rasterizar la geometría del vector con un revestimiento simple de entrada / salida, y luego reducir la escala del resultado al tamaño de la imagen original, puede lograr el resultado esperado.

Sin embargo, para una comparación perfecta de la calidad de representación de los bordes del canal alfa de 8 bits, los búferes deberían ser 256 veces más grandes en ambas direcciones, es decir, el número de píxeles debería aumentar en 2 16tiempos Como vimos anteriormente, al reducir la profundidad de bits para los valores de cobertura, aún puede obtener resultados satisfactorios, por lo que en la práctica puede usar una escala más pequeña.

También vale la pena señalar que tales problemas a menudo se pueden evitar con relativa facilidad sin el uso de enormes mapas de bits. Por ejemplo, en lugar de dibujar dos círculos superpuestos, simplemente puede dibujar dos cuadrados uno encima del otro y luego enmascarar el resultado para formar un círculo.

Valores lineales


Si ha actualizado su conocimiento de los espacios de color , puede recordar que la mayoría de ellos codifican valores de color de forma no lineal, y es necesaria una linealización preliminar para realizar las operaciones matemáticas correctas. Cuando se completa esta etapa, el resultado de la composición es el siguiente; preste atención al hermoso tinte amarillento de las partes superpuestas entre sí:


Círculos rojos difusos superpuestos mediante la composición sobre un fondo verde utilizando valores lineales.

Sin embargo, en la mayoría de los casos, la composición no es así. La forma estándar para la web y la mayoría de los software de gráficos es mezclar directamente valores no lineales:


Círculos rojos difusos superpuestos por un compositor sobre un fondo verde usando valores no lineales

Tenga en cuenta que las áreas donde el rojo sobre las superposiciones verdes son mucho más oscuras. Están lejos de ser ideales, pero en algunos casos, las operaciones incorrectas están profundamente arraigadas en la comprensión de cómo percibimos el color. Por ejemplo, el 50% de gris opaco del espacio sRGB se ve exactamente como negro puro con un 50% de opacidad mezclado con un fondo blanco:


Composición de dos colores sobre un fondo blanco sin linealización

En la figura siguiente, los colores sRGB de las imágenes de origen y destino se linealizan y luego se vuelven a convertir en codificación no lineal para su visualización. Así es como deberían verse estos colores:


Composición de dos colores sobre un fondo blanco con linealización.

Tenemos una discrepancia que no cumple con nuestras expectativas. La única forma de obtener uniformidad visual usando este método es seleccionar todos los colores usando valores lineales, pero esto es muy diferente de lo que todos están acostumbrados. El 50% de gris con valores lineales parece gris en el 73.5% del espacio sRGB.

Además, debe tener especial cuidado al trabajar con alfa premultiplicado. La multiplicación previa debe realizarse con valores lineales , es decir, antes de codificar a no lineal. Debido a esto, el paso de linealización finalizará correctamente con los valores lineales correctos, previamente multiplicados por alfa.

Alfa y profundidad de bits premultiplicados


A pesar de su gran utilidad para la composición, el filtrado y la interpolación, el alfa premultiplicado no es una "bala de plata" y tiene sus inconvenientes. El más serio de ellos es la reducción en la profundidad de bits de los colores que se pueden imaginar. Imagine una codificación de 8 bits con un valor de 150, que se multiplica previamente por alfa 20%. Después de la multiplicación preliminar por alfa, obtenemos

redondo (150 × 0.2) = 30

Si repetimos el mismo procedimiento con un valor de 151, obtenemos:

redondo (151 × 0.2) = 30

El valor codificado será el mismo, a pesar de la diferencia en los valores iniciales. De hecho, después de multiplicar por alfa, los valores de 148, 149, 150, 151 y 152 se codifican en 30, y la diferencia original entre estos cinco colores únicos se pierde:


La multiplicación previa por alfa del 20% reduce los diversos valores de 8 bits a uno.

Naturalmente, cuanto más pequeño es el alfa, más destructivo es su efecto. Del rango posible de 256 4 (aproximadamente 4,3 mil millones) de varias combinaciones de valores RGBA de 8 bits, después de la multiplicación preliminar por alfa, solo el 25.2% conserva una representación única; de hecho, perdemos casi 2 bits del rango de 32 bits.

Para convertir colores entre diferentes espacios de color, a veces es necesario revertir la multiplicación preliminar, es decir, dividir los valores por el componente alfa para obtener el brillo del color original. Este paso es necesario porque, como se mencionó anteriormente, la codificación se realiza de forma no lineal. La existencia de la multiplicación previa reduce la precisión de la representación del color y las conversiones entre espacios de color pueden ser imperfectas.

En la práctica, reducir la profundidad de bits rara vez es importante, especialmente en la composición. Cuanto más bajo es el valor alfa, menos visible es el color y menos influencia tiene sobre la composición. En última instancia, si se esfuerza por operaciones de color pedagógicamente precisas, no utilizará su representación de 8 bits; para este propósito, los formatos son mucho más adecuadospunto flotante .

Lectura adicional


El concepto del canal alfa fue creado por los cofundadores de Pixar Studio, Elvy Smith y Ed Catmell . El artículo de Smith "Alpha and the History of Digital Compositing" describe la historia de la invención y las fuentes del nombre "alpha", así como cómo estos conceptos evolucionaron y reemplazaron gradualmente el concepto de máscaras en la producción cinematográfica .

Para entender el significado de alfa, le recomiendo que lea "Interpretación alfa" de Andrew Glassner . Este artículo proporciona una derivación matemática rigurosa pero muy accesible de alfa como medida de la interacción entre opacidad y cobertura.

Se puede estudiar una discusión detallada del alfa premultiplicado en"Las GPU prefieren la premultiplicación" de Eric Haines. El artículo no solo proporciona una excelente visión general de los problemas causados ​​por la falta de multiplicación preliminar, especialmente en la representación 3D, sino que también proporciona enlaces a muchos otros artículos sobre este tema.

En conclusión


Inicialmente, este artículo pretendía ser una explicación de los operadores de composición de Porter-Duff, pero todos los demás conceptos relacionados con la composición alfa resultaron ser tan interesantes que no podía faltarlos.

Lo que más me gusta de alpha es que es solo un número adicional que acompaña a los componentes RGB, pero al mismo tiempo crea muchas capacidades de renderizado únicas. Alpha literalmente creó un nuevo cambio de oportunidad en el aburrido y viejo mundo de la composición y el renderizado 2D.

La próxima vez que vea los bordes suaves de las formas vectoriales o note una superposición oscura que oscurezca algunas partes de la interfaz de usuario, piense en un componente pequeño pero poderoso que lo hizo posible.

Source: https://habr.com/ru/post/468067/


All Articles