#ifndef RENDER_IBL_HLSLI
#define RENDER_IBL_HLSLI

// 必须与 IBLResources in ibl_resources.h.的定义顺序相同
TextureCube<float4> gIblIrradianceMap     : register(t9);
TextureCube<float4> gIblPrefilteredMap    : register(t10);
Texture2D<float4>   gIblBrdfLut           : register(t11);
SamplerState        gIblSampler           : register(s3);

cbuffer CBIBL : register(b4) {
    // x=intensity, y=prefilter max mip, z=Y rotation in radians, w=enabled
    float4 gIblParams;
};

float3 IBL_RotateAroundY(float3 direction, float radians){
    float sineValue;
    float cosineValue;
    sincos(radians, sineValue, cosineValue);

    return float3( cosineValue * direction.x + sineValue * direction.z,
        direction.y,
        -sineValue * direction.x + cosineValue * direction.z);
}

float3 IBL_FresnelSchlickRoughness(float cosTheta, float3 f0, float roughness)
{
    const float oneMinusRoughness = 1.0f - roughness;
    const float3 grazing = max(float3(oneMinusRoughness, oneMinusRoughness,
                                      oneMinusRoughness), f0);
    const float factor = pow(1.0f - saturate(cosTheta), 5.0f);
    return f0 + (grazing - f0) * factor;
}

// V 点从表面到相机。 N 是世界空间着色法线。返回的值是线性 HDR 辐射度，必须在色调映射之前添加。
float3 EvaluateIBL(float3 N, float3 V, float3 baseColor,
                   float metallic, float roughness, float ambientOcclusion)
{
    float3 finalRet = float3(0.0f,0.0f,0.0f);
    if (gIblParams.w < 0.5f){
        finalRet=float3(0.0f, 0.0f, 0.0f);
    } else {
        N = normalize(N);
        V = normalize(V);
        metallic = saturate(metallic);
        roughness = clamp(roughness, 0.045f, 1.0f);
        ambientOcclusion = saturate(ambientOcclusion);

        const float nDotV = saturate(dot(N, V));
        const float3 dielectricF0 = float3(0.04f, 0.04f, 0.04f);
        const float3 f0 = lerp(dielectricF0, baseColor, metallic);
        const float3 F = IBL_FresnelSchlickRoughness(nDotV, f0, roughness);
        const float3 kD = (float3(1.0f, 1.0f, 1.0f) - F) * (1.0f - metallic);

        const float3 sampleN = IBL_RotateAroundY(N, gIblParams.z);
        const float3 irradiance = gIblIrradianceMap.SampleLevel(gIblSampler, sampleN, 0.0f).rgb;
        const float3 diffuse = irradiance * baseColor;

        const float3 reflection = reflect(-V, N);
        const float3 sampleR = IBL_RotateAroundY(reflection, gIblParams.z);
        const float lod = roughness * max(gIblParams.y, 0.0f);
        const float3 prefiltered = gIblPrefilteredMap.SampleLevel(gIblSampler, sampleR, lod).rgb;

        const float2 brdf = gIblBrdfLut.SampleLevel(gIblSampler, float2(nDotV, roughness), 0.0f).rg;
        const float3 specular = prefiltered * (F * brdf.x + brdf.y);

        finalRet = (kD * diffuse + specular) * ambientOcclusion * gIblParams.x;
    }

    return finalRet;
}

#endif // RENDER_IBL_HLSLI
