anchor.diff:      p.anisotropy = 0.0;
   #endif
   
  -    vec3 clearCoatLobe = isEvaluateIBL(p, clearCoatNormal, shading_view, clearCoatNoV);
  +    vec3 clearCoatLobe = isEvaluateSpecularIBL(p, clearCoatNormal, shading_view, clearCoatNoV);
       Fr += clearCoatLobe * (specularAO * pixel.clearCoat);
   #endif
   }
  
positive.code:     return h;
  }
  
  float prefilteredImportanceSampling(float ipdf, float iblRoughnessOneLevel) {
      // See: "Real-time Shading with Filtered Importance Sampling", Jaroslav Krivanek
      // Prefiltering doesn't work with anisotropy
      const float numSamples = float(IBL_INTEGRATION_IMPORTANCE_SAMPLING_COUNT);
      const float invNumSamples = 1.0 / float(numSamples);
            float dim = float(textureSize(light_iblSpecular, 0).x);
            float omegaP = (4.0 * PI) / (6.0 * dim * dim);
            float invOmegaP = 1.0 / omegaP;
      const float K = 4.0;
      float omegaS = invNumSamples * ipdf;
      float mipLevel = clamp(log2(K * omegaS * invOmegaP) * 0.5, 0.0, iblRoughnessOneLevel);
      return mipLevel;
  }
  
  vec3 isEvaluateIBL(const PixelParams pixel, vec3 n, vec3 v, float NoV) {
      // TODO: for a true anisotropic BRDF, we need a real tangent space
      vec3 up = abs(n.z) < 0.9999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
  
      mat3 tangentToWorld;
      tangentToWorld[0] = normalize(cross(up, n));
      tangentToWorld[1] = cross(n, tangentToWorld[0]);
      tangentToWorld[2] = n;
  
      float roughness = pixel.roughness;
      float a2 = roughness * roughness;
  
      float iblRoughnessOneLevel = frameUniforms.iblRoughnessOneLevel;
      const uint numSamples = uint(IBL_INTEGRATION_IMPORTANCE_SAMPLING_COUNT);
      const float invNumSamples = 1.0 / float(numSamples);
  
      vec3 indirectSpecular = vec3(0.0);
      for (uint i = 0u; i < numSamples; i++) {
          vec2 u = hammersley(i);
          vec3 h = tangentToWorld * importanceSamplingNdfDggx(u, roughness);
  
          // Since anisotropy doesn't work with prefiltering, we use the same "faux" anisotropy
          // we do when we use the prefiltered cubemap
          vec3 l = getReflectedVector(pixel, v, h);
  
          // Compute this sample's contribution to the brdf
          float NoL = dot(n, l);
          if (NoL > 0.0) {
              float NoH = dot(n, h);
              float LoH = max(dot(l, h), 0.0);
  
              // PDF inverse (we must use D_GGX() here, which is used to generate samples)
              float ipdf = (4.0 * LoH) / (D_GGX(roughness, NoH, h) * NoH);
  
              float mipLevel = prefilteredImportanceSampling(ipdf, iblRoughnessOneLevel);
  
              // we use texture() instead of textureLod() to take advantage of mipmapping
              vec3 L = decodeDataForIBL(texture(light_iblSpecular, l, mipLevel));
  
              float D = distribution(roughness, NoH, h);
              float V = visibility(roughness, NoV, NoL);
              vec3  F = fresnel(pixel.f0, LoH);
              vec3 Fr = F * (D * V * NoL * ipdf * invNumSamples);
  
              indirectSpecular += (Fr * L);
  
negative.code:     Fr = E * prefilteredRadiance(r, pixel.perceptualRoughness);
  #elif IBL_INTEGRATION == IBL_INTEGRATION_IMPORTANCE_SAMPLING
      vec3 E = vec3(0.0); // TODO: fix for importance sampling
      Fr = isEvaluateSpecularIBL(pixel, shading_normal, shading_view, shading_NoV);
  #endif
      Fr *= singleBounceAO(specularAO) * pixel.energyCompensation;
  
  