//-----------------------------------------------------------------------------
// File: ShadowMap.fx
//
// Desc: Effect file for high dynamic range cube mapping sample.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------


#define SMAP_SIZE 256


#define SHADOW_EPSILON 0.0005f


float4x4 g_mWorldView;
float4x4 g_mProj;
float4x4 g_mViewToLightProj;  // Transform from view space to light projection space
float4x4 g_mLightWorldView;		//vertex position in light view space
float4x4 g_mLightProj;
float4   g_vMaterial;
texture  g_txShadow;
float3   g_vLightPos;  // Light position in view space
float3   g_vLightDir;  // Light direction in view space
float4   g_vLightDiffuse = float4( 1.0f, 1.0f, 1.0f, 1.0f );  // Light diffuse color
float4   g_vLightAmbient = float4( 0.3f, 0.3f, 0.3f, 1.0f );  // Use an ambient light of 0.3
float    g_fCosTheta;  // Cosine of theta of the spot light
int		 g_iSMWidth = 128;
int		 g_iSMHeight = 128;
texture  g_txJitter;

sampler2D g_samShadow =
sampler_state
{
    Texture = <g_txShadow>;
    MinFilter = Point;
    MagFilter = Point;
    MipFilter = Point;
    AddressU = Clamp;
    AddressV = Clamp;
};

sampler3D g_samJitter = 
sampler_state
{
	Texture = <g_txJitter>;
    MinFilter = Point;
    MagFilter = Point;
    MipFilter = Point;
    AddressU = Wrap;
    AddressV = Wrap;
};

#define BLOCKER_SEARCH_NUM_SAMPLES 4
#define PCF_NUM_SAMPLES 8
#define NEAR_PLANE 0.1
#define LIGHT_WORLD_SIZE 0.2
#define LIGHT_FRUSTUM_WIDTH 1
// Assuming that LIGHT_FRUSTUM_WIDTH == LIGHT_FRUSTUM_HEIGHT
#define LIGHT_SIZE_UV (LIGHT_WORLD_SIZE / LIGHT_FRUSTUM_WIDTH)
//Texture2D<float> tDepthMap;
//cbuffer POISSON_DISKS
//{
	float2 poissonDisk[16] = {
		float2( -0.94201624, -0.39906216 ),
		float2( 0.94558609, -0.76890725 ),
		float2( -0.094184101, -0.92938870 ),
		float2( 0.34495938, 0.29387760 ),
		float2( -0.91588581, 0.45771432 ),
		float2( -0.81544232, -0.87912464 ),
		float2( -0.38277543, 0.27676845 ),
		float2( 0.97484398, 0.75648379 ),
		float2( 0.44323325, -0.97511554 ),
		float2( 0.53742981, -0.47373420 ),
		float2( -0.26496911, -0.41893023 ),
		float2( 0.79197514, 0.19090188 ),
		float2( -0.24188840, 0.99706507 ),
		float2( -0.81409955, 0.91437590 ),
		float2( 0.19984126, 0.78641367 ),
		float2( 0.14383161, -0.14100790 )
	};

float poissonDiskUV[16] = {
	0.0,
	0.0625,
	0.125,
	0.1875,
	0.25,
	0.3125,
	0.375,
	0.4375,
	0.5,
	0.5625,
	0.625,
	0.6875,
	0.75,
	0.8125,
	0.875,
	0.9375
	};
//};

float PenumbraSize(float zReceiver, float zBlocker) //Parallel plane estimation
{
	return (zReceiver - zBlocker) / zBlocker;
}

void FindBlocker(out float wAvgBlockerDepth,
				out float numBlockers,
				float4 coords, 
				sampler2D shadowMapTex )
{
	//This uses similar triangles to compute what //area of the shadow map we should search
	float zReceiver = coords.z * coords.w;
	float searchWidth = LIGHT_SIZE_UV * (zReceiver - NEAR_PLANE) / zReceiver;
	float wBlockerSum = 0;
	numBlockers = 0;
	for( int i = 0; i < BLOCKER_SEARCH_NUM_SAMPLES; ++i )
	{
	/*	float shadowMapDepth = tDepthMap.SampleLevel(
		PointSampler,
		uv + poissonDisk[i] * searchWidth,
		0);*/
		float wShadowMapDepth = tex2D(shadowMapTex, coords.xy + poissonDisk[i] * searchWidth);
		if ( wShadowMapDepth < coords.z ) {
			wBlockerSum += wShadowMapDepth;
			numBlockers++;
		}
	}
	wAvgBlockerDepth = wBlockerSum / numBlockers;
}

int SampleCmpLevelZero(sampler2D shadowMapTex, float2 uv, float compare)
{
	int result = (tex2D(shadowMapTex, uv) > compare)? 1:0;
	return result;
}

float PCF_Filter(float4 coords, float filterRadiusUV, sampler2D shadowMapTex )
{
	float sum = 0.0f;
	for ( int i = 0; i < PCF_NUM_SAMPLES; ++i )
	{
		float2 offset = poissonDisk[i] * filterRadiusUV;
	//	float2 offset = tex3D(g_samJitter, float3(coords.x*100, coords.y*100, poissonDiskUV[i])) * filterRadiusUV;
		
	//	sum += tDepthMap.SampleCmpLevelZero(PCF_Sampler, uv + offset, zReceiver);
	//	sum += SampleCmpLevelZero(shadowMapTex, uv + offset, zReceiver);
		//test
	//	sum += (tex2D(shadowMapTex, uv + offset) < zReceiver)? 0:1;
	//	float tempf = tex2D(shadowMapTex, coords.xy + offset);
		sum += (tex2D(shadowMapTex, coords.xy + offset) + SHADOW_EPSILON< coords.z)? 0:1;
	}
	return sum / PCF_NUM_SAMPLES;
}

float PCSS ( sampler2D shadowMapTex, float4 coords )
{
//	float2 uv = coords.xy;
//	float zReceiver = coords.z; // Assumed to be eye-space z in this code
	float zReceiver = coords.z * coords.w;
	// STEP 1: blocker search
	float wAvgBlockerDepth = 0;
	float numBlockers = 0;
	FindBlocker( wAvgBlockerDepth, numBlockers, coords, shadowMapTex );
	
	float result = 1.0f;
	if( numBlockers > 0 )
	//There are no occluders so early out (this saves filtering)
	{
		// STEP 2: penumbra size
		float penumbraRatio = PenumbraSize(coords.z, wAvgBlockerDepth);
		float filterRadiusUV = penumbraRatio * LIGHT_SIZE_UV * NEAR_PLANE / zReceiver;
		// STEP 3: filtering
		result = PCF_Filter(coords, filterRadiusUV , shadowMapTex);
	}
	return result;
}
//==========================================================================================
//-----------------------------------------------------------------------------
// Vertex Shader: VertShadow
// Desc: Process vertex for the shadow map
//-----------------------------------------------------------------------------
void VertShadowMap( float4 Pos : POSITION,
                 out float4 oPos : POSITION,
                 out float2 Depth : TEXCOORD0 )
{
    oPos = mul( Pos, g_mLightWorldView );
    oPos = mul( oPos, g_mLightProj );
    Depth.xy = oPos.zw;
}


//-----------------------------------------------------------------------------
// Pixel Shader: PixShadow
// Desc: Process pixel for the shadow map
//-----------------------------------------------------------------------------
void PixShadowMap( float2 Depth : TEXCOORD0,
                out float4 Color : COLOR )
{
    Color = Depth.x / Depth.y;
}

//-----------------------------------------------------------------------------
void VertShadow( float4 iPos : POSITION,
				float3 iNormal : NORMAL,
				out float4 oPos : POSITION,
				out float4 vPosLightProj : TEXCOORD0,
				out float4 vPos : TEXCOORD1,
				out float3 vNormal : TEXCOORD2)
{
    vPos = mul( iPos, g_mWorldView );
    oPos = mul( vPos, g_mProj );
    vPosLightProj = mul( vPos, g_mViewToLightProj );
    vNormal = mul(iNormal, g_mWorldView);
}

float4 PixShadow(float4 vPosLightProj : TEXCOORD0,
				float4 vPos : TEXCOORD1,
				float3 vNormal : TEXCOORD2) : COLOR
{
    float4 Diffuse = 1.0;

    // vLight is the unit vector from the light to this pixel
    float3 vLight = normalize( float3( vPos - g_vLightPos ) );

    // Compute diffuse from the light
	if(dot( -vLight, normalize( vNormal ) ) > 0)
    {
        // Pixel is in lit area. Find out if it's
        // in shadow using 2x2 percentage closest filtering

        //transform from RT space to texture space.
        //RT space is -1~1, (0, 0) at center
        //texture space is 0~1, (0, 0) at left top
		float2 ShadowTexC = 0.5 * vPosLightProj.xy / vPosLightProj.w + float2( 0.5, 0.5 );
		ShadowTexC.y = 1.0f - ShadowTexC.y;

        // transform to texel space
        float2 texelpos = SMAP_SIZE * ShadowTexC;
        
        // Determine the lerp amounts           
        float2 lerps = frac( texelpos );

        //read in bilerp stamp, doing the shadow checks
        float sourcevals[4];
     /*   sourcevals[0] = (tex2D( g_samShadow, ShadowTexC ) + SHADOW_EPSILON < vPosLightProj.z / vPosLightProj.w)? 0.0f: 1.0f;  
        sourcevals[1] = (tex2D( g_samShadow, ShadowTexC + float2(1.0/SMAP_SIZE, 0) ) + SHADOW_EPSILON < vPosLightProj.z / vPosLightProj.w)? 0.0f: 1.0f;  
        sourcevals[2] = (tex2D( g_samShadow, ShadowTexC + float2(0, 1.0/SMAP_SIZE) ) + SHADOW_EPSILON < vPosLightProj.z / vPosLightProj.w)? 0.0f: 1.0f;  
        sourcevals[3] = (tex2D( g_samShadow, ShadowTexC + float2(1.0/SMAP_SIZE, 1.0/SMAP_SIZE) ) + SHADOW_EPSILON < vPosLightProj.z / vPosLightProj.w)? 0.0f: 1.0f;*/
        sourcevals[0] = (tex2D( g_samShadow, ShadowTexC ) + SHADOW_EPSILON < vPosLightProj.z / vPosLightProj.w)? 0.0f: 1.0f;
        
        //讓不在陰影下的pixel為全亮,而不再取樣附近的點
        if(sourcevals[0] == 0.0f)
        {
			sourcevals[1] = (tex2D( g_samShadow, ShadowTexC + float2(1.0/g_iSMWidth, 0) ) + SHADOW_EPSILON < vPosLightProj.z / vPosLightProj.w)? 0.0f: 1.0f;  
			sourcevals[2] = (tex2D( g_samShadow, ShadowTexC + float2(0, 1.0/g_iSMHeight) ) + SHADOW_EPSILON < vPosLightProj.z / vPosLightProj.w)? 0.0f: 1.0f;  
			sourcevals[3] = (tex2D( g_samShadow, ShadowTexC + float2(1.0/g_iSMWidth, 1.0/g_iSMHeight) ) + SHADOW_EPSILON < vPosLightProj.z / vPosLightProj.w)? 0.0f: 1.0f;
	       
			// lerp between the shadow values to calculate our light amount
			float LightAmount = lerp( lerp( sourcevals[0], sourcevals[1], lerps.x ),
									lerp( sourcevals[2], sourcevals[3], lerps.x ),
									lerps.y );
	                                  
			//test
		//  LightAmount = (tex2D( g_samShadow, ShadowTexC ) + SHADOW_EPSILON < vPosLightProj.z / vPosLightProj.w)? 0.0f: 1.0f;
	                                  
			// Light it
			Diffuse.xyz = LightAmount * ( 1 - g_vLightAmbient) + g_vLightAmbient;	//let shadow be gray
		}
    }
    
    return Diffuse;
}

//================================================================================================
void VertSoftShadowMap( float4 Pos : POSITION,
						out float4 oPos : POSITION,
						out float2 Depth : TEXCOORD0 )
{
    oPos = mul( Pos, g_mLightWorldView );
    oPos = mul( oPos, g_mLightProj );
    Depth.xy = oPos.zw;
}

void PixSoftShadowMap( float2 Depth : TEXCOORD0,
						out float4 Color : COLOR )
{
    Color = Depth.x / Depth.y;
}

//-----------------------------------------------------------------------------
void VertSoftShadow( float4 iPos : POSITION,
					float3 iNormal : NORMAL,
					out float4 oPos : POSITION,
					out float4 vPosLightProj : TEXCOORD0,
					out float4 vPos : TEXCOORD1,
					out float3 vNormal : TEXCOORD2)
{
    vPos = mul( iPos, g_mWorldView );
    oPos = mul( vPos, g_mProj );
    vPosLightProj = mul( vPos, g_mViewToLightProj );
//	vPosLightProj.xyz = vPosLightProj.xyz / vPosLightProj.w;	//for reduce arithmetic instruction slots in ps
    vNormal = mul(iNormal, g_mWorldView);
    vNormal = normalize(vNormal);	//for reduce arithmetic instruction slots in ps
}

float4 PixSoftShadow(float4 vPosLightProj : TEXCOORD0,
					float4 vPos : TEXCOORD1,
					float3 vNormal : TEXCOORD2) : COLOR
{
    float4 Diffuse = 1.0;

    // vLight is the unit vector from the light to this pixel
    float3 vLight = normalize( float3( vPos - g_vLightPos ) );

    // Compute diffuse from the light
	if(dot(-vLight, vNormal) > 0)
    {
    //test
/*		float4 ShadowTexC = vPosLightProj;
		ShadowTexC.xy = 0.5 * ShadowTexC.xy/ShadowTexC.w + float2( 0.5, 0.5 );
		ShadowTexC.y = 1.0f - ShadowTexC.y;
		ShadowTexC.z = vPosLightProj.z/vPosLightProj.w;*/
                                  
		float4 ShadowTexC = vPosLightProj/vPosLightProj.w;
		ShadowTexC.xy = 0.5 * ShadowTexC.xy + float2( 0.5, 0.5 );
		ShadowTexC.y = 1.0f - ShadowTexC.y;
		
		//float LightAmount = PCSS(g_samShadow, ShadowTexC);
		float LightAmount = PCF_Filter(ShadowTexC, 0.001, g_samShadow);
		
		// Light it
		Diffuse.xyz = LightAmount * ( 1 - g_vLightAmbient) + g_vLightAmbient;	//let shadow be gray*/
        // transform to texel space
  /*      float2 texelpos = SMAP_SIZE * ShadowTexC;
        
        // Determine the lerp amounts           
        float2 lerps = frac( texelpos );

        //read in bilerp stamp, doing the shadow checks
        float sourcevals[4];
        sourcevals[0] = (tex2D( g_samShadow, ShadowTexC ) + SHADOW_EPSILON < vPosLightProj.z/vPosLightProj.w)? 0.0f: 1.0f;
        
        //讓不在陰影下的pixel為全亮,而不再取樣附近的點
        if(sourcevals[0] == 0.0f)
        {
			sourcevals[1] = (tex2D( g_samShadow, ShadowTexC + float2(1.0/g_iSMWidth, 0) ) + SHADOW_EPSILON < vPosLightProj.z/vPosLightProj.w)? 0.0f: 1.0f;  
			sourcevals[2] = (tex2D( g_samShadow, ShadowTexC + float2(0, 1.0/g_iSMHeight) ) + SHADOW_EPSILON < vPosLightProj.z/vPosLightProj.w)? 0.0f: 1.0f;  
			sourcevals[3] = (tex2D( g_samShadow, ShadowTexC + float2(1.0/g_iSMWidth, 1.0/g_iSMHeight) ) + SHADOW_EPSILON < vPosLightProj.z/vPosLightProj.w)? 0.0f: 1.0f;
	       
			// lerp between the shadow values to calculate our light amount
			float LightAmount = lerp( lerp( sourcevals[0], sourcevals[1], lerps.x ),
									lerp( sourcevals[2], sourcevals[3], lerps.x ),
									lerps.y );
	                                  
			//test
		//  LightAmount = (tex2D( g_samShadow, ShadowTexC ) + SHADOW_EPSILON < vPosLightProj.z / vPosLightProj.w)? 0.0f: 1.0f;
	                                  
			// Light it
			Diffuse.xyz = LightAmount * ( 1 - g_vLightAmbient) + g_vLightAmbient;	//let shadow be gray
		}*/
	}

    return Diffuse;
}
//========================================================================================

//-----------------------------------------------------------------------------
// Technique: RenderShadow
// Desc: Renders the shadow map
//-----------------------------------------------------------------------------
technique RenderShadowMap
{
    pass p0
    {
        VertexShader = compile vs_1_1 VertShadowMap();
        PixelShader = compile ps_2_0 PixShadowMap();
    }
}

technique RenderShadow
{
    pass p0
    {
        VertexShader = compile vs_1_1 VertShadow();
        PixelShader = compile ps_2_0 PixShadow();
    }
}

technique RenderSoftShadowMap
{
    pass p0
    {
        VertexShader = compile vs_1_1 VertSoftShadowMap();
        PixelShader = compile ps_2_0 PixSoftShadowMap();
    }
}

technique RenderSoftShadow
{
    pass p0
    {
        VertexShader = compile vs_1_1 VertSoftShadow();
        PixelShader = compile ps_2_0 PixSoftShadow();
    }
}