
#define SunDirection Arg[0].xyz
#define SunIntensity Arg[0].w
#define CloudColor_High Arg[1]
#define CloudColor_Middle Arg[2]
#define CloudColor_Low Arg[3]

// You may modify this to move more data from the vertex shader to the pixel shader.
// It needs at least the position to work.
struct VS_to_PS {
	float4 Pos : SV_POSITION;
	float4 Tex : TEXCOORD0;
	float4 Color : COLOR;
	float3 Normal_WorldSpace : NORMAL;
	float4 Pos_WorldSpace : TEXCOORD1;
	float4 Pos_CameraSpace : TEXCOORD2;
};

//--------------------------------------------------------------------------------------
// Vertex Shader
//--------------------------------------------------------------------------------------
VS_to_PS VS( VS_structure input ) {
	VS_to_PS output = (VS_to_PS)0;
	
	// Convert vertice positions from object space to world space
	output.Pos_WorldSpace = mul( input.Pos, ObjectToWorld );
	// Convert vertice positions from world space to camera space without refraction
	output.Pos_CameraSpace = mul( output.Pos_WorldSpace, WorldToCamera_NoRefraction );
	// Convert vertice positions from camera space to 2D projection
	output.Pos = mul( output.Pos_CameraSpace, CameraToImage );
	
	return output;
}

float InverseLerp(float A, float B, float Value) {
	return (Value - A) / (B - A);
}

// The real angular radius of the sun is around 0.004 radians
#define SunRadiusExternal 0.008f
#define SunRadiusInternal 0.007f

//--------------------------------------------------------------------------------------
// Pixel Shader
//--------------------------------------------------------------------------------------
float4 PS( VS_to_PS input) : SV_Target {
	// If the camera has a world space cutting plane, clip on the negative side.
	//UseCuttingPlane(input.Pos_WorldSpace) // Disabled
	
	float3 ViewDirection = normalize(input.Pos_WorldSpace.xyz - CameraPos_WorldSpace);
	
	// Apply refraction in the pixel shader instead of the vertex shader
	// This is specialized in refraction along the Y axis since that is the most common use
	ViewDirection.y = ViewDirection.y / mul(float3(WorldToCamera._21,WorldToCamera._22,WorldToCamera._23),transpose((float3x3)WorldToCamera_NoRefraction)).y;
	ViewDirection = normalize(ViewDirection);
	
	float2 SkyOffsetHight = (float2(0.13f,-0.12f) * GlobalTime) + float2(53.631f,26.385f);
	float2 SkyOffsetMiddle = float2(0.18f,0.03f) * GlobalTime + float2(76.24f,18.853f);
	float2 SkyOffsetLow = float2(0.21f,0.23f) * GlobalTime + float2(64.243f,46.379f);
	
	float4 CloudLayerHight = tex_0.Sample( samMipmap, ((ViewDirection.xz / ViewDirection.y) + SkyOffsetHight) / 8.0f) * CloudColor_High;
	float4 CloudLayerMiddle = tex_0.Sample( samMipmap, ((ViewDirection.xz / ViewDirection.y) + SkyOffsetMiddle) / 10.0f) * CloudColor_Middle;
	float4 CloudLayerLow = tex_0.Sample( samMipmap, ((ViewDirection.xz / ViewDirection.y) + SkyOffsetLow) / 15.0f) * CloudColor_Low;
	
	// Set the sky color
	float3 SkyColor = InstanceColor.rgb;
	
	float SunDot = -dot(SunDirection,ViewDirection);
	
	// Draw the sun
	SkyColor += saturate(InverseLerp(cos(SunRadiusExternal),cos(SunRadiusInternal),SunDot)) * SunIntensity * 30.0f;
	
	// Draw sunlight diffused in the air
	SkyColor += (SunDot + 1.0f) * SunIntensity * 0.2f;
	
	// Calculate how much light refract from the clouds
	float CloudIntensity = 1.0f + pow((SunDot + 1.0f) / 2.0f,2.0f) * SunIntensity * 0.2f;
	
	// Draw clouds
	SkyColor = lerp(SkyColor,CloudLayerHight.rgb,CloudLayerHight.a) * CloudIntensity;
	SkyColor = lerp(SkyColor,CloudLayerMiddle.rgb,CloudLayerMiddle.a) * CloudIntensity;
	SkyColor = lerp(SkyColor,CloudLayerLow.rgb,CloudLayerLow.a) * CloudIntensity;
	
	float4 finalColor = float4(SkyColor,1.0f);
	
	// Show the final color with fog on R,G,B channels
	//return float4((lerp(finalColor,FogColor,saturate(length(input.Pos_CameraSpace) / FogDistance) * MaxFogIntensity)).xyz,finalColor.w);
	return float4((lerp(finalColor,FogColor,saturate(1.0f - abs(ViewDirection.y) - min(0.0f,CameraPos_WorldSpace.y / FogDistance)) * MaxFogIntensity)).xyz,finalColor.w);
}
