颜色空间相关
颜色空间相关
一直以来对颜色空间的理解都不够深入,今天稍微整理了一下。
主要针对Unity中的Gamma和Linear两种颜色空间进行分析。
首先是为什么会有Gamma这个东西。
早期的显示器(CRT)是基于电子束扫描的,亮度的变化也是非线性的,即输入两倍的电压,并不能得到两倍的亮度,而是大概输入电压的2.2次方。
这导致了显示的亮度偏暗。
因此在输出到显示器之前,先对颜色进行Gamma校正,即将颜色值进行0.45次方的处理,这样在显示器上显示时,亮度就会接近预期。
这只是最早期Gamma的由来,现在液晶显示器已经没有了这种特性,依然保留Gamma更多是因为下述的SRGB空间的原因。
人眼对亮度的感知是非线性的,具体来说,人眼对低亮度的变化更敏感,对高亮度的变化不敏感。
因为上述人眼的特点,利用Gamma空间的非线性特性,使用73%的空间来存储低亮度的颜色,使用27%的空间来存储高亮度的颜色。这就是SRGB颜色空间。
因此一般(除了HDR)的纹理贴图等都是在SRGB空间中存储的,
而在着色器中进行计算时,一般要使用线性空间进行计算,所以在Unity中导入贴图时,会有一个选项来选择贴图的颜色空间。目的就是为了便于在输入到着色器之前移除Gamma校正。
一般只有和颜色相关的纹理(如Albedo、Emission等)需要SRGB空间,其他纹理(如Normal、Metallic等)则不需要。
早期因为部分硬件的限制,Unity默认使用Gamma颜色空间,但目前大部分硬件都支持线性空间,因此基本不考虑在Gamma空间下进行开发。
值得注意的是,PS默认是在Gamma空间进行透明度混合,而Unity则是在线性空间下进行透明度混合。所以要美术配合,修改PS的透明度混合方式。
所以,有以下事实。
unity在着色器中默认使用线性空间进行计算。
如果输入的颜色是SRGB空间的颜色,在进入着色器之前,需要进行将其转为线性空间的颜色,即进行2.2次方的处理。
在全部渲染完成后,Unity会将颜色再次转换成SRGB空间的颜色进行显示,即进行0.45次方的处理。
如果新建一UnLitShader,输入一浮点0.5,则输出是188,188,188.
使用FrameDebugger可以看到,在这个着色器执行时,确实输出的是128,128,128。
但在最后一个BlitFinalToBackBuffer的Pass中,Unity会将颜色进行0.45次方的处理,得到188,188,188。
但如果选择输入一颜色(128,128,128)
使用FrameDebugger可以看到,输入的颜色是128,128,128时,传入的颜色实际是0.214,0.214,0.214,说明Unity认为我们输入的是SRGB空间的颜色,要转成线性空间的颜色。
最后一个BlitFinalToBackBuffer的Pass中,Unity会将颜色进行0.45次方的处理,得到128,128,128。