前段时间学习shader时发现了一个问题,一张纯红色透明度为128的图片叠加在一张纯绿色的图片上在unity中得出的结果与ps中的结果不一致。网上查找了ps中的透明混合的公式为
color = A.rgb*A.alpha + B.rgb*(1-A.alpha)。自己计算了一下结果总是不对。
红色透明度128的图 绿色透明度255的图
ps中红色在上绿色在下叠加后的结果色
Unity叠加后的结果色
可以明显看出ps混合后的数值要比unity中的数值低。
问题产生原因是unity使用的颜色空间(Color Space)为线性空间数值(Linear),而ps使用的是伽马矫正后的空间数值(gamma)。一般被gamma矫正后的图片会比线性空间的数值要低。所以ps混合后的数值要比unity中的数值低。
在查阅资料学习时关于gamma矫正产生的原因基本上围绕着两点
1. CRT 显示器的物理特性
2. 人眼对光强的感知能力并不是线性的
在早期我们的电脑显示器基本上是阴极射线管显示器(CRT),就是上面有个后脑勺的显示器。显示器一般是通过电压来控制每个像素的数值从而显示出图片的,但是CRT显示器有一个物理特性是输入的电压会输出为约等于输入电压2.2次幂的亮度。从理论上讲这个值应该是2.5倍,但在当时根据实际统计市面上的产品的特性总结出来的值约是2.2倍。这样就会导致显示器的图比实际图像要暗。图片借用文章
我们想要让图片最终展示的亮度与原图一致就需要对gamma2.2进行一个矫正,这个矫正值就是gamma1/2.2(2.2次幂的对数0.45) ,看下方的矫正示意图。
我们为什么要进行gamma1/2.2(0.45)的矫正呢?除了上方需要与gamma2.2进行抵消后显示原图外还有一点就是我们渲染计算的数值要在伽马值为 1 的理想线性空间进行的。
这里举个例子:
从矫正图来看假如我们输入一个暗红色的光照RGB颜色为(0.5, 0.0, 0.0)的图, 然后将这个颜色提升一倍变为(1.0, 0.0, 0.0). 但由于显示器的非线性特性, 我们最终调节的颜色实际是从 (0.218, 0.0, 0.0) 变成了 (1.0, 0.0, 0.0)。
所以gamma矫正是为了解决CRT的物理特性。但基于人眼的感知特性这个gamma矫正又是怎么实现的呢?我们继续往下看。
这是一个黑白渐变图。在rgba数值中是(0,0,0,1)~(1,1,1,1)
根据上面这个图左侧黑色数值为0,右侧白色数值为1,那么半灰色是不是0~1区间的中间数0.5呢?在数学中0~1的中间值的确应该是0.5。但是人眼在自然界中看到的半灰色实际在上面这张图的3/4的位置即:0.75。来我们看下面这张图:
我们根据 “物理亮度图” 第0.5的半灰色对比去找 “人类自然界感知亮度图”中对应的颜色应该是在0.75左右。所以“ 人眼对光强的感知能力并不是线性的 ” 的意思就是如此。
人类的视觉系统在黑暗环境下的辨识能力要强于明亮环境这是进化过程中出现的特性, 这样有助于我们及时发现黑暗中隐藏的危险。而且人眼的这一特性与CRT显示器的gamma2.2相似。
网上查的好多资料视频有的说gamma矫正是因为人眼感知特性,有的又说是为了解决CRT显示问题。但其实一开始gamma矫正是为了解决CRT的物理特性巧合的是人眼的感知特性也是如此,而且后面Led等液晶显示器现世后已经可以达到与现实色彩呈线性的数值因为人眼特性和兼容CRT显示器处理的图片才保留了gamma矫正。我感觉这才是正确的。如果有什么不对的也欢迎大家指正。
其他:
- gamma本身是人眼对于不同灰阶画面切换时候,一个让眼睛觉得更自然的亮度补充.
- 保存在计算机中的图片数据是经过gamma1/2.2(0.45)后的数据。
这个空间对应的就是物理空间亮度图。半灰色在0~1区间中属于正常的0.5。根据上方伽马空间描述了解到人眼感受的亮度值比实际物理空间的亮度值暗,所以unity中线性空间下的图片亮度会比gamma矫正后的图片要亮。
Texture纹理图片sRGB(Color Texture)属性只有在线性空间下才管用。勾选是开启gamma矫正,未勾选是不开启gamma矫正。
在线性空间下勾选了sRGB效果图:
运算公式 color = (A.rgb^2.2 * A.alpha + B.rgb^2.2 * (1-A.alpha)) ^ (1/2.2)
未勾选sRGB效果图:
运算公式 color = (A.rgb * A.alpha + B.rgb*(1-A.alpha)) ^(1/2.2)
修改 File—>Build Settings—>Player Settings—>Other Setting选项卡中的Color Space为gamma即可。
“编辑”—>“颜色设置”—>勾选“用灰度系数混合RGB颜色”。
修改后ps的最后混合颜色结果 与unity中的一致。