// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only

#ifndef BSDF_GLSLIB
#define BSDF_GLSLIB 1

#ifndef M_PI
#define M_PI 3.14159265359
#endif

vec3 qt_schlick3(in vec3 f0, in vec3 f90, in float VdotH)
{
    return f0 + (f90 - f0) * (1.0 - clamp(pow( VdotH, 5.0), 0.0, 1.0 ) );
}

float qt_G_GGX_2cos(in float cos_theta_m, in float alpha) {
    float k = 0.5 * alpha;
    return 0.5 / (cos_theta_m * (1.0 - k) + k);
}

float qt_D_GGX(in float cos_theta_m, in float alpha) {
    float alpha2 = alpha * alpha;
    float d = 1.0 + (alpha2 - 1.0) * cos_theta_m * cos_theta_m;
    return alpha2 / (M_PI * d * d);
}

float qt_V_GGX(in float NdotL, in float NdotV, in float alphaRoughness)
{
    return qt_G_GGX_2cos(NdotL, alphaRoughness) * qt_G_GGX_2cos(NdotV, alphaRoughness);
}

// An alternative to using a LUT is to use this function
// see: https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile
vec2 qt_brdfApproximation(in vec3 N, in vec3 viewDir, in float roughness)
{
    float nDotV = clamp(dot(N, viewDir), 0.0, 1.0);
    const vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022);
    const vec4 c1 = vec4(1.0, 0.0425, 1.04, -0.04);
    vec4 r = roughness * c0 + c1;
    float a004 = min(r.x * r.x, exp2(-9.28 * nDotV)) * r.x + r.y;
    return vec2(-1.04, 1.04) * a004 + r.zw;
}

float qt_applyIorToRoughness(in float roughness, in float ior)
{
    // Scale roughness with IOR so that an IOR of 1.0 results in no microfacet refraction and
    // an IOR of 1.5 results in the default amount of microfacet refraction.
    return roughness * clamp(ior * 2.0 - 2.0, 0.0, 1.0);
}

vec3 qt_getPunctualRadianceTransmission(in vec3 normal, in vec3 view, in vec3 pointToLight, in float roughness,
    in vec3 f0, in vec3 f90, in vec3 baseColor, in float ior)
{
    float clampedRoughness = clamp(roughness, 0.0001, 1.0);
    float alphaRoughness = clampedRoughness * clampedRoughness;
    float transmissionRougness = qt_applyIorToRoughness(alphaRoughness, ior);

    vec3 n = normalize(normal);           // Outward direction of surface point
    vec3 v = normalize(view);             // Direction from surface point to view
    vec3 l = normalize(pointToLight);
    vec3 l_mirror = normalize(l + 2.0*n*dot(-l, n));     // Mirror light reflection vector on surface
    vec3 h = normalize(l_mirror + v);            // Halfway vector between transmission light vector and v

    float D = qt_D_GGX(clamp(dot(n, h), 0.0, 1.0), transmissionRougness);
    vec3 F = qt_schlick3(f0, f90, clamp(dot(v, h), 0.0, 1.0));
    float Vis = qt_V_GGX(clamp(dot(n, l_mirror), 0.0, 1.0), clamp(dot(n, v), 0.0, 1.0), transmissionRougness);

    // Transmission BTDF
    return (1.0 - F) * baseColor * D * Vis;
}

#endif
