Gamma矫正

发布时间 2023-04-08 17:40:07作者: kayiko

Gamma矫正 

    颜色空间

          定义颜色空间需要:色域、白点和Gamma值 (色域指一个颜色空间所有可能颜色的总称 ,白点是色域中最亮的点,gamma指的是对色域中色彩采样的方式,gamma=1就是均匀切割,否则为不均匀切割。)

          下图中sRGB和Rec.709的颜色空间是一样的,它们只是传递函数不同,那么什么是传递函数? 

        

  传递函数

    即把颜色空间下的三原色值转化为电子信号,这就是传递函数

    它会把线性的光转化为非线性的电信号,当我们需要显示时,再把电信号转化为线性的光信号

             

     这个传递函数其实就是Gamma使用的函数。

 

     Gamma编码:光转电传递函数,负责把场景线性光转到非线性视频信号值。

    Gamma解码:电转光传递函数,负责把非线性视频信号值转换成显示光亮度。 

    具体例子:

        

 

 

   Gamma校正的原因:

     由于我们用于显示图像数据都是8bit,且人眼对暗部变化更敏感,如果要充分利用带宽,那么就需要使用更多位置去存储暗部值。也就是说暗部使用更高精度保存,而亮部使用更低精度保存。

      

      上面的时美术中灰,下面的是物理中灰。

           自然界中0.2的亮度值对应人心理上0.5的亮度值。暗部部分的曲线更加陡峭,即人眼对于暗部的变化更加敏感,这符合韦伯定律。 

 线性工作流

   在生产的各个环节,正确使用gamma编码及gamma解码,使得最终得到的颜色数据与最初输入的物理数据一致。

        如果是使用Gamma空间的贴图,在传给着色器前需要从Gamma空间转到线性空间,这样在计算的时候,我们使用的是线性空间的颜色值。

     

 

      

  第一步,输入的纹理如果是sRGB(Gamma0.45),那我们要进行一个操作转换到线性空间。这个操作叫做Remove Gamma Correction,在数学上是一个2.2的幂运算。如果输入不是sRGB,而是已经在线性空间的纹理了呢?那就可以跳过Remove Gamma Correction了。

  注:美术输出资源时都是在sRGB空间的,但Normal Map等其他生成出来的纹理则一般在线性空间。所以开发大型项目,弄清楚区别并统一工作流是首要任务!

        第二步,现在输入已经在线性空间了,那么进行Shader中光照、插值等计算后就是比较真实的结果了(上文解释了哦~),如果不对sRGB进行Remove Gamma Correction直接就进入Shader计算,那算出来的就会不自然,就像前面那两张球的光照结果一样。

        第三步,Shader计算完成后,需要进行Gamma Correction,从线性空间变换到Gamma0.45空间,在数学上是一个约为0.45的幂运算。如果不进行Gamma Correction输出会怎么样?那显示器就会将颜色从线性空间转换到Gamma2.2空间,接着再被你看到,结果会更暗。

        第四步,经过了前面的Gamma Correction,显示器输出在了线性空间,这就和人眼看物理世界的过程是一样的了!

Unity中如何选择线性空间

   通过点击菜单Edit->Project Settings->Player页签->Other Settings下的Rendering部分进行修改,参数Color Space可以选择Gamma或Linear。

        当选择Gamma Space时,Unity不会做任何处理。

        当选择Linear Space时,引擎的渲染流程在线性空间计算,理想情况下项目使用线性空间的贴图颜色,不需要勾选sRGB。如果勾选了sRGB的贴图,会通过硬件特性采样时进行线性转换。(即没勾选sRGB的贴图不会被线性处理)

        有时候你可能需要想让一个Float也被Gamma Correction Removed,那么就需要在ShaderLab中使用[Gamma]前缀:[Gamma]_Metallic("Metallic",Range(0,1))=0 

tips:

   1、如果unity使用了gamma工作流,那么无论图片是勾选还是不勾选srgb,那么unity是不做任何处理,不做2.2的变线性,也不做处理之后的1/2.2的操作。

        2、如果unity使用了linear工作流,那么如果图片选择了srgb模式,那么unity则为其做2.2的降线性处理,然后在输出的时候,自动进行1/2.2的处理。

        而如果图片不勾选srgb模式,那么unity不会为其做2.2的降线性,而在处理之后,缺依然保留做1/2.2的操作。那么图片最终会变亮了。

        依然原本图片就是srgb空间,在拍摄或者ps中,就已经做了一次1/2.2的操作。而又因为我们没有勾选srgb,所以没有做2.2的降线性,而在最后输出结果,unity自动做了一次1/2.2的操作,所以连续做了两次1/2.2的操作,于是图片变亮了。

        3、如何区分一个图片是否要勾选srgb呢?如果是选用unity的gamma工作流,我们够不够选无所谓,因为unity不会为为我们做任何事情。都是按照怎么输入,怎么来的处理。

而如果是线性工作流,那么则需要人为的操作了,则需要人为判断这个图片是否需要勾选srgb。

        4、哪些图片需要勾选srgb呢?勾选了srgb实际上给unity暗示了什么?

        对于在线性工作流下,也就是unity使用了linear模式,那么对于那些美术给的漫反射贴图,则是直接在ps中绘制的,而在ps中绘制的图片,则是存储为srgb空间的(与美术商量好)。也就是存储在了(1/2.2的空间中)上曲线中。那么此时导入unity之后,就要知道这个图是非线性的图了。那么则要勾选srgb。而其他的图,比如法线图、mask图、噪声图,程序纹理,都是线性的图,那么为了避免勾选srgb而造成unity帮我们做了一次2.2的操作,所以要明确告诉unity,这个你不需要转线性了,我自己已经是线性的图了。