float GetAuroraVisibility(in float VdotU) {
    float visibility = sqrt1(clamp01(VdotU * (AURORA_DRAW_DISTANCE * 1.125 + 0.75) - 0.225)) - sunVisibility;
    #ifndef CLEAR_SKY_WHEN_RAINING
        visibility -= rainFactor;
    #else
        float altitudeVisibility = 0.0;
        if (cameraPosition.y > maximumCloudsHeight) altitudeVisibility = 1.0;
        visibility -= mix(rainFactor * 0.8, 0.0, altitudeVisibility * max(0.0, -1.0 / (cameraPosition.y - maximumCloudsHeight) + 1.0));
    #endif
    visibility *= 1.0 - VdotU * 0.9;

    #if AURORA_CONDITION == 1 || AURORA_CONDITION == 3
        visibility -= moonPhase;
    #endif
    #if AURORA_CONDITION == 2 || AURORA_CONDITION == 3
        visibility *= inSnowy;
    #endif
    #if AURORA_CONDITION == 4
        visibility = max(visibility * inSnowy, visibility - moonPhase);
    #endif

    return visibility;
}

void GetAuroraColor(in vec2 wpos, out vec3 auroraUp, out vec3 auroraDown) {
    #ifdef RGB_AURORA
        auroraUp = getRainbowColor(wpos, 0.06);
        auroraDown = getRainbowColor(wpos, 0.05);
    #elif AURORA_COLOR_PRESET == 0
        auroraUp = vec3(AURORA_UP_R, AURORA_UP_G, AURORA_UP_B);
        auroraDown = vec3(AURORA_DOWN_R, AURORA_DOWN_G, AURORA_DOWN_B);
    #else
        const vec3 auroraUpA[] = vec3[](
            vec3(112.0, 36.0, 192.0),
            vec3(255.0, 56.0, 64.0),
            vec3(255.0, 80.0, 112.0),
            vec3(72.0, 96.0, 192.0),
            vec3(112.0, 80.0, 255.0),
            vec3(232.0, 116.0, 232.0),
            vec3(212.0, 108.0, 216.0),
            vec3(120.0, 212.0, 56.0),
            vec3(64.0, 255.0, 255.0),
            vec3(168.0, 36.0, 88.0),
            vec3(255.0, 68.0, 124.0),
            vec3(216.0, 8.0, 255.0),
            vec3(0.0, 255.0, 255.0),
            vec3(0.0, 20.0, 60.0)
        );
        const vec3 auroraDownA[] = vec3[](
            vec3(96.0, 255.0, 192.0),
            vec3(204.0, 172.0, 12.0),
            vec3(80.0, 255.0, 180.0),
            vec3(172.0, 44.0, 88.0),
            vec3(80.0, 255.0, 180.0),
            vec3(244.0, 188.0, 28.0),
            vec3(92.0, 188.0, 180.0),
            vec3(176.0, 88.0, 72.0),
            vec3(128.0, 64.0, 128.0),
            vec3(60.0, 184.0, 152.0),
            vec3(160.0, 96.0, 255.0),
            vec3(32.0, 176.0, 33.0),
            vec3(180.0, 0.0, 0.0),
            vec3(0.0, 24.0, 36.0)
        );

        #if AURORA_COLOR_PRESET == 1
            int p = worldDay % auroraUpA.length();
        #elif AURORA_COLOR_PRESET == 2
            int p = worldDay % (auroraUpA.length() * 8) / 8;
        #else
            const int p = AURORA_COLOR_PRESET - 2;
        #endif

        auroraUp = auroraUpA[p];
        auroraDown = auroraDownA[p];
    #endif

    auroraUp *= (AURORA_UP_I * 0.093 + 3.1) / GetLuminance(auroraUp);
    auroraDown *= (AURORA_DOWN_I * 0.245 + 8.15) / GetLuminance(auroraDown);
}

void AuroraAmbientColor(inout vec3 color, in vec3 viewPos) {
    float visibility = GetAuroraVisibility(0.5);
	if (visibility > 0) {
        vec3 wpos = (gbufferModelViewInverse * vec4(viewPos, 1.0)).xyz;
        wpos.xz /= (abs(wpos.y) + length(wpos.xz));

		vec3 auroraUp, auroraDown;
        GetAuroraColor(wpos.xz, auroraUp, auroraDown);

        vec3 auroraColor = mix(auroraUp, auroraDown, 0.8);
		#ifdef DEFERRED1
            auroraColor *= 0.2;
            visibility *= AURORA_CLOUD_INFLUENCE_INTENSITY;
        #elif defined GBUFFERS_CLOUDS
            auroraColor *= 0.6;
            visibility *= AURORA_CLOUD_INFLUENCE_INTENSITY;
        #else
            auroraColor *= 0.05;
            visibility *= AURORA_TERRAIN_INFLUENCE_INTENSITY;
        #endif
		color *= mix(vec3(1.0), auroraColor, visibility);
    }
}

vec3 GetAuroraBorealis(vec3 viewPos, float VdotU, float dither) {
    float visibility = GetAuroraVisibility(VdotU);

    if (visibility > 0.0) {
	    if (max(blindness, darknessFactor) > 0.1) return vec3(0.0);

        vec3 aurora = vec3(0.0);

        vec3 wpos = (gbufferModelViewInverse * vec4(viewPos, 1.0)).xyz;
        wpos.xz /= wpos.y;
        vec2 cameraPositionM = cameraPosition.xz * 0.0075;
        cameraPositionM.x += syncedTime * 0.04;

        int sampleCount = 25;
        int sampleCountP = sampleCount + 5;
        float ditherM = dither + 5.0;
        float auroraAnimate = frameTimeCounter * 0.001;

        vec3 auroraUp, auroraDown;
        GetAuroraColor(wpos.xz, auroraUp, auroraDown);

        for (int i = 0; i < sampleCount; i++) {
            float current = pow2((i + ditherM) / sampleCountP);

            vec2 planePos = wpos.xz * (AURORA_SIZE * 0.6 + 0.4 + current) * 11.0 + cameraPositionM;
            #if AURORA_STYLE == 1
                planePos = floor(planePos) * 0.0007;
                
                float noise = texture2D(noisetex, planePos).b;
                noise = pow2(pow2(pow2(pow2(1.0 - 2.0 * abs(noise - 0.5)))));
                
                noise *= pow1_5(texture2D(noisetex, planePos * 100.0 + auroraAnimate).b);
            #else
                planePos *= 0.0007;

                float noise = texture2D(noisetex, planePos).r;
                noise = pow2(pow2(pow2(pow2(1.0 - 2.0 * abs(noise - 0.5)))));

                noise *= texture2D(noisetex, planePos * 3.0 + auroraAnimate).b;
                noise *= texture2D(noisetex, planePos * 5.0 - auroraAnimate).b;
            #endif

            float currentM = 1.0 - current;

            aurora += noise * currentM * mix(auroraUp, auroraDown, pow2(pow2(currentM)));
        }
    
        #if AURORA_STYLE == 1
            aurora *= 1.3;
        #else
            aurora *= 1.8;
        #endif

        return aurora * visibility / sampleCount;
    }

    return vec3(0.0);
}
