#version 330 core

const float DIELECTRIC_SPECULAR = 0.04;

// math.glsl
float M_PI();
float M_1_PI();
float sqr(float x);
float pow5(float x);

/*
 * Fresnel, Schlick's approximation.
 */
vec3 F_Schlick(float VdotH, vec3 f0)
{
    return f0 + (vec3(1.0) - f0) * pow5(clamp(1.0 - VdotH, 0.0, 1.0));
}

/*
 * Fresnel, Schlick's approximation, monochromatic.
 */
float F_Schlick(float VdotH, float f0)
{
    return f0 + (1.0 - f0) * pow5(clamp(1.0 - VdotH, 0.0, 1.0));
}

/*
 * Normal distribution function, Trowbridge-Reitz/GGX microfacet distribution.
 */
float D_GGX(float NdotH, float a2)
{
    float f = (NdotH * a2 - NdotH) * NdotH + 1.0;
    return M_PI() * f * f;
}

/*
 * Geometric attenuation, Smith-GGX formulation.
 */
float G1_Smith_GGX(float NdotX, float a2)
{
    return NdotX + sqrt(NdotX * (NdotX - NdotX * a2) + a2);
}

/*
 * Get the Fresnel reflectance at 0 degrees (light hitting the surface
 * perpendicularly) from PBR parameters.
 */
vec3 f0_from_pbr(vec3 base_color, float metallic)
{
    return mix(vec3(DIELECTRIC_SPECULAR), base_color, metallic);
}

/*
 * Evaluate the color of a standard PBR surface illuminated by an analytical
 * light source. The evaluated BSDF consists of a specular lobe and a diffuse
 * lobe. The specular lobe is the Cook-Torrance microfacet model, and the
 * diffuse lobe is a simple Lambertian.
 *
 * Note that the calculations here do not match the literature. Brian Karis
 * approach of refactoring by NdotX/NdotX has been used to optimize the code.
 */
vec3 surface_eval_analytical(
    // Material
    vec3 base_color, float metallic, float roughness, vec3 f0,
    // Light
    vec3 light_intensity, float occlusion,
    // Vectors
    vec3 N, vec3 L, vec3 V)
{
    float NdotL = dot(N, L);
    // Skip fragments that are completely occluded or that are not facing the light
    if (occlusion <= 0.0 || NdotL <= 0.0)
        return vec3(0.0);
    NdotL = max(NdotL, 1e-4);

    float a = max(sqr(roughness), 0.045);
    float a2 = sqr(a);

    vec3 H = normalize(L + V);
    float NdotV = max(dot(N, V), 1e-4);
    float NdotH = max(dot(N, H), 1e-4);
    float VdotH = max(dot(V, H), 1e-4);

    vec3 c_diff = mix(base_color * (1.0 - DIELECTRIC_SPECULAR), vec3(0.0), metallic);

    vec3  F = F_Schlick(VdotH, f0);
    float G = G1_Smith_GGX(NdotV, a2) * G1_Smith_GGX(NdotL, a2);
    float D = D_GGX(NdotH, a2);

    vec3 f_specular = (F * a2) / (D * G);
    vec3 f_diffuse = (vec3(1.0) - F) * c_diff * M_1_PI();
    vec3 bsdf = f_diffuse + f_specular;

    return bsdf * light_intensity * occlusion * NdotL;
}