#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl)

uniform float sampleCount;
uniform float z;

out vec4 FragColor;

void main()
{
  float x = floor(gl_FragCoord.x) / (LUT_SIZE - 1.0);
  float y = floor(gl_FragCoord.y) / (LUT_SIZE - 1.0);

  float ior = clamp(sqrt(x), 0.05, 0.999);
  /* ior is sin of critical angle. */
  float critical_cos = sqrt(1.0 - saturate(ior * ior));

  y = y * 2.0 - 1.0;
  /* Maximize texture usage on both sides of the critical angle. */
  y *= (y > 0.0) ? (1.0 - critical_cos) : critical_cos;
  /* Center LUT around critical angle to avoid strange interpolation issues when the critical
   * angle is changing. */
  y += critical_cos;
  float NV = clamp(y, 1e-4, 0.9999);

  float a = z * z;
  float a2 = clamp(a * a, 1e-8, 0.9999);

  vec3 V = vec3(sqrt(1.0 - NV * NV), 0.0, NV);

  /* Integrating BTDF */
  float btdf_accum = 0.0;
  float fresnel_accum = 0.0;
  for (float j = 0.0; j < sampleCount; j++) {
    for (float i = 0.0; i < sampleCount; i++) {
      vec3 Xi = (vec3(i, j, 0.0) + 0.5) / sampleCount;
      Xi.yz = vec2(cos(Xi.y * M_2PI), sin(Xi.y * M_2PI));

      /* Microfacet normal. */
      vec3 H = sample_ggx(Xi, a2, V);

      float VH = dot(V, H);

      /* Check if there is total internal reflections. */
      float fresnel = F_eta(ior, VH);

      fresnel_accum += fresnel;

      float eta = 1.0 / ior;
      if (dot(H, V) < 0.0) {
        H = -H;
        eta = ior;
      }

      vec3 L = refract(-V, H, eta);
      float NL = -L.z;

      if ((NL > 0.0) && (fresnel < 0.999)) {
        float LH = dot(L, H);

        /* Balancing the adjustments made in G1_Smith. */
        float G1_l = NL * 2.0 / G1_Smith_GGX_opti(NL, a2);

        /* btdf = abs(VH*LH) * (ior*ior) * D * G(V) * G(L) / (Ht2 * NV)
         * pdf = (VH * abs(LH)) * (ior*ior) * D * G(V) / (Ht2 * NV) */
        float btdf = G1_l * abs(VH * LH) / (VH * abs(LH));

        btdf_accum += btdf;
      }
    }
  }
  btdf_accum /= sampleCount * sampleCount;
  fresnel_accum /= sampleCount * sampleCount;

  if (z == 0.0) {
    /* Perfect mirror. Increased precision because the roughness is clamped. */
    fresnel_accum = F_eta(ior, NV);
  }

  if (x == 0.0) {
    /* Special case. */
    fresnel_accum = 1.0;
    btdf_accum = 0.0;
  }

  /* There is place to put multi-scatter result (which is a little bit different still)
   * and / or lobe fitting for better sampling of. */
  FragColor = vec4(btdf_accum, fresnel_accum, 0.0, 1.0);
}
