Unity的gamma矫正、颜色空间及其转换的问题

Unity的gamma矫正、颜色空间及其转换的问题

  • 1,伽马矫正
  • 2,unity的颜色空间
    • 2.1两种颜色空间流程
    • 2.2pbr贴图格式
    • 2.3手动完成空间切换
    • 2.4升级URP后亮度不一致问题的问题
  • 3,Linear使用gamma空间素材的透明混合问题及解决办法
    • 3.1 使用在线性空间下制作的资源
    • 3.2 linear空间全部取消sRGB+后处理
    • 3.3 在gamma空间中手动实现linear的变换
    • 3.4 直接对alpha对一个矫正
    • 3.5 定制化管线
  • 参考资料

做PBR时涉及到线性空间的计算,发现这里有些坑,记录一下:

1,伽马矫正

简单说下原理:

  • 在线性空间里,伽马值是1.0;但人眼对光的感知不是线性的(对暗色调的感知度强于亮色调),如下图,颜色编码的亮度分级与人眼主观亮度感受线性对应,人们对物理线性的颜色编码做0.45次方的gamma校正,即sRGB颜色空间。「校正完成后,相当于使用了0-128的范围来表达原来与物理强度保持线性时0-55的亮度变化。」
    Unity的gamma矫正、颜色空间及其转换的问题_第1张图片
  • 为了让便于保存和传输的颜色编码变回物理线性的形式,以便人眼观察显示器时能得到与观察现实世界时相近的感受,因此显视器这端做了gamma2.2的变换,与gamma0.45平衡后回到了Gamma1.0的线性空间。

2,unity的颜色空间

2.1两种颜色空间流程

  • unity提供了两种颜色空间,Gamma Space和Linear Space

    1. Gamma Space:不做任何gamma变换,输入sRGB即Gamma0.45空间就在Gamma0.45空间计算,输入线性就在线性计算。
    2. Linear Space:先对sRGB格式的输入进行gamma2.2变换,将颜色数值变为线性,并在线性空间计算,输出时再进行gamma0.45变换,返回sRGB格式。「输入的color也变换,但所有alpha通道都不会变换」
  • Linear空间实际流程:
    Unity的gamma矫正、颜色空间及其转换的问题_第2张图片

  • unity在内置管线中默认是Gamma Space,这是因为线性空间需要支持OpenGL ES 3.0,而部分手机和苹果机不支持;但在URP管线中默认是Linear Space(坑!)。

  • UE4默认是线性空间,2017以后maya默认线性空间,贴图软件比如sp,sd都是线性空间。

2.2pbr贴图格式

  • pbr固有色贴图、有颜色的贴图如自发光是sRGB的,即gamma空间
  • 法线贴图是是线性的
  • 其他lut和sss贴图、mask贴图、arm贴图等,如果是近年在相关dcc软件中制作的,就是线性的

2.3手动完成空间切换

由于各种问题不方便切换时,可以用Unity自带的拟合函数内部切换,能比pow快一点:

half3 LinearToGammaSpace (half3 linRGB)----拟合pow(x, 0.45)
half3 GammaToLinearSpace (half3 sRGB)----拟合pow(x, 2.2)

2.4升级URP后亮度不一致问题的问题

内置管线默认是Gamma Space,在gamma0.45空间上完成计算的,但计算球谐光照时却是线性的,见ShadeSH9函数;因此他加了一句如果是gamma颜色空间,就把数据再转到gamma0.45上,这样就导致间接光这块的光照强度部分的计算在gamma1.0空间上了。而线性空间全部在1.0空间完成,相当于
线性空间 ( x + y ) 0.45 < g a m m a 空间 x 0.45 + y 0.45 线性空间(x+y)^{0.45} < gamma空间x^{0.45}+y^{0.45} 线性空间(x+y)0.45<gamma空间x0.45+y0.45

  • 因此gamma空间的间接光部分会更亮
    Unity的gamma矫正、颜色空间及其转换的问题_第3张图片

3,Linear使用gamma空间素材的透明混合问题及解决办法

切换空间后,即使纹理贴图都设置对了,但半透明效果还是对不上。这是因为在gamma空间里的混合公式为: r e s = s r c ∗ a l p h a + d s t ∗ ( 1 − a l p h a ) res = src * alpha + dst*(1-alpha) res=srcalpha+dst(1alpha)
而Linear空间的混合公式为: r e s = ( s r c 2.2 ∗ a l p h a + d s t 2.2 ∗ ( 1 − a l p h a ) ) 0.45 res = (src^{2.2} * alpha + dst^{2.2} *(1-alpha) )^{0.45} res=(src2.2alpha+dst2.2(1alpha))0.45
「linear空间不对alpha通道矫正」

比如假设src为白色+0.2,dst为黑色,则gamma空间下的计算结果为0.2,linear空间下为0.48116,如果叠加次数多的话误差更大。

对于这个问题的常见做法有以下几种:

3.1 使用在线性空间下制作的资源

在PS默认使用gamma空间,需要设置为线性,这样就可以和unity一致,不用再转换。
Unity的gamma矫正、颜色空间及其转换的问题_第4张图片

但如果不是项目一开始就制定好规则,多半美术是不会同意改的。

3.2 linear空间全部取消sRGB+后处理

  1. 所有素材取消勾选sRGB选项,这样得到的混合公式为: r e s = ( s r c ∗ a l p h a + d s t ∗ ( 1 − a l p h a ) ) 0.45 res =( src * alpha + dst*(1-alpha) )^{0.45} res=(srcalpha+dst(1alpha))0.45
  2. 内部手动统一素材空间
  3. 在最后输出的颜色上做一个pow(2.2),内置管线可以用后处理实现,URP可以在BlitPass中实现。

3.3 在gamma空间中手动实现linear的变换

涉及颜色、光照的都自己实现pow 2.2和pow 0.45,这样alpha还在gamma空间。

3.4 直接对alpha对一个矫正

这样算出来还是不一样,自己骗自己-_-,短暂糊弄事可以用。

3.5 定制化管线

见参考链接3.
Unity的gamma矫正、颜色空间及其转换的问题_第5张图片

参考资料

1,Unity内置管线升级URP之色彩空间
2,Gamma、Linear、sRGB 和Unity Color Space
3,Unity线性空间下半透明UI的解决方案

你可能感兴趣的:(unity图形之路,unity,贴图,游戏引擎)