简易IBL渲染

通过一个Cubemap,使用IBL可以渲染出非常接近Unity Standard Shader的效果。常用DCC工具中实时渲染效果最好的大概是Substance Painter,它采用的就是IBL,不过是算法更加复杂,更接近真实的IBL。这里提供一种实现简易IBL的思路,尤其在移动端平台中,合理使用IBL可以使性能和效果达到非常好的平衡。

第一个思路

diffuse部分:

1
fixed3 indirectDiffuse = texCUBElod(_IBLCube, float4(normal, _LodLevel)) * albedo;

这里使用5的LOD Level对Cubemap进行采样(视效果调节Level值)

specular部分:

1
fixed3 indirectSpec = texCUBElod(_IBLCube, float4(reflection, _Gloss));

按照光滑度对cubemap进行采样,越粗糙反射结果就越模糊

两者根据金属度做lerp混合,即kS = Metalic,kD = 1 - Metalic

1
lerp(indirectDiffuse, indirectSpec, _Metalic)

这个思路是一个非常简单直接的经验模型,效果是这样:
以右边的Standard Shader效果作为对比,发现diffuse部分暗很多,以及发现还少了菲涅尔的效果


实际上用这个方案,在diffuse和specular上乘两个可调参数控制一下强度,已经可以达到比较好看的效果了。

改进Diffuse

diffuse部分会偏暗是由于对cubemap直接做了降采样处理,而正确的做法会比较接近这种情况:

右边的这种卷积处理比普通的降采样亮很多,会”强调”cubemap中的光照部分,更好的表现了光照信息。

Unity中是采用球谐函数计算的,这里直接使用Unity的函数

1
fixed3 indirectDiffuse = ShadeSH9(float4(normal,1)) * albedo;


当albedo为白色时,会发现diffuse的部分比起之前的效果看起来“正确”了许多,但是还缺少菲涅尔,在albedo为黑色时尤其明显。

添加菲涅尔

这里参考了 LearnOpenGL的IBL教程 的做法,最后使用的镜面反射部分是这篇文章方法的简化版。

菲涅尔的计算如下:

1
2
3
4
5
6
fixed3 fresnelSchlickRoughness(float cosTheta, fixed3 F0, float glossness)
{
return F0 + (max(glossness * fixed3(1,1,1), F0) - F0) * pow(1.0 - cosTheta, 5.0);
}
fixed3 F0 = lerp(fixed3(0.04, 0.04, 0.04), _Color, _Metalic); //非金属也有0.04的镜面反射
fixed3 fresnel = fresnelSchlickRoughness(NdV, F0, _Gloss);

镜面反射系数就是菲涅尔,即kS = fresnel,漫反射系数为kD = (1 - kS)*(1 - Metalic),效果是这样:

可以发现有了菲涅尔之后,效果非常接近了,当然要达到更好的效果还需要更加细致的计算。