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; half4 _MainTex_ST; uniform float2 _InvRenderTargetSize; half4 _CameraDepthTexture_ST; half4 _TapLowBackground_ST; half4 _TapLowForeground_ST; half4 _TapMedium_ST; v2f vert( appdata_img v ) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.uv1.xy = v.texcoord.xy; return o; } v2fRadius vertWithRadius( appdata_img v ) { v2fRadius o; o.pos = UnityObjectToClipPos(v.vertex); o.uv.xy = UnityStereoScreenSpaceUVAdjust(v.texcoord.xy, _MainTex_ST); 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 = UnityStereoScreenSpaceUVAdjust(v.texcoord.xy + 5.0 * _MainTex_TexelSize.xy * blurOffsets[0], _MainTex_ST); o.uv1[1].xy = UnityStereoScreenSpaceUVAdjust(v.texcoord.xy + 5.0 * _MainTex_TexelSize.xy * blurOffsets[1], _MainTex_ST); o.uv1[2].xy = UnityStereoScreenSpaceUVAdjust(v.texcoord.xy + 5.0 * _MainTex_TexelSize.xy * blurOffsets[2], _MainTex_ST); o.uv1[3].xy = UnityStereoScreenSpaceUVAdjust(v.texcoord.xy + 5.0 * _MainTex_TexelSize.xy * blurOffsets[3], _MainTex_ST); o.uv1[0].zw = UnityStereoScreenSpaceUVAdjust(v.texcoord.xy + 3.0 * _MainTex_TexelSize.xy * blurOffsets[0], _MainTex_ST); o.uv1[1].zw = UnityStereoScreenSpaceUVAdjust(v.texcoord.xy + 3.0 * _MainTex_TexelSize.xy * blurOffsets[1], _MainTex_ST); o.uv1[2].zw = UnityStereoScreenSpaceUVAdjust(v.texcoord.xy + 3.0 * _MainTex_TexelSize.xy * blurOffsets[2], _MainTex_ST); o.uv1[3].zw = UnityStereoScreenSpaceUVAdjust(v.texcoord.xy + 3.0 * _MainTex_TexelSize.xy * blurOffsets[3], _MainTex_ST); return o; } v2fDofApply vertDofApply( appdata_img v ) { v2fDofApply o; o.pos = UnityObjectToClipPos(v.vertex); o.uv.xy = v.texcoord.xy; return o; } v2fDown vertDownsampleWithCocConserve(appdata_img v) { v2fDown o; o.pos = UnityObjectToClipPos(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, UnityStereoScreenSpaceUVAdjust(i.uv0.xy, _MainTex_ST)); half4 sampleA = tex2D(_MainTex, UnityStereoScreenSpaceUVAdjust(i.uv[0].xy + rowOfs[0], _MainTex_ST)); half4 sampleB = tex2D(_MainTex, UnityStereoScreenSpaceUVAdjust(i.uv[1].xy + rowOfs[0], _MainTex_ST)); half4 sampleC = tex2D(_MainTex, UnityStereoScreenSpaceUVAdjust(i.uv[0].xy + rowOfs[2], _MainTex_ST)); half4 sampleD = tex2D(_MainTex, UnityStereoScreenSpaceUVAdjust(i.uv[1].xy + rowOfs[2], _MainTex_ST)); 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, UnityStereoScreenSpaceUVAdjust(i.uv.xy, _MainTex_ST)); #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, UnityStereoScreenSpaceUVAdjust(i.uv.xy, _TapLowBackground_ST)); // already mixed with medium blur tapHigh = lerp (tapHigh, tapLow, tapHigh.a); return tapHigh; } half4 fragDofApplyBgDebug (v2fDofApply i) : SV_Target { half4 tapHigh = tex2D (_MainTex, UnityStereoScreenSpaceUVAdjust(i.uv.xy, _MainTex_ST)); half4 tapLow = tex2D (_TapLowBackground, UnityStereoScreenSpaceUVAdjust(i.uv.xy, _TapLowBackground_ST)); half4 tapMedium = tex2D (_TapMedium, UnityStereoScreenSpaceUVAdjust(i.uv.xy, _TapMedium_ST)); 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, UnityStereoScreenSpaceUVAdjust(i.uv.xy, _TapLowBackground_ST)) * 0.5; return lerp (tapHigh, tapLow, tapHigh.a); } half4 fragDofApplyFg (v2fDofApply i) : SV_Target { half4 fgBlur = tex2D(_TapLowForeground, UnityStereoScreenSpaceUVAdjust(i.uv.xy, _TapLowForeground_ST)); #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, UnityStereoScreenSpaceUVAdjust(i.uv.xy, _MainTex_ST)); //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, UnityStereoScreenSpaceUVAdjust(i.uv.xy, _TapLowForeground_ST)); half4 fgColor = tex2D(_MainTex, UnityStereoScreenSpaceUVAdjust(i.uv.xy, _MainTex_ST)); 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, UnityStereoScreenSpaceUVAdjust(i.uv1.xy, _MainTex_ST)); 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, UnityStereoScreenSpaceUVAdjust(i.uv1.xy, _MainTex_ST)); 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, UnityStereoScreenSpaceUVAdjust(i.uv1.xy, _MainTex_ST)); 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, UnityStereoScreenSpaceUVAdjust(i.uv1.xy, _MainTex_ST) ); return from; } half4 fragAddFgBokeh (v2f i) : SV_Target { half4 from = tex2D( _MainTex, UnityStereoScreenSpaceUVAdjust(i.uv1.xy, _MainTex_ST) ); 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 }