|
| 1 | +Shader "Hidden/EdgeDetection" |
| 2 | +{ |
| 3 | + HLSLINCLUDE |
| 4 | + #include "Packages/com.yetman.render-pipelines.universal.postprocessing/ShaderLibrary/Core.hlsl" |
| 5 | + // In URP 10, this should be replaced by the same file from URP's ShaderLibrary |
| 6 | + #include "Packages/com.yetman.render-pipelines.universal.postprocessing/ShaderLibrary/DeclareNormalsTexture.hlsl" |
| 7 | + #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl" |
| 8 | + |
| 9 | + TEXTURE2D_X(_MainTex); |
| 10 | + float4 _MainTex_TexelSize; |
| 11 | + |
| 12 | + // The blending factor for the edges with the original color |
| 13 | + float _intensity; |
| 14 | + // The threshold ranges for edge detection (xy used for normals, zw used for depth) |
| 15 | + float4 _threshold; |
| 16 | + // The thickness of the edge (determines how far the neighbour samples spread around the center sample) |
| 17 | + float _thickness; |
| 18 | + // The color of the edge |
| 19 | + float3 _color; |
| 20 | + |
| 21 | + // Curtom Scene normal and depth sampling function to read from a custom texture and sampler |
| 22 | + float3 SampleSceneNormals(float2 uv, TEXTURE2D_X_FLOAT(_Texture), SAMPLER(sampler_Texture)) |
| 23 | + { |
| 24 | + return UnpackNormalOctRectEncode(SAMPLE_TEXTURE2D_X(_Texture, sampler_Texture, UnityStereoTransformScreenSpaceTex(uv)).xy) * float3(1.0, 1.0, -1.0); |
| 25 | + } |
| 26 | + |
| 27 | + float SampleSceneDepth(float2 uv, TEXTURE2D_X_FLOAT(_Texture), SAMPLER(sampler_Texture)) |
| 28 | + { |
| 29 | + return SAMPLE_TEXTURE2D_X(_Texture, sampler_Texture, UnityStereoTransformScreenSpaceTex(uv)).r; |
| 30 | + } |
| 31 | + |
| 32 | + // A Bilinear sampler to allow for subpixel thickness for the edge |
| 33 | + SAMPLER(sampler_linear_clamp); |
| 34 | + |
| 35 | + // Sample normal & depth and combine them to a single 4D vector (xyz for normal, w for depth) |
| 36 | + float4 SampleSceneDepthNormal(float2 uv){ |
| 37 | + float depth = SampleSceneDepth(uv, _CameraDepthTexture, sampler_linear_clamp); |
| 38 | + float depthEye = LinearEyeDepth(depth, _ZBufferParams); |
| 39 | + float3 normal = SampleSceneNormals(uv, _CameraNormalsTexture, sampler_linear_clamp); |
| 40 | + return float4(normal, depthEye); |
| 41 | + } |
| 42 | + |
| 43 | + // Read the 8 surrounding samples and average them with perspective correction |
| 44 | + // The perspective correction helps when the surface normal is almost orthogonal to the view direction |
| 45 | + float4 SampleNeighborhood(float2 uv, float thickness){ |
| 46 | + // The surrounding pixel offsets |
| 47 | + const float2 offsets[8] = { |
| 48 | + float2(-1, -1), |
| 49 | + float2(-1, 0), |
| 50 | + float2(-1, 1), |
| 51 | + float2(0, -1), |
| 52 | + float2(0, 1), |
| 53 | + float2(1, -1), |
| 54 | + float2(1, 0), |
| 55 | + float2(1, 1) |
| 56 | + }; |
| 57 | + |
| 58 | + float2 delta = _MainTex_TexelSize.xy * thickness; |
| 59 | + float4 sum = 0; |
| 60 | + float weight = 0; |
| 61 | + for(int i=0; i<8; i++){ |
| 62 | + float4 sample = SampleSceneDepthNormal(uv + delta * offsets[i]); |
| 63 | + // The sum is weight by 1/depth for perspecive correction |
| 64 | + sum += sample / sample.w; |
| 65 | + weight += 1/sample.w; |
| 66 | + } |
| 67 | + sum /= weight; |
| 68 | + // Doesn't make a visible difference but it feels more correct |
| 69 | + // May remove it for performance benefits |
| 70 | + sum.xyz = normalize(sum.xyz); |
| 71 | + return sum; |
| 72 | + } |
| 73 | + |
| 74 | + float4 EdgeDetectionFragmentProgram (PostProcessVaryings input) : SV_Target |
| 75 | + { |
| 76 | + UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input); |
| 77 | + |
| 78 | + float2 uv = UnityStereoTransformScreenSpaceTex(input.texcoord); |
| 79 | + float4 color = LOAD_TEXTURE2D_X(_MainTex, uv * _ScreenParams.xy); |
| 80 | + |
| 81 | + float4 center = SampleSceneDepthNormal(uv); |
| 82 | + float4 neighborhood = SampleNeighborhood(uv, _thickness); |
| 83 | + // Normal similarity is calculated using a dot product |
| 84 | + float normalSame = smoothstep(_threshold.x, _threshold.y, dot(center.xyz, neighborhood.xyz)); |
| 85 | + // Depth similarity is calculated using absolute difference |
| 86 | + float depthSame = smoothstep(_threshold.w, _threshold.z, abs(center.w - neighborhood.w)); |
| 87 | + // Combine normal and depth sameness to get edge factor |
| 88 | + float edge = 1 - normalSame * depthSame; |
| 89 | + |
| 90 | + color.rgb = lerp(color.rgb, _color, edge * _intensity); |
| 91 | + return color; |
| 92 | + } |
| 93 | + ENDHLSL |
| 94 | + |
| 95 | + SubShader |
| 96 | + { |
| 97 | + Cull Off ZWrite Off ZTest Always |
| 98 | + Pass |
| 99 | + { |
| 100 | + HLSLPROGRAM |
| 101 | + #pragma vertex FullScreenTrianglePostProcessVertexProgram |
| 102 | + #pragma fragment EdgeDetectionFragmentProgram |
| 103 | + ENDHLSL |
| 104 | + } |
| 105 | + } |
| 106 | + Fallback Off |
| 107 | +} |
0 commit comments