#ifndef INCLUDE_LIGHT_AND_AMBIENT_COLORS
    #define INCLUDE_LIGHT_AND_AMBIENT_COLORS

    // --- Blackbody Radiation (Planckian locus) -> RGB ---
    // Tanner Helland / Neil Bartlett analytic approximation, valid ~1000K-12000K.
    // Used to derive sun/light color continuously from a color temperature instead
    // of a fixed, hand-picked RGB triplet.
    vec3 KelvinToRGB(float kelvin) {
        float temp = kelvin * 0.01;
        float r, g, b;

        r = temp <= 66.0 ? 255.0 : 329.698727446 * pow(temp - 60.0, -0.1332047592);

        g = temp <= 66.0
            ? 99.4708025861 * log(temp) - 161.1195681661
            : 288.1221695283 * pow(temp - 60.0, -0.0755148492);

        b = temp >= 66.0 ? 255.0 : (temp <= 19.0 ? 0.0 : 138.5177312231 * log(temp - 10.0) - 305.0447927307);

        return clamp(vec3(r, g, b) / 255.0, 0.0, 1.0);
    }

    // Maps sun elevation proxy (noonFactor: 0 = horizon, 1 = zenith) to a color temperature.
    // Non-linear ramp: real solar color temperature climbs fast just above the horizon
    // then plateaus near noon, so a simple linear mix under-sells the golden-hour warmth.
    float GetSunKelvin(float noonF) {
        float t = pow(clamp(noonF, 0.0, 1.0), 0.45);
        return mix(float(SUN_KELVIN_SUNSET), float(SUN_KELVIN_NOON), t);
    }

    // Rayleigh scattering removes blue light fastest, red slowest (~1/wavelength^4).
    // As the sun gets lower, direct light travels through more atmosphere ("airmass"),
    // so blue gets filtered out progressively more -> physically-motivated reddening,
    // continuous with sun angle instead of a fixed hardcoded sunset color.
    vec3 GetRayleighExtinctionTint(float noonF) {
        const vec3 rayleighK = vec3(0.25, 0.55, 1.0) * RAYLEIGH_MIE_STRENGTH; // R, G, B relative scattering rates
        float airmass    = 1.0 / clamp(noonF, 0.15, 1.0); // ~1/sin(elevation), grows near the horizon
        vec3 extinction  = exp(-airmass * rayleighK);
        vec3 refExtinction = exp(-1.0 * rayleighK); // reference at airmass = 1 (zenith/noon) so noon stays neutral
        vec3 tint = extinction / refExtinction;
        return tint / GetLuminance(tint); // normalize so this only shifts hue, never brightness
    }

    #if defined OVERWORLD
        #ifndef COMPOSITE1
            vec3 noonClearLightColor = vec3(0.65, 0.55, 0.375) * 2.05; //ground and cloud color
        #else
            vec3 noonClearLightColor = vec3(0.4, 0.75, 1.3); //light shaft color
        #endif
        vec3 noonClearAmbientColor = pow(skyColor, vec3(0.75)) * 0.72;

        #ifndef COMPOSITE1
            vec3 sunsetClearLightColor = pow(vec3(0.48, 0.46, 0.38), vec3(1.0 + invNoonFactor * 0.3)) * 4.0; //ground and cloud color
        #else
            vec3 sunsetClearLightColor = pow(vec3(0.46, 0.44, 0.38), vec3(1.0 + invNoonFactor * 0.3)) * 5.5; //light shaft color
        #endif
        // ("ambient pagi/sore kelihatan biru gelap" fix): warm-up kan tilt AMBIENT ke
        // emas/merah ketika matahari rendah (invNoonFactor besar), supaya golden
        // hour tidak berubah jadi biru gelap.
        vec3 sunsetClearAmbientColor = noonClearAmbientColor * vec3(1.35, 0.92, 0.62) * 0.95;

        #if !defined COMPOSITE1 && !defined DEFERRED1
            vec3 nightClearLightColor = 0.9 * vec3(0.06, 0.08, 0.18) * (0.3 + vsBrightness * 0.3); //ground color
        #elif defined DEFERRED1
            vec3 nightClearLightColor = 0.9 * vec3(0.05, 0.08, 0.18); //cloud color
        #else
            vec3 nightClearLightColor = vec3(0.04, 0.07, 0.18); //light shaft color
        #endif
        vec3 nightClearAmbientColor   = 0.9 * vec3(0.04, 0.07, 0.15) * (1.2 + vsBrightness * 0.5);

        #ifdef SPECIAL_BIOME_WEATHER
            vec3 drlcSnowM = inSnowy * vec3(-0.06, 0.0, 0.04);
            vec3 drlcDryM = inDry * vec3(0.01, -0.035, -0.06);
        #else
            vec3 drlcSnowM = vec3(0.0), drlcDryM = vec3(0.0);
        #endif
        #if RAIN_STYLE == 2
            vec3 drlcRainMP = vec3(-0.03, 0.0, 0.02);
            #ifdef SPECIAL_BIOME_WEATHER
                vec3 drlcRainM = inRainy * drlcRainMP;
            #else
                vec3 drlcRainM = drlcRainMP;
            #endif
        #else
            vec3 drlcRainM = vec3(0.0);
        #endif
        vec3 dayRainLightColor   = vec3(0.21, 0.16, 0.13) * 0.72 + noonFactor * vec3(0.0, 0.02, 0.06)
                                 + drlcRainM + drlcSnowM + drlcDryM;
        vec3 dayRainAmbientColor = vec3(0.2, 0.2, 0.25) * (1.55 + 0.5 * vsBrightness);

        vec3 nightRainLightColor   = vec3(0.03, 0.035, 0.05) * (0.5 + 0.5 * vsBrightness);
        vec3 nightRainAmbientColor = vec3(0.16, 0.20, 0.3) * (0.75 + 0.6 * vsBrightness);

        #ifndef COMPOSITE1
            float noonFactorDM = noonFactor; //ground and cloud factor
        #else
            float noonFactorDM = noonFactor * noonFactor; //light shaft factor
        #endif
        vec3 dayLightColorEnvelope = mix(sunsetClearLightColor, noonClearLightColor, noonFactorDM); // original brightness envelope - kept identical
        float dayLightLum = GetLuminance(dayLightColorEnvelope);

        #ifndef COMPOSITE1
            // Ground/cloud direct light: drive hue from blackbody Kelvin + Rayleigh airmass extinction,
            // but keep the exact same luminance as the original hand-tuned envelope above.
            vec3 kelvinTintRaw = KelvinToRGB(GetSunKelvin(noonFactorDM));
            vec3 kelvinTint = kelvinTintRaw / GetLuminance(kelvinTintRaw);
            vec3 rayleighTint = GetRayleighExtinctionTint(noonFactorDM);
            vec3 dayLightColor = kelvinTint * rayleighTint * dayLightLum;
        #else
            // Light-shaft variant keeps its original distinct (intentionally cool/hazy) look untouched.
            vec3 dayLightColor = dayLightColorEnvelope;
        #endif
        vec3 dayAmbientColor = mix(sunsetClearAmbientColor, noonClearAmbientColor, noonFactorDM);

        vec3 clearLightColor   = mix(nightClearLightColor, dayLightColor, sunVisibility2);
        vec3 clearAmbientColor = mix(nightClearAmbientColor, dayAmbientColor, sunVisibility2);

        float rainShadowVisReduce = 0.0
            #ifdef SUN_MOON_DURING_RAIN
                #ifdef SPECIAL_BIOME_WEATHER
                    + 0.2 * inSnowy + 0.2 * inDry
                #elif RAIN_STYLE == 2
                    + 0.2
                #endif
            #else
                + 0.4
            #endif
        ;

        vec3 rainLightColor   = mix(nightRainLightColor, dayRainLightColor * (1.0 - rainShadowVisReduce), sunVisibility2) * 2.5;
        vec3 rainAmbientColor = mix(nightRainAmbientColor, dayRainAmbientColor * (1.0 + rainShadowVisReduce), sunVisibility2);

        vec3 lightColor   = mix(clearLightColor, rainLightColor, rainFactor);
        vec3 ambientColor = mix(clearAmbientColor, rainAmbientColor, rainFactor);
    #elif defined NETHER
        vec3 lightColor   = vec3(0.0);
        vec3 ambientColor = (netherColor + 0.5 * lavaLightColor) * (0.9 + 0.45 * vsBrightness);
    #elif defined END
        vec3 endLightColor = vec3(0.68, 0.51, 1.07);
        vec3 endOrangeCol = vec3(1.0, 0.3, 0.0);
        float endLightBalancer = 0.2 * vsBrightness;
        vec3 lightColor    = endLightColor * (0.35 - endLightBalancer);
        vec3 ambientColor  = endLightColor * (0.2 + endLightBalancer);
    #endif

#endif //INCLUDE_LIGHT_AND_AMBIENT_COLORS