anchor.diff:      return h;
   }
   
  -float prefilteredImportanceSampling(float ipdf, float iblRoughnessOneLevel) {
  +float prefilteredImportanceSampling(float ipdf, float omegaP) {
       // 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);
  +    float mipLevel = log2(K * omegaS / omegaP) * 0.5;    // log4
       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;
  +vec3 isEvaluateSpecularIBL(const PixelParams pixel, vec3 n, vec3 v, float NoV) {
       const uint numSamples = uint(IBL_INTEGRATION_IMPORTANCE_SAMPLING_COUNT);
       const float invNumSamples = 1.0 / float(numSamples);
  +    const vec3 up = vec3(0.0, 0.0, 1.0);
  +
  +    // TODO: for a true anisotropic BRDF, we need a real tangent space
  +    // tangent space
  +    mat3 T;
  +    T[0] = normalize(cross(up, n));
  +    T[1] = cross(n, T[0]);
  +    T[2] = n;
  +
  +    // Random rotation around N per pixel
  +    const vec3 m = vec3(0.06711056, 0.00583715, 52.9829189);
  +    float a = 2.0 * PI * fract(m.z * fract(dot(gl_FragCoord.xy, m.xy)));
  +    float c = cos(a);
  +    float s = sin(a);
  +    mat3 R;
  +    R[0] = vec3( c, s, 0);
  +    R[1] = vec3(-s, c, 0);
  +    R[2] = vec3( 0, 0, 1);
  +    T *= R;
  +
  +    float roughness = pixel.roughness;
  +    float dim = float(textureSize(light_iblSpecular, 0).x);
  +    float omegaP = (4.0 * PI) / (6.0 * dim * dim);
   
       vec3 indirectSpecular = vec3(0.0);
       for (uint i = 0u; i < numSamples; i++) {
           vec2 u = hammersley(i);
  -        vec3 h = tangentToWorld * importanceSamplingNdfDggx(u, roughness);
  +        vec3 h = T * 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);
  +        float NoL = saturate(dot(n, l));
           if (NoL > 0.0) {
               float NoH = dot(n, h);
  -            float LoH = max(dot(l, h), 0.0);
  +            float LoH = saturate(dot(l, h));
   
               // 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 mipLevel = prefilteredImportanceSampling(ipdf, omegaP);
  +            vec3 L = decodeDataForIBL(textureLod(light_iblSpecular, l, mipLevel));
   
               float D = distribution(roughness, NoH, h);
               float V = visibility(roughness, NoV, NoL);
  -            vec3  F = fresnel(pixel.f0, LoH);
  +            vec3 F = fresnel(pixel.f0, LoH);
               vec3 Fr = F * (D * V * NoL * ipdf * invNumSamples);
   
               indirectSpecular += (Fr * L);
  
positive.code:     p.anisotropy = 0.0;
  #endif
  
      vec3 clearCoatLobe = isEvaluateIBL(p, clearCoatNormal, shading_view, clearCoatNoV);
      Fr += clearCoatLobe * (specularAO * pixel.clearCoat);
  #endif
  }
  
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;
  
  