经常接触纹理制作或是shader编写应该多多少少都听说过Gamma校正
Gamma校正本身很好理解,\(Gamma\ =\ Linear^{2.2}\)
通过\(pow2.2\)这条曲线,将线性的颜色值映射到非线性
耳熟能详的案例是中灰值
人眼对灰度的感知不是线性的,如果线性地显示深度会觉得中灰偏亮
这是因为人眼对于暗部更加敏感,对于线性的灰度值,人眼得到的暗部信息和亮部对半,因此在非线性映射之后,暗部信息更多了,就更符合人眼的直觉了。
这就是所谓的gamma校正
而CRT显示器输入电压与输出亮度的 2.2曲线则似乎是一个巧合,不过时至今日我们使用的显示器依然会默认对输出亮度进行 pow2.2 的映射。
线性空间与gamma空间
(个人认为使用空间描述有些反直觉,无法直观地理解灰度的整个输出过程)
我们在使用PS等绘图软件时,能够发现其中的渐变工具其实是非线性的
将PS中0.5的灰度值导出为贴图导入unity,来看看unity中其真实的灰度值为多少。
ColorSpace 设置为 Gamma
纹理勾选SRGB
直接输出纹理
纹理取消勾选SRGB
直接输出纹理
纹理勾选SRGB
判断灰度值是否为0.5(白色表示是)
纹理取消勾选SRGB
判断灰度值是否为0.5
当Unity的色彩空间设置为Gamma空间时,勾选SRGB无意义。
PS在SRGB空间下输出的中灰值,在Unity的Gamma空间中也是0.5
有意思的事情发生在将Unity的色彩空间改为Linear时。
ColorSpace 设置为 Linear
纹理勾选SRGB
直接输出纹理
纹理取消勾选SRGB
直接输出纹理
纹理勾选SRGB
判断灰度值是否为0.5
纹理取消勾选SRGB
判断灰度值是否为0.5
勾选SRGB意味着什么呢?
通过上面四张图可以得出结论,在Linear空间下,勾选SRGB之后,纹理的灰度值不再是0.5
事实上这个值是0.217左右,是 0.5 的 2.2 次方
而勾选了SRGB之后,灰度值则回到了0.5
在前两张图中也可以看出勾选SRGB之后颜色更深。
而在Gamma空间下,勾选SRGB并没有影响纹理的灰度值。
我们可以这样认为,将色彩空间设置为Gamma,那么Unity会将所有纹理输出时自动进行pow2.2的操作
相当于自动在shader最后加了一句pow2.2,并且此时SRGB失效。
这就要求,我们在生产纹理时使用的是SRGB空间,即输出值为0.5,表现为美术中灰。
在Linear空间下,只有当我们勾选SRGB时,输出的纹理才是符合预期结果的
此时纹理的灰度值为0.217,勾选SRGB这个操作对纹理进行了一次 pow2.2的操作。
取消勾选则纹理的灰度值为正常的0.5
也就是说,设置Gamma空间与勾选SRGB的效果类似
前者是统一对纹理进行校正操作
后者是对单独纹理进行校正操作
这就是Gamma校正在Unity中的主要应用。
为什么需要线性空间?
因为许多图形学算法需要在线性空间下进行计算,而不是人眼的感知曲线。
事实上,所谓的线性空间,也只是变化需要是线性的,最终的效果与人眼的感知效果应该是一样的。
如果将PS的颜色空间设置为Linear,那么如果我们要输出美术中灰,其灰度值将会是0.217
这也要求我们将Unity的色彩空间设置为Linear,并且取消勾选SRGB
否则Unity会对纹理再进行一次映射,最终图像会被压暗。
一般的正确工作流程是这样的:
PS使用SRGB,Unity色彩空间设置为Gamma,纹理勾不勾SRGB无所谓,Shader采样纹理后正常计算颜色,无需手动校正直接输出。
这种情况,在Gamma空间下,纹理本身灰度是可以线性计算的,最终会被统一校正。
PS使用SRGB,Unity色彩空间设置为Linear,纹理勾选SRGB,Shader采样纹理后,进行 pow(1/2.2) 的校正再计算颜色,计算完成后,再 pow2.2 校正回去输出。
这种情况下,因为纹理勾选了SRGB,灰度值被校正到了0.217,已经不在线性空间了,我们需要的是对0.5进行计算,所以需要手动校正回线性空间,输出时再校正回去。
PS使用Linear,Unity色彩空间设置为Linear,纹理不勾选SRGB,Shader采样纹理后正常计算颜色,无需手动校正直接输出。
这种情况,是相同的Linear空间,我们直接计算线性空间下的颜色,所见即所得,无需校正。
主流的做法是前两种。