     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);
+float DistributionGGX(float NdH, float roughness)
+{
+    float a      = roughness * roughness;
+    float a2     = a * a;
+    float NdH2 = NdH * NdH;
+
+    float num   = a2;
+    float denom = (NdH2 * (a2 - 1.0) + 1.0);
+    denom = PI * denom * denom;
+
+    return num / denom;
+}
+
+float GeometrySchlickGGX(float NdV, float roughness)
+{
+    float r = (roughness + 1.0);
+    float k = (r * r) / 8.0;
+    float num   = NdV;
+    float denom = NdV * (1.0 - k) + k;
+    return num / denom;
+}
+
+float GeometrySmith(float NdV, float NdL, float roughness)
+{
+    float ggx2  = GeometrySchlickGGX(NdV, roughness);
+    float ggx1  = GeometrySchlickGGX(NdL, roughness);
     return ggx1 * ggx2;
 }
 

    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;



// cook-torrance specular calculation
vec3 cooktorrance_specular(vec3 F, float NdL, float NdV, float NdH, float roughness)
{
    //float D = D_GGX(roughness, NdH);
    //float G = G_schlick(roughness, NdV, NdL);
    //float rim = mix(1.0 - roughness * 0.9, 1.0, NdV);
    //return (1.0 / rim) * F * G * D;

    // cook-torrance brdf
    float NDF = DistributionGGX(NdH, roughness);
    float G   = GeometrySmith(NdV, NdL, roughness);
    vec3 numerator    = NDF * G * F;
    float denominator = 4.0 * NdV * NdL;
    return numerator / max(denominator, 0.001);
}

vec2 env_BRDF_pproximate(float NdV, float roughness)
{
    // see https://www.unrealengine.com/blog/physically-based-shading-on-mobile
    const vec4 c0 = vec4(-1.0, -0.0275, -0.572,  0.022);
    const vec4 c1 = vec4( 1.0,  0.0425,  1.040, -0.040);
    vec4 r = roughness * c0 + c1;
    float a004 = min(r.x * r.x, exp2(-9.28 * NdV)) * r.x + r.y;
    return vec2(-1.04, 1.04) * a004 + r.zw;
}

