 Shader "Hidden/Dof/DepthOfField34" {
	Properties {
		_MainTex ("Base", 2D) = "" {}
		_TapLowBackground ("TapLowBackground", 2D) = "" {}
		_TapLowForeground ("TapLowForeground", 2D) = "" {}
		_TapMedium ("TapMedium", 2D) = "" {}
	}

	CGINCLUDE
	
	#include "UnityCG.cginc"
	
	struct v2f {
		half4 pos : SV_POSITION;
		half2 uv1 : TEXCOORD0;
	};
	
	struct v2fDofApply {
		half4 pos : SV_POSITION;
		half2 uv : TEXCOORD0;
	};
	
	struct v2fRadius {
		half4 pos : SV_POSITION;
		half2 uv : TEXCOORD0;
		half4 uv1[4] : TEXCOORD1;
	};
	
	struct v2fDown {
		half4 pos : SV_POSITION;
		half2 uv0 : TEXCOORD0;
		half2 uv[2] : TEXCOORD1;
	};	 
			
	sampler2D _MainTex;
	sampler2D_float _CameraDepthTexture;
	sampler2D _TapLowBackground;	
	sampler2D _TapLowForeground;
	sampler2D _TapMedium;
			
	half4 _CurveParams;
	half _ForegroundBlurExtrude;
	uniform half3 _Threshhold;	
	uniform float4 _MainTex_TexelSize;
	uniform float2 _InvRenderTargetSize;
	
	v2f vert( appdata_img v ) {
		v2f o;
		o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
		o.uv1.xy = v.texcoord.xy;
		return o;
	} 

	v2fRadius vertWithRadius( appdata_img v ) {
		v2fRadius o;
		o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
		o.uv.xy = v.texcoord.xy;

		const half2 blurOffsets[4] = {
			half2(-0.5, +1.5),
			half2(+0.5, -1.5),
			half2(+1.5, +0.5),
			half2(-1.5, -0.5)
		}; 	
				
		o.uv1[0].xy = v.texcoord.xy + 5.0 * _MainTex_TexelSize.xy * blurOffsets[0];
		o.uv1[1].xy = v.texcoord.xy + 5.0 * _MainTex_TexelSize.xy * blurOffsets[1];
		o.uv1[2].xy = v.texcoord.xy + 5.0 * _MainTex_TexelSize.xy * blurOffsets[2];
		o.uv1[3].xy = v.texcoord.xy + 5.0 * _MainTex_TexelSize.xy * blurOffsets[3];
		
		o.uv1[0].zw = v.texcoord.xy + 3.0 * _MainTex_TexelSize.xy * blurOffsets[0];
		o.uv1[1].zw = v.texcoord.xy + 3.0 * _MainTex_TexelSize.xy * blurOffsets[1];
		o.uv1[2].zw = v.texcoord.xy + 3.0 * _MainTex_TexelSize.xy * blurOffsets[2];
		o.uv1[3].zw = v.texcoord.xy + 3.0 * _MainTex_TexelSize.xy * blurOffsets[3];
		
		return o;
	} 
	
	v2fDofApply vertDofApply( appdata_img v ) {
		v2fDofApply o;
		o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
		o.uv.xy = v.texcoord.xy;
		return o;
	} 	
		
	v2fDown vertDownsampleWithCocConserve(appdata_img v) {
		v2fDown o;
		o.pos = mul(UNITY_MATRIX_MVP, v.vertex);	
		o.uv0.xy = v.texcoord.xy;
		o.uv[0].xy = v.texcoord.xy + half2(-1.0,-1.0) * _InvRenderTargetSize;
		o.uv[1].xy = v.texcoord.xy + half2(1.0,-1.0) * _InvRenderTargetSize;		
		return o; 
	} 
	
	half4 BokehPrereqs (sampler2D tex, half4 uv1[4], half4 center, half considerCoc) {		
		
		// @NOTE 1:
		// we are checking for 3 things in order to create a bokeh.
		// goal is to get the highest bang for the buck.
		// 1.) contrast/frequency should be very high (otherwise bokeh mostly unvisible)
		// 2.) luminance should be high
		// 3.) no occluder nearby (stored in alpha channel)
		
		// @NOTE 2: about the alpha channel in littleBlur:
		// the alpha channel stores an heuristic on how likely it is 
		// that there is no bokeh occluder nearby.
		// if we didn't' check for that, we'd get very noise bokeh
		// popping because of the sudden contrast changes

		half4 sampleA = tex2D(tex, uv1[0].zw);
		half4 sampleB = tex2D(tex, uv1[1].zw);
		half4 sampleC = tex2D(tex, uv1[2].zw);
		half4 sampleD = tex2D(tex, uv1[3].zw);
		
		half4 littleBlur = 0.125 * (sampleA + sampleB + sampleC + sampleD);
		
		sampleA = tex2D(tex, uv1[0].xy);
		sampleB = tex2D(tex, uv1[1].xy);
		sampleC = tex2D(tex, uv1[2].xy);
		sampleD = tex2D(tex, uv1[3].xy);		

		littleBlur += 0.125 * (sampleA + sampleB + sampleC + sampleD);
				
		littleBlur = lerp (littleBlur, center, saturate(100.0 * considerCoc * abs(littleBlur.a - center.a)));
				
		return littleBlur;
	}	
	
	half4 fragDownsampleWithCocConserve(v2fDown i) : SV_Target {
		half2 rowOfs[4];   
		
  		rowOfs[0] = half2(0.0, 0.0);  
  		rowOfs[1] = half2(0.0, _InvRenderTargetSize.y);  
  		rowOfs[2] = half2(0.0, _InvRenderTargetSize.y) * 2.0;  
  		rowOfs[3] = half2(0.0, _InvRenderTargetSize.y) * 3.0; 
  		
  		half4 color = tex2D(_MainTex, i.uv0.xy); 	
			
		half4 sampleA = tex2D(_MainTex, i.uv[0].xy + rowOfs[0]);  
		half4 sampleB = tex2D(_MainTex, i.uv[1].xy + rowOfs[0]);  
		half4 sampleC = tex2D(_MainTex, i.uv[0].xy + rowOfs[2]);  
		half4 sampleD = tex2D(_MainTex, i.uv[1].xy + rowOfs[2]);  
		
		color += sampleA + sampleB + sampleC + sampleD;
		color *= 0.2;
		
		// @NOTE we are doing max on the alpha channel for 2 reasons:
		// 1) foreground blur likes a slightly bigger radius
		// 2) otherwise we get an ugly outline between high blur- and medium blur-areas
		// drawback: we get a little bit of color bleeding  		
		
		color.a = max(max(sampleA.a, sampleB.a), max(sampleC.a, sampleD.a));
  		
		return color;
	}
	
	half4 fragDofApplyBg (v2fDofApply i) : SV_Target {		
		half4 tapHigh = tex2D (_MainTex, i.uv.xy);
		
		#if UNITY_UV_STARTS_AT_TOP
		if (_MainTex_TexelSize.y < 0)
			i.uv.xy = i.uv.xy * half2(1,-1)+half2(0,1);
		#endif
		
		half4 tapLow = tex2D (_TapLowBackground, i.uv.xy); // already mixed with medium blur
		tapHigh = lerp (tapHigh, tapLow, tapHigh.a);
		return tapHigh; 
	}	
	
	half4 fragDofApplyBgDebug (v2fDofApply i) : SV_Target {		
		half4 tapHigh = tex2D (_MainTex, i.uv.xy); 	
		
		half4 tapLow = tex2D (_TapLowBackground, i.uv.xy);
		
		half4 tapMedium = tex2D (_TapMedium, i.uv.xy);
		tapMedium.rgb = (tapMedium.rgb + half3 (1, 1, 0)) * 0.5;	
		tapLow.rgb = (tapLow.rgb + half3 (0, 1, 0)) * 0.5;
		
		tapLow = lerp (tapMedium, tapLow, saturate (tapLow.a * tapLow.a));		
		tapLow = tapLow * 0.5 + tex2D (_TapLowBackground, i.uv.xy) * 0.5;

		return lerp (tapHigh, tapLow, tapHigh.a);
	}		
	
	half4 fragDofApplyFg (v2fDofApply i) : SV_Target {
		half4 fgBlur = tex2D(_TapLowForeground, i.uv.xy);	
		
		#if UNITY_UV_STARTS_AT_TOP
		if (_MainTex_TexelSize.y < 0)
			i.uv.xy = i.uv.xy * half2(1,-1)+half2(0,1);
		#endif
				
		half4 fgColor = tex2D(_MainTex,i.uv.xy);
				
		//fgBlur.a = saturate(fgBlur.a*_ForegroundBlurWeight+saturate(fgColor.a-fgBlur.a));
		//fgBlur.a = max (fgColor.a, (2.0 * fgBlur.a - fgColor.a)) * _ForegroundBlurExtrude;
		fgBlur.a = max(fgColor.a, fgBlur.a * _ForegroundBlurExtrude); //max (fgColor.a, (2.0*fgBlur.a-fgColor.a)) * _ForegroundBlurExtrude;
		
		return lerp (fgColor, fgBlur, saturate(fgBlur.a));
	}	
	
	half4 fragDofApplyFgDebug (v2fDofApply i) : SV_Target {
		half4 fgBlur = tex2D(_TapLowForeground, i.uv.xy);		
					
		half4 fgColor = tex2D(_MainTex,i.uv.xy);
		
		fgBlur.a = max(fgColor.a, fgBlur.a * _ForegroundBlurExtrude); //max (fgColor.a, (2.0*fgBlur.a-fgColor.a)) * _ForegroundBlurExtrude;
		
		half4 tapMedium = half4 (1, 1, 0, fgBlur.a);	
		tapMedium.rgb = 0.5 * (tapMedium.rgb + fgColor.rgb);
		
		fgBlur.rgb = 0.5 * (fgBlur.rgb + half3(0,1,0));
		fgBlur.rgb = lerp (tapMedium.rgb, fgBlur.rgb, saturate (fgBlur.a * fgBlur.a));
		
		return lerp ( fgColor, fgBlur, saturate(fgBlur.a));
	}	
		
	half4 fragCocBg (v2f i) : SV_Target {
		
		float d = SAMPLE_DEPTH_TEXTURE (_CameraDepthTexture, i.uv1.xy);
		d = Linear01Depth (d);
		half coc = 0.0; 
		
		half focalDistance01 = _CurveParams.w + _CurveParams.z;
		
		if (d > focalDistance01) 
			coc = (d - focalDistance01);
	
		coc = saturate (coc * _CurveParams.y);	
		return coc;
	} 
	
	half4 fragCocFg (v2f i) : SV_Target {		
		half4 color = tex2D (_MainTex, i.uv1.xy);
		color.a = 0.0;

		#if UNITY_UV_STARTS_AT_TOP
		if (_MainTex_TexelSize.y < 0)
			i.uv1.xy = i.uv1.xy * half2(1,-1)+half2(0,1);
		#endif

		float d = SAMPLE_DEPTH_TEXTURE (_CameraDepthTexture, i.uv1.xy);
		d = Linear01Depth (d);	
		
		half focalDistance01 = (_CurveParams.w - _CurveParams.z);	
		
		if (d < focalDistance01) 
			color.a = (focalDistance01 - d);
		
		color.a = saturate (color.a * _CurveParams.x);	
		return color;	
	}	
	
	// not being used atm
	
	half4 fragMask (v2f i) : SV_Target {
		return half4(0,0,0,0); 
	}	
	
	// used for simple one one blend
	
	half4 fragAddBokeh (v2f i) : SV_Target {	
		half4 from = tex2D( _MainTex, i.uv1.xy );
		return from;
	}
	
	half4 fragAddFgBokeh (v2f i) : SV_Target {		
		half4 from = tex2D( _MainTex, i.uv1.xy );
		return from; 
	}
		
	half4 fragDarkenForBokeh(v2fRadius i) : SV_Target {		
		half4 fromOriginal = tex2D(_MainTex, i.uv.xy);
		half4 lowRez = BokehPrereqs (_MainTex, i.uv1, fromOriginal, _Threshhold.z);
		half4 outColor = half4(0,0,0, fromOriginal.a);
		half modulate = fromOriginal.a;		
		
		// this code imitates the if-then-else conditions below
		half2 conditionCheck = half2( dot(abs(fromOriginal.rgb-lowRez.rgb), half3(0.3,0.5,0.2)), Luminance(fromOriginal.rgb));
		conditionCheck *= fromOriginal.a;
		conditionCheck = saturate(_Threshhold.xy - conditionCheck);
		outColor = lerp (outColor, fromOriginal, saturate (dot(conditionCheck, half2(1000.0,1000.0))));
		
		/*
		if ( abs(dot(fromOriginal.rgb - lowRez.rgb,  half3 (0.3,0.5,0.2))) * modulate < _Threshhold.x)
			outColor = fromOriginal; // no darkening
		if (Luminance(fromOriginal.rgb) * modulate < _Threshhold.y)
			outColor = fromOriginal; // no darkening
		if (lowRez.a < _Threshhold.z) // need to make foreground not cast false bokeh's
			outColor = fromOriginal; // no darkenin
		*/	
		 
		return outColor;
	}
 
 	half4 fragExtractAndAddToBokeh (v2fRadius i) : SV_Target {	
		half4 from = tex2D(_MainTex, i.uv.xy);
		half4 lowRez = BokehPrereqs(_MainTex, i.uv1, from, _Threshhold.z);
		half4 outColor = from;

		// this code imitates the if-then-else conditions below
		half2 conditionCheck = half2( dot(abs(from.rgb-lowRez.rgb), half3(0.3,0.5,0.2)), Luminance(from.rgb));
		conditionCheck *= from.a;
		conditionCheck = saturate(_Threshhold.xy - conditionCheck);
		outColor = lerp (outColor, half4(0,0,0,0), saturate (dot(conditionCheck, half2(1000.0,1000.0))));
		
		/*
		if ( abs(dot(from.rgb - lowRez.rgb,  half3 (0.3,0.5,0.2))) * modulate < _Threshhold.x)
			outColor = half4(0,0,0,0); // don't add
		if (Luminance(from.rgb) * modulate < _Threshhold.y)
			outColor = half4(0,0,0,0); // don't add
		if (lowRez.a < _Threshhold.z) // need to make foreground not cast false bokeh's
			outColor = half4(0,0,0,0); // don't add
		*/
							
		return outColor;
	}
 
	ENDCG
	
Subshader {
 
 // pass 0
 
 Pass {
	  ZTest Always Cull Off ZWrite Off

      CGPROGRAM
      #pragma vertex vertDofApply
      #pragma fragment fragDofApplyBg
      
      ENDCG
  	}

 // pass 1
 
 Pass {
	  ZTest Always Cull Off ZWrite Off
	  ColorMask RGB

      CGPROGRAM
      #pragma vertex vertDofApply
      #pragma fragment fragDofApplyFgDebug

      ENDCG
  	}

 // pass 2

 Pass {
	  ZTest Always Cull Off ZWrite Off
	  ColorMask RGB

      CGPROGRAM
      #pragma vertex vertDofApply
      #pragma fragment fragDofApplyBgDebug

      ENDCG
  	}
  	
  	
 
 // pass 3
 
 Pass {
	  ZTest Always Cull Off ZWrite Off
	  ColorMask A

      CGPROGRAM
      #pragma vertex vert
      #pragma fragment fragCocBg

      ENDCG
  	}  
  	 	
	
 // pass 4

  
 Pass {
	  ZTest Always Cull Off ZWrite Off
	  ColorMask RGB
	  //Blend One One

      CGPROGRAM
      #pragma vertex vertDofApply
      #pragma fragment fragDofApplyFg
      
      ENDCG
  	}  	

 // pass 5
  
 Pass {
	  ZTest Always Cull Off ZWrite Off
	  ColorMask ARGB

      CGPROGRAM
      #pragma vertex vert
      #pragma fragment fragCocFg

      ENDCG
  	} 

 // pass 6
 
 Pass {
	  ZTest Always Cull Off ZWrite Off

      CGPROGRAM
      #pragma vertex vertDownsampleWithCocConserve
      #pragma fragment fragDownsampleWithCocConserve

      ENDCG
  	} 

 // pass 7
 // not being used atm
 
 Pass { 
	  ZTest Always Cull Off ZWrite Off
	  ColorMask RGBA

      CGPROGRAM
      #pragma vertex vert
      #pragma fragment fragMask

      ENDCG
  	} 

 // pass 8
 
 Pass {
	  ZTest Always Cull Off ZWrite Off
	  Blend SrcAlpha OneMinusSrcAlpha
	  ColorMask RGB

      CGPROGRAM
      #pragma vertex vert
      #pragma fragment fragAddBokeh

      ENDCG
  	} 
  	
 // pass 9
 
 Pass {
	  ZTest Always Cull Off ZWrite Off
	  Blend One One
	  ColorMask RGB

      CGPROGRAM
      #pragma vertex vertWithRadius
      #pragma fragment fragExtractAndAddToBokeh

      ENDCG
  	} 
  	
 // pass 10
 
 Pass {
	  ZTest Always Cull Off ZWrite Off

      CGPROGRAM
      #pragma vertex vertWithRadius
      #pragma fragment fragDarkenForBokeh

      ENDCG
  	}   	
  	
 // pass 11
 
 Pass {
	  ZTest Always Cull Off ZWrite Off

      CGPROGRAM
      #pragma vertex vertWithRadius
      #pragma fragment fragExtractAndAddToBokeh

      ENDCG
  	}   	
  }
  
Fallback off

}
