RGBカラーを使用しているとしましょう。各カラーは3つの強度または明るさで表されます。「リニアRGB」と「sRGB」のどちらかを選択する必要があります。今のところ、3つの異なる強度を無視して物事を単純化し、強度が1つだけであると想定します。つまり、グレーの色合いのみを扱っているということです。
線形色空間では、格納する数値とそれらが表す強度の関係は線形です。実際には、これは、数値を2倍にすると、強度(灰色の明度)が2倍になることを意味します。2つの強度を一緒に追加したい場合(2つの光源の寄与に基づいて強度を計算しているため、または不透明なオブジェクトの上に透明なオブジェクトを追加しているため)、これを行うには、一緒に2つの数字。あらゆる種類の2Dブレンディングや3Dシェーディング、またはほぼすべての画像処理を行っている場合は、線形色空間で強度が必要です。、したがって、数値を加算、減算、乗算、および除算するだけで、強度に同じ影響を与えることができます。ほとんどのカラー処理およびレンダリングアルゴリズムは、すべてに重みを追加しない限り、線形RGBでのみ正しい結果を提供します。
それは本当に簡単に聞こえますが、問題があります。光に対する人間の目の感度は、高強度よりも低強度の方が細かくなります。つまり、区別できるすべての強度のリストを作成すると、明るいものよりも暗いものの方が多くなります。別の言い方をすれば、明るいグレーの色合いよりも暗いグレーの色合いを区別することができます。特に、強度を表すために8ビットを使用していて、これを線形色空間で行うと、明るい色合いが多すぎて、暗い色合いが不十分になります。暗い領域ではバンディングが発生しますが、明るい領域では、ユーザーが区別できない、白に近いさまざまな色合いのビットを無駄にしています。
この問題を回避し、これらの8ビットを最大限に活用するために、sRGBを使用する傾向があります。sRGB規格は、色を非線形にするために使用する曲線を示しています。カーブは下部が浅いため、濃い灰色が多くなり、上部が急になるため、明るい灰色が少なくなります。数値を2倍にすると、強度が2倍以上になります。これは、sRGBカラーを一緒に追加すると、本来よりも明るい結果になることを意味します。最近では、ほとんどのモニターが入力色をsRGBとして解釈します。したがって、画面に色を配置する場合、またはチャネルごとに8ビットのテクスチャで保存する場合は、sRGBとして保存して、これらの8ビットを最大限に活用します。
問題が発生していることに気付くでしょう。色は線形空間で処理されますが、sRGBで保存されます。つまり、読み取り時にsRGBから線形への変換を実行し、書き込み時に線形からsRGBへの変換を実行することになります。線形8ビット強度には十分な暗さがないことをすでに述べたように、これは問題を引き起こす可能性があるため、もう1つの実用的なルールがあります。回避できる場合は、8ビット線形色を使用しないでください。8ビットカラーは常にsRGBであるという規則に従うのが一般的になりつつあるため、強度を8ビットから16ビットに、または整数から浮動小数点に広げると同時に、sRGBから線形への変換を行います。同様に、浮動小数点処理が終了すると、sRGBへの変換と同時に8ビットに絞り込みます。これらのルールに従うと、
sRGB画像を読み取っていて、線形強度が必要な場合は、次の式を各強度に適用します。
float s = read_channel();
float linear;
if (s <= 0.04045) linear = s / 12.92;
else linear = pow((s + 0.055) / 1.055, 2.4);
逆に、画像をsRGBとして記述したい場合は、次の式を各線形強度に適用します。
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 )
どちらの場合も、浮動小数点の値の範囲は0〜1です。したがって、8ビット整数を読み取る場合は、最初に255で除算し、8ビット整数を書き込む場合は255を乗算します。最後に、通常と同じ方法で。sRGBを使用するために知っておく必要があるのはこれだけです。
これまで、私は1つの強度のみを扱ってきましたが、色に関してはもっと賢いことがあります。人間の目は、さまざまな色合いよりもさまざまな明るさを区別できるため(技術的には、クロミナンスよりも輝度分解能が優れています)、色合いとは別に明るさを保存することで、24ビットをさらに有効に活用できます。これは、YUV、YCrCbなどの表現が行おうとしていることです。Yチャネルは色の全体的な明度であり、他の2つのチャネルよりも多くのビットを使用します(またはより多くの空間解像度を持ちます)。このように、RGB強度の場合のように(常に)曲線を適用する必要はありません。YUVは線形色空間であるため、Yチャネルの数値を2倍にすると、色の明度が2倍になりますが、RGBカラーのようにYUVカラーを加算または乗算することはできないため、
それがあなたの質問に答えると思うので、簡単な歴史的メモで終わります。sRGB以前は、古いCRTには非線形性が組み込まれていました。ピクセルの電圧を2倍にすると、強度は2倍以上になります。モニターごとにどれだけ異なるか、このパラメーターはガンマと呼ばれていました。この動作は、ライトよりも暗くなる可能性があるため便利でしたが、最初にキャリブレーションしない限り、ユーザーのCRTでの色の明るさを判断できないことも意味していました。ガンマ補正開始する色(おそらく線形)を変換し、ユーザーのCRTのガンマに変換することを意味します。OpenGLはこの時代から来ているため、sRGBの動作が少し混乱することがあります。しかし、GPUベンダーは、上記の規則を使用する傾向があります。つまり、8ビットの強度をテクスチャまたはフレームバッファーに格納する場合はsRGBであり、色を処理する場合は線形です。たとえば、OpenGL ES 3.0では、各フレームバッファとテクスチャに「sRGBフラグ」があり、読み取りと書き込みの際に自動変換を有効にすることができます。sRGB変換やガンマ補正を明示的に行う必要はまったくありません。