Cg版本
引自: UnityCG.cginc
//--Gamma到Linear转换
inline half3 GammaToLinearSpace (half3 sRGB)
{
// Approximate version from http://chilliant.blogspot.com.au/2012/08/srgb-approximations-for-hlsl.html?m=1
return sRGB * (sRGB * (sRGB * 0.305306011h + 0.682171111h) + 0.012522878h);
// Precise version, useful for debugging.
//return half3(GammaToLinearSpaceExact(sRGB.r), GammaToLinearSpaceExact(sRGB.g), GammaToLinearSpaceExact(sRGB.b));
}
//--精确转换
inline float GammaToLinearSpaceExact (float value)
{
if (value <= 0.04045F)
return value / 12.92F;
else if (value < 1.0F)
return pow((value + 0.055F)/1.055F, 2.4F);
else
return pow(value, 2.2F);
}
//--Linear到Gamma的转换
inline half3 LinearToGammaSpace (half3 linRGB)
{
linRGB = max(linRGB, half3(0.h, 0.h, 0.h));
// An almost-perfect approximation from http://chilliant.blogspot.com.au/2012/08/srgb-approximations-for-hlsl.html?m=1
return max(1.055h * pow(linRGB, 0.416666667h) - 0.055h, 0.h);
// Exact version, useful for debugging.
//return half3(LinearToGammaSpaceExact(linRGB.r), LinearToGammaSpaceExact(linRGB.g), LinearToGammaSpaceExact(linRGB.b));
}
--精确转换
inline float LinearToGammaSpaceExact (float value)
{
if (value <= 0.0F)
return 0.0F;
else if (value <= 0.0031308F)
return 12.92F * value;
else if (value < 1.0F)
return 1.055F * pow(value, 0.4166667F) - 0.055F;
else
return pow(value, 0.45454545F);
}
个人理解
Gamma空间
什么是Gamma空间?为什么有Gamma空间?自古江湖上就有定论,刚开始我也是云里雾里,总觉得他们说的有问题。我们先看结果
OK~~没错,上图就是Unity的处理流程。没说的,需要注意的就是如果是线性空间,有的项目贴图的某个通道可能会作为数据图,注意如果是RGB通道的话,数据获取是有错误的,我们可以选择使用取消掉贴图导入是的SRGB选项。或者对数据再次进行Gamma1/2.2的矫正。
当初的疑惑
之前一直就有疑问,先做Gamma1/2.2的贴图压缩,显示器在做Gamma2.2的矫正,这不就是原始数据么?如下曲线图所示,假设一个0.5的数据贴图压缩成0.72974,显示器矫正后成0.5,这不是多此一举么?直接0.5他不香么?
带着疑问去寻找解决问题的答案:
1.图像编码gamma的目的是为了解决低容宽时,图像保存的信息过少的问题。
问:怎么解决呢?
答:人眼对暗色比较敏感,对亮色不敏感。比如白天亮个灯没啥感觉(相对于不亮来说亮度是有增加的),晚上一个萤火虫都存在感比较强。
问:然后呢?
答:根据人眼的规律,我们可以在有限的存储空间中,记录更多的暗的数据,去掉一些亮的数据,这样操作来尽量保持和原来的效果看上去是相似的, 上面的贴图压缩曲线就是把本来暗的区域提亮了。
问:能看出来啊~提亮后,显示器显示时不就又把提升的数据,变回去了么?有啥用?
答:WTF!原来你的点在这。假设我们显得贴图只能存储0,0.2,0.4,0.6,0.8,1这三种数据,毕竟贴图的存储数据是有限的:
实际数据 | 0 | 0.1 | 0.2 | 0.3 | 0.4 | 0.5 | 0.6 | 0.7 | 0.8 | 0.9 | 1 |
---|---|---|---|---|---|---|---|---|---|---|---|
贴图编码后 | 0 | 0.35 | 0.48 | 0.58 | 0.66 | 0.73 | 0.79 | 0.85 | 0.90 | 0.95 | 1 |
存储数据 | 0 | 0.4 | 0.4 | 0.6 | 0.6 | 0.8 | 0.8 | 0.8 | 1 | 1 | 1 |
显示器显示 | 0 | 0.13 | 0.13 | 0.32 | 0.32 | 0.61 | 0.61 | 0.61 | 1 | 1 | 1 |
上面可以看出,真实数据通过贴图的这个中间者传递信息给显示器,而贴图本身能力有限,能存的数据就那么几种,所以他需要存储的时候多存一些暗部的信息,丢弃一些亮部的信息,以保证是个人(注意必须是个人,猫啊狗的就不一样了)看上去感觉还比较逼真。
2.显示器无法线性显示。一般CRT显示器的亮度响应曲线,可以看到其输入电压提高一倍,亮度输出并不是提高一倍,是非线性的。比如红色的亮度为50%,如果一个未经过Gamma矫正的CRT显示器的Gamma值是2.2,那么输出结果的亮度将分别为22%.
问:以前的CRT显示器,由于自身原因才出现的这种情况?还是人们故意做的。
答:确实是硬件的问题。
问:显示的这个变化和上面人类的视觉刺激是巧合还是?
答:Z,这谁知道。。
线性空间
线性空间还比较简单,主要就是为啥Es3.0才支持线性空间,其实还是硬件问题,线性空间下unity会自动开启framebuffer_sRGB。其实主要的还是处理Gamma到Linear的转换问题。
结束语
终于最近又迎来的有时间写东西的时候了~疫情期间,希望大家注意防护!