anchor.diff:          // Compute this sample's contribution to the brdf
           float NoL = 2.0 * cosTheta2 - 1.0;
           if (NoL > 0.0) {
  -            VoH = max(VoH, 0.0);
  -            float NoH = max(cosTheta,  0.0);
  +            float LoH = max(dot(l, h), 0.0);
  +            float NoH = cosTheta;
   
               // PDF inverse (we must use D_GGX() here, which is used to generate samples
  -            float ipdf = (4.0 * VoH) / (D_GGX(linearRoughness, NoH, h) * NoH);
  +            float ipdf = (4.0 * LoH) / (D_GGX(linearRoughness, NoH, h) * NoH);
   
               // see: "Real-time Shading with Filtered Importance Sampling", Jaroslav Krivanek
               // prefiltering doesn't work with anisotropy
  
positive.code: 
              // BRDF to evaluate
              float D = distribution(linearRoughness, NoH, h);
              float V = visibility(pixel.roughness, linearRoughness, NoV, NoL, VoH);
              vec3  F = fresnel(pixel.f0, VoH);
  
              // integral
              vec3 Fr = F * (D * V * ipdf * NoL);
  
negative.code:         float sinTheta = sqrt(1.0 - cosTheta2);
  
          vec3 h = tangentToWorld * vec3(cos(phi) * sinTheta, sin(phi) * sinTheta, cosTheta);
  
          // since anisotropy doesn't work with prefiltering, we use the same "faux" anisotropy
          // we do when we use the prefiltered cubemap
  