在Unity的Gamma颜色空间下使用Standard Shader的总结

起因

今天在使用自己修改的Standard Shader调PBR效果的时候,发现项目中使用Substance导出的贴图应用到Unity中要明显比原本软件中亮,整体有一种发灰的感觉,而之前使用空工程的时候效果和Substance一致。一开始以为是shader有修改,反复检查shader和贴图都完全一样,天空盒和环境光方向光设置也一样,但最终效果却不一样。

查看Substance导出到Unity的文档https://support.allegorithmic.com/documentation/display/SPDOC/Unity+5,发现其中的第一个步骤为”In the Player project settings, set the Color Space to Linear”,即需要将项目的颜色空间切换到线性空间,查看项目的Color Space设置是Gamma,尝试切换到Linear空间则效果正常。

原因探究

关于Linear和Gamma空间的区别,冯乐乐的文章总结的很好:
http://blog.csdn.net/candycat1992/article/details/46228771
在Unity中如果使用Gamma颜色空间且不做任何手工处理时,所有计算过程是在Gamma空间下的,即使用了非线性的值当做线性值进行计算,这个计算过程无疑是错误的,在较为简单的经典光照模型中,美术可以通过手动调节Unity材质上的材质,达到想要的效果(通过错误的参数和错误的计算流程)。而这样不经校正的输入贴图颜色值,如果使用在PBR中,这个就会偏差很大,而PBR材质中并不具备足够的参数来手动调节结果到正常的值(计算过程较为复杂),因此在PBR中一定要处理Gamma校正才能保证最终的结果可以接受。

那是否可以简单的将项目Color Space设置为Linear呢?在PC平台上这样基本就能解决问题,但移动端就会有一定的兼容性问题。根据Unity官方文档https://docs.unity3d.com/Manual/LinearRendering-GammaTextures.html对于线性空间的描述:

Linear supported platforms Linear rendering is not supported on allplatforms. The build targets that support the feature are:
- Windows,Mac OS X and Linux (Standalone)
- Xbox One
- PlayStation 4
- Android
- iOS
- WebGL
There is no fallback to gamma when linear rendering is notsupported by the device. In this situation, the Player quits. You can check the active color space from a script by looking at QualitySettings.activeColorSpace. On Android, linear renderingrequires at least OpenGL ES 3.0 graphics API and Android 4.3. On iOS, linear rendering requires the Metal graphics API. On WebGL, linear rendering requires at least WebGL 2.0 graphics API.

即Android设备需要支持OpenGL ES 3.0和至少Android 4.3的系统,IOS需要Metal graphics API(系统至少为IOS 8),这一部分机器的比重有多大呢?

根据Unity自己的调查https://blogs.unity3d.com/cn/2016/12/07/linear-rendering-support-on-android-and-ios/

With Unity 5.5, linear rendering is now available on Android and iOS. On Android, linear rendering requires OpenGL ES 3 graphics API which represents 61.1% of the Android devices. On iOS, linear rendering requires Metal graphics API which represents 71.1% of the iOS devices.

即目前还有38.9%的Android设备和28.9%的IOS设备无法使用Linear颜色空间,是否要兼容这部分用户,需要每个项目自己斟酌。对于我们自己项目而言,是需要兼容到这部分用户的,因此就需要在项目颜色空间设置为gamma的情况下,自己在shader中对输入的贴图颜色进行校正转化。

操作步骤

在shader中进行输入贴图采样值进行转换时,有两个问题需要注意:

  • 需要确保输入的贴图是Gamma Encoded的,即经过了Gamma编码。目前大部分的美术工具如果输出时不经过特别的设置,默认都是经过了Gamma编码(这样就可以直接在显示器上显示),但也有某些特殊情况下贴图直接是在线性空间下的,对于这部分贴图就不需要进行转换,转换后的结果反而是错误的。

  • 只有内容为颜色的贴图需要进行转换,法线贴图和通道控制图则不需要。对于内容为数值的贴图来说,这部分贴图在美术工具中导出的结果就是线性的,即不需要进行Gamma转化就可以直接使用。

Unity在UnityCG.cginc头文件中提供了GammaToLinearSpace和LinearToGammaSpace进行两个空间的转化,其中的算法是近似算法,效率还比较高,其中注释指出了对应近似算法的介绍:

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 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));
}

同时还提供了一个判断函数判断当前项目是否在Gamma颜色空间下:

inline bool IsGammaSpace()
{
#if defined(UNITY_NO_LINEAR_COLORSPACE)
    return true;
#else
    // unity_ColorSpaceLuminance.w == 1 when in Linear space, otherwise == 0
    return unity_ColorSpaceLuminance.w == 0;
#endif
}

根据这个函数的返回值,可以选择针对贴图的采样值进行处理或者不处理。

在稍微修改了Standard Shader中对于输入贴图采样值转换到线性空间,并在最终输出结果时进行gamma转换后,显示的结果基本和Substance一致,修改片段如下,针对输入:
1、处理输入反射率贴图(albedo)的采样值

half3 diffColor = DiffuseAndSpecularFromMetallic1 (Albedo(i_tex), metallic, /*out*/ specColor, /*out*/ oneMinusReflectivity);
FragmentCommonData o = (FragmentCommonData)0;
o.diffColor = GammaToLinearSpace(diffColor);
o.specColor = GammaToLinearSpace(specColor);

2、处理环境贴图(简介光照)的采样值

UnityGI gi = UnityGlobalIlluminationMAD (d, occlusion, s.normalWorld, g);
gi.indirect.diffuse = GammaToLinearSpace(gi.indirect.diffuse);
gi.indirect.specular = GammaToLinearSpace(gi.indirect.specular);
return gi;

针对输出:

half3 color = BRDF3_Direct(diffColor, specColor, rlPow4, oneMinusRoughness);
color *= light.color * nl;
color += BRDF3_Indirect(diffColor, specColor, gi, grazingTerm, fresnelTerm);
color = LinearToGammaSpace1(color);
return half4(color, 1);

这样就基本完成Standard Shader在Gamma颜色空间下的修改使用。

  • 8
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值