const float epsilon = 0.0001;
const vec3 lumConv = vec3(0.27, 0.67, 0.06);

float HDR_GetLum(in vec3 color){
    return dot(color, lumConv);
}

vec4 HDR_EncodeLum(in float lum){
    float Le = 2.0 * log2(lum + epsilon) + 127.0;
    vec4 result = vec4(0.0);
    result.a = fract(Le);
    result.rgb = vec3((Le - (floor(result.a * 255.0)) / 255.0) / 255.0);
    return result;
}

float HDR_DecodeLum(in vec4 logLum){
    float Le = logLum.r * 255.0 + logLum.a;
    return exp2((Le - 127.0) / 2.0);
}

const mat3 rgbToXyz = mat3(
    0.2209, 0.3390, 0.4184,
    0.1138, 0.6780, 0.7319,
    0.0102, 0.1130, 0.2969);

const mat3 xyzToRgb = mat3(
    6.0013,    -2.700,    -1.7995,
    -1.332,    3.1029,    -5.7720,
    .3007,    -1.088,    5.6268);

vec4 HDR_LogLuvEncode(in vec3 rgb){
    vec4 result;
    vec3 Xp_Y_XYZp = rgb * rgbToXyz;
    Xp_Y_XYZp = max(Xp_Y_XYZp, vec3(1e-6, 1e-6, 1e-6));
    result.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z;
    float Le = 2.0 * log2(Xp_Y_XYZp.y) + 127.0;
    result.w = fract(Le);
    result.z = (Le - (floor(result.w * 255.0)) / 255.0) / 255.0;
    return result;
}

vec3 HDR_LogLuvDecode(in vec4 logLuv){
    float Le = logLuv.z * 255.0 + logLuv.w;
    vec3 Xp_Y_XYZp;
    Xp_Y_XYZp.y = exp2((Le - 127.0) / 2.0);
    Xp_Y_XYZp.z = Xp_Y_XYZp.y / logLuv.y;
    Xp_Y_XYZp.x = logLuv.x * Xp_Y_XYZp.z;
    vec3 rgb = Xp_Y_XYZp * xyzToRgb;
    return max(rgb, 0.0);
}

vec3 HDR_ToneMap(in vec3 color, in float lumAvg, in float a, in float white){
    white *= white;
    float lumHDR = HDR_GetLum(color);
    float L = (a / lumAvg) * lumHDR;
    float Ld = 1.0 + (L / white);
    Ld = (Ld * L) / (1.0 + L);
    return (color / lumHDR) * Ld;
    //return color * vec3(Ld);
}

vec3 HDR_ToneMap2(in vec3 color, in float lumAvg, in float a, in float white){
    float scale = a / (lumAvg + 0.001);
    return (vec3(scale) * color) / (color + vec3(1.0));
}