// Volumetric Fog fullscreen shader for Oblivion/Skyrim Reloaded

float4x4 TESR_ProjectionTransform; 
float4 TESR_FogColor;
float4 TESR_FogData;
float4 TESR_VolumetricFogData;
float4 TESR_CameraPosition;
float4 TESR_CameraForward;
float4x4 TESR_ViewTransform;
float4 TESR_ReciprocalResolution;
float4 TESR_SunAmount;
float4 TESR_GameTime;
float4 TESR_SunColor;
float4 TESR_SunDirection;
sampler2D TESR_RenderedBuffer : register(s0) = sampler_state { ADDRESSU = CLAMP; ADDRESSV = CLAMP; MAGFILTER = LINEAR; MINFILTER = LINEAR; MIPFILTER = LINEAR; };
sampler2D TESR_DepthBuffer : register(s1) = sampler_state { ADDRESSU = CLAMP; ADDRESSV = CLAMP; MAGFILTER = LINEAR; MINFILTER = LINEAR; MIPFILTER = LINEAR; };

/*Height-based fog settings*/
static const float3 GroundFogRange = float3(8192, 0.0, 8192); //x = heightfog vertical range, y = heightfog start, z = heightfog horizontal range 
static const float GroundFogExponent = 2.0; //increase for softer vertical fade
static const float groundFogMul = 1.0; //density/ visibilty of heightfog
static const float sunScatter = 8.0; //raise to decrease the intensity of sun fog
#undef CREPUSCULAR //define to limit height fog to dawn/dusk


static const float4 sp = TESR_SunDirection * 999999;
static const float2 texproj = 0.5f * float2(1.0f, -TESR_ReciprocalResolution.y / TESR_ReciprocalResolution.x) / tan(radians(TESR_ReciprocalResolution.w) * 0.5f);
static const float d = dot(TESR_CameraForward, sp);
static const float sunview_v = mul(sp / d, TESR_ViewTransform);
static const float sunview = float(0.5f) + sunview_v * texproj;
static const float nearZ = TESR_ProjectionTransform._43 / TESR_ProjectionTransform._33;
static const float farZ = (TESR_ProjectionTransform._33 * nearZ) / (TESR_ProjectionTransform._33 - 1.0f);
static const float depthRange = nearZ - farZ;
static const float3 eyepos = float3(TESR_CameraPosition.x, TESR_CameraPosition.y, TESR_CameraPosition.z);

struct VSOUT
{
	float4 vertPos : POSITION;
	float2 UVCoord : TEXCOORD0;
};

struct VSIN
{
	float4 vertPos : POSITION0;
	float2 UVCoord : TEXCOORD0;
};

VSOUT FrameVS(VSIN IN)
{
	VSOUT OUT = (VSOUT)0.0f;
	OUT.vertPos = IN.vertPos;
	OUT.UVCoord = IN.UVCoord;
	return OUT;
}

float3 toWorld(float2 tex)
{
    float3 v = float3(TESR_ViewTransform[0][2], TESR_ViewTransform[1][2], TESR_ViewTransform[2][2]);
    v += (1 / TESR_ProjectionTransform[0][0] * (2 * tex.x - 1)).xxx * float3(TESR_ViewTransform[0][0], TESR_ViewTransform[1][0], TESR_ViewTransform[2][0]);
    v += (-1 / TESR_ProjectionTransform[1][1] * (2 * tex.y - 1)).xxx * float3(TESR_ViewTransform[0][1], TESR_ViewTransform[1][1], TESR_ViewTransform[2][1]);
    return v;
}

float readDepth(in float2 coord : TEXCOORD0)
{
	float posZ = tex2D(TESR_DepthBuffer, coord).x;
	return (2.0f * nearZ) / (nearZ + farZ - posZ * (farZ - nearZ));
}

float4 VolumetricFog(VSOUT IN) : COLOR0 {
	float3 color = tex2D(TESR_RenderedBuffer, IN.UVCoord).rgb;
	float2 screenPos = IN.UVCoord / TESR_ReciprocalResolution.xy;
    float depth = readDepth(IN.UVCoord);
	float distance = depth * depthRange;	
	float3 camera_vector = toWorld(IN.UVCoord);
	float3 world_pos = eyepos + camera_vector * distance;
	float  height = world_pos.z;
	
	/*OR exponential fog */
	float distanceFogAmount = 1.0f - exp(-max(0.0f, distance - TESR_FogData.x) / ((TESR_FogData.y  - TESR_FogData.x ) * TESR_VolumetricFogData.x));
	
	/*heightfog formulae for horizontal and vertical ranges */
	float groundFogDistance = 1.0f - exp(-max(0.0f, distance - GroundFogRange.y) / ((GroundFogRange.z  - GroundFogRange.y)* TESR_VolumetricFogData.x));	
	float groundFogAmount = 1.0f - exp(-max(0.0f, -height + GroundFogRange.x) / ((GroundFogRange.y + GroundFogRange.x) * GroundFogExponent) * groundFogDistance);
	
	/*Limit groundfog to dawn/dusk*/
	float mul = groundFogMul;
	#ifdef CREPUSCULAR 
	mul = groundFogMul * (TESR_GameTime.y < 12.00f) ? TESR_SunAmount.x : TESR_SunAmount.z;
	#endif
	float FogAmount = distanceFogAmount + (groundFogAmount * mul);
	
	/*VFog looks ugly at night so fade it out*/
	float finalFog = lerp(FogAmount, 0.0f, TESR_SunColor.w);
	
	/*Tint fog according to sun colour and direction */
	float3  sunAmount = pow((0.5 * dot(TESR_SunDirection, normalize(world_pos-eyepos)) + 0.5), sunScatter);
	float3 sunFogColor = lerp(TESR_FogColor.rgb, TESR_SunColor.rgb, sunAmount);
	
	/*check to prevent black sun fog*/
	float3 FogColor = max(TESR_FogColor.rgb, sunFogColor.rgb);
	
	color = lerp(color, saturate(FogColor.rgb * TESR_VolumetricFogData.y), saturate(finalFog * TESR_VolumetricFogData.z));
	
	return float4(color, 1.0f);
	
}

technique
{
	pass
	{
		VertexShader = compile vs_3_0 FrameVS();
		PixelShader  = compile ps_3_0 VolumetricFog();
	}
}