     // Exactly correct fomular.
     // return (A + (B * phiDiff * sin(alpha) * tan(beta))) * NdotL / PI;
 
-    return (A + (B * phiDiff * sin(alpha) * tan(beta))) * NdotL;
-}
-
-// compute fresnel specular factor for given base specular and product
-// product could be NdV or VdH depending on used technique
-vec3 fresnel_factor(in vec3 f0, in float product)
-{
-    return mix(f0, vec3(1.0), pow(clamp(1.01 - product, 0.0, 1.0), 5.0));
-}
-
-float D_blinn(in float roughness, in float NdH)
+    return vec3((A + (B * phiDiff * sin(alpha) * tan(beta))) * NdotL);
+}
+
+vec3 fresnelSchlick(float cosTheta, vec3 F0)
+{
+    return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
+}
+
+vec3 fresnelSchlickRoughness(float cosTheta, vec3 F0, float roughness)
+{
+    return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0);
+}
+
+float D_blinn(float roughness, float NdH)
 {
     float m = roughness * roughness;
     float m2 = m * m;

    return (n + 2.0) / (2.0 * PI) * pow(NdH, n);
}

float D_beckmann(in float roughness, in float NdH)
{
    float m = roughness * roughness;
    float m2 = m * m;

    float HdV = max(0.001, dot(H, V));
    float LdV = max(0.001, dot(L, V));

    vec3 result = vec3(0.0, 0.0, 0.0);
    float opacity = base_color.w;

    vec3 F0 = vec3(0.04);
    F0 = mix(max(vec3(0.04), reflectance), base_color.xyz, metallic);

    vec3 fresnel = fresnelSchlick(HdV, F0);
    vec3 diffuse_light = vec3(0.0, 0.0, 0.0);
    vec3 specular_light = vec3(0.0, 0.0, 0.0);

#if TRANSPARENT_MATERIAL == 1
    float reflectivity = max(max(F0.r, F0.g), F0.b);
    opacity = clamp(base_color.w + base_color.w * reflectivity, 0.0, 1.0);
#endif

    // Directional lighting
    {
        diffuse_light += oren_nayar(roughness, NdL, NdV, N, V, L) / PI * NdL * light_color;
        specular_light += cooktorrance_specular(fresnel, NdL, NdV, NdH, roughness) * NdL * light_color;
    }

    // Image based lighting
    {
        const vec2 env_size = textureSize(texture_probe, 0);
        const float env_mipmap_count = log2(min(env_size.x, env_size.y));

        vec3 ibl_diffuse_light = textureLod(texture_probe, invert_y(N), env_mipmap_count - 1.0).xyz;
        vec3 ibl_specular_light = textureLod(texture_probe, invert_y(R), env_mipmap_count * roughness).xyz;

        // Note : because texture_probe is HDR and not sRGB.
        //ibl_diffuse_light = pow(ibl_diffuse_light, vec3(2.2));
        //ibl_specular_light = pow(ibl_specular_light, vec3(2.2));

        // mix scene reflection
        if(RENDER_SSR == 1.0f)
        {
            ibl_specular_light.xyz = mix(ibl_specular_light.xyz, scene_reflect_color.xyz, scene_reflect_color.w);
        }

        vec2 envBRDF = clamp(env_BRDF_pproximate(NdV, roughness), 0.0, 1.0);
        vec3 shValue = fresnelSchlick(NdV, F0) * envBRDF.x + envBRDF.y;

        diffuse_light += ibl_diffuse_light * shValue;
        specular_light += ibl_specular_light * shValue;
    }

    // final result
    diffuse_light *= base_color.xyz * clamp((vec3(1.0) - fresnel) * (1.0 - metallic), 0.0, 1.0);
    specular_light = mix(specular_light, specular_light * base_color.xyz, vec3(metallic));

    result = (diffuse_light + specular_light) * shadow_factor;
    result += scene_in_scatter;

    // SSAO
