关于Unity3D内置Blinn Phong光照模型计算在移动设备上的曝光问题

Unity3D的surface shader用起来很是方便,比直接的cg写起来省事的多,而且有很多现成的东西可以include,比如光照部分就是,常用的lambert, blinn phong都提供了现成的实现,但是最近在做specular时发现,内置(https://unity3d.com/unity/download/archive下载全部内置shader source或{unity install path}/Data/CGIncludes/Lighting.cginc Windows, /Applications/Unity/Unity.app/Contents/CGIncludes/Lighting.cginc Mac)的LightingBlinnPhong在移动设备上使用时会出现高光曝掉的问题!

简单看lighting.cginc中的相关函数:

// NOTE: some intricacy in shader compiler on some GLES2.0 platforms (iOS) needs 'viewDir' & 'h'
// to be mediump instead of lowp, otherwise specular highlight becomes too bright.
inline fixed4 LightingBlinnPhong (SurfaceOutput s, fixed3 lightDir, half3 viewDir, fixed atten)
{
    half3 h = normalize (lightDir + viewDir);

    fixed diff = max (0, dot (s.Normal, lightDir));

    float nh = max (0, dot (s.Normal, h));
    float spec = pow (nh, s.Specular*128.0) * s.Gloss;

    fixed4 c;
    c.rgb = (s.Albedo * _LightColor0.rgb * diff + _LightColor0.rgb * _SpecColor.rgb * spec) * (atten * 2);
    c.a = s.Alpha + _LightColor0.a * _SpecColor.a * spec * atten;
    return c;
}

其中上面的注释已经明确提到在iOS上的浮点数精度会导致高光过曝的问题,而涉及到的viewDir和中间变量h都已是mediump的half了,试了下换成float也没有任何变化。
忽略中途google和若干各种尝试,最终发现问题还是精度导致的spec溢出,因为旁边比较关键的pow幂运算,而最终的解决方法就是将上面两个max的比较取值换为saturate,限制结果在0-1之间,从而不至于让运算结果过大导致本来想要衰减较大高光较暗的感觉变得太亮甚至过曝。

博主友情提示:

如您在评论中需要提及如QQ号、电子邮件地址或其他隐私敏感信息,欢迎使用>>博主专用加密工具v3<<处理后发布,原文只有博主可以看到。