Skip to content

Instantly share code, notes, and snippets.

@MattRix
Created December 12, 2015 19:22
Show Gist options
  • Save MattRix/3ea93b6cf3e3402eb2bb to your computer and use it in GitHub Desktop.
Save MattRix/3ea93b6cf3e3402eb2bb to your computer and use it in GitHub Desktop.

Revisions

  1. MattRix created this gist Dec 12, 2015.
    325 changes: 325 additions & 0 deletions EdgeDetectNormals.shader
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,325 @@

    Shader "Hidden/EdgeDetect" {
    Properties {
    _MainTex ("Base (RGB)", 2D) = "" {}
    }

    CGINCLUDE

    #include "UnityCG.cginc"

    struct v2f {
    float4 pos : SV_POSITION;
    float2 uv[5] : TEXCOORD0;
    };

    struct v2fd {
    float4 pos : SV_POSITION;
    float2 uv[2] : TEXCOORD0;
    };

    sampler2D _MainTex;
    uniform float4 _MainTex_TexelSize;

    sampler2D _CameraDepthNormalsTexture;
    sampler2D_float _CameraDepthTexture;

    uniform half4 _Sensitivity;
    uniform half4 _BgColor;
    uniform half _BgFade;
    uniform half _SampleDistance;
    uniform float _Exponent;

    uniform float _Threshold;

    struct v2flum {
    float4 pos : SV_POSITION;
    float2 uv[3] : TEXCOORD0;
    };

    v2flum vertLum (appdata_img v)
    {
    v2flum o;
    o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
    float2 uv = MultiplyUV( UNITY_MATRIX_TEXTURE0, v.texcoord );
    o.uv[0] = uv;
    o.uv[1] = uv + float2(-_MainTex_TexelSize.x, -_MainTex_TexelSize.y) * _SampleDistance;
    o.uv[2] = uv + float2(+_MainTex_TexelSize.x, -_MainTex_TexelSize.y) * _SampleDistance;
    return o;
    }


    fixed4 fragLum (v2flum i) : SV_Target
    {
    fixed4 original = tex2D(_MainTex, i.uv[0]);

    // a very simple cross gradient filter

    half3 p1 = original.rgb;
    half3 p2 = tex2D(_MainTex, i.uv[1]).rgb;
    half3 p3 = tex2D(_MainTex, i.uv[2]).rgb;

    half3 diff = p1 * 2 - p2 - p3;
    half len = dot(diff, diff);
    len = step(len, _Threshold);
    //if(len >= _Threshold)
    // original.rgb = 0;

    return len * lerp(original, _BgColor, _BgFade);
    }

    inline half CheckSame (half2 centerNormal, float centerDepth, half4 theSample)
    {
    // difference in normals
    // do not bother decoding normals - there's no need here
    half2 diff = abs(centerNormal - theSample.xy) * _Sensitivity.y;
    half isSameNormal = (diff.x + diff.y) * _Sensitivity.y < 0.1;
    // difference in depth
    float sampleDepth = DecodeFloatRG (theSample.zw);
    float zdiff = abs(centerDepth-sampleDepth);
    // scale the required threshold by the distance
    half isSameDepth = zdiff * _Sensitivity.x < 0.09 * centerDepth;

    // return:
    // 1 - if normals and depth are similar enough
    // 0 - otherwise

    return isSameNormal * isSameDepth;
    }

    v2f vertRobert( appdata_img v )
    {
    v2f o;
    o.pos = mul(UNITY_MATRIX_MVP, v.vertex);

    float2 uv = v.texcoord.xy;
    o.uv[0] = uv;

    #if UNITY_UV_STARTS_AT_TOP
    if (_MainTex_TexelSize.y < 0)
    uv.y = 1-uv.y;
    #endif

    // calc coord for the X pattern
    // maybe nicer TODO for the future: 'rotated triangles'

    o.uv[1] = uv + _MainTex_TexelSize.xy * half2(1,1) * _SampleDistance;
    o.uv[2] = uv + _MainTex_TexelSize.xy * half2(-1,-1) * _SampleDistance;
    o.uv[3] = uv + _MainTex_TexelSize.xy * half2(-1,1) * _SampleDistance;
    o.uv[4] = uv + _MainTex_TexelSize.xy * half2(1,-1) * _SampleDistance;

    return o;
    }

    v2f vertThin( appdata_img v )
    {
    v2f o;
    o.pos = mul (UNITY_MATRIX_MVP, v.vertex);

    float2 uv = v.texcoord.xy;
    o.uv[0] = uv;

    #if UNITY_UV_STARTS_AT_TOP
    if (_MainTex_TexelSize.y < 0)
    uv.y = 1-uv.y;
    #endif

    o.uv[1] = uv;
    o.uv[4] = uv;

    // offsets for two additional samples
    o.uv[2] = uv + float2(-_MainTex_TexelSize.x, -_MainTex_TexelSize.y) * _SampleDistance;
    o.uv[3] = uv + float2(+_MainTex_TexelSize.x, -_MainTex_TexelSize.y) * _SampleDistance;

    return o;
    }

    v2fd vertD( appdata_img v )
    {
    v2fd o;
    o.pos = mul (UNITY_MATRIX_MVP, v.vertex);

    float2 uv = v.texcoord.xy;
    o.uv[0] = uv;

    #if UNITY_UV_STARTS_AT_TOP
    if (_MainTex_TexelSize.y < 0)
    uv.y = 1-uv.y;
    #endif

    o.uv[1] = uv;

    return o;
    }

    float4 fragDCheap(v2fd i) : SV_Target
    {
    // inspired by borderlands implementation of popular "sobel filter"

    float centerDepth = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv[1]));
    float4 depthsDiag;
    float4 depthsAxis;

    float2 uvDist = _SampleDistance * _MainTex_TexelSize.xy;

    depthsDiag.x = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv[1]+uvDist)); // TR
    depthsDiag.y = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv[1]+uvDist*float2(-1,1))); // TL
    depthsDiag.z = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv[1]-uvDist*float2(-1,1))); // BR
    depthsDiag.w = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv[1]-uvDist)); // BL

    depthsAxis.x = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv[1]+uvDist*float2(0,1))); // T
    depthsAxis.y = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv[1]-uvDist*float2(1,0))); // L
    depthsAxis.z = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv[1]+uvDist*float2(1,0))); // R
    depthsAxis.w = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv[1]-uvDist*float2(0,1))); // B

    depthsDiag -= centerDepth;
    depthsAxis /= centerDepth;

    const float4 HorizDiagCoeff = float4(1,1,-1,-1);
    const float4 VertDiagCoeff = float4(-1,1,-1,1);
    const float4 HorizAxisCoeff = float4(1,0,0,-1);
    const float4 VertAxisCoeff = float4(0,1,-1,0);

    float4 SobelH = depthsDiag * HorizDiagCoeff + depthsAxis * HorizAxisCoeff;
    float4 SobelV = depthsDiag * VertDiagCoeff + depthsAxis * VertAxisCoeff;

    float SobelX = dot(SobelH, float4(1,1,1,1));
    float SobelY = dot(SobelV, float4(1,1,1,1));
    float Sobel = sqrt(SobelX * SobelX + SobelY * SobelY);

    Sobel = 1.0-pow(saturate(Sobel), _Exponent);
    return Sobel * lerp(tex2D(_MainTex, i.uv[0].xy), _BgColor, _BgFade);
    }

    // pretty much also just a sobel filter, except for that edges "outside" the silhouette get discarded
    // which makes it compatible with other depth based post fx

    float4 fragD(v2fd i) : SV_Target
    {
    // inspired by borderlands implementation of popular "sobel filter"

    float centerDepth = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv[1]));
    float4 depthsDiag;
    float4 depthsAxis;

    float2 uvDist = _SampleDistance * _MainTex_TexelSize.xy;

    depthsDiag.x = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv[1]+uvDist)); // TR
    depthsDiag.y = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv[1]+uvDist*float2(-1,1))); // TL
    depthsDiag.z = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv[1]-uvDist*float2(-1,1))); // BR
    depthsDiag.w = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv[1]-uvDist)); // BL

    depthsAxis.x = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv[1]+uvDist*float2(0,1))); // T
    depthsAxis.y = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv[1]-uvDist*float2(1,0))); // L
    depthsAxis.z = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv[1]+uvDist*float2(1,0))); // R
    depthsAxis.w = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv[1]-uvDist*float2(0,1))); // B

    // make it work nicely with depth based image effects such as depth of field:
    depthsDiag = (depthsDiag > centerDepth.xxxx) ? depthsDiag : centerDepth.xxxx;
    depthsAxis = (depthsAxis > centerDepth.xxxx) ? depthsAxis : centerDepth.xxxx;

    depthsDiag -= centerDepth;
    depthsAxis /= centerDepth;

    const float4 HorizDiagCoeff = float4(1,1,-1,-1);
    const float4 VertDiagCoeff = float4(-1,1,-1,1);
    const float4 HorizAxisCoeff = float4(1,0,0,-1);
    const float4 VertAxisCoeff = float4(0,1,-1,0);

    float4 SobelH = depthsDiag * HorizDiagCoeff + depthsAxis * HorizAxisCoeff;
    float4 SobelV = depthsDiag * VertDiagCoeff + depthsAxis * VertAxisCoeff;

    float SobelX = dot(SobelH, float4(1,1,1,1));
    float SobelY = dot(SobelV, float4(1,1,1,1));
    float Sobel = sqrt(SobelX * SobelX + SobelY * SobelY);

    Sobel = 1.0-pow(saturate(Sobel), _Exponent);
    return Sobel * lerp(tex2D(_MainTex, i.uv[0].xy), _BgColor, _BgFade);
    }

    half4 fragRobert(v2f i) : SV_Target {
    half4 sample1 = tex2D(_CameraDepthNormalsTexture, i.uv[1].xy);
    half4 sample2 = tex2D(_CameraDepthNormalsTexture, i.uv[2].xy);
    half4 sample3 = tex2D(_CameraDepthNormalsTexture, i.uv[3].xy);
    half4 sample4 = tex2D(_CameraDepthNormalsTexture, i.uv[4].xy);

    half edge = 1.0;

    edge *= CheckSame(sample1.xy, DecodeFloatRG(sample1.zw), sample2);
    edge *= CheckSame(sample3.xy, DecodeFloatRG(sample3.zw), sample4);

    return edge * lerp(tex2D(_MainTex, i.uv[0]), _BgColor, _BgFade);
    }

    half4 fragThin (v2f i) : SV_Target
    {
    half4 original = tex2D(_MainTex, i.uv[0]);

    half4 center = tex2D (_CameraDepthNormalsTexture, i.uv[1]);
    half4 sample1 = tex2D (_CameraDepthNormalsTexture, i.uv[2]);
    half4 sample2 = tex2D (_CameraDepthNormalsTexture, i.uv[3]);

    // encoded normal
    half2 centerNormal = center.xy;
    // decoded depth
    float centerDepth = DecodeFloatRG (center.zw);

    half edge = 1.0;

    edge *= CheckSame(centerNormal, centerDepth, sample1);
    edge *= CheckSame(centerNormal, centerDepth, sample2);

    return edge * lerp(original, _BgColor, _BgFade);
    }

    ENDCG

    Subshader {
    Pass {
    ZTest Always Cull Off ZWrite Off

    CGPROGRAM
    #pragma vertex vertThin
    #pragma fragment fragThin
    ENDCG
    }
    Pass {
    ZTest Always Cull Off ZWrite Off

    CGPROGRAM
    #pragma vertex vertRobert
    #pragma fragment fragRobert
    ENDCG
    }
    Pass {
    ZTest Always Cull Off ZWrite Off

    CGPROGRAM
    #pragma target 3.0
    #pragma vertex vertD
    #pragma fragment fragDCheap
    ENDCG
    }
    Pass {
    ZTest Always Cull Off ZWrite Off

    CGPROGRAM
    #pragma target 3.0
    #pragma vertex vertD
    #pragma fragment fragD
    ENDCG
    }
    Pass {
    ZTest Always Cull Off ZWrite Off

    CGPROGRAM
    #pragma target 3.0
    #pragma vertex vertLum
    #pragma fragment fragLum
    ENDCG
    }
    }

    Fallback off

    } // shader