写成数学公式:
x = 1.0/(b-a) ,b为结束Roughness值,a为开始Roughness值
y = - a / (b-a)
把上述x,y代入到MobileComputeMixingWeight中的MixingAlpha式中得
MixingAlpha
= smoothstep(0 ,1 , Roughness/(b-a) - a/(b-a)
= smoothstep(0 , 1 , (Roughness - a)/(b-a))
可以看到MixingAlpha值和Roughness值成正比,Roughness越大,则MixingAlpha值也越大,最大值不超过1(staturate所限)。
2. 计算MixingWeight(Normalized Cubemap)
这一个变量的命名和原始的注释非常迷惑,按主流的说法,它该叫Normalized Cubemap,其作用是把当前IBL的间接漫反射亮度缩放到和从Lightmap或SH(ILC)中接收到的间接GI亮度一致。indirect_irradiance来源是物体当前像素的光照图的亮度(静态物体)或ILC的亮度(动态物体)。
算法简化成公式如下:
normalized_scale = indirect_irradiance / ibl_average_brightness
一般的最终specular_IBL计算公式(UE4不完全一样,见步骤3说明):
specular_IBL = sampled_cube * normalized_scale
其中ibl_average_brightness来源是当前经过卷积后的cubemap所计算出来的平均亮度,即roughness为1时所采的这张1*1的最小mipmap的亮度。
UE4中计算该IBL亮度时不是使用普通的亮度计算公式,而是使rgb的贡献平均化。
ibl_average_brightness = dot(color.rgb ,float3(0.333,0.333,0.333)
UE4的亮度计算之所以使用均值,是因为如果使用标准的亮度计算公式,无法处理一些特殊的IBL边界情形:当IBL中只存在蓝色时,亮度会非常小,而当IBL中只存在绿色时,亮度又会非常大。但这么做却也并不是最优的选择,我们将在第四部分进行说明。
3. 插值得到最终的缩放系数
注意到步骤1中结论:Roughness值越大,MixingAlpha越大,当Roughness大于等于结束Roughness时,缩放系数完全等于步骤2中计算出来的normalized_scale;当roughness小于等于开始Roughnes时,缩放系数等于1; 缩放系数其它情形下处于[1,normalized_scale]之间。
由于在大部分情形下,normalized_scale小于1,所以也就可以看到一个现象:越光滑的物体,反射球对它的影响就越强,越粗糙的物体,反射球对它的亮度影响就越弱。
至于场景美术所提第二个问题——“金属物体上的阴影总是比非金属上的阴影来得更黑”,经查是TA做了一个很不PBR的材质规范:非金属材质没有AO通道,AO图直接乘到了BaseColor上;金属材质留有材质AO通道输入了AO图,BaseColor上不带光照和遮挡信息。由于材质AO会同时作用于BaseColor和间接光照的亮度(indirect_irradiance *= AO),所以金属物体的IBL缩放值会更小,从而更黑。
UE4移动端IBL的可用优化1. 压缩格式 :UE4移动端的Skylight cubemap是用的float原生的图,既然定位等同于Reflection Capture,可以考虑使用RGBM的方式同样的压一压?Reflection Capture的RGBM在Cook时也不接受ASTC/ETC/PVR方式的压缩,要不要统一压成硬件支持的格式?(UE4.26 Preview中,移动端已实现Reflection Capture的Cubemap压缩,压缩格式为ETC2)
2. Cubemap转2D纹理:到ES3.1都不支持CubemapArray,故IBL的变化会导致动态Instance失效,是否可以一下把 Cubemap转为经纬图或椭球纹理,从而使用TextureArray Custom PrimitiveData进行动态Instance合批的最大化,从而有效的降低Drawcall ?转为2D纹理有2个小风险,经纬图的纹理浪费比cubemap大概有30%左右,有同事在小米6/小米9实测,相对于Cubemap,经纬图纹理采样的CacheMiss也更高。
3.算法优化/效果优化:UE4的IBL Normalized算法使用的是标量的ibl_average_brightness和标量的indirect_irradiance来计算,这种方式计算所带来的问题:
在未开启Lightmap方向性的情况下,lightmap的亮度只会计算上半图的亮度,而ILC中的亮度可能来自于任意方向,这可能会给使用ILC和Lightmap的物体带来不同的IBL亮度。