//==============================================================================
//
// MME SSAO Filter for MikuMikuMoving
// mqdlさんのSSAOを改変させていただきました。
//
// 2012 Mogg
//==============================================================================

//-----------------------------------------------------
//パラメータ

float density
<
   string UIName = "Density";
   string UIWidget = "Slider";
   bool UIVisible =  true;
   float UIMin = 1.0;
   float UIMax = 50.0;
> = 20.0;
float strength
<
   string UIName = "Strength";
   string UIWidget = "Slider";
   bool UIVisible =  true;
   float UIMin = 0.001;
   float UIMax = 0.02;
> = 0.01;
float falloff
<
   string UIName = "FallOff";
   string UIWidget = "Numeric";
   bool UIVisible =  true;
   float UIMin = 0.000001;
   float UIMax = 0.000005;
> = 0.000001;
float rad
<
   string UIName = "Radius";
   string UIWidget = "Slider";
   bool UIVisible =  true;
   float UIMin = 0.01;
   float UIMax = 1.0;
> = 0.1;
float blur
<
   string UIName = "Blur";
   string UIWidget = "Slider";
   bool UIVisible =  true;
   float UIMin = 0.1;
   float UIMax = 3.0;
> = 1.0;
float dotRange
<
   string UIName = "DotRange";
   string UIWidget = "Slider";
   bool UIVisible =  true;
   float UIMin = 0.0;
   float UIMax = 1.0;
> = 0.5;

float optimize
<
   string UIName = "Optimize";
   string UIWidget = "Slider";
   bool UIVisible =  true;
   float UIMin = 0.0;
   float UIMax = 1.0;
> = 1.0;

//-----------------------------------------------------
//ここから先はエフェクトに詳しい方はいじらないほうが良いです

#define SAMPLES 10

#if SAMPLES == 16
float3 samples[16] = {
	float3(0.53812504, 0.18565957, -0.43192),
	float3(0.13790712, 0.24864247, 0.44301823),
	float3(0.33715037, 0.56794053, -0.005789503),
	float3(-0.6999805, -0.04511441, -0.0019965635),
	float3(0.06896307, -0.15983082, -0.85477847),
	float3(0.056099437, 0.006954967, -0.1843352),
	float3(-0.014653638, 0.14027752, 0.0762037),
	float3(0.010019933, -0.1924225, -0.034443386),
	float3(-0.35775623, -0.5301969, -0.43581226),
	float3(-0.3169221, 0.106360726, 0.015860917),
	float3(0.010350345, -0.58698344, 0.0046293875),
	float3(-0.08972908, -0.49408212, 0.3287904),
	float3(0.7119986, -0.0154690035, -0.09183723),
	float3(-0.053382345, 0.059675813, -0.5411899),
	float3(0.035267662, -0.063188605, 0.54602677),
	float3(-0.47761092, 0.2847911, -0.0271716)
};

#elif SAMPLES == 12
float3 samples[12] = {
	float3(-0.13657719, 0.30651027, 0.16118456),
	float3(-0.14714938, 0.33245975, -0.113095455),
	float3(0.030659059, 0.27887347, -0.7332209),
	float3(0.009913514, -0.89884496, 0.07381549),
	float3(0.040318526, 0.40091, 0.6847858),
	float3(0.22311053, -0.3039437, -0.19340435),
	float3(0.36235332, 0.21894878, -0.05407306),
	float3(-0.15198798, -0.38409665, -0.46785462),
	float3(-0.013492276, -0.5345803, 0.11307949),
	float3(-0.4972847, 0.037064247, -0.4381323),
	float3(-0.024175806, -0.008928787, 0.17719103),
	float3(0.694014, -0.122672155, 0.33098832)
};

#elif SAMPLES == 8
float3 samples[8] = {
	float3(0.24710192, 0.6445882, 0.033550154),
	float3(0.00991752, -0.21947019, 0.7196721),
	float3(0.25109035, -0.1787317, -0.011580509),
	float3(-0.08781511, 0.44514698, 0.56647956),
	float3(-0.011737816, -0.0643377, 0.16030222),
	float3(0.035941467, 0.04990871, -0.46533614),
	float3(-0.058801126, 0.7347013, -0.25399926),
	float3(-0.24799341, -0.022052078, -0.13399573)
};

#else
float3 samples[10] = {
	float3(-0.010735935, 0.01647018, 0.0062425877),
	float3(-0.06533369, 0.3647007, -0.13746321),
	float3(-0.6539235, -0.016726388, -0.53000957),
	float3(0.40958285, 0.0052428036, -0.5591124),
	float3(-0.1465366, 0.09899267, 0.15571679),
	float3(-0.44122112, -0.5458797, 0.04912532),
	float3(0.03755566, -0.10961345, -0.33040273),
	float3(0.019100213, 0.29652783, 0.066237666),
	float3(0.8765323, 0.011236004, 0.28265962),
	float3(0.29264435, -0.40794238, 0.15964167)
};
#endif

float	fmRange	= 0.8f;
float	rnd_offset = 18.0;
float	invSamples = 1.0/SAMPLES;
int		blur_limit = 4;
float	pix_offsets[16] = {
	-1.0f,1.0f,
	-2.0f,2.0f,
	-3.0f,3.0f,
	-4.0f,4.0f,
	-5.0f,5.0f,
	-6.0f,6.0f,
	-7.0f,7.0f,
	-8.0f,8.0f
};


float Script : STANDARDSGLOBAL <
	string ScriptOutput = "color";
	string ScriptClass = "scene";
	string ScriptOrder = "postprocess";
> = 0.8;

// スクリーンサイズ
float2 ViewportSize : VIEWPORTPIXELSIZE;

static float2 ViewportOffset = (float2(0.5,0.5)/ViewportSize);
static float2 SampStep = (float2(blur,blur)/ViewportSize);

// レンダリングターゲットのクリア値
float4 ClearColor = {1,1,1,1};
float ClearDepth  = 1.0;

//-----------------------------------------------------------------------------
// オリジナルの描画結果を記録するためのレンダーターゲット
//
//-----------------------------------------------------------------------------
texture2D ScnMap : RENDERCOLORTARGET <
	float2 ViewPortRatio = {1.0,1.0};
	int MipLevels = 1;
	string Format = "X8R8G8B8" ;
>;
sampler2D ScnSamp = sampler_state {
	texture = <ScnMap>;
	MinFilter = LINEAR;
	MagFilter = LINEAR;
	MipFilter = NONE;
	AddressU  = CLAMP;
	AddressV = CLAMP;
};

texture2D DepthBuffer : RENDERDEPTHSTENCILTARGET <
	float2 ViewPortRatio = {1.0,1.0};
	string Format = "D24S8";
>;

//-----------------------------------------------------------------------------
// サンプリングの結果を記録するためのレンダーターゲット
//
//-----------------------------------------------------------------------------
texture2D SSAOMapRT : RENDERCOLORTARGET <
	float2 ViewPortRatio = {1.0,1.0};
	float4 ClearColor = {1,1,1,1};
	float ClearDepth = 1.0;
	int MipLevels = 1;
	bool AntiAlias = false;
	string Format = "X8R8G8B8" ;
>;
sampler2D SSAOMap = sampler_state {
	texture = <SSAOMapRT>;
	MinFilter = LINEAR;
	MagFilter = LINEAR;
	MipFilter = NONE;
	AddressU  = CLAMP;
	AddressV = CLAMP;
};

//-----------------------------------------------------------------------------
// ぼかしの結果を記録するためのレンダーターゲット
//
//-----------------------------------------------------------------------------
texture2D BlurMapRT : RENDERCOLORTARGET <
	float2 ViewPortRatio = {1.0,1.0};
	float4 ClearColor = {1,1,1,1};
	float ClearDepth = 1.0;
	int MipLevels = 1;
	string Format = "X8R8G8B8" ;
>;
sampler2D BlurMap = sampler_state {
	texture = <BlurMapRT>;
	MinFilter = LINEAR;
	MagFilter = LINEAR;
	MipFilter = NONE;
	AddressU  = CLAMP;
	AddressV = CLAMP;
};

//-----------------------------------------------------------------------------
// XYぼかしの結果を記録するためのレンダーターゲット
//
//-----------------------------------------------------------------------------
texture2D BlurXYMapRT : RENDERCOLORTARGET <
	float2 ViewPortRatio = {1.0,1.0};
	float4 ClearColor = {1,1,1,1};
	float ClearDepth = 1.0;
	int MipLevels = 1;
	string Format = "X8R8G8B8" ;
>;
sampler2D BlurXYMap = sampler_state {
	texture = <BlurXYMapRT>;
	MinFilter = LINEAR;
	MagFilter = LINEAR;
	MipFilter = NONE;
	AddressU  = CLAMP;
	AddressV = CLAMP;
};

//-----------------------------------------------------------------------------
//乱数テクスチャ
//
//-----------------------------------------------------------------------------
texture2D rndTex <
	string ResourceName = "rand.dds";
>;
sampler RandomMap = sampler_state {
	texture = <rndTex>;
};

//-----------------------------------------------------------------------------
// 法線マップ
//
//-----------------------------------------------------------------------------
texture NormalMapRT: OFFSCREENRENDERTARGET <
	string Description = "OffScreen RenderTarget for NormalMap.fxsub";
	float4 ClearColor = {0,0,0,1};
	float ClearDepth = 1.0;
	string Format = "X8R8G8B8" ;
	bool AntiAlias = true;
	string DefaultEffect = 
		"self = hide;"
		"* = NormalMap.fxsub";
>;

sampler NormalMap = sampler_state {
	texture = <NormalMapRT>;
	AddressU  = BORDER;
	AddressV = BORDER;
	Filter = NONE;
};

//-----------------------------------------------------------------------------
// 深度マップ
//
//-----------------------------------------------------------------------------
texture DepthMapRT: OFFSCREENRENDERTARGET <
	string Description = "OffScreen RenderTarget for DepthMap.fxsub";
	float4 ClearColor = {0,0,0,1};
	float ClearDepth = 1.0;
	string Format = "D3DFMT_R32F" ;
	bool AntiAlias = false;
	string DefaultEffect = 
		"self = hide;"
		"* = DepthMap.fxsub";
>;

sampler DepthMap = sampler_state {
	texture = <DepthMapRT>;
	AddressU  = BORDER;
	AddressV = BORDER;
	Filter = NONE;
};


//-----------------------------------------------------------------------------
// 固定定義
//
//-----------------------------------------------------------------------------
struct VS_OUTPUT
{
	float4 Pos			: POSITION;
	float2 TexCoord		: TEXCOORD0;
};

//-----------------------------------------------------------------------------
// SSAO
//-----------------------------------------------------------------------------
VS_OUTPUT VS_SSAO(float4 Pos:POSITION, float4 Tex:TEXCOORD0)
{
	VS_OUTPUT Out = (VS_OUTPUT)0; 

	Out.Pos = Pos;
	Out.TexCoord = Tex + ViewportOffset;

	return Out;
}

float4 PS_SSAO( VS_OUTPUT IN ) : COLOR
{

	float3 fres = normalize((tex2D(RandomMap,IN.TexCoord*rnd_offset).xyz*2.0) - 1.0);
	
	float4 currentPixelSample = tex2D(NormalMap,IN.TexCoord);
	float currentPixelDepth = tex2D(DepthMap,IN.TexCoord).r;
	float3 norm = normalize(currentPixelSample.xyz * 2.0f - 1.0f);

	float bl = 0.0;
	float radD = rad/currentPixelDepth;

	float3 ray, occNorm;
	float2 se;
	float occluderDepth, depthDifference, normDiff;

	float	finalColor = 0.0f;

	for(int i = 0; i < SAMPLES ; i++)
	{
		ray = radD * reflect(samples[i], fres);
		se = IN.TexCoord + sign(dot(ray,norm)) * ray * float2(1.0f, -1.0f);

		float4 occluderFragment = tex2D(NormalMap, se);
		
		occNorm = normalize(occluderFragment.xyz * 2.0f - 1.0f);

		depthDifference = currentPixelDepth - tex2D(DepthMap, se).r;
		normDiff = 1.0 - (occNorm.x*norm.x + occNorm.y*norm.y + occNorm.z*norm.z);

		bl +=  step(falloff,depthDifference) *normDiff * (1.0 - smoothstep(falloff, strength, depthDifference));
	
	}

	float ao = 1.0 - density * bl * invSamples;

	return  float4(ao,ao,ao,1);
}

//-----------------------------------------------------------------------------
// Blur
//-----------------------------------------------------------------------------
VS_OUTPUT VS_Blur(float4 Pos:POSITION, float4 Tex:TEXCOORD0)
{
	VS_OUTPUT Out = (VS_OUTPUT)0; 

	Out.Pos = Pos;
	Out.TexCoord = Tex + ViewportOffset;
	
	return Out;
}

float4 PS_Blur( VS_OUTPUT IN ) : COLOR
{
	float3	nv = tex2D( NormalMap, IN.TexCoord).xyz * 2.0f - 1.0f;
	float	ao = tex2D( SSAOMap, IN.TexCoord).r;
	float	cnt = 1;

	for(int i = 0; i < 16; i++)
	{
		float2 uv = IN.TexCoord + float2(pix_offsets[i],0)*SampStep;
		
		float3 sample_nv = tex2D(NormalMap ,uv).xyz * 2.0f - 1.0f;
		
		if(dotRange < dot(normalize(nv),normalize(sample_nv)))
		{
			cnt++;
			ao += tex2D(SSAOMap, uv).r;
		}else{
			if(blur_limit < i)
				break;
		}
	}

	return float4(ao/cnt, ao/cnt, ao/cnt,1);
}

//-----------------------------------------------------------------------------
// BlurXY
//-----------------------------------------------------------------------------
VS_OUTPUT VS_BlurXY(float4 Pos:POSITION, float4 Tex:TEXCOORD0)
{
	VS_OUTPUT Out = (VS_OUTPUT)0; 

	float2 c = float2(0.5, 0.5);

	Out.Pos = Pos;
	Out.TexCoord = (Tex + ViewportOffset - c) * fmRange + c;

	return Out;
}

float4 PS_BlurXY( VS_OUTPUT IN ) : COLOR
{
	float3	nv = tex2D( NormalMap, IN.TexCoord).xyz * 2.0f - 1.0f;
	float	ao = tex2D( BlurMap, IN.TexCoord).r;
	float	cnt = 1;

	for(int i = 0; i < 16; i++)
	{
		float2 uv = IN.TexCoord + float2(0,pix_offsets[i])*SampStep;
		
		float3 sample_nv = tex2D(NormalMap ,uv).xyz * 2.0f - 1.0f;
		
		if(dotRange < dot(normalize(nv),normalize(sample_nv)))
		{
			cnt++;
			ao += tex2D(BlurMap, uv).r;
		}else{
			if(blur_limit < i)
				break;
		}
	}
	
	return float4(ao/cnt, ao/cnt, ao/cnt,1);
}

//-----------------------------------------------------------------------------
// OutPut
//-----------------------------------------------------------------------------

VS_OUTPUT VS_Last(float4 Pos:POSITION, float2 Tex:TEXCOORD0)
{
	VS_OUTPUT Out = (VS_OUTPUT)0; 

	Out.Pos = Pos;
	Out.TexCoord = Tex + ViewportOffset;
		
	return Out;
}

float4 PS_Last( VS_OUTPUT IN ) : COLOR
{   
	float4 Color;
	
	float ao = tex2D( BlurXYMap, IN.TexCoord ).r;

	Color = lerp(tex2D( BlurXYMap, IN.TexCoord), tex2D( ScnSamp, IN.TexCoord ) ,optimize);
	Color = lerp(Color, float4(1, 1, 1, 1) , ao);
	
	return Color * tex2D( ScnSamp, IN.TexCoord );

}
////////////////////////////////////////////////////////////////////////////////////////////////

technique AmbientOcclusion <
	string Script = 
		"RenderColorTarget0=ScnMap;"
		"RenderDepthStencilTarget=DepthBuffer;"
		"ClearSetColor=ClearColor;"
		"ClearSetDepth=ClearDepth;"
		"Clear=Color;"
		"Clear=Depth;"
		"ScriptExternal=Color;"

		"RenderColorTarget0=SSAOMapRT;"
		"RenderDepthStencilTarget=DepthBuffer;"
		"ClearSetColor=ClearColor;"
		"ClearSetDepth=ClearDepth;"
		"Clear=Color;"
		"Clear=Depth;"
		"Pass=SSAOPass;"
		
		"RenderColorTarget0=BlurMapRT;"
		"RenderDepthStencilTarget=DepthBuffer;"
		"ClearSetColor=ClearColor;"
		"ClearSetDepth=ClearDepth;"
		"Clear=Color;"
		"Clear=Depth;"
		"Pass=BlurPass;"
		
		"RenderColorTarget0=BlurXYMapRT;"
		"RenderDepthStencilTarget=DepthBuffer;"
		"ClearSetColor=ClearColor;"
		"ClearSetDepth=ClearDepth;"
		"Clear=Color;"
		"Clear=Depth;"
		"Pass=BlurXYPass;"

		//最終合成
		"RenderColorTarget0=;"
		"RenderDepthStencilTarget=;"
		"Pass=LastPass;"
	;
> {
	pass SSAOPass < string Script= "Draw=Buffer;"; > {
		AlphaBlendEnable = FALSE;
		VertexShader = compile vs_3_0 VS_SSAO();
		PixelShader  = compile ps_3_0 PS_SSAO();
	}

	pass BlurPass < string Script= "Draw=Buffer;"; > {
		AlphaBlendEnable = FALSE;
		VertexShader = compile vs_3_0 VS_Blur();
		PixelShader  = compile ps_3_0 PS_Blur();
	}
	
	pass BlurXYPass < string Script= "Draw=Buffer;"; > {
		AlphaBlendEnable = FALSE;
		VertexShader = compile vs_3_0 VS_BlurXY();
		PixelShader  = compile ps_3_0 PS_BlurXY();
	}
	
	pass LastPass < string Script= "Draw=Buffer;"; > {
		AlphaBlendEnable = FALSE;
		VertexShader = compile vs_3_0 VS_Last();
		PixelShader  = compile ps_3_0 PS_Last();
	}
}
////////////////////////////////////////////////////////////////////////////////////////////////
