Unityのシェーダで色空間設定がガンマでもリニアでも同じ計算結果を示すシェーダを書く方法についてまとめました。
Unity2019.2.18
やりたいこと
sRGB色空間では人間の目に50%のグレーとして見える色がRGB=(0.5, 0.5, 0.5)として表されます。
これに対してリニア色空間では人間の目に50%のグレーとして見える色がRGB=約(0.22, 0.22, 0.22)として表されます。
このあたりのリニアワークフローの基礎知識は以下の記事にまとめていますので、必要に応じて参照してください。
さてこの違いによりリニアワークフローではより物理的に正しいレンダリング結果が得られるわけですが、
例えば「sRGBのカラーテクスチャをプロシージャルに生成するツール」を作ることを考えると、
Unityの色空間の設定にかかわらず同じ計算結果を得られないとツールとして機能しません。
本記事ではこのようなケースのために、色空間設定が違っても同じ計算結果を示すシェーダを作ります。
シェーダ
やるべきことはシンプルで、色空間に合わせてフラグメントシェーダの最初と最後で変換を掛けるだけです。
今回は色空間がリニア設定だった場合にフラグメントシェーダに渡ってきた値をsRGB空間に変換し、
またフラグメントシェーダから出力する際にリニア空間の値に変換する実装にしました。
Shader "Example" { Properties { _ExampleColor("Example Color", Color) = (1,1,1,1) } SubShader { Tags { "RenderType"="Opaque" } Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; }; struct v2f { float4 vertex : SV_POSITION; }; float4 _ExampleColor; #define FLT_EPSILON 1.192092896e-07 float3 PositivePow(float3 base, float3 power) { return pow(max(abs(base), float3(FLT_EPSILON, FLT_EPSILON, FLT_EPSILON)), power); } half3 LinearToSRGB(half3 c) { 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; } half3 SRGBToLinear(half3 c) { 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; } v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); return o; } fixed4 frag (v2f i) : SV_Target { #if !UNITY_COLORSPACE_GAMMA // 入力値がリニアの値で渡ってくるのでSRGBに変換 _ExampleColor.rgb = LinearToSRGB(_ExampleColor.rgb); #endif float4 color = _ExampleColor; // 何かしらの計算 color.rgb = color.rgb + color.rgb; #if !UNITY_COLORSPACE_GAMMA // SRGBで計算した値をリニアで返す color.rgb = SRGBToLinear(color.rgb); #endif return color; } ENDCG } } }
Unityはワークフロー設定がガンマになっているとUNITY_COLORSPACE_GAMMAが定義されます。
今回はこれを利用して、リニア設定の場合にsRGBに変換してから計算しています。
色空間の変換関数については以下の記事を参照してください。