Search Unity

Convert Custom Shader To URP?

Discussion in 'Universal Render Pipeline' started by SameDev, Dec 1, 2020.

  1. SameDev

    SameDev

    Joined:
    Apr 14, 2013
    Posts:
    18
    Hey guys. I have been working on a shader here which is supposed to apply a trilinear filtering effect to the texture like the nintendo 64. Its a really nice shader for my needs and I cant part with its functionality, how would I go about translating it from the old language to the new one? On another note, would this kind of effect be better off as its own custom render pass?

    Code (CSharp):
    1. Shader "Custom/Trilinear64"
    2. {
    3.     Properties
    4.     {
    5.         _Color("Color", Color) = (1,1,1,1)
    6.         _MainTex("Albedo (RGB)", 2D) = "white" {}
    7.     [Range(0,1)]
    8.         _Cutoff("Alpha Cutoff", Range(0,1)) = 0.5
    9.     [Range(0,1)]
    10.         _Glossiness("Smoothness", Range(0,1)) = 0.5
    11.     [Range(0,1)]
    12.         _Metallic("Metallic", Range(0,1)) = 0.0
    13.     }
    14.         SubShader
    15.     {
    16.         Tags { "RenderType" = "Opaque" }
    17.         LOD 200
    18.         CGPROGRAM
    19.         #pragma surface surf Standard alphatest:_Cutoff fullforwardshadows
    20.         sampler2D _MainTex;
    21.         float4 _MainTex_TexelSize;
    22.         struct Input
    23.         {
    24.             float2 uv_MainTex;
    25.             float4 vertexColor : COLOR;
    26.         };
    27.         struct v2f {
    28.             float4 pos : SV_POSITION;
    29.             fixed4 color : COLOR;
    30.         };
    31.         fixed4 N64Filtering(sampler2D tex, float2 uv, float4 scale)
    32.         {
    33.             //texel coords
    34.             float2 texel = uv * scale.zw;
    35.             //get mip map coords and scaling
    36.             float2 mipX = ddx(texel), mipY = ddy(texel);
    37.             float delta_max_sqr = max(dot(mipX, mipX), dot(mipY, mipY));
    38.             float mip = max(0.0, 0.5 * log2(delta_max_sqr));
    39.          
    40.             float size = pow(2, floor(mip));
    41.             scale.xy *= size;
    42.             scale.zw /= size;
    43.             texel = texel / size - 0.5;
    44.             //sample points
    45.             float2 fracTexl = frac(texel);
    46.             float2 uv1 = (floor(texel + fracTexl.yx) + 0.5) * scale.xy;
    47.             fixed4 out1 = tex2Dlod(tex, float4(uv1, 0, mip));
    48.             float2 uv2 = (floor(texel) + float2(1.5, 0.5)) * scale.xy;
    49.             fixed4 out2 = tex2Dlod(tex, float4(uv2, 0, mip));
    50.             float2 uv3 = (floor(texel) + float2(0.5, 1.5)) * scale.xy;
    51.             fixed4 out3 = tex2Dlod(tex, float4(uv3, 0, mip));
    52.             //calculate blend and apply
    53.             float3 blend = float3(abs(fracTexl.x + fracTexl.y - 1), min(abs(fracTexl.xx - float2(0, 1)), abs(fracTexl.yy - float2(1, 0))));
    54.             float4 _outTex = out1 * blend.x + out2 * blend.y + out3 * blend.z;
    55.             // blend and return
    56.             return _outTex;
    57.         }
    58.         half _Glossiness;
    59.         half _Metallic;
    60.         fixed4 _Color;
    61.         void surf(Input IN, inout SurfaceOutputStandard o)
    62.         {
    63.             fixed4 c = N64Filtering(_MainTex, IN.uv_MainTex, _MainTex_TexelSize) * _Color * IN.vertexColor;;
    64.             o.Albedo = c.rgb * IN.vertexColor; // Combine normal color with the vertex color
    65.             // Metallic and smoothness come from slider variables
    66.              o.Metallic = _Metallic;
    67.              o.Smoothness = _Glossiness;
    68.              o.Alpha = c.a;
    69.         }
    70.         ENDCG
    71.     }
    72.         FallBack "Diffuse"
    73. }
     
  2. weiping-toh

    weiping-toh

    Joined:
    Sep 8, 2015
    Posts:
    192
    Looks easy to translate.
    First, try rewriting the surface shader to a vertex-fragment shader.
    Instead of CG, we should now be using HLSL, so CGPROGRAM -> HLSLPROGRAM
    Include the URP ShaderLibrary's Core.hlsl.
    Take reference/Copy-paste from the UnlitShader for the vert inputs and outputs.
    Throw all your material property-declared variables into a CBUFFER called UnityPerMaterial

    And you are almost done.
    If you wish to add light and shadow, do it in the vertex function.

    Sidenote: your N64Filtering code appears applicable for fragment shader too.
     
  3. SameDev

    SameDev

    Joined:
    Apr 14, 2013
    Posts:
    18
    Im doing everything youre saying however am a bit lost still. Should I be adding this as a seperate shader pass or should I blend my functions into the vert and frag inputs in the existing shader pass? I have this so far. Sorry!
    Code (CSharp):
    1. Shader "Custom/Trilinear64"
    2. {
    3.     Properties
    4.     {
    5.         [MainTexture] _BaseMap("Texture", 2D) = "white" {}
    6.         [MainColor]   _BaseColor("Color", Color) = (1, 1, 1, 1)
    7.         _Cutoff("AlphaCutout", Range(0.0, 1.0)) = 0.5
    8.  
    9.             // BlendMode
    10.             [HideInInspector] _Surface("__surface", Float) = 0.0
    11.             [HideInInspector] _Blend("__blend", Float) = 0.0
    12.             [HideInInspector] _AlphaClip("__clip", Float) = 0.0
    13.             [HideInInspector] _SrcBlend("Src", Float) = 1.0
    14.             [HideInInspector] _DstBlend("Dst", Float) = 0.0
    15.             [HideInInspector] _ZWrite("ZWrite", Float) = 1.0
    16.             [HideInInspector] _Cull("__cull", Float) = 2.0
    17.  
    18.             // Editmode props
    19.             [HideInInspector] _QueueOffset("Queue offset", Float) = 0.0
    20.  
    21.             // ObsoleteProperties
    22.             [HideInInspector] _MainTex("BaseMap", 2D) = "white" {}
    23.             [HideInInspector] _Color("Base Color", Color) = (0.5, 0.5, 0.5, 1)
    24.             [HideInInspector] _SampleGI("SampleGI", float) = 0.0 // needed from bakedlit
    25.     }
    26.         SubShader
    27.             {
    28.                 Tags { "RenderType" = "Opaque" "IgnoreProjector" = "True" "RenderPipeline" = "UniversalPipeline" }
    29.                 LOD 100
    30.  
    31.                 Blend[_SrcBlend][_DstBlend]
    32.                 ZWrite[_ZWrite]
    33.                 Cull[_Cull]
    34.  
    35.                 Pass
    36.                 {
    37.                     Name "Unlit"
    38.                     HLSLPROGRAM
    39.                 // Required to compile gles 2.0 with standard srp library
    40.                 #pragma prefer_hlslcc gles
    41.                 #pragma exclude_renderers d3d11_9x
    42.  
    43.                 #pragma vertex vert
    44.                 #pragma fragment frag
    45.                 #pragma shader_feature _ALPHATEST_ON
    46.                 #pragma shader_feature _ALPHAPREMULTIPLY_ON
    47.  
    48.                 // -------------------------------------
    49.                 // Unity defined keywords
    50.                 #pragma multi_compile_fog
    51.                 #pragma multi_compile_instancing
    52.  
    53.                 #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
    54.  
    55.                 CBUFFER_START(UnityPerMaterial)
    56.  
    57.         sampler2D _MainTex;
    58.         float4 _MainTex_TexelSize;
    59. CBUFFER_END
    60.  
    61.                 struct Attributes
    62.                 {
    63.                     float4 positionOS       : POSITION;
    64.                     float2 uv               : TEXCOORD0;
    65.  
    66.                     float2 uv_MainTex;
    67.                     float4 vertexColor : COLOR;
    68.                     UNITY_VERTEX_INPUT_INSTANCE_ID
    69.                 };
    70.  
    71.                 struct Varyings
    72.                 {
    73.                     float2 uv        : TEXCOORD0;
    74.                     float fogCoord : TEXCOORD1;
    75.                     float4 vertex : SV_POSITION;
    76.                     fixed4 color : COLOR;
    77.  
    78.                     UNITY_VERTEX_INPUT_INSTANCE_ID
    79.                     UNITY_VERTEX_OUTPUT_STEREO
    80.                 };
    81.  
    82.                 Varyings vert(Attributes input)
    83.                 {
    84.                     Varyings output = (Varyings)0;
    85.  
    86.                     UNITY_SETUP_INSTANCE_ID(input);
    87.                     UNITY_TRANSFER_INSTANCE_ID(input, output);
    88.                     UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
    89.  
    90.                     VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz);
    91.                     output.vertex = vertexInput.positionCS;
    92.                     output.uv = TRANSFORM_TEX(input.uv, _BaseMap);
    93.                     output.fogCoord = ComputeFogFactor(vertexInput.positionCS.z);
    94.  
    95.                     return output;
    96.                 }
    97.  
    98.                 half4 frag(Varyings input) : SV_Target
    99.                 {
    100.                     UNITY_SETUP_INSTANCE_ID(input);
    101.                     UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
    102.  
    103.                     half2 uv = input.uv;
    104.                     half4 texColor = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, uv);
    105.                     half3 color = texColor.rgb * _BaseColor.rgb;
    106.                     half alpha = texColor.a * _BaseColor.a;
    107.                     AlphaDiscard(alpha, _Cutoff);
    108.  
    109.     #ifdef _ALPHAPREMULTIPLY_ON
    110.                     color *= alpha;
    111.     #endif
    112.  
    113.                     color = MixFog(color, input.fogCoord);
    114.                     alpha = OutputAlpha(alpha);
    115.  
    116.                     return half4(color, alpha);
    117.                 }
    118.                 ENDHLSL
    119.             }
    120.             Pass
    121.             {
    122.                 Tags{"LightMode" = "DepthOnly"}
    123.  
    124.                 ZWrite On
    125.                 ColorMask 0
    126.  
    127.                 HLSLPROGRAM
    128.                     // Required to compile gles 2.0 with standard srp library
    129.                     #pragma prefer_hlslcc gles
    130.                     #pragma exclude_renderers d3d11_9x
    131.                     #pragma target 2.0
    132.  
    133.                     #pragma vertex DepthOnlyVertex
    134.                     #pragma fragment DepthOnlyFragment
    135.  
    136.                     // -------------------------------------
    137.                     // Material Keywords
    138.                     #pragma shader_feature _ALPHATEST_ON
    139.  
    140.                     //--------------------------------------
    141.                     // GPU Instancing
    142.                     #pragma multi_compile_instancing
    143.  
    144.                     #include "Packages/com.unity.render-pipelines.universal/Shaders/UnlitInput.hlsl"
    145.                     #include "Packages/com.unity.render-pipelines.universal/Shaders/DepthOnlyPass.hlsl"
    146.                     ENDHLSL
    147.                 }
    148.  
    149.                     // This pass it not used during regular rendering, only for lightmap baking.
    150.                     Pass
    151.                     {
    152.                         Name "Meta"
    153.                         Tags{"LightMode" = "Meta"}
    154.  
    155.                         Cull Off
    156.  
    157.                         HLSLPROGRAM
    158.                     // Required to compile gles 2.0 with standard srp library
    159.                     #pragma prefer_hlslcc gles
    160.                     #pragma exclude_renderers d3d11_9x
    161.                     #pragma vertex UniversalVertexMeta
    162.                     #pragma fragment UniversalFragmentMetaUnlit
    163.  
    164.                     #include "Packages/com.unity.render-pipelines.universal/Shaders/UnlitInput.hlsl"
    165.                     #include "Packages/com.unity.render-pipelines.universal/Shaders/UnlitMetaPass.hlsl"
    166.  
    167.                     ENDHLSL
    168.                 }
    169.             }
    170.                 FallBack "Hidden/Universal Render Pipeline/FallbackError"
    171.                     CustomEditor "UnityEditor.Rendering.Universal.ShaderGUI.UnlitShader"
    172. }
    173.  
     
  4. weiping-toh

    weiping-toh

    Joined:
    Sep 8, 2015
    Posts:
    192
    Okay, I am not sure what exactly is the end result you are looking for. But I have translated the shader for you.

    Code (CSharp):
    1. Shader "Custom/Trilinear64"
    2. {
    3.     Properties
    4.     {
    5.  
    6.         _MainTex("BaseMap", 2D) = "white" {}
    7.         _Color("Base Color", Color) = (0.5, 0.5, 0.5, 1)
    8.         _Cutoff("AlphaCutout", Range(0.0, 1.0)) = 0.5
    9.     }
    10.     SubShader
    11.     {
    12.         Tags { "RenderType" = "Opaque" "IgnoreProjector" = "True" "RenderPipeline" = "UniversalPipeline" }
    13.         LOD 100
    14.         Pass
    15.         {
    16.             HLSLPROGRAM
    17.             // Required to compile gles 2.0 with standard srp library
    18.  
    19.             #pragma prefer_hlslcc gles
    20.             #pragma exclude_renderers d3d11_9x
    21.             #pragma vertex vert
    22.             #pragma fragment frag
    23.             // -------------------------------------
    24.             // Unity defined keywords
    25.             #pragma multi_compile_fog
    26.             #pragma multi_compile_instancing
    27.             #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
    28.  
    29.             TEXTURE2D(_MainTex);
    30.             SAMPLER(sampler_MainTex);
    31.             CBUFFER_START(UnityPerMaterial)
    32.             float4 _MainTex_ST;
    33.             float4 _MainTex_TexelSize;
    34.             float4 _Color;
    35.             float _Cutoff;
    36.             CBUFFER_END
    37.             struct Attributes
    38.             {
    39.                 float4 positionOS       : POSITION;
    40.                 float2 uv               : TEXCOORD0;
    41.                 float4 vertexColor : COLOR;
    42.                 UNITY_VERTEX_INPUT_INSTANCE_ID
    43.             };
    44.             struct Varyings
    45.             {
    46.                 float2 uv        : TEXCOORD0;
    47.                 float fogCoord : TEXCOORD1;
    48.                 float4 vertex : SV_POSITION;
    49.                 float4 color : COLOR;
    50.                 UNITY_VERTEX_INPUT_INSTANCE_ID
    51.                 UNITY_VERTEX_OUTPUT_STEREO
    52.             };
    53.  
    54.             Varyings vert(Attributes input)
    55.             {
    56.                 Varyings output = (Varyings)0;
    57.                 UNITY_SETUP_INSTANCE_ID(input);
    58.                 UNITY_TRANSFER_INSTANCE_ID(input, output);
    59.                 UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
    60.                 VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz);
    61.                 output.vertex = vertexInput.positionCS;
    62.                 output.uv = TRANSFORM_TEX(input.uv, _MainTex);
    63.                 output.fogCoord = ComputeFogFactor(vertexInput.positionCS.z);
    64.                 output.color = input.vertexColor;
    65.                 return output;
    66.             }
    67.  
    68.             float4 N64Filtering(float2 uv, float4 scale)
    69.             {
    70.                 //texel coords
    71.  
    72.                 float2 texel = uv * scale.zw;
    73.  
    74.                 //get mip map coords and scaling
    75.  
    76.                 float2 mipX = ddx(texel), mipY = ddy(texel);
    77.                 float delta_max_sqr = max(dot(mipX, mipX), dot(mipY, mipY));
    78.                 float mip = max(0.0, 0.5 * log2(delta_max_sqr));
    79.        
    80.                 float size = pow(2, floor(mip));
    81.                 scale.xy *= size;
    82.                 scale.zw /= size;
    83.                 texel = texel / size - 0.5;
    84.                 //sample points
    85.                 float2 fracTexl = frac(texel);
    86.                 float2 uv1 = (floor(texel + fracTexl.yx) + 0.5) * scale.xy;
    87.                 float4 out1 = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv1);
    88.                 float2 uv2 = (floor(texel) + float2(1.5, 0.5)) * scale.xy;
    89.                 float4 out2 = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv2);
    90.                 float2 uv3 = (floor(texel) + float2(0.5, 1.5)) * scale.xy;
    91.                 float4 out3 = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv3);
    92.                 //calculate blend and apply
    93.                 float3 blend = float3(abs(fracTexl.x + fracTexl.y - 1), min(abs(fracTexl.xx - float2(0, 1)), abs(fracTexl.yy - float2(1, 0))));
    94.                 float4 _outTex = out1 * blend.x + out2 * blend.y + out3 * blend.z;
    95.                 // blend and return
    96.                 return _outTex;
    97.             }
    98.             half4 frag(Varyings input) : SV_Target
    99.             {
    100.                 UNITY_SETUP_INSTANCE_ID(input);
    101.                 UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
    102.                 half2 uv = input.uv;
    103.                 half4 texColor = N64Filtering(uv, _MainTex_TexelSize);
    104.                 half3 color = texColor.rgb * _Color.rgb;
    105.                 half alpha = texColor.a * _Color.a;
    106.                 AlphaDiscard(alpha, _Cutoff);
    107.                 color = MixFog(color, input.fogCoord);
    108.                 return half4(color, alpha);
    109.             }
    110.             ENDHLSL
    111.         }
    112.     }
    113. }
     
  5. SameDev

    SameDev

    Joined:
    Apr 14, 2013
    Posts:
    18
    Beautiful! The shader was meant to apply Nintendo 64 texture filtering in the URP.

    The left is your shader(the intended result) and the right is the standard URP Shader. Its still missing lighting and vertex colors, but Ill try to figure it out and post my results
     

    Attached Files:

  6. mpDev_TND

    mpDev_TND

    Joined:
    Nov 22, 2019
    Posts:
    3
    Hi, i tried to convert my shader in URP by adding the render pipeline on it but it gives me an error, if someone can help me or explaoin how to convert would be really awesome!
    The shader is this one.

    Code (CSharp):
    1. Shader "Custom/VertexColors"
    2. {
    3.     Properties
    4.     {
    5.         _Color ("Color", Color) = (1,1,1,1)
    6.         _MainTex ("Albedo (RGB)", 2D) = "white" {}
    7.         _GridTex("Grid Texture", 2D) = "white" {}
    8.         _Glossiness ("Smoothness", Range(0,1)) = 0.5
    9.         _Metallic ("Metallic", Range(0,1)) = 0.0
    10.     }
    11.     SubShader
    12.     {
    13.         Tags
    14.         {
    15.             "RenderType"="Opaque"
    16.         }
    17.         LOD 200
    18.  
    19.         CGPROGRAM
    20.         // Physically based Standard lighting model, and enable shadows on all light types
    21.         #pragma surface surf Standard fullforwardshadows
    22.  
    23.         // Use shader model 3.0 target, to get nicer looking lighting
    24.         #pragma target 3.5
    25.  
    26.         sampler2D _MainTex;
    27.  
    28.         sampler2D _GridTex;
    29.  
    30.         struct Input
    31.         {
    32.             //float2 uv_MainTex;
    33.             float4 color : COLOR;
    34.             float3 worldPos;
    35.         };
    36.  
    37.         half _Glossiness;
    38.         half _Metallic;
    39.         fixed4 _Color;
    40.  
    41.         // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
    42.         // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
    43.         // #pragma instancing_options assumeuniformscaling
    44.         UNITY_INSTANCING_BUFFER_START(Props)
    45.             // put more per-instance properties here
    46.         UNITY_INSTANCING_BUFFER_END(Props)
    47.  
    48.         void surf (Input IN, inout SurfaceOutputStandard o)
    49.         {
    50.             // Albedo comes from a texture tinted by color
    51.             float2 uv = IN.worldPos.xy * 0.02;
    52.             fixed4 c = tex2D (_MainTex, float3(uv,0)/*IN.uv_MainTex*/) * _Color;
    53.  
    54.             float2 gridUV = IN.worldPos.xz;
    55.             gridUV.x *= 1 / (4 * 8.66025404);
    56.             gridUV.y *= 1 / (2 * 15.0);
    57.  
    58.             fixed4 grid = tex2D(_GridTex, gridUV);
    59.  
    60.             o.Albedo = c.rgb * grid * _Color;
    61.             // Metallic and smoothness come from slider variables
    62.             o.Metallic = _Metallic;
    63.             o.Smoothness = _Glossiness;
    64.             o.Alpha = c.a;
    65.         }
    66.         ENDCG
    67.     }
    68.     FallBack "Diffuse"
    69. }
     
  7. jbooth

    jbooth

    Joined:
    Jan 6, 2014
    Posts:
    5,461
    Your code is a surface shader. Unity did not implement surface shaders or a replacement for them into SRPs, forcing you into a brittle and much more complex vertex/fragment pipeline, or forcing you to use a shader graph instead. I also wrote a replacement surface shader like system called Better Shaders as another option.
     
    Ony, mpDev_TND and revolute like this.
  8. bjj0922907

    bjj0922907

    Joined:
    Mar 14, 2018
    Posts:
    2
    I have a same issue
    could you translate this shader as well?

    Shader "Toon/Custom/IK_Smooth_Outline" {
    Properties {
    _MainTex ("Base (RGB)", 2D) = "white" {}
    _ShadeTex ("Shade Tex", 2D) = "white" {}

    _RimColor ( "Rim Color" , Color ) = (0.26,0.19,0.16,0.0)
    _RimPower ( "Rim Power" , Range(0.5,8.0)) = 3.0

    _OutlineColor("Outline Color", Color) = (0,0,0,1)
    _Outline("Outline Width", Range(.001, 0.1)) = .0016

    //_AmbientFactor("Outline Width", Range(0.1, 5)) = 2.7
    }
    SubShader {
    Tags { "RenderType"="Opaque" }
    LOD 200
    Cull Off

    CGPROGRAM
    #pragma surface surf Shade noambient

    sampler2D _MainTex;
    sampler2D _ShadeTex;

    float4 _RimColor;
    float _RimPower;

    //uniform float _AmbientFactor;

    struct Input {
    float2 uv_MainTex;
    float3 viewDir;
    };

    struct SurfaceOutputCustom {
    fixed3 Albedo;
    fixed3 Normal;
    fixed3 Emission;
    half Specular;
    fixed Gloss;
    fixed Alpha;
    };

    half4 LightingShade(SurfaceOutputCustom s,half3 lightDir,half attend)
    {

    half NdotL = dot(s.Normal,lightDir);

    float3 d_Albedo = s.Albedo * s.Albedo;

    //어두운 부분은 좀 더 밝게,
    float shadeOffset = lerp(0.50, 0.40, NdotL);
    half shade = NdotL*0.1 + shadeOffset;
    //half shade = NdotL*0.1+0.5;
    half cell_shade = saturate(shade * 6 -3 );

    half3 ramp = tex2D(_ShadeTex,float2(shade,shade));

    fixed3 ambient = 0;
    ambient = UNITY_LIGHTMODEL_AMBIENT * 2.7;

    float3 light = clamp(_LightColor0.rgb, 0.0, 1.0);
    ambient = clamp(ambient, 1.4, 1.7)*light;

    half4 c;
    c.rgb = lerp(d_Albedo, s.Albedo, cell_shade) * ramp * ambient;
    c.a = s.Alpha;

    return c;
    }

    void surf (Input IN, inout SurfaceOutputCustom o) {
    half4 c = tex2D (_MainTex, IN.uv_MainTex);

    o.Albedo = c.rgb ;// * diffuse;
    o.Alpha = c.a;

    half rim = 1.0 - saturate(dot (normalize(IN.viewDir) , o.Normal));
    o.Emission = _RimColor.rgb * pow(rim,_RimPower);
    }
    ENDCG

    CGINCLUDE
    #include "UnityCG.cginc"

    struct appdata {
    float4 vertex : POSITION;
    float3 normal : NORMAL;
    };

    struct v2f {
    float4 pos : POSITION;
    float4 color : COLOR;
    };

    uniform float _Outline;
    uniform float4 _OutlineColor;

    v2f vert(appdata v) {
    v2f o;
    o.pos = UnityObjectToClipPos(v.vertex);
    float3 clipNormal = mul((float3x3) UNITY_MATRIX_VP, mul((float3x3) UNITY_MATRIX_M, v.normal));

    float2 offset = normalize(clipNormal.xy) * _Outline * o.pos.w;
    //float2 offset = normalize(clipNormal.xy) / _ScreenParams.xy * _Outline * o.pos.w;

    o.pos.xy += offset;
    o.color = _OutlineColor;

    /*o.pos = v.vertex;
    o.pos.xyz += v.normal.xyz * _Outline;
    o.pos = UnityObjectToClipPos(o.pos);

    o.color = _OutlineColor;*/

    return o;
    }
    ENDCG

    Pass{
    Name "OUTLINE"
    Tags{ "LightMode" = "Always" }
    Cull Front
    ZWrite Off
    ColorMask RGB
    Blend SrcAlpha OneMinusSrcAlpha

    CGPROGRAM
    #pragma vertex vert
    #pragma fragment frag
    half4 frag(v2f i) :COLOR{ return i.color; }
    ENDCG
    }
    }
    FallBack "Diffuse"
    }