Unityのシェーダにおける値のリニア <-> sRGB変換関数を紹介します。
Unity2019.2.18
はじめに
sRGB色空間だけでなくリニア色空間にも対応するようなシェーダを書く場合、
シェーダ内で色空間の変換を行うことがあります。
PostProcessingのソースコードを見ていたところいい感じの色空間の変換用のコードがあったので本記事にまとめておきます。
元のソースコードは以下のgithubを参照してください。
シェーダ
それではシェーダです。
#define FLT_EPSILON 1.192092896e-07 // Using pow often result to a warning like this // "pow(f, e) will not work for negative f, use abs(f) or conditionally handle negative values if you expect them" // PositivePow remove this warning when you know the value is positive and avoid inf/NAN. float PositivePow(float base, float power) { return pow(max(abs(base), float(FLT_EPSILON)), power); } float2 PositivePow(float2 base, float2 power) { return pow(max(abs(base), float2(FLT_EPSILON, FLT_EPSILON)), power); } float3 PositivePow(float3 base, float3 power) { return pow(max(abs(base), float3(FLT_EPSILON, FLT_EPSILON, FLT_EPSILON)), power); } float4 PositivePow(float4 base, float4 power) { return pow(max(abs(base), float4(FLT_EPSILON, FLT_EPSILON, FLT_EPSILON, FLT_EPSILON)), power); } half SRGBToLinear(half c) { #if USE_VERY_FAST_SRGB return c * c; #elif USE_FAST_SRGB return c * (c * (c * 0.305306011 + 0.682171111) + 0.012522878); #else half linearRGBLo = c / 12.92; half linearRGBHi = PositivePow((c + 0.055) / 1.055, 2.4); half linearRGB = (c <= 0.04045) ? linearRGBLo : linearRGBHi; return linearRGB; #endif } half3 SRGBToLinear(half3 c) { #if USE_VERY_FAST_SRGB return c * c; #elif USE_FAST_SRGB return c * (c * (c * 0.305306011 + 0.682171111) + 0.012522878); #else half3 linearRGBLo = c / 12.92; half3 linearRGBHi = PositivePow((c + 0.055) / 1.055, half3(2.4, 2.4, 2.4)); half3 linearRGB = (c <= 0.04045) ? linearRGBLo : linearRGBHi; return linearRGB; #endif } half4 SRGBToLinear(half4 c) { return half4(SRGBToLinear(c.rgb), c.a); } half LinearToSRGB(half c) { #if USE_VERY_FAST_SRGB return sqrt(c); #elif USE_FAST_SRGB return max(1.055 * PositivePow(c, 0.416666667) - 0.055, 0.0); #else half sRGBLo = c * 12.92; half sRGBHi = (PositivePow(c, 1.0 / 2.4) * 1.055) - 0.055; half sRGB = (c <= 0.0031308) ? sRGBLo : sRGBHi; return sRGB; #endif } half3 LinearToSRGB(half3 c) { #if USE_VERY_FAST_SRGB return sqrt(c); #elif USE_FAST_SRGB return max(1.055 * PositivePow(c, 0.416666667) - 0.055, 0.0); #else half3 sRGBLo = c * 12.92; half3 sRGBHi = (PositivePow(c, half3(1.0 / 2.4, 1.0 / 2.4, 1.0 / 2.4)) * 1.055) - 0.055; half3 sRGB = (c <= 0.0031308) ? sRGBLo : sRGBHi; return sRGB; #endif } half4 LinearToSRGB(half4 c) { return half4(LinearToSRGB(c.rgb), c.a); }
SRGBToLinearでsRGBの値をリニアの値に変換し、LinearToSRGBでその逆の処理を行います。
それぞれ1次元、3次元、4次元の値の入出力に対応しています。
また、これらの変換にはある程度の計算コストがかかるため、二つの近似式が用意されています。
USE_VERY_FAST_SRGBやUSE_FAST_SRGBを定義することで使用する式を選択できます。