Digamos que você esteja trabalhando com cores RGB: cada cor é representada com três intensidades ou brilhos. Você tem que escolher entre "RGB linear" e "sRGB". Por enquanto, vamos simplificar as coisas ignorando as três diferentes intensidades e assumir que você tem apenas uma intensidade: ou seja, você está lidando apenas com tons de cinza.
Em um espaço de cores linear, a relação entre os números que você armazena e as intensidades que eles representam é linear. Praticamente, isso significa que se você dobrar o número, dobrará a intensidade (a claridade do cinza). Se você quiser adicionar duas intensidades juntas (porque está calculando uma intensidade com base nas contribuições de duas fontes de luz ou porque está adicionando um objeto transparente em cima de um objeto opaco), você pode fazer isso apenas adicionando o dois números juntos. Se você está fazendo qualquer tipo de combinação 2D ou sombreamento 3D, ou quase qualquer processamento de imagem, então você quer suas intensidades em um espaço de cor linear, então você pode apenas adicionar, subtrair, multiplicar e dividir os números para ter o mesmo efeito nas intensidades. A maioria dos algoritmos de processamento de cores e renderização só fornecem resultados corretos com RGB linear, a menos que você adicione pesos extras a tudo.
Parece muito fácil, mas há um problema. A sensibilidade do olho humano à luz é melhor em baixas intensidades do que em altas intensidades. Ou seja, se você fizer uma lista de todas as intensidades que consegue distinguir, há mais intensidades escuras do que claras. Em outras palavras, você pode diferenciar os tons escuros de cinza melhor do que os tons de cinza claros. Em particular, se você estiver usando 8 bits para representar sua intensidade, e fizer isso em um espaço de cor linear, você acabará com muitos tons claros e poucos tons escuros. Você obtém faixas nas áreas escuras, enquanto nas áreas claras, você está desperdiçando pedaços em diferentes tons de quase branco que o usuário não consegue distinguir.
Para evitar esse problema e fazer o melhor uso desses 8 bits, costumamos usar sRGB . O padrão sRGB indica uma curva a ser usada para tornar as cores não lineares. A curva é mais rasa na parte inferior, então você pode ter mais cinzas escuros e mais íngreme na parte superior, então você tem menos cinza claro. Se você dobrar o número, mais do que dobrará a intensidade. Isso significa que, se você adicionar cores sRGB, obterá um resultado mais claro do que deveria. Atualmente, a maioria dos monitores interpreta suas cores de entrada como sRGB. Portanto, ao colocar uma cor na tela ou armazená-la em uma textura de 8 bits por canal, armazene-a como sRGB , para que você faça o melhor uso desses 8 bits.
Você notará que agora temos um problema: queremos nossas cores processadas no espaço linear, mas armazenadas em sRGB. Isso significa que você acaba fazendo a conversão de sRGB em linear na leitura e a conversão de linear em sRGB na gravação. Como já dissemos, as intensidades lineares de 8 bits não têm escurecimento suficiente, isso causaria problemas, então há mais uma regra prática: não use cores lineares de 8 bits se puder evitar. Está se tornando convencional seguir a regra de que cores de 8 bits são sempre sRGB, então você faz sua conversão sRGB em linear ao mesmo tempo que amplia sua intensidade de 8 para 16 bits, ou de inteiro para ponto flutuante; da mesma forma, ao terminar o processamento de ponto flutuante, você reduz para 8 bits ao mesmo tempo que converte para sRGB. Se você seguir essas regras,
Quando você está lendo uma imagem sRGB e deseja intensidades lineares, aplique esta fórmula para cada intensidade:
float s = read_channel();
float linear;
if (s <= 0.04045) linear = s / 12.92;
else linear = pow((s + 0.055) / 1.055, 2.4);
Por outro lado, quando você quiser escrever uma imagem como sRGB, aplique esta fórmula para cada intensidade linear:
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 )
Em ambos os casos, o valor do ponto flutuante varia de 0 a 1, então se você estiver lendo inteiros de 8 bits, você deseja dividir por 255 primeiro, e se estiver escrevendo inteiros de 8 bits, você deseja multiplicar por 255 por último, da mesma forma que você normalmente faria. Isso é tudo que você precisa saber para trabalhar com sRGB.
Até agora, lidei com apenas uma intensidade, mas existem coisas mais inteligentes para fazer com as cores. O olho humano pode distinguir diferentes brilhos melhor do que tons diferentes (mais tecnicamente, tem melhor resolução de luminância do que crominância), então você pode fazer um uso ainda melhor de seus 24 bits, armazenando o brilho separadamente do matiz. Isso é o que as representações YUV, YCrCb etc. tentam fazer. O canal Y é a claridade geral da cor e usa mais bits (ou tem mais resolução espacial) do que os outros dois canais. Dessa forma, você não precisa (sempre) aplicar uma curva como faz com as intensidades RGB. YUV é um espaço de cor linear, então se você dobrar o número no canal Y, você dobra a luminosidade da cor, mas você não pode adicionar ou multiplicar as cores YUV juntas como você pode com as cores RGB, então '
Acho que isso responde à sua pergunta, então vou terminar com uma rápida nota histórica. Antes do sRGB, os CRTs antigos costumavam ter uma não linearidade incorporada. Se você dobrar a voltagem de um pixel, terá mais do que o dobro da intensidade. Quanto mais era diferente para cada monitor, e este parâmetro foi chamado de gama . Esse comportamento era útil porque significava que você poderia obter mais escuros do que luzes, mas também significava que você não poderia dizer o quão brilhantes seriam as cores no CRT do usuário, a menos que você o calibrasse primeiro. Correção de gamasignifica transformar as cores com as quais você começa (provavelmente lineares) e transformá-las para a gama do CRT do usuário. O OpenGL vem dessa era, e é por isso que seu comportamento sRGB às vezes é um pouco confuso. Mas os fornecedores de GPU agora tendem a trabalhar com a convenção que descrevi acima: quando você está armazenando uma intensidade de 8 bits em uma textura ou framebuffer, é sRGB, e quando você está processando cores, é linear. Por exemplo, um OpenGL ES 3.0, cada framebuffer e textura tem um "sinalizador sRGB" que você pode ativar para habilitar a conversão automática ao ler e gravar. Você não precisa fazer explicitamente a conversão de sRGB ou correção de gama.