float4x4 g_WorldMatrix;
float4x4 g_ViewMatrix;
float4x4 g_ProjectionMatrix;

float4x4 g_ViewMatrixInverse;
float4x4 g_ProjectionMatrixInverse;

float2 g_Viewport;

float3 g_DiffuseLight;
float3 g_SpecularLight;
float3 g_LightDirection;

bool g_ShadowEnabled;

float4x4 g_LightViewMatrix;
float4x4 g_LightProjectionMatrix;

Texture2D g_ShadowMap;

Texture2D g_MRT0;
Texture2D g_MRT1;
Texture2D g_MRT2;


SamplerState samPoint
{
    Filter = MIN_MAG_MIP_POINT;
    
    AddressU = Wrap;
    AddressV = Wrap;
};

SamplerState samPointClamp
{
    Filter = MIN_MAG_MIP_POINT;
    
    AddressU = Clamp;
    AddressV = Clamp;
};

SamplerState samLinear
{
    Filter = MIN_MAG_MIP_LINEAR;
    
    AddressU = Wrap;
    AddressV = Wrap;
};

SamplerState samLinearClamp
{
    Filter = MIN_MAG_MIP_LINEAR;
    
    AddressU = Clamp;
    AddressV = Clamp;
};

SamplerState samAniso
{
    Filter = ANISOTROPIC;
    MaxAnisotropy = 16;
    
    AddressU = Wrap;
    AddressV = Wrap;
};

SamplerState samAnisoClamp
{
    Filter = ANISOTROPIC;
    MaxAnisotropy = 16;
    
    AddressU = Clamp;
    AddressV = Clamp;
};

BlendState AlphaBlending
{
    BlendEnable[0] = TRUE;
    SrcBlend = ONE;
    DestBlend = ONE;
    BlendOp = ADD;
    RenderTargetWriteMask[0] = 0x0F;
};

BlendState DisableBlend 
{ 
	BlendEnable[0] = FALSE; 
};

DepthStencilState DepthEnabling 
{ 
	DepthEnable = TRUE; 
};

DepthStencilState DepthDisabling
{
	DepthEnable = FALSE;
};

RasterizerState DisableCulling
{
	CullMode = NONE;
};

RasterizerState DisableCullingMSAA
{
	CullMode = NONE;
	MultisampleEnable = True;
};




struct VS_INPUT
{
    float4 Pos  : POSITION;
    float3 Norm : NORMAL;
    float3 Tang : TANGENT; 
    float2 Tex  : TEXCOORD;
};

struct PS_INPUT
{
    float4 Pos  : SV_Position;
    float2 Tex  : TEXCOORD0;
};

struct PS_OUTPUT
{
	float4 data0 : SV_Target0;
	float4 data1 : SV_Target1;
};



//--------------------------------------------------------------------------------------
// Vertex Shader
//--------------------------------------------------------------------------------------
PS_INPUT LIGHT_VS( VS_INPUT input )
{
    PS_INPUT output = (PS_INPUT)0;

    output.Pos = float4(2.0*input.Pos.x-1.0, 1.0-2.0*input.Pos.y, 0.0, 1.0);
    output.Tex = input.Tex;
    
    return output;
}


//--------------------------------------------------------------------------------------
// Pixel Shader
//--------------------------------------------------------------------------------------

PS_OUTPUT LIGHT_PS ( PS_INPUT input )
{
	const float g_LightBleedReduction = 0.8;
	const float g_MinVariance = 1.0;
	
	PS_OUTPUT output = (PS_OUTPUT)0;
	
	float4 sample = g_MRT2.Sample(samPoint, input.Tex);
	float3 normal = sample.rgb;
	float depth = sample.a;
	
	float3 eyespaceVertex = normalize(mul(float4(2.0*input.Tex.x-1.0, 1.0-2.0*input.Tex.y, 1.0, 1.0), g_ProjectionMatrixInverse).xyz) * depth;
	
	float shadowTerm = 1.0;
	
	
	if (g_ShadowEnabled) {
		float4 vertex = float4(eyespaceVertex, 1.0);
		vertex = mul(vertex, g_ViewMatrixInverse);
		vertex = mul(vertex, g_LightViewMatrix);
		float lightspaceDepth = length(vertex);
		vertex = mul(vertex, g_LightProjectionMatrix);
		vertex /= vertex.w;
		vertex.x = 0.5*vertex.x+0.5;
		vertex.y = 0.5-0.5*vertex.y;
		
		if ((vertex.x >= 0.0) && (vertex.x <= 1.0) && (vertex.y >= 0.0) && (vertex.y <= 1.0)) {
			float2 moments = g_ShadowMap.Sample(samAnisoClamp, vertex.xy).rg;
			float E_x2 = moments.y;
			float Ex_2 = moments.x * moments.x;
			float variance = E_x2 - Ex_2;
			//variance = max(g_MinVariance, variance);    
			float mD = (moments.x - lightspaceDepth);
			float mD_2 = mD * mD;
			float p = variance / (variance + mD_2);
			p = clamp((p - g_LightBleedReduction) / (1.0 - g_LightBleedReduction), 0.0, 1.0);
			shadowTerm = max(p, lightspaceDepth <= moments.x);
		}
	}
	
	
	float4 diffuseMaterial  = g_MRT0.Sample(samPoint, input.Tex);
	float4 specularMaterial = g_MRT1.Sample(samPoint, input.Tex);
	
	float3 lightDir = mul(g_LightDirection, (float3x3)g_WorldMatrix);
	lightDir = -normalize(mul(lightDir, (float3x3)g_ViewMatrix));
	float3 viewDir = -normalize(eyespaceVertex);
	float3 halfVector = normalize(lightDir + viewDir);
    
    float nDotL = saturate(dot(normal, lightDir));
    float nDotH = saturate(dot(normal, halfVector));
    float power = (nDotL == 0.0f) ? 0.0f : pow(nDotH, specularMaterial.a);   

	output.data0 = float4(diffuseMaterial.rgb * g_DiffuseLight * nDotL * shadowTerm, 0.0);
	output.data1 = float4(specularMaterial.rgb * g_SpecularLight * power * shadowTerm, 0.0);
	
	return output;
}


//--------------------------------------------------------------------------------------
technique10 Render
{
    pass P0
    {
		SetRasterizerState(DisableCulling);
		SetBlendState( AlphaBlending, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF );
		SetDepthStencilState( DepthDisabling, 0 );
    
        SetVertexShader( CompileShader( vs_4_0, LIGHT_VS() ) );
        SetGeometryShader( NULL );
        SetPixelShader( CompileShader( ps_4_0, LIGHT_PS() ) );
    }
}