Digamos que está trabajando con colores RGB: cada color se representa con tres intensidades o brillos. Tienes que elegir entre "RGB lineal" y "sRGB". Por ahora, simplificaremos las cosas ignorando las tres intensidades diferentes y asumiremos que solo tiene una intensidad: es decir, solo se trata de tonos de gris.
En un espacio de color lineal, la relación entre los números que almacena y las intensidades que representan es lineal. Prácticamente, esto significa que si duplicas el número, duplicas la intensidad (la claridad del gris). Si desea agregar dos intensidades juntas (porque está calculando una intensidad basada en las contribuciones de dos fuentes de luz, o porque está agregando un objeto transparente encima de un objeto opaco), puede hacerlo simplemente agregando el dos números juntos. Si está haciendo cualquier tipo de mezcla 2D o sombreado 3D, o casi cualquier procesamiento de imagen, entonces desea sus intensidades en un espacio de color lineal., por lo que puede sumar, restar, multiplicar y dividir números para tener el mismo efecto en las intensidades. La mayoría de los algoritmos de procesamiento y reproducción del color solo dan resultados correctos con RGB lineal, a menos que agregue pesos adicionales a todo.
Suena muy fácil, pero hay un problema. La sensibilidad del ojo humano a la luz es más fina a bajas intensidades que a altas intensidades. Es decir, si haces una lista de todas las intensidades que puedes distinguir, hay más oscuras que claras. Para decirlo de otra manera, puede distinguir los tonos oscuros de gris mejor que con los tonos claros de gris. En particular, si está usando 8 bits para representar su intensidad, y lo hace en un espacio de color lineal, terminará con demasiados tonos claros y no suficientes tonos oscuros. Obtienes bandas en tus áreas oscuras, mientras que en tus áreas claras, estás desperdiciando partes en diferentes tonos de casi blanco que el usuario no puede distinguir.
Para evitar este problema y aprovechar al máximo esos 8 bits, solemos utilizar sRGB . El estándar sRGB le indica una curva que debe usar para que sus colores no sean lineales. La curva es menos profunda en la parte inferior, por lo que puede tener más grises oscuros y más pronunciada en la parte superior, por lo que tiene menos grises claros. Si duplica el número, más del doble de intensidad. Esto significa que si suma colores sRGB, obtendrá un resultado más claro de lo que debería ser. En estos días, la mayoría de los monitores interpretan sus colores de entrada como sRGB. Por lo tanto, cuando coloque un color en la pantalla o lo almacene en una textura de 8 bits por canal, guárdelo como sRGB para aprovechar al máximo esos 8 bits.
Notarás que ahora tenemos un problema: queremos que nuestros colores se procesen en un espacio lineal, pero se almacenen en sRGB. Esto significa que termina haciendo conversión de sRGB a lineal en lectura y conversión de lineal a sRGB en escritura. Como ya dijimos que las intensidades lineales de 8 bits no tienen suficientes sombras, esto causaría problemas, por lo que hay una regla práctica más: no use colores lineales de 8 bits si puede evitarlo. Se está volviendo convencional seguir la regla de que los colores de 8 bits son siempre sRGB, por lo que hace su conversión de sRGB a lineal al mismo tiempo que amplía su intensidad de 8 a 16 bits, o de entero a punto flotante; de manera similar, cuando haya terminado su procesamiento de punto flotante, se reducirá a 8 bits al mismo tiempo que convierte a sRGB. Si sigue estas reglas,
Cuando esté leyendo una imagen sRGB y desee intensidades lineales, aplique esta fórmula a cada intensidad:
float s = read_channel();
float linear;
if (s <= 0.04045) linear = s / 12.92;
else linear = pow((s + 0.055) / 1.055, 2.4);
En el sentido contrario, cuando desee escribir una imagen como sRGB, aplique esta fórmula a cada intensidad lineal:
float linear = do_processing();
float s;
if (linear <= 0.0031308) s = linear * 12.92;
else s = 1.055 * pow(linear, 1.0/2.4) - 0.055; ( Edited: The previous version is -0.55 )
En ambos casos, el valor del punto flotante varía de 0 a 1, por lo que si está leyendo números enteros de 8 bits, primero desea dividirlos por 255, y si está escribiendo números enteros de 8 bits, desea multiplicarlos por 255. por último, de la misma manera que lo haría normalmente. Eso es todo lo que necesita saber para trabajar con sRGB.
Hasta ahora, me he ocupado de una sola intensidad, pero hay cosas más inteligentes que hacer con los colores. El ojo humano puede diferenciar los diferentes brillos mejor que los diferentes tintes (más técnicamente, tiene una mejor resolución de luminancia que la crominancia), por lo que puede hacer un uso aún mejor de sus 24 bits almacenando el brillo por separado del tinte. Esto es lo que intentan hacer las representaciones YUV, YCrCb, etc. El canal Y es la luminosidad general del color y utiliza más bits (o tiene más resolución espacial) que los otros dos canales. De esta manera, no es necesario (siempre) aplicar una curva como lo hace con las intensidades RGB. YUV es un espacio de color lineal, por lo que si duplica el número en el canal Y, duplica la luminosidad del color, pero no puede agregar o multiplicar los colores YUV juntos como puede hacerlo con los colores RGB, así que '
Creo que eso responde a tu pregunta, así que terminaré con una breve nota histórica. Antes de sRGB, los CRT antiguos solían tener una no linealidad incorporada. Si duplicara el voltaje de un píxel, duplicaría la intensidad. Cuánto más fue diferente para cada monitor, y este parámetro se llamó gamma . Este comportamiento fue útil porque significaba que podía obtener más sombras que luces, pero también significaba que no podía saber qué tan brillantes serían sus colores en el CRT del usuario, a menos que lo calibrara primero. Corrección gammasignifica transformar los colores con los que comienza (probablemente lineales) y transformarlos para la gamma del CRT del usuario. OpenGL proviene de esta era, por lo que su comportamiento sRGB a veces es un poco confuso. Pero los proveedores de GPU ahora tienden a trabajar con la convención que describí anteriormente: que cuando estás almacenando una intensidad de 8 bits en una textura o framebuffer, es sRGB, y cuando estás procesando colores, es lineal. Por ejemplo, un OpenGL ES 3.0, cada framebuffer y textura tiene un "indicador sRGB" que puede activar para habilitar la conversión automática al leer y escribir. No es necesario que realice una conversión sRGB o una corrección gamma de forma explícita.