1
2 Shader "Hidden/EdgeDetect" {
3 Properties {
4 _MainTex ("Base (RGB)", 2D) = "" {}
5 }
6
7 CGINCLUDE
8
9 #include "UnityCG.cginc"
10
11 struct v2f {
12 float4 pos : SV_POSITION;
13 float2 uv[5] : TEXCOORD0;
14 };
15
16 struct v2fd {
17 float4 pos : SV_POSITION;
18 float2 uv[2] : TEXCOORD0;
19 };
20
21 sampler2D _MainTex;
22 uniform float4 _MainTex_TexelSize;
23
24 sampler2D _CameraDepthNormalsTexture;
25 sampler2D_float _CameraDepthTexture;
26
27 uniform half4 _Sensitivity;
28 uniform half4 _BgColor;
29 uniform half _BgFade;
30 uniform half _SampleDistance;
31 uniform float _Exponent;
32
33 uniform float _Threshold;
34
35 struct v2flum {
36 float4 pos : SV_POSITION;
37 float2 uv[3] : TEXCOORD0;
38 };
39
40 v2flum vertLum (appdata_img v)
41 {
42 v2flum o;
43 o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
44 float2 uv = MultiplyUV( UNITY_MATRIX_TEXTURE0, v.texcoord );
45 o.uv[0] = uv;
46 o.uv[1] = uv + float2(-_MainTex_TexelSize.x, -_MainTex_TexelSize.y) * _SampleDistance;
47 o.uv[2] = uv + float2(+_MainTex_TexelSize.x, -_MainTex_TexelSize.y) * _SampleDistance;
48 return o;
49 }
50
51
52 fixed4 fragLum (v2flum i) : SV_Target
53 {
54 fixed4 original = tex2D(_MainTex, i.uv[0]);
55
56 // a very simple cross gradient filter
57
58 half3 p1 = original.rgb;
59 half3 p2 = tex2D(_MainTex, i.uv[1]).rgb;
60 half3 p3 = tex2D(_MainTex, i.uv[2]).rgb;
61
62 half3 diff = p1 * 2 - p2 - p3;
63 half len = dot(diff, diff);
64 len = step(len, _Threshold);
65 //if(len >= _Threshold)
66 // original.rgb = 0;
67
68 return len * lerp(original, _BgColor, _BgFade);
69 }
70
71 inline half CheckSame (half2 centerNormal, float centerDepth, half4 theSample)
72 {
73 // difference in normals
74 // do not bother decoding normals - there's no need here
75 half2 diff = abs(centerNormal - theSample.xy) * _Sensitivity.y;
76 half isSameNormal = (diff.x + diff.y) * _Sensitivity.y < 0.1;
77 // difference in depth
78 float sampleDepth = DecodeFloatRG (theSample.zw);
79 float zdiff = abs(centerDepth-sampleDepth);
80 // scale the required threshold by the distance
81 half isSameDepth = zdiff * _Sensitivity.x < 0.09 * centerDepth;
82
83 // return:
84 // 1 - if normals and depth are similar enough
85 // 0 - otherwise
86
87 return isSameNormal * isSameDepth;
88 }
89
90 v2f vertRobert( appdata_img v )
91 {
92 v2f o;
93 o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
94
95 float2 uv = v.texcoord.xy;
96 o.uv[0] = uv;
97
98 #if UNITY_UV_STARTS_AT_TOP
99 if (_MainTex_TexelSize.y < 0)
100 uv.y = 1-uv.y;
101 #endif
102
103 // calc coord for the X pattern
104 // maybe nicer TODO for the future: 'rotated triangles'
105
106 o.uv[1] = uv + _MainTex_TexelSize.xy * half2(1,1) * _SampleDistance;
107 o.uv[2] = uv + _MainTex_TexelSize.xy * half2(-1,-1) * _SampleDistance;
108 o.uv[3] = uv + _MainTex_TexelSize.xy * half2(-1,1) * _SampleDistance;
109 o.uv[4] = uv + _MainTex_TexelSize.xy * half2(1,-1) * _SampleDistance;
110
111 return o;
112 }
113
114 v2f vertThin( appdata_img v )
115 {
116 v2f o;
117 o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
118
119 float2 uv = v.texcoord.xy;
120 o.uv[0] = uv;
121
122 #if UNITY_UV_STARTS_AT_TOP
123 if (_MainTex_TexelSize.y < 0)
124 uv.y = 1-uv.y;
125 #endif
126
127 o.uv[1] = uv;
128 o.uv[4] = uv;
129
130 // offsets for two additional samples
131 o.uv[2] = uv + float2(-_MainTex_TexelSize.x, -_MainTex_TexelSize.y) * _SampleDistance;
132 o.uv[3] = uv + float2(+_MainTex_TexelSize.x, -_MainTex_TexelSize.y) * _SampleDistance;
133
134 return o;
135 }
136
137 v2fd vertD( appdata_img v )
138 {
139 v2fd o;
140 o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
141
142 float2 uv = v.texcoord.xy;
143 o.uv[0] = uv;
144
145 #if UNITY_UV_STARTS_AT_TOP
146 if (_MainTex_TexelSize.y < 0)
147 uv.y = 1-uv.y;
148 #endif
149
150 o.uv[1] = uv;
151
152 return o;
153 }
154
155 float4 fragDCheap(v2fd i) : SV_Target
156 {
157 // inspired by borderlands implementation of popular "sobel filter"
158
159 float centerDepth = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv[1]));
160 float4 depthsDiag;
161 float4 depthsAxis;
162
163 float2 uvDist = _SampleDistance * _MainTex_TexelSize.xy;
164
165 depthsDiag.x = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv[1]+uvDist)); // TR
166 depthsDiag.y = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv[1]+uvDist*float2(-1,1))); // TL
167 depthsDiag.z = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv[1]-uvDist*float2(-1,1))); // BR
168 depthsDiag.w = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv[1]-uvDist)); // BL
169
170 depthsAxis.x = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv[1]+uvDist*float2(0,1))); // T
171 depthsAxis.y = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv[1]-uvDist*float2(1,0))); // L
172 depthsAxis.z = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv[1]+uvDist*float2(1,0))); // R
173 depthsAxis.w = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv[1]-uvDist*float2(0,1))); // B
174
175 depthsDiag -= centerDepth;
176 depthsAxis /= centerDepth;
177
178 const float4 HorizDiagCoeff = float4(1,1,-1,-1);
179 const float4 VertDiagCoeff = float4(-1,1,-1,1);
180 const float4 HorizAxisCoeff = float4(1,0,0,-1);
181 const float4 VertAxisCoeff = float4(0,1,-1,0);
182
183 float4 SobelH = depthsDiag * HorizDiagCoeff + depthsAxis * HorizAxisCoeff;
184 float4 SobelV = depthsDiag * VertDiagCoeff + depthsAxis * VertAxisCoeff;
185
186 float SobelX = dot(SobelH, float4(1,1,1,1));
187 float SobelY = dot(SobelV, float4(1,1,1,1));
188 float Sobel = sqrt(SobelX * SobelX + SobelY * SobelY);
189
190 Sobel = 1.0-pow(saturate(Sobel), _Exponent);
191 return Sobel * lerp(tex2D(_MainTex, i.uv[0].xy), _BgColor, _BgFade);
192 }
193
194 // pretty much also just a sobel filter, except for that edges "outside" the silhouette get discarded
195 // which makes it compatible with other depth based post fx
196
197 float4 fragD(v2fd i) : SV_Target
198 {
199 // inspired by borderlands implementation of popular "sobel filter"
200
201 float centerDepth = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv[1]));
202 float4 depthsDiag;
203 float4 depthsAxis;
204
205 float2 uvDist = _SampleDistance * _MainTex_TexelSize.xy;
206
207 depthsDiag.x = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv[1]+uvDist)); // TR
208 depthsDiag.y = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv[1]+uvDist*float2(-1,1))); // TL
209 depthsDiag.z = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv[1]-uvDist*float2(-1,1))); // BR
210 depthsDiag.w = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv[1]-uvDist)); // BL
211
212 depthsAxis.x = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv[1]+uvDist*float2(0,1))); // T
213 depthsAxis.y = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv[1]-uvDist*float2(1,0))); // L
214 depthsAxis.z = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv[1]+uvDist*float2(1,0))); // R
215 depthsAxis.w = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv[1]-uvDist*float2(0,1))); // B
216
217 // make it work nicely with depth based image effects such as depth of field:
218 depthsDiag = (depthsDiag > centerDepth.xxxx) ? depthsDiag : centerDepth.xxxx;
219 depthsAxis = (depthsAxis > centerDepth.xxxx) ? depthsAxis : centerDepth.xxxx;
220
221 depthsDiag -= centerDepth;
222 depthsAxis /= centerDepth;
223
224 const float4 HorizDiagCoeff = float4(1,1,-1,-1);
225 const float4 VertDiagCoeff = float4(-1,1,-1,1);
226 const float4 HorizAxisCoeff = float4(1,0,0,-1);
227 const float4 VertAxisCoeff = float4(0,1,-1,0);
228
229 float4 SobelH = depthsDiag * HorizDiagCoeff + depthsAxis * HorizAxisCoeff;
230 float4 SobelV = depthsDiag * VertDiagCoeff + depthsAxis * VertAxisCoeff;
231
232 float SobelX = dot(SobelH, float4(1,1,1,1));
233 float SobelY = dot(SobelV, float4(1,1,1,1));
234 float Sobel = sqrt(SobelX * SobelX + SobelY * SobelY);
235
236 Sobel = 1.0-pow(saturate(Sobel), _Exponent);
237 return Sobel * lerp(tex2D(_MainTex, i.uv[0].xy), _BgColor, _BgFade);
238 }
239
240 half4 fragRobert(v2f i) : SV_Target {
241 half4 sample1 = tex2D(_CameraDepthNormalsTexture, i.uv[1].xy);
242 half4 sample2 = tex2D(_CameraDepthNormalsTexture, i.uv[2].xy);
243 half4 sample3 = tex2D(_CameraDepthNormalsTexture, i.uv[3].xy);
244 half4 sample4 = tex2D(_CameraDepthNormalsTexture, i.uv[4].xy);
245
246 half edge = 1.0;
247
248 edge *= CheckSame(sample1.xy, DecodeFloatRG(sample1.zw), sample2);
249 edge *= CheckSame(sample3.xy, DecodeFloatRG(sample3.zw), sample4);
250
251 return edge * lerp(tex2D(_MainTex, i.uv[0]), _BgColor, _BgFade);
252 }
253
254 half4 fragThin (v2f i) : SV_Target
255 {
256 half4 original = tex2D(_MainTex, i.uv[0]);
257
258 half4 center = tex2D (_CameraDepthNormalsTexture, i.uv[1]);
259 half4 sample1 = tex2D (_CameraDepthNormalsTexture, i.uv[2]);
260 half4 sample2 = tex2D (_CameraDepthNormalsTexture, i.uv[3]);
261
262 // encoded normal
263 half2 centerNormal = center.xy;
264 // decoded depth
265 float centerDepth = DecodeFloatRG (center.zw);
266
267 half edge = 1.0;
268
269 edge *= CheckSame(centerNormal, centerDepth, sample1);
270 edge *= CheckSame(centerNormal, centerDepth, sample2);
271
272 return edge * lerp(original, _BgColor, _BgFade);
273 }
274
275 ENDCG
276
277 Subshader {
278 Pass {
279 ZTest Always Cull Off ZWrite Off
280
281 CGPROGRAM
282 #pragma vertex vertThin
283 #pragma fragment fragThin
284 ENDCG
285 }
286 Pass {
287 ZTest Always Cull Off ZWrite Off
288
289 CGPROGRAM
290 #pragma vertex vertRobert
291 #pragma fragment fragRobert
292 ENDCG
293 }
294 Pass {
295 ZTest Always Cull Off ZWrite Off
296
297 CGPROGRAM
298 #pragma target 3.0
299 #pragma vertex vertD
300 #pragma fragment fragDCheap
301 ENDCG
302 }
303 Pass {
304 ZTest Always Cull Off ZWrite Off
305
306 CGPROGRAM
307 #pragma target 3.0
308 #pragma vertex vertD
309 #pragma fragment fragD
310 ENDCG
311 }
312 Pass {
313 ZTest Always Cull Off ZWrite Off
314
315 CGPROGRAM
316 #pragma target 3.0
317 #pragma vertex vertLum
318 #pragma fragment fragLum
319 ENDCG
320 }
321 }
322
323 Fallback off
324
325 } // shader