// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' Shader "Hidden/SMAA" { Properties { _MainTex ("", 2D) = "black" {} } CGINCLUDE #include "UnityCG.cginc" ///////// from SMAA.fx float4 _PixelSize; #define SMAA_PIXEL_SIZE _PixelSize.xy #define SMAA_PRESET_HIGH 1 // porting the DX9 version of functions #define SMAA_HLSL_3 1 #define SMAA_ONLY_COMPILE_PS 1 #include "SMAA.cginc" sampler2D _CameraDepthTexture; sampler2D_float _CameraMotionVectors; sampler2D _VelTex; sampler2D colorTex; sampler2D edgesTex; sampler2D blendTex; sampler2D areaTex; sampler2D searchTex; sampler2D accumTex; sampler2D smaaTex; int _JitterOffset; float4 _MainTex_TexelSize; float4x4 _ToPrevViewProjCombined; // combined float4 _PixelOffset; float _TemporalAccum; float _DepthThreshold; struct v2f { half4 pos : SV_POSITION; half2 uv : TEXCOORD0; }; sampler2D _MainTex; // set implicitly in Graphics.Blit() v2f vert( appdata_img v ) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.uv = v.texcoord.xy; return o; } half4 fragCopy(v2f i) : SV_Target { half4 color = tex2D (_MainTex, i.uv); return color; } half4 fragBlack(v2f i) : SV_Target { return float4(0,0,0,0); } half4 DX9_SMAALumaEdgeDetectionPS(v2f i) : SV_Target { float2 texcoord = i.uv; float4 offset[3]; offset[0] = texcoord.xyxy + SMAA_PIXEL_SIZE.xyxy * float4(-1.0, 0.0, 0.0, -1.0); offset[1] = texcoord.xyxy + SMAA_PIXEL_SIZE.xyxy * float4( 1.0, 0.0, 0.0, 1.0); offset[2] = texcoord.xyxy + SMAA_PIXEL_SIZE.xyxy * float4(-2.0, 0.0, 0.0, -2.0); half4 color = SMAALumaEdgeDetectionPS(texcoord,offset,colorTex); return color; } half4 DX9_SMAAColorEdgeDetectionPS(v2f i) : SV_Target { float2 texcoord = i.uv; float4 offset[3]; offset[0] = texcoord.xyxy + SMAA_PIXEL_SIZE.xyxy * float4(-1.0, 0.0, 0.0, -1.0); offset[1] = texcoord.xyxy + SMAA_PIXEL_SIZE.xyxy * float4( 1.0, 0.0, 0.0, 1.0); offset[2] = texcoord.xyxy + SMAA_PIXEL_SIZE.xyxy * float4(-2.0, 0.0, 0.0, -2.0); half4 color = SMAAColorEdgeDetectionPS(texcoord,offset,colorTex); return color; } half4 DX9_SMAADepthEdgeDetectionPS(v2f i) : SV_Target { float2 texcoord = i.uv; float4 offset[3]; offset[0] = texcoord.xyxy + SMAA_PIXEL_SIZE.xyxy * float4(-1.0, 0.0, 0.0, -1.0); offset[1] = texcoord.xyxy + SMAA_PIXEL_SIZE.xyxy * float4( 1.0, 0.0, 0.0, 1.0); offset[2] = texcoord.xyxy + SMAA_PIXEL_SIZE.xyxy * float4(-2.0, 0.0, 0.0, -2.0); half4 color = SMAADepthEdgeDetectionPS(texcoord,offset,_CameraDepthTexture,_DepthThreshold); return color; } half4 DX9_SMAABlendingWeightCalculationPS(v2f i) : SV_Target { float2 texcoord = i.uv; float2 pixcoord; float4 offset[3]; pixcoord = texcoord / SMAA_PIXEL_SIZE; // these are taken from SMAABlendingWeightCalculationVS // We will use these offsets for the searches later on (see @PSEUDO_GATHER4): offset[0] = texcoord.xyxy + SMAA_PIXEL_SIZE.xyxy * float4(-0.25, -0.125, 1.25, -0.125); offset[1] = texcoord.xyxy + SMAA_PIXEL_SIZE.xyxy * float4(-0.125, -0.25, -0.125, 1.25); // And these for the searches, they indicate the ends of the loops: offset[2] = float4(offset[0].xz, offset[1].yw) + float4(-2.0, 2.0, -2.0, 2.0) * SMAA_PIXEL_SIZE.xxyy * float(SMAA_MAX_SEARCH_STEPS); int4 jitterData = int4(_JitterOffset,_JitterOffset,_JitterOffset,0); half4 color = SMAABlendingWeightCalculationPS(texcoord, pixcoord, offset, edgesTex, areaTex, searchTex, jitterData); return color; } half4 DX9_SMAANeighborhoodBlendingPS(v2f i) : SV_Target { float2 texcoord = i.uv; float4 offset[2]; offset[0] = texcoord.xyxy + SMAA_PIXEL_SIZE.xyxy * float4(-1.0, 0.0, 0.0, -1.0); offset[1] = texcoord.xyxy + SMAA_PIXEL_SIZE.xyxy * float4( 1.0, 0.0, 0.0, 1.0); half4 color = SMAANeighborhoodBlendingPS(texcoord, offset, colorTex, blendTex); return color; } float PixelsFromEdge(float2 uv) { float distX = min(uv.x,1.0-uv.x) / SMAA_PIXEL_SIZE.x; float distY = min(uv.y,1.0-uv.y) / SMAA_PIXEL_SIZE.y; float bestDist = min(distX,distY); return bestDist; } float K; float3 FetchPreviousUvWeight(float2 currUv) { float2 depth_uv = currUv; #if UNITY_UV_STARTS_AT_TOP //if (_MainTex_TexelSize.y < 0) { depth_uv.y = 1 - depth_uv.y; } #endif // read depth float d = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, depth_uv); // calculate position from pixel from depth float3 clipPos = float3(depth_uv.x*2.0-1.0, depth_uv.y*2.0-1.0, d); // only 1 matrix mul: float4 prevClipPos = mul(_ToPrevViewProjCombined, float4(clipPos, 1.0)); prevClipPos.xyz /= prevClipPos.w; float2 prevUv = prevClipPos.xyz*.5f+.5f; #if UNITY_UV_STARTS_AT_TOP //if (_MainTex_TexelSize.y < 0) { prevUv.y = 1.0f - prevUv.y; } #endif float distFromEdge = PixelsFromEdge(prevUv); float weight = saturate(distFromEdge - .5f); float distSqr = dot(prevUv - currUv, prevUv - currUv); // 1% of the screen seems like a good number float rho = 0.01; float invRR = 1.0f/(rho*rho); float distW = exp(-invRR * distSqr); weight *= distW; // weight = 0.5 * max(0, 1 - K * sqrt(abs(length(currUv) - length(prevUv)))); return float3(prevUv,weight); } // color is the current value, accum is the previous accumulation half4 ClampAccumulation(float2 texcoord, half4 accum, half4 color) { // get the corner pixels for max/max float3 pixelUL = tex2D(colorTex, texcoord + float2( 0.5f, 0.5f)*SMAA_PIXEL_SIZE.xy).rgb; float3 pixelUR = tex2D(colorTex, texcoord + float2(-0.5f, 0.5f)*SMAA_PIXEL_SIZE.xy).rgb; float3 pixelDL = tex2D(colorTex, texcoord + float2( 0.5f,-0.5f)*SMAA_PIXEL_SIZE.xy).rgb; float3 pixelDR = tex2D(colorTex, texcoord + float2(-0.5f,-0.5f)*SMAA_PIXEL_SIZE.xy).rgb; float3 colorMax = max(color,max(max(pixelUL,pixelUR),max(pixelDL,pixelDR))); float3 colorMin = min(color,min(min(pixelUL,pixelUR),min(pixelDL,pixelDR))); // since we are sampling half way to the pixels, we need to multiply by 2.0 on the intensity // which gives a rough guess as to min and max color in a 1 pixel radius float scale = 2.0f; colorMin = color + scale*(colorMin - color); colorMax = color + scale*(colorMax - color); accum.rgb = max(colorMin,min(colorMax,accum.rgb)); return accum; } // same as above, but also blend with accumulation buffer half4 DX9_SMAANeighborhoodBlendingAccumPS(v2f i) : SV_Target { float2 uv = i.uv; #if UNITY_UV_STARTS_AT_TOP uv.y = 1 - uv.y; #endif float4 position = float4(2. * uv - 1., SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, uv), 1.); float4 previousPosition = mul(_ToPrevViewProjCombined, position); previousPosition.xyz /= previousPosition.w; uv = previousPosition.xy * .5 + .5; #if UNITY_UV_STARTS_AT_TOP uv.y = 1. - uv.y; #endif // Inverse velocity from the current position to the previous position float2 velocity = uv - i.uv; half4 current = SMAASample(colorTex, i.uv); half4 previous = SMAASample(colorTex, uv); float delta = abs(current.a * current.a - previous.a * previous.a) / 5.0; float weight = .5 * SMAASaturate(1. - (sqrt(delta) * K)); // K ~= SMAA_REPROJECTION_WEIGHT_SCALE //return half4(.5 * (uv - i.uv) + .5, 0., 1.); return lerp(current, previous, weight); } half4 DX9_SMAADebugDepthPS(v2f i) : SV_Target { return SMAASampleDepth_Unity(i.uv, _CameraDepthTexture, i.uv); } // same as above, but also blend with accumulation buffer half4 fragAccumulateOffset(v2f i) : SV_Target { float3 prevUvWeight = FetchPreviousUvWeight(i.uv); float prevWeight = prevUvWeight.z; float2 prevUv = prevUvWeight.xy; half4 accum = tex2D (accumTex, prevUv); half4 color = tex2D (smaaTex, i.uv + _PixelOffset.xy); // get better accumulation { half4 clampAccum = ClampAccumulation(i.uv + _PixelOffset.xy,accum,color); half2 edgeData = tex2D (edgesTex, i.uv + SMAA_PIXEL_SIZE.xy*0.5f); float edgeVal = saturate(max(edgeData.x,edgeData.y)); accum = lerp(clampAccum,accum,edgeVal); } float t = _TemporalAccum; t = t + (1.0f-t)*(1.0f-prevWeight); half4 res = accum * (1.0f-t) + color * t; return res; } ENDCG Subshader { // 0 - just copy Pass { ZTest Always Cull Off ZWrite Off CGPROGRAM #pragma vertex vert #pragma fragment fragCopy ENDCG } // 1 - luma detection Pass { ZTest Always Cull Off ZWrite Off CGPROGRAM #pragma vertex vert #pragma fragment DX9_SMAALumaEdgeDetectionPS ENDCG } // 2 - just clear to black, necessary becuase luma detection will discard Pass { ZTest Always Cull Off ZWrite Off CGPROGRAM #pragma vertex vert #pragma fragment fragBlack ENDCG } // 3 - weight calculation Pass { ZTest Always Cull Off ZWrite Off CGPROGRAM #pragma vertex vert #pragma fragment DX9_SMAABlendingWeightCalculationPS ENDCG } // 4 - apply weights and blend Pass { ZTest Always Cull Off ZWrite Off CGPROGRAM #pragma vertex vert #pragma fragment DX9_SMAANeighborhoodBlendingPS ENDCG } // 5 - apply weights and blend Pass { ZTest Always Cull Off ZWrite Off CGPROGRAM #pragma vertex vert #pragma fragment DX9_SMAANeighborhoodBlendingAccumPS ENDCG } // 6 - color detection Pass { ZTest Always Cull Off ZWrite Off CGPROGRAM #pragma vertex vert #pragma fragment DX9_SMAAColorEdgeDetectionPS ENDCG } // 7 - merge previous frame with current frame (with offset) Pass { ZTest Always Cull Off ZWrite Off CGPROGRAM #pragma vertex vert #pragma fragment fragAccumulateOffset ENDCG } // 8 - depth detection Pass { ZTest Always Cull Off ZWrite Off CGPROGRAM #pragma vertex vert #pragma fragment DX9_SMAADepthEdgeDetectionPS ENDCG } // 9 - debug depth Pass { ZTest Always Cull Off ZWrite Off CGPROGRAM #pragma vertex vert #pragma fragment DX9_SMAADebugDepthPS ENDCG } } Fallback off } // shader