     float HdV = max(0.001, dot(H, V));
     float LdV = max(0.001, dot(L, V));
 
-    // Fresnel specular reflectance at normal incidence
-    const float ior = 1.38;
-    vec3 f0 = vec3(abs ((1.0 - ior) / (1.0 + ior)));
-    f0 = mix(max(vec3(0.04), f0 * reflectance * reflectance), base_color.xyz, metallic);
-
+    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);
+    float reflectivity = max(max(F0.r, F0.g), F0.b);
     opacity = clamp(base_color.w + base_color.w * reflectivity, 0.0, 1.0);
 #endif
 
-    // specular : mix between metal and non-metal material, for non-metal constant base specular factor of 0.04 grey is used
-    vec3 specfresnel = fresnel_factor(f0, HdV);
-    vec3 specular_lighting = cooktorrance_specular(NdL, NdV, NdH, specfresnel, roughness);
-    specular_lighting = specular_lighting * light_color * NdL;
-
-    // diffuse
-    vec3 diffuse_light = vec3(oren_nayar(roughness, NdL, NdV, N, V, L));
-    diffuse_light = diffuse_light * (vec3(1.0) - specfresnel) * light_color;
+    // 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));
-
-    vec2 envBRDF = clamp(env_BRDF_pproximate(NdV, roughness), 0.0, 1.0);
-    vec3 shValue = fresnel_factor(f0, NdV) * envBRDF.x + envBRDF.y;
-    vec3 ibl_diffuse_color = textureLod(texture_probe, invert_y(N), env_mipmap_count - 1.0).xyz;
-    vec3 ibl_specular_color = textureLod(texture_probe, invert_y(R), env_mipmap_count * roughness).xyz;
-
-    // Note : because texture_probe is HDR and not sRGB.
-    //ibl_diffuse_color = pow(ibl_diffuse_color, vec3(2.2));
-    //ibl_specular_color = pow(ibl_specular_color, vec3(2.2));
-
-    // mix scene reflection
-    if(RENDER_SSR == 1.0f)
-    {
-        ibl_specular_color.xyz = mix(ibl_specular_color.xyz, scene_reflect_color.xyz, scene_reflect_color.w);
-    }
-
-    diffuse_light += shValue * ibl_diffuse_color;
-    specular_lighting += shValue * ibl_specular_color;
-    specular_lighting = mix(specular_lighting, specular_lighting * base_color.xyz, vec3(metallic));
+    {
+        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
-    vec3 result = (base_color.xyz * diffuse_light * (1.0 - metallic) + specular_lighting) * shadow_factor;
+    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

    return exp((NdH2 - 1.0) / (m2 * NdH2)) / (PI * m2 * NdH2 * NdH2);
}

float D_GGX(in float roughness, in float NdH)
{
    /*float m = roughness * roughness;
    float m2 = m * m;
    float d = (NdH * m2 - NdH) * NdH + 1.0;
    return m2 / (PI * d * d);*/

    float m2     = roughness * roughness;
    float denom  = NdH * NdH * (m2 - 1.0) + 1.0;
    return m2 / (PI * denom * denom);
}

float G_schlick(in float roughness, in float NdV, in float NdL)
{
    /*float k = roughness * roughness * 0.5;
    float V = NdV * (1.0 - k) + k;
    float L = NdL * (1.0 - k) + k;
    return 0.25 / (V * L);*/

    float k = roughness * roughness * 0.5;
    float ggx1 = NdV / (NdV * (1.0 - k) + k);
    float ggx2 = NdL / (NdL * (1.0 - k) + k);
    return ggx1 * ggx2;
}




// https://en.wikipedia.org/wiki/Oren%E2%80%93Nayar_reflectance_model
vec3 oren_nayar(float roughness2, float NdotL, float NdotV, vec3 N, vec3 V, vec3 L)
{
    float incidentTheta = acos(NdotL);
    float outTheta = acos(NdotV);
