

// Определения слоёв облаков
#define CIRRUS_LAYER 4 // [0 1 2 3 4]
#define ALTOSTRATUS_LAYER 3 // [0 1 2 3 4] 
#define CUMULONIMBUS_LAYER 2 // [0 1 2 3 4]
#define LARGECUMULUS_LAYER 1 // [0 1 2 3 4]
#define SMALLCUMULUS_LAYER 0 // [0 1 2 3 4]


#define CLOUD_CUMULUS_CLEAR_ALTITUDE	1000 // [60 80 100 150 200 300 356 400 500 600 700 800 1000 1200 1500 2000 5000 10000]

#define CLOUD_CUMULUS_CLEAR_THICKNESS	1400 // [0 100 200 300 400 500 550 600 700 800 1000 1200 1400 1500 1800 2000 3000 5000 6000]

#define CLOUD_CUMULUS_CLEAR_COVERY		1.0  // [0.5 0.7 0.8 0.9 1.0 1.1 1.15 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.1 2.3 2.5 2.7 3.0]

#define CLOUD_CUMULUS_CLEAR_DENSITY 	1.0  // [0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.5 3.0 5.0]

#define CLOUD_CUMULUS_CLEAR_SUNLIGHTING	1.0  // [0.1 0.3 0.35 0.4 0.45 0.5 0.7 0.9 1.0 1.1 1.3 1.5 1.7 1.9 2.1 2.3 2.5]

#define CLOUD_CUMULUS_CLEAR_SKYLIGHTING	1.0	 // [0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.5 3.0 5.0]


#define CLOUD_CUMULUS_RAIN_ALTITUDE		800  // [60 80 100 150 200 300 356 400 500 600 700 800 1000 1200 1500 2000 5000 10000]

#define CLOUD_CUMULUS_RAIN_THICKNESS	3000 // [0 100 200 300 400 465 500 600 800 1000 1200 1500 2000 2500 3000 3500 5000 6000 7000]

#define CLOUD_CUMULUS_RAIN_COVERY		1.2  // [0.5 0.7 0.8 0.9 1.0 1.1 1.15 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.1 2.3 2.5 2.7 3.0]

#define CLOUD_CUMULUS_RAIN_DENSITY 		1.0  // [0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.5 3.0 5.0]

#define CLOUD_CUMULUS_RAIN_SUNLIGHTING	0.3  // [0.1 0.15 0.2 0.3 0.5 0.7 0.9 1.1 1.3 1.5 1.7 1.9 2.1 2.3 2.5]

#define CLOUD_CUMULUS_RAIN_SKYLIGHTING	0.3	 // [0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.5 3.0 5.0]

#define CLOUD_LAYER2_ENABLED 1 // [0 1]
#define CLOUD_LAYER2_CLEAR_ALTITUDE_OFFSET 7500.0  // [60 80 100 150 200 300 356 400 500 600 700 800 1000 1200 1500 2000 5000 5500 6000 6500 7000 7500 8000 8500 10000 20000 40000 60000]
#define CLOUD_LAYER2_RAIN_ALTITUDE_OFFSET 1000.0  // [60 80 100 150 200 300 356 400 500 600 700 800 1000 1200 1500 2000 5000 5500 6000 6500 7000 7500 8000 8500 10000 20000 40000 60000]
#define CLOUD_LAYER2_CLEAR_THICKNESS	2400 // [0 100 200 300 400 465 500 600 800 1000 1200 1500 2000 2500 3000 3500 5000 6000 7000]
#define CLOUD_LAYER2_CLEAR_COVERY		1.1	 // [0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.5 3.0 5.0]
#define CLOUD_LAYER2_CLEAR_DENSITY 		0.3  // [0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.5 3.0 5.0]
#define CLOUD_LAYER2_CLEAR_SUNLIGHTING	2.1	 // [0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.5 3.0 5.0]
#define CLOUD_LAYER2_CLEAR_SKYLIGHTING	5.0	 // [0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.5 3.0 5.0]
#define CLOUD_LAYER2_RAIN_THICKNESS		4800 // [0 100 200 300 400 465 500 600 800 1000 1200 1500 2000 2500 3000 3500 5000 6000 7000]
#define CLOUD_LAYER2_RAIN_COVERY		1.0	 // [0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.5 3.0 5.0]
#define CLOUD_LAYER2_RAIN_DENSITY 		0.8  // [0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.5 3.0 5.0]
#define CLOUD_LAYER2_RAIN_SUNLIGHTING	1.4	 // [0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.5 3.0 5.0]
#define CLOUD_LAYER2_RAIN_SKYLIGHTING	1.1	 // [0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.5 3.0 5.0]

#define CLOUD_LAYER3_ENABLED 1 // [0 1]
#define CLOUD_LAYER3_CLEAR_ALTITUDE_OFFSET 9000.0  // [60 80 100 150 200 300 356 400 500 600 700 800 1000 1200 1500 2000 5000 5500 6000 6500 7000 7500 8000 8500 10000 20000 40000 60000]
#define CLOUD_LAYER3_RAIN_ALTITUDE_OFFSET 1200.0  // [60 80 100 150 200 300 356 400 500 600 700 800 1000 1200 1500 2000 5000 5500 6000 6500 7000 7500 8000 8500 10000 20000 40000 60000]
#define CLOUD_LAYER3_CLEAR_THICKNESS	2400 // [0 100 200 300 400 465 500 600 800 1000 1200 1500 2000 2500 3000 3500 5000 6000 7000]
#define CLOUD_LAYER3_CLEAR_COVERY		1.1  // [0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.5 3.0 5.0]
#define CLOUD_LAYER3_CLEAR_DENSITY 		0.2  // [0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.5 3.0 5.0]
#define CLOUD_LAYER3_CLEAR_SUNLIGHTING	2.1	 // [0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.5 3.0 5.0]
#define CLOUD_LAYER3_CLEAR_SKYLIGHTING	5.0	 // [0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.5 3.0 5.0]
#define CLOUD_LAYER3_RAIN_THICKNESS		5500 // [0 100 200 300 400 465 500 600 800 1000 1200 1500 2000 2500 3000 3500 5000 6000 7000]
#define CLOUD_LAYER3_RAIN_COVERY		1.2  // [0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.5 3.0 5.0]
#define CLOUD_LAYER3_RAIN_DENSITY 		0.6  // [0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.5 3.0 5.0]
#define CLOUD_LAYER3_RAIN_SUNLIGHTING	1.0	 // [0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.5 3.0 5.0]
#define CLOUD_LAYER3_RAIN_SKYLIGHTING	1.0	 // [0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.5 3.0 5.0]


#define CLOUD_CUMULUS_SAMPLES 			32 	 // [4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 36 40 50 60 100]
#define CLOUD_CUMULUS_SUNLIGHT_SAMPLES 	4 	 // [2 3 4 5 6 7 8 9 10 12 15 17 20]
#define CLOUD_CUMULUS_SKYLIGHT_SAMPLES 	2 	 // [2 3 4 5 6 7 8 9 10 12 15 17 20]
#define CLOUD_BOTTOM_SHADOW_STRENGTH 0.3  // [0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0]

#define CLOUD_LOCAL_COVERAGE

// Структура свойств облаков (твоя оригинальная)
struct CloudProperties {
	float altitude;
	float thickness;
	float coverage;
	float density;
	float sunlighting, skylighting;
	float maxAltitude;
	float noiseScale;
	float cloudPeakWeight;
	vec3 offset;
};

#ifdef CLOUDS_WEATHER
	flat in vec3 cloudDynamicWeather;
#endif


CloudProperties GetGlobalCloudProperties() {
	CloudProperties cloudProperties;

	#ifdef CLOUDS_WEATHER
		if (cloudDynamicWeather.z > 5e-3) {
			cloudProperties.altitude    = mix(CLOUD_CUMULUS_CLEAR_ALTITUDE, CLOUD_CUMULUS_RAIN_ALTITUDE, wetness) * (1.0 + cloudDynamicWeather.z * 2.0);
			cloudProperties.density     = mix(CLOUD_CUMULUS_CLEAR_DENSITY, CLOUD_CUMULUS_RAIN_DENSITY, wetness) * oneMinus(cloudDynamicWeather.z * 0.3);
			cloudProperties.sunlighting = mix(CLOUD_CUMULUS_CLEAR_SUNLIGHTING, CLOUD_CUMULUS_RAIN_SUNLIGHTING, wetness) * (1.0 + cloudDynamicWeather.z * 0.2);
			cloudProperties.skylighting = mix(CLOUD_CUMULUS_CLEAR_SKYLIGHTING, CLOUD_CUMULUS_RAIN_SKYLIGHTING, wetness) * (1.0 + cloudDynamicWeather.z * 0.2);
		} else
	#endif
	{
		cloudProperties.altitude    = mix(CLOUD_CUMULUS_CLEAR_ALTITUDE, CLOUD_CUMULUS_RAIN_ALTITUDE, wetness);
		cloudProperties.density     = mix(CLOUD_CUMULUS_CLEAR_DENSITY, CLOUD_CUMULUS_RAIN_DENSITY, wetness);
		cloudProperties.sunlighting = mix(CLOUD_CUMULUS_CLEAR_SUNLIGHTING, CLOUD_CUMULUS_RAIN_SUNLIGHTING, wetness);
		cloudProperties.skylighting = mix(CLOUD_CUMULUS_CLEAR_SKYLIGHTING, CLOUD_CUMULUS_RAIN_SKYLIGHTING, wetness);
	}
	cloudProperties.thickness   = mix(CLOUD_CUMULUS_CLEAR_THICKNESS, CLOUD_CUMULUS_RAIN_THICKNESS, wetness);
	cloudProperties.coverage    = mix(CLOUD_CUMULUS_CLEAR_COVERY, CLOUD_CUMULUS_RAIN_COVERY, wetness);
	cloudProperties.maxAltitude	= cloudProperties.altitude + cloudProperties.thickness;
	cloudProperties.noiseScale 	= 4e-4 + 6e-5 * wetness;
	cloudProperties.cloudPeakWeight = 0.1 + 0.7 * wetness;
	cloudProperties.offset = vec3(0.0, 0.0, 0.0);

	return cloudProperties;
}

CloudProperties GetLayer2CloudProperties() {
	CloudProperties cloudProperties;
	
	float baseAltitude = mix(CLOUD_CUMULUS_CLEAR_ALTITUDE, CLOUD_CUMULUS_RAIN_ALTITUDE, wetness);
	float altitudeOffset = mix(CLOUD_LAYER2_CLEAR_ALTITUDE_OFFSET, CLOUD_LAYER2_RAIN_ALTITUDE_OFFSET, wetness);
	cloudProperties.altitude = baseAltitude + altitudeOffset;
	cloudProperties.thickness   = mix(CLOUD_LAYER2_CLEAR_THICKNESS, CLOUD_LAYER2_RAIN_THICKNESS, wetness);
	cloudProperties.coverage    = mix(CLOUD_LAYER2_CLEAR_COVERY, CLOUD_LAYER2_RAIN_COVERY, wetness);
	cloudProperties.density     = mix(CLOUD_LAYER2_CLEAR_DENSITY, CLOUD_LAYER2_RAIN_DENSITY, wetness);
	cloudProperties.sunlighting = mix(CLOUD_LAYER2_CLEAR_SUNLIGHTING, CLOUD_LAYER2_RAIN_SUNLIGHTING, wetness);
	cloudProperties.skylighting = mix(CLOUD_LAYER2_CLEAR_SKYLIGHTING, CLOUD_LAYER2_RAIN_SKYLIGHTING, wetness);
	cloudProperties.maxAltitude = cloudProperties.altitude + cloudProperties.thickness;
	cloudProperties.noiseScale  = 3e-4 + 5e-5 * wetness;
	cloudProperties.cloudPeakWeight = 0.08 + 0.5 * wetness;
	cloudProperties.offset = vec3(300.0, 0.0, 250.0);
	
	return cloudProperties;
}

CloudProperties GetLayer3CloudProperties() {
	CloudProperties cloudProperties;
	
	float baseAltitude = mix(CLOUD_CUMULUS_CLEAR_ALTITUDE, CLOUD_CUMULUS_RAIN_ALTITUDE, wetness);
	float altitudeOffset = mix(CLOUD_LAYER3_CLEAR_ALTITUDE_OFFSET, CLOUD_LAYER3_RAIN_ALTITUDE_OFFSET, wetness);
	cloudProperties.altitude = baseAltitude + altitudeOffset;
	cloudProperties.thickness   = mix(CLOUD_LAYER3_CLEAR_THICKNESS, CLOUD_LAYER3_RAIN_THICKNESS, wetness);
	cloudProperties.coverage    = mix(CLOUD_LAYER3_CLEAR_COVERY, CLOUD_LAYER3_RAIN_COVERY, wetness);
	cloudProperties.density     = mix(CLOUD_LAYER3_CLEAR_DENSITY, CLOUD_LAYER3_RAIN_DENSITY, wetness);
	cloudProperties.sunlighting = mix(CLOUD_LAYER3_CLEAR_SUNLIGHTING, CLOUD_LAYER3_RAIN_SUNLIGHTING, wetness);
	cloudProperties.skylighting = mix(CLOUD_LAYER3_CLEAR_SKYLIGHTING, CLOUD_LAYER3_RAIN_SKYLIGHTING, wetness);
	cloudProperties.maxAltitude = cloudProperties.altitude + cloudProperties.thickness;
	cloudProperties.noiseScale  = 2.5e-4 + 4e-5 * wetness;
	cloudProperties.cloudPeakWeight = 0.05 + 0.3 * wetness;
	cloudProperties.offset = vec3(-280.0, 0.0, 320.0);
	
	return cloudProperties;
}


// Твои оригинальные переменные ветра
vec3 wind = vec3(2e-3, 2e-4, 1e-3) * worldTimeCounter * CLOUDS_SPEED;
vec3 wind2 = vec3(1.5e-3, 1.5e-4, 1.2e-3) * worldTimeCounter * CLOUDS_SPEED;
vec3 wind3 = vec3(2.5e-3, 3e-4, 0.8e-3) * worldTimeCounter * CLOUDS_SPEED;


float cloudForwardG = 0.6 - wetness * 0.2, cloudBackwardG = -0.4 + wetness * 0.2;
const float cloudBackwardWeight = 0.25, octWeight = 0.5, octScale = 3.0;

// ============================================================================
// НОВЫЕ ФУНКЦИИ ИЗ VL SHADER
// ============================================================================

// Utility functions
float rand(float co) {
	vec2 co2 = vec2(co, co * 2.0);
	return fract(sin(dot(co2, vec2(12.9898, 78.233))) * 43758.5453);
}

float getRainDensity(float currentDensity) {
	return min(currentDensity + wetness * 0.1, 1.0);
}

// 3D noise sampling
float densityAtPos(in vec3 pos) {
	pos /= 18.0;
	pos.xz *= 0.5;
	vec3 p = floor(pos);
	vec3 f = fract(pos);
	vec2 uv = p.xz + f.xz + p.y * vec2(0.0, 193.0);
	vec2 coord = uv / 512.0;
	
	vec2 xy = texture(noisetex, coord).yx;
	return mix(xy.r, xy.g, f.y);
}

// Curl noise для cirrus облаков
vec2 hash2(vec2 p) {
	vec3 p3 = fract(vec3(p.xyx) * vec3(.1031, .1030, .0973));
	p3 += dot(p3, p3.yzx + 33.33);
	return fract((p3.xx + p3.yz) * p3.zy);
}

vec2 normalize_hash(vec2 p) {
	return normalize(hash2(p) - 0.5);
}

vec2 perlin_gradient(vec2 coord) {
	vec2 i = floor(coord);
	vec2 f = fract(coord);
	vec2 u = f * f * (3.0 - 2.0 * f);
	vec2 du = 30.0 * f * f * (f * (f - 2.0) + 1.0);

	vec2 g0 = normalize_hash(i);
	vec2 g1 = normalize_hash(i + vec2(1.0, 0.0));
	vec2 g2 = normalize_hash(i + vec2(0.0, 1.0));
	vec2 g3 = normalize_hash(i + vec2(1.0, 1.0));

	float v0 = dot(g0, f);
	float v1 = dot(g1, f - vec2(1.0, 0.0));
	float v2 = dot(g2, f - vec2(0.0, 1.0));
	float v3 = dot(g3, f - vec2(1.0, 1.0));

	vec2 omu = 1.0 - u;
	return vec2(
		((v1 - v0) * omu.y + (v3 - v2) * u.y) * du.x,
		((v2 - v0) * omu.x + (v3 - v1) * u.x) * du.y
	);
}

vec2 curl2D(vec2 coord) {
	vec2 gradient = perlin_gradient(coord);
	return vec2(gradient.y, -gradient.x);
}



float getCloudShape(int LayerIndex, int LOD, in vec3 position, float minHeight, float maxHeight) {
	float coverage = 0.0;
	float shape = 0.0;
	float largeCloud = 0.0;
	float smallCloud = 0.0;

	vec3 samplePos = position * vec3(0.25, 0.005, 0.25);
	float tallness = maxHeight - minHeight;
	float posToMax = maxHeight - position.y;

	switch (LayerIndex) {
		case CIRRUS_LAYER: {
			coverage = 0.5;

			vec2 coord = position.zx + 6.0 * wind.xz;
			
			vec2 curl = curl2D(0.00002 * coord) * 0.5 +
			            curl2D(0.00005 * coord) * 0.25 +
			            curl2D(0.00018 * coord) * 0.125;

			largeCloud = texture(noisetex, (position.xz + wind.xz * 2.0) / 80000.0).b;
			smallCloud = texture(noisetex, 0.000005 * coord).r;
		
			float detail_amplitude = 0.3;
			float detail_frequency = 0.00002;
			float curl_strength = 1.3;

			for (int i = 0; i < 3; ++i) {
				float detail = texture(noisetex, coord * detail_frequency + curl * curl_strength).r;
				smallCloud -= detail * detail_amplitude;
				detail_amplitude *= 0.5;
				detail_frequency *= 4.0;
				curl_strength *= 2.7;
			}
			
			smallCloud = abs(largeCloud * -0.4) + smallCloud;
			float val = coverage;
			shape = min(max(val - smallCloud, 0.0) / sqrt(val), 1.0);

			if (position.y < (-0.0001 * minHeight + 0.8) * minHeight) shape = 0.0;
			return shape;
		}

		case ALTOSTRATUS_LAYER: {
			coverage = 0.5 + wetness * 0.2;

			largeCloud = texture(noisetex, (position.xz + wind2.xz * 20.0) / 100000.0).b;
			smallCloud = 1.0 - texture(noisetex, ((position.xz + vec2(-wind2.x, wind2.z) * 20.0) / 7500.0 - 
			             vec2(1.0 - largeCloud, -largeCloud) / 5.0)).b;

			smallCloud = largeCloud + smallCloud * 0.4 * clamp(0.9 - largeCloud, 0.0, 1.0);
			
			float val = coverage;
			shape = min(max(val - smallCloud, 0.0) / sqrt(val), 1.0);
			shape *= shape;

			if (position.y < (-0.0001 * minHeight + 0.8) * minHeight) shape = 0.0;
			return shape;
		}

		case LARGECUMULUS_LAYER: {
			coverage = 0.5 + wetness * 0.2;

			largeCloud = texture(noisetex, (samplePos.zx + wind.xz * 3.0) / 10000.0).b;
			smallCloud = texture(noisetex, (samplePos.zx - wind.xz * 3.0) / 2500.0).b;
			
			smallCloud = abs(largeCloud * -0.7) + smallCloud;
			float val = coverage;
			shape = min(max(val - smallCloud, 0.0) / sqrt(val), 1.0);
			break;
		}

		case SMALLCUMULUS_LAYER: {
			coverage = 0.5 + wetness * 0.2;

			largeCloud = texture(noisetex, (samplePos.xz + wind.xz) / 5000.0).b;
			smallCloud = 1.0 - texture(noisetex, (samplePos.xz - wind.xz) / 500.0).r;

			smallCloud = abs(largeCloud - 0.6) + smallCloud * smallCloud;
			float val = coverage;
			shape = min(max(val - smallCloud, 0.0) / sqrt(val), 1.0);
			break;
		}
	}

	// Ограничение плотности облака в его границах
	shape = min(min(shape, clamp(posToMax, 0, 1)), 1.0 - clamp(minHeight - position.y, 0, 1));

	// Округление нижней части
	float bottomShape = 1.0 - pow(1.0 - min(max(position.y - minHeight, 0.0) / 25.0, 1.0), 5.0);

	// Формирование верхней части
	float topShape = min(max(posToMax, 0.0) / max(tallness, 1.0), 1.0);
	topShape = min(exp(-0.5 * (1.0 - topShape)), 1.0 - pow(1.0 - topShape, 5.0));

	shape = max((shape - 1.0) + topShape * bottomShape, 0.0);

	// Эрозия для детализации
	if (shape > 0.001 && LOD >= 1) {
		float erodeAmount = 0.5;

		samplePos.xz += wind.xz / 4.0;
		samplePos.xz += pow(max(position.y - (minHeight + 20.0), 0.0) / (max(tallness, 1.0) * 0.20), 1.5);

		float erosion = 0.0;
		float omShape = 1.0 - shape;

		if (LayerIndex == SMALLCUMULUS_LAYER || LayerIndex == LARGECUMULUS_LAYER) {
			erosion += (1.0 - densityAtPos(samplePos * 3.0)) * sqrt(omShape);

			float falloff = 1.0 - clamp(posToMax / tallness, 0.0, 1.0);
			erosion += abs(densityAtPos(samplePos * 10.0) - falloff) * 0.75 * (omShape * omShape) * (1.0 - falloff * 0.25);

			erosion = erosion * erosion * erosion * erosion;
		}

		return max(shape - erosion * erodeAmount, 0.0);
	}

	return shape;
}



vec3 GetMakeupCloudColor(in float rainFactor) {
	vec3 cloudColorDay = vec3(1.4, 1.35, 1.3);
	vec3 darkCloudColorDay = vec3(0.5, 0.55, 0.6);
	vec3 cloudColorSunset = vec3(1.5, 0.8, 0.5);
	vec3 darkCloudColorSunset = vec3(0.7, 0.4, 0.3);
	vec3 cloudColorNight = vec3(0.15, 0.18, 0.22);
	vec3 darkCloudColorNight = vec3(0.05, 0.06, 0.08);
	vec3 cloudColorRain = vec3(0.6, 0.65, 0.7);
	vec3 darkCloudColorRain = vec3(0.3, 0.32, 0.35);
	
	float dayTime = worldSunVector.y;
	vec3 cloudColor, darkCloudColor;
	
	if (dayTime > 0.0) {
		float sunsetFactor = saturate((0.1 - dayTime) * 5.0);
		cloudColor = mix(cloudColorDay, cloudColorSunset, sunsetFactor);
		darkCloudColor = mix(darkCloudColorDay, darkCloudColorSunset, sunsetFactor);
	} else {
		float sunriseFactor = saturate((dayTime + 0.1) * 5.0);
		cloudColor = mix(cloudColorNight, cloudColorSunset, sunriseFactor);
		darkCloudColor = mix(darkCloudColorNight, darkCloudColorSunset, sunriseFactor);
	}
	
	cloudColor = mix(cloudColor, cloudColorRain, rainFactor);
	darkCloudColor = mix(darkCloudColor, darkCloudColorRain, rainFactor);
	
	return cloudColor;
}

vec4 RenderMakeupClouds(in vec3 worldDir, in vec3 skyColor, in float dither) {
	if (worldDir.y <= 0.06) return vec4(skyColor, 1.0);
	
	vec3 basePos = vec3(0.0, eyeAltitude, 0.0);
	float viewYInv = 1.0 / worldDir.y;
	
	float planeDistInf = (MAKEUP_CLOUD_PLANE - basePos.y) * viewYInv;
	vec3 intersectionPos = (worldDir * planeDistInf) + basePos;
	
	float planeDistSup = (MAKEUP_CLOUD_PLANE_SUP - basePos.y) * viewYInv;
	vec3 intersectionPosSup = (worldDir * planeDistSup) + basePos;
	
	float difSup = MAKEUP_CLOUD_PLANE_SUP - MAKEUP_CLOUD_PLANE_CENTER;
	float difInf = MAKEUP_CLOUD_PLANE_CENTER - MAKEUP_CLOUD_PLANE;
	
	vec3 increment = (intersectionPosSup - intersectionPos) / float(MAKEUP_CLOUD_SAMPLES);
	float incrementDist = length(increment);
	
	float distAuxCoeff = (MAKEUP_CLOUD_PLANE_SUP - MAKEUP_CLOUD_PLANE) * 0.075;
	float distAuxCoeffBlur = distAuxCoeff * 0.3;
	float opacityDist = distAuxCoeff * 2.0 * viewYInv;
	
	float rainFactor = wetness;
	float brightness = saturate(worldSunVector.y * 2.0);
	
	vec3 cloudColorDay = vec3(1.4, 1.35, 1.3);
	vec3 darkCloudColorDay = vec3(0.5, 0.55, 0.6);
	vec3 cloudColorSunset = vec3(1.5, 0.8, 0.5);
	vec3 darkCloudColorSunset = vec3(0.7, 0.4, 0.3);
	vec3 cloudColorNight = vec3(0.15, 0.18, 0.22);
	vec3 darkCloudColorNight = vec3(0.05, 0.06, 0.08);
	vec3 cloudColorRain = vec3(0.6, 0.65, 0.7);
	vec3 darkCloudColorRain = vec3(0.3, 0.32, 0.35);
	
	vec3 cloudColor, darkCloudColor;
	float dayTime = worldSunVector.y;
	
	if (dayTime > 0.0) {
		float sunsetFactor = saturate((0.1 - dayTime) * 5.0);
		cloudColor = mix(cloudColorDay, cloudColorSunset, sunsetFactor);
		darkCloudColor = mix(darkCloudColorDay, darkCloudColorSunset, sunsetFactor);
	} else {
		float sunriseFactor = saturate((dayTime + 0.1) * 5.0);
		cloudColor = mix(cloudColorNight, cloudColorSunset, sunriseFactor);
		darkCloudColor = mix(darkCloudColorNight, darkCloudColorSunset, sunriseFactor);
	}
	
	cloudColor = mix(cloudColor, cloudColorRain, rainFactor);
	darkCloudColor = mix(darkCloudColor, darkCloudColorRain, rainFactor);
	
	float umbral = mix(0.55, 0.25, 1.0 - rainFactor);
	
	float cloudValue = 0.0;
	float density = 0.0;
	bool firstContact = true;
	
	intersectionPos += increment * dither;
	
	for (int i = 0; i < MAKEUP_CLOUD_SAMPLES; i++) {
		vec2 sampleUV = intersectionPos.xz * MAKEUP_CLOUD_SCALE + vec2(frameTimeCounter * MAKEUP_CLOUD_SPEED);
		float currentValue = texture(colortex12, sampleUV).r;
		

		
		currentValue = (currentValue - umbral) / (1.0 - umbral);
		
		float surfaceInf = MAKEUP_CLOUD_PLANE_CENTER - (currentValue * difInf);
		float surfaceSup = MAKEUP_CLOUD_PLANE_CENTER + (currentValue * difSup);
		float cloudThickness = surfaceSup - surfaceInf;
		
		float currentOpacity = 0.0;
		
		if (intersectionPos.y > surfaceInf && intersectionPos.y < surfaceSup) {
			currentOpacity = min(incrementDist, cloudThickness);
		} else if (cloudThickness > 0.0 && i > 0) {
			float distanceAux = min(abs(intersectionPos.y - surfaceInf), abs(intersectionPos.y - surfaceSup));
			
			if (distanceAux < distAuxCoeffBlur) {
				float blurFactor = 1.0 - (distanceAux / distAuxCoeffBlur);
				currentOpacity = min(blurFactor * incrementDist, cloudThickness);
			}
		}
		
		if (currentOpacity > 0.0) {
			cloudValue += currentOpacity;
			
			if (firstContact) {
				firstContact = false;
				density = (surfaceSup - intersectionPos.y) / (MAKEUP_CLOUD_PLANE_SUP - MAKEUP_CLOUD_PLANE);
			}
		}
		
		intersectionPos += increment;
	}
	
	cloudValue = saturate(cloudValue / opacityDist);
	density = saturate(density);
	
	float attFactor = mix(1.0, 0.75, brightness * (1.0 - rainFactor));
	
	vec3 finalCloudColor = mix(cloudColor * attFactor, darkCloudColor * attFactor, sqrt(sqrt(density)));
	
	float sunHalo = (1.0 - sqrt(sqrt(cloudValue))) * brightness * brightness * (1.0 - rainFactor);
	finalCloudColor = mix(finalCloudColor, finalCloudColor * 13.0, sunHalo);
	
	float horizonFade = saturate((worldDir.y - 0.06) * 5.0);
	
	vec3 result = mix(skyColor, finalCloudColor, cloudValue * horizonFade);
	
	float transmittance = 1.0 - cloudValue * horizonFade;
	return vec4(result, transmittance);
}


float CloudVolumeDensity(in CloudProperties cloudProperties, in vec3 worldPos, in uint steps, in float noiseDetail, in vec3 windOffset) {
	vec3 offsetPos = worldPos + cloudProperties.offset;
	
	#ifdef CLOUD_LOCAL_COVERAGE
		float localCoverage = texture(noisetex, offsetPos.xz * 2e-7 - windOffset.xz * 2e-3).y;
		localCoverage = saturate(fma(localCoverage, 3.0, wetness - 0.4)) * 0.5 + 0.5;
		if (localCoverage < 0.1) return 0.0;
	#endif

	vec3 position = offsetPos * cloudProperties.noiseScale - windOffset;

	float density = noiseDetail * 0.03, weight = 0.5;

	for (uint i = 0u; i < steps; ++i, weight *= octWeight) {
		density += weight * Get3DNoiseSmooth(position);
		position = position * octScale - windOffset;
	}

	density += octWeight / octScale / float(steps);

	if (density < 1e-6) return 0.0;

	#ifdef CLOUD_LOCAL_COVERAGE
		density *= localCoverage;
	#endif

	float normalizedHeight  = saturate((worldPos.y - cloudProperties.altitude) * rcp(cloudProperties.thickness));
	float heightAttenuation = saturate(normalizedHeight * 6.6) * saturate(oneMinus(normalizedHeight) * (2.0 + wetness));

	density = cloudProperties.coverage == 1.0 ? density : saturate((density - 1.0 + cloudProperties.coverage) * rcp(cloudProperties.coverage));

	density *= heightAttenuation * 1.9;
	density -= heightAttenuation * 0.9 + normalizedHeight * 0.5 + 0.1;

	return saturate(density * 3.0 * cloudProperties.density);
}

#ifdef CLOUDS_SHADOW
	float GetShadow3DNoiseSmooth(in vec3 position) {
		vec3 p = floor(position);
		vec3 b = curve(position - p);

		ivec2 texel = ivec2(p.xy + 97.0 * p.z);

		vec2 s0 = texelFetch(noisetex, texel % noiseTextureResolution, 0).xy;
		vec2 s1 = texelFetch(noisetex, (texel + ivec2(1, 0)) % noiseTextureResolution, 0).xy;
		vec2 s2 = texelFetch(noisetex, (texel + ivec2(0, 1)) % noiseTextureResolution, 0).xy;
		vec2 s3 = texelFetch(noisetex, (texel + ivec2(1, 1)) % noiseTextureResolution, 0).xy;

		vec2 rg = mix(mix(s0, s1, b.x), mix(s2, s3, b.x), b.y);

		return mix(rg.x, rg.y, b.z);
	}

	float CloudVolumeDensitySmooth(in CloudProperties cloudProperties, in vec3 worldPos, in vec3 windOffset) {
		vec3 offsetPos = worldPos + cloudProperties.offset;
		
		#ifdef CLOUD_LOCAL_COVERAGE
			float localCoverage = texture(noisetex, offsetPos.xz * 2e-7 - windOffset.xz * 2e-3 + 0.5).y;
			localCoverage = saturate(fma(localCoverage, 3.0, wetness - 0.4)) * 0.5 + 0.5;
			if (localCoverage < 0.1) return 0.0;
		#endif

		vec3 position = offsetPos * cloudProperties.noiseScale - windOffset;

		float density = 0.03, weight = 0.5;

		for (uint i = 0u; i < 4u; ++i, weight *= octWeight) {
			density += weight * GetShadow3DNoiseSmooth(position);
			position = (position - windOffset) * octScale;
		}

		density += octWeight / octScale * 0.25;

		if (density < 1e-6) return 0.0;

		#ifdef CLOUD_LOCAL_COVERAGE
			density *= localCoverage;
		#endif

		float normalizedHeight  = saturate((worldPos.y - cloudProperties.altitude) * rcp(cloudProperties.thickness));
		float heightAttenuation = saturate(normalizedHeight * 6.6) * saturate(oneMinus(normalizedHeight) * (2.0 + wetness));

		density = cloudProperties.coverage == 1.0 ? density : saturate((density - 1.0 + cloudProperties.coverage) * rcp(cloudProperties.coverage));

		density *= heightAttenuation * 1.9;
		density -= heightAttenuation * 0.9 + normalizedHeight * 0.5 + 0.1;

		return saturate(density * 3.0 * cloudProperties.density);
	}
#endif

float GetNoiseDetail(in vec3 worldDir) {
	worldDir *= 48.0;

	float pnoise = Get3DNoise(worldDir - wind);
	worldDir += pnoise * 1e-3 - wind;
	pnoise += Get3DNoise(worldDir * 2.0);
	worldDir += pnoise * 1e-3 - wind;
	pnoise += Get3DNoise(worldDir * 4.0) * 0.5;
	worldDir += pnoise * 1e-3 - wind;
	pnoise += Get3DNoise(worldDir * 8.0) * 0.25;
	worldDir += pnoise * 1e-3 - wind;
	pnoise += Get3DNoise(worldDir * 16.0) * 0.125;

	return pnoise - 0.15;
}

float CloudVolumeSunLightOD(in CloudProperties cloudProperties, in vec3 rayPos, in float lightNoise, in vec3 windOffset) {
	float rayLength = cloudProperties.thickness * (0.2 / float(CLOUD_CUMULUS_SUNLIGHT_SAMPLES));
	vec4 rayStep = vec4(worldLightVector, 1.0) * rayLength;

	float opticalDepth = 0.0;

	for (uint i = 0u; i < CLOUD_CUMULUS_SUNLIGHT_SAMPLES; ++i, rayPos += rayStep.xyz) {
		rayStep *= 2.0;

		float density = CloudVolumeDensity(cloudProperties, rayPos + rayStep.xyz * lightNoise, 5u, 1.0, windOffset);
		if (density < 1e-4) continue;

		opticalDepth += density;
	}

	return opticalDepth * rayLength * 0.12;
}

float CloudVolumeSkyLightOD(in CloudProperties cloudProperties, in vec3 rayPos, in float lightNoise, in vec3 windOffset) {
	float rayLength = cloudProperties.thickness * (0.2 / float(CLOUD_CUMULUS_SKYLIGHT_SAMPLES));
	vec4 rayStep = vec4(vec3(0.0, 1.0, 0.0), 1.0) * rayLength;

	float opticalDepth = 0.0;

	for (uint i = 0u; i < CLOUD_CUMULUS_SKYLIGHT_SAMPLES; ++i, rayPos += rayStep.xyz) {
		rayStep *= 2.0;

		float density = CloudVolumeDensity(cloudProperties, rayPos + rayStep.xyz * lightNoise, 3u, 1.0, windOffset);
		if (density < 1e-4) continue;

		opticalDepth += density;
	}

	return opticalDepth * rayLength * 0.04;
}


// ============================================================================
// ГЛАВНАЯ ФУНКЦИЯ РЕНДЕРА СЛОЯ ОБЛАКОВ
// ============================================================================

vec4 RenderCloudLayer(
	in CloudProperties cloudProperties,
	in vec3 worldDir,
	in float dither,
	in vec4 phases,
	in vec3 windOffset,
	in vec3 skyRadiance,
	in vec3 planeOrigin,
	in float noiseDetail
) {
	vec4 cloudsData = vec4(0.0, 0.0, 0.0, 1.0);
	
	bool intersectsGround = RaySphereIntersection(planeOrigin, worldDir, planetRadius).y >= 0.0;
	
	if ((worldDir.y > 0.0 && eyeAltitude < cloudProperties.altitude) ||
	    (intersectsGround && eyeAltitude > cloudProperties.maxAltitude)) {

		vec2 bottomIntersection = RaySphereIntersection(planeOrigin, worldDir, planetRadius + cloudProperties.altitude);
		vec2 topIntersection = RaySphereIntersection(planeOrigin, worldDir, planetRadius + cloudProperties.maxAltitude);

		float startLength, endLength;
		if (eyeAltitude > cloudProperties.maxAltitude) {
			startLength = topIntersection.x;
			endLength = bottomIntersection.x;
		} else {
			startLength = bottomIntersection.y;
			endLength = topIntersection.y;
		}

		float rayRange = oneMinus(saturate((eyeAltitude - cloudProperties.maxAltitude) * 0.1)) *
		                 oneMinus(saturate((cloudProperties.altitude - eyeAltitude) * 0.1));

		float rayDist = bottomIntersection.y >= 0.0 && eyeAltitude > cloudProperties.altitude ? 
		                bottomIntersection.x : topIntersection.y;

		startLength *= oneMinus(rayRange);
		endLength = mix(endLength, rayDist, rayRange);

		uint raySteps = CLOUD_CUMULUS_SAMPLES;
		raySteps = uint(mix(raySteps, uint(raySteps * 0.6), abs(worldDir.y)));

		float rayLength = clamp(endLength - startLength, 0.0, 2e4) * rcp(raySteps);
		vec3 rayStep = rayLength * worldDir;
		vec3 rayPos = (startLength + rayLength * dither) * worldDir + cameraPosition;

		float scatteringSun = 0.0;
		float scatteringSky = 0.0;
		float transmittance = 1.0;

		for (uint i = 0u; i < raySteps; ++i, rayPos += rayStep) {
			if (transmittance < minTransmittance) break;
			if (rayPos.y < cloudProperties.altitude || rayPos.y > cloudProperties.maxAltitude) continue;

			float dist = distance(rayPos, cameraPosition);
			if (dist > planetRadius + cloudProperties.maxAltitude) continue;

			float density = CloudVolumeDensity(cloudProperties, rayPos, 5u, mix(noiseDetail, 1.0, exp2(-dist * 0.001f)), windOffset);
			if (density < 1e-4) continue;

			vec2 lightNoise = hash2(fract(rayPos.xz)) * 0.4 + 0.3;

			float sunlightOD = CloudVolumeSunLightOD(cloudProperties, rayPos, lightNoise.x, windOffset);

			float normalizedHeight = saturate((rayPos.y - cloudProperties.altitude) * rcp(cloudProperties.thickness));
			float bottomShadow = 1.0 - (1.0 - normalizedHeight) * CLOUD_BOTTOM_SHADOW_STRENGTH;

			float bounceEstimate = oneMinus(expf(-density * 36.0)) * 0.82;
			bounceEstimate /= 1.0 - bounceEstimate;

			float sunlightEnergy = expf(-sunlightOD * 2.0) * phases.x +
			                       expf(-sunlightOD * 0.8) * phases.y +
			                       expf(-sunlightOD * 0.3) * phases.z +
			                       expf(-sunlightOD * 0.1) * phases.w;
			sunlightEnergy *= bottomShadow;

			float skylightOD = CloudVolumeSkyLightOD(cloudProperties, rayPos, lightNoise.y, windOffset);
			float skylightEnergy = expf(-skylightOD) + expf(-skylightOD * 0.1) * 0.1;
			skylightEnergy *= bottomShadow;

			float stepTransmittance = expf(-density * 0.12 * rayLength);
			float cloudsTemp = bounceEstimate * transmittance * oneMinus(stepTransmittance);
			scatteringSun += sunlightEnergy * cloudsTemp;
			scatteringSky += skylightEnergy * cloudsTemp;
			transmittance *= stepTransmittance;	
		}

		if (transmittance < 1.0 - minTransmittance) {
			bool moonlit = worldSunVector.y < -0.04;
			vec3 scattering = scatteringSun * 22.0 * cloudProperties.sunlighting * (moonlit ? moonIlluminance : sunIlluminance);
			scattering += scatteringSky * 0.15 * cloudProperties.skylighting * skyIlluminance;

			if (isLightningFlashing > 1e-2) scattering += sqr(scatteringSky) * 0.1 * lightningColor;

			#ifdef AURORA
				scattering += scatteringSky * vec3(0.0, 0.005, 0.0025) * auroraAmount;
			#endif

			rayPos -= cameraPosition;
			float atmosFade = expf(-length(rayPos) * (0.2 + 0.1 * wetness) * 1e-4);
			scattering = scattering * atmosFade + skyRadiance * oneMinus(transmittance) * oneMinus(atmosFade);

			cloudsData = vec4(scattering, transmittance);
		}
	}
	
	return cloudsData;
}

	
