WebGL PBR F0 与 菲涅尔F

3,255 阅读3分钟

现有PBR流程中与F0有关的公式

F0的定义

  • F0,表示平面的基础反射率,它是利用所谓折射指数(Indices of Refraction)或者说IOR计算得出的
    f0 = vec3(pow(( u_Ior - 1.0) /  (u_Ior + 1.0), 2.0)); //f0 通过ior计算
    
  • 当光线垂直(以0度角)撞击表面时,该光线被反射(Reflected)为镜面反射光的比率被称为F0,(当然也有F90的说法)
  • 一般F0都是0.04了,不直接通过uniform的方式输入,而是以baseColor里面或者通过ior来写

菲涅尔(直接光照)

  • image.png
  • F为菲涅尔反射系数(Fresnel reflection coefficient)
  • 这里的F0是带有颜色的,高光流是直接取specular贴图颜色,金属流是mix(f0, baseColor, metallic)取得
  • 因为示意图近似指数函数,所以函数这么写
  • PBR的原理大部分与菲涅尔和F0相关 PBR原理 (juejin.cn)
  • 菲涅尔效应表示观察看到的镜面反射光线的"量"(反射和折射)与视角相关的现象,是个百分比来的
  • 金属所有可见颜色都来自菲涅尔反射(DFG中,G是一种百分比遮挡,D是镜面波瓣)
  • 下面这张图是描述上面这个公式的,
    • 当h与v的角度90°的时候(掠射角入射),公式的值是最大的,为1,菲涅尔反射率最大.就最亮
    • 当h与v的角度0°的时候,公式的值是F0.反射率不一定最小.
    • F0即0度角入射的菲涅尔反射率
    • 影响公式的有v和h(即l),两个向量
    • 对于0°和45°之间的入射角,反射率几乎是恒定的。
    • 在45°和75°之间,反射率变化更为显着(通常但不总是有所增加)。
    • 在75°和90°之间,反射率总是迅速变为1
    • 该光线被反射(Reflected)为镜面反射光的比率被称为F0。
    • 折射(refracted)到表面中的光量则为为1-F0。
    • 光滑表面在切线入射时有将接近100%的镜面反射。 image.png

image.png image.png

  • f0 进行菲涅尔公式计算
vec3 V = normalize(camPos - WorldPos);
vec3 L = normalize(lightPositions[i] - WorldPos);
vec3 H = normalize(V + L); //半程向量
vec3 F    = fresnelSchlick(clamp(dot(H, V), 0.0, 1.0), F0);
vec3 fresnelSchlick(float cosTheta, vec3 F0)
{
    return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
}
  • 当然一般不用pow5,用下面这个
half Pow5(half v)
{
	return v * v * v * v * v;
}
  • 当IOR接近1的时候 可能有较大误差,直接使用精确的菲涅尔

image.png

金属与非金属与F0

  • 金属与非金属
    • 非金属 大部分不会高于0.17,
    • 金属 大部分0.5和1.0之间变化
    • 金属与非金属跟粗糙度无关
    • f0与表面混合 以(osg.js框架的pbr为准 IBL)
    • 金属的metallic为1,非金属的metallic为0,只取整数
    • 金属和非金属都有菲尼尔效应
vec3 f0 = vec3(0.04);
vec3 diffuseColor = mix(baseColor.rgb, vec3(0., 0., 0.), metallic); 
vec3 specularColor = mix(f0, baseColor, metallic); 
  • 非金属
    • 非金属有spcular
    • 非金属 0.04作为基础反射率已经足够好了,4%的反射
vec3 f0 = vec3(0.04);
vec3 diffuseColor = baseColor.rgb;
vec3 specularColor = f0; 
  • 金属
    • 金属没有漫反射
    • 金属的可见颜色都来自菲涅尔反射
    • 金属表面而言基础反射率一般是带有色彩的
    • 金属会立即吸收任何折射光线,不会出现任何次表面散射或透明感
vec3 f0 = vec3(0.04);
vec3 diffuseColor = 0
vec3 specularColor = baseColor

能量守恒(直接光照)

  • image.png
uniform float metallic;
vec3 kS = F;
kd = (vec3(1.0) - F)(1.0 - metallic)

BRDF积分贴图(间接光照)

  • 2D查找纹理存储是菲涅耳响应的系数(R 通道)和偏差值(G 通道)
float lod             = getMipLevelFromRoughness(roughness);
vec3 prefilteredColor = textureCubeLod(PrefilteredEnvMap, refVec, lod); //roughnesss
vec2 envBRDF          = texture2D(BRDFIntegrationMap, vec2(NdotV, roughness)).xy; //brdf
vec3 indirectSpecular = prefilteredColor * (F * envBRDF.x + envBRDF.y)  //fr