Unity Gamma空间和Linear空间转换代码

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的转换问题。

结束语

终于最近又迎来的有时间写东西的时候了~疫情期间,希望大家注意防护!

猜你喜欢

转载自blog.csdn.net/u010778229/article/details/113056026