颜色空间
为什么现代显示器还保留了Gamma解码
现在的液晶显示器依然保留了2.2次方的解码gamma校正。但这并不是什么历史遗留问题,也不是因为CRT的物理特性,而是现代数据编码上实实在在的需求。
对物理线性的颜色编码做0.45次方的gamma校正,目的是为了让颜色编码的亮度分级与人眼主观亮度感受线性对应。这样在相同的数据位数下,图像数据可以保留更多人眼敏感的信息。以8位色为例:由于人眼对暗色调更加敏感,那就对物理线性的颜色做0.45次方的处理,也就是编码gamma。校正完成后,相当于使用了0~128的范围来表达原来与物理强度保持线性时0~55的亮度变化。
因此,显示器做解码gamma的目的是为了让便于保存和传输的颜色编码变回物理线性的形式,以便人眼观察显示器时能得到与观察现实世界时相近的感受。
美术制作标准
在美术制作过程中并不是所有贴图输出的都是在Linear空间下,像颜色贴图为了能够让人肉眼判断颜色的区别所有一般都是sRGB的存储。其实也就是我们需要通过肉眼来区分颜色信息的贴图要储存为sRGB,其他的通道图没有颜色信息的都是储存为Linear。
sRGB:Base Color固有色贴图,Emission自发光贴图
Linear:Metallic金属度贴图,Roughness粗糙度贴图,Smoothness光滑度贴图
Normal法线贴图是不需要关注在哪个色彩空间下的。
硬件要求
线性空间需要图形API的硬件支持,在移动平台下开发,安卓设备需要支持OpenGL3.0,iOS设备需要支持Metal特性。
sRGB Frame Buffer 和 sRGB Sampler
透明度偏差
直接对透明通道进行Gamma校正或者在Shader中校正,并不能让线性空间和Gamma空间的半透明效果保持一致。
主要原因是线性空间和Gamma空间的AlphaBlend计算公式不一样。
例如:Blend SrcAlpha OneMinusSrcAlpha
Gamma空间下的混合公式:color = src * srcAlpha + dest * OneMinusSrcAlpha
Linear空间下的混合公式:color = (src^2.2 * srcAlpha + dest^2.2 * OneMinusSrcAlpha) ^ (1/2.2);
说明:dest是屏幕FrameBuffer。 屏幕FrameBuffer是gamma数值,混合时需要矫正为线性。
注意:在Unity引擎中,如果勾选了sRGB的贴图是RGBA格式,在sRGB Sampler采样的时候并不会对Alpah通道做线性校正。在线性空间做Alpha Blend混合时,也会在线性空间计算混合。
透明度偏差解决方案
1. URP可编程渲染管线内,UI相机以Gamma空间渲染
2. 后处理方法解决,也可以考虑直接渲染到RT上来优化
3. 美术在线性空间下制作半透明的美术资源
终极解决方案
https://cmwdexint.com/2019/05/30/3d-scene-need-linear-but-ui-need-gamma/
参考:
https://zhuanlan.zhihu.com/p/66558476
https://zhuanlan.zhihu.com/p/79351489
https://github.com/chenjd/Unity_UI_Gamma