Adding Outline to a Curved Shader

Hi, I got a problem when trying to add an outline(just like as a toon shader) to a curved shader with unity 5.

This one is the original curved shader which works fine.

Shader "Custom/Curved" {
     Properties {
         _MainTex ("Base (RGB)", 2D) = "white" {}
         _QOffset ("Offset", Vector) = (0,0,0,0)
         _Brightness ("Brightness", Float) = 0.0
         _Dist ("Distance", Float) = 100.0
     }
    
     SubShader {
         Tags { "Queue" = "Transparent"}
         Pass
         {
            
             Blend SrcAlpha OneMinusSrcAlpha
             CGPROGRAM
             #pragma vertex vert
             #pragma fragment frag
             #include "UnityCG.cginc"
            
            
             sampler2D _MainTex;
             float4 _QOffset;
             float _Dist;
             float _Brightness;
            
             struct v2f {
                 float4 pos : SV_POSITION;
                 float4 uv : TEXCOORD0;
                 float3 viewDir : TEXCOORD1;
                 fixed4 color : COLOR;
             };
             v2f vert (appdata_full v)
             {
                v2f o;
                UNITY_INITIALIZE_OUTPUT(v2f, o);
                float4 vPos = mul (UNITY_MATRIX_MV, v.vertex);
                float zOff = vPos.z/_Dist;
                vPos += _QOffset*zOff*zOff;
                o.pos = mul (UNITY_MATRIX_P, vPos);
                o.uv = v.texcoord;
                return o;
             }
             half4 frag (v2f i) : COLOR0
             {
                   half4 col = tex2D(_MainTex, i.uv.xy);
                   col *= UNITY_LIGHTMODEL_AMBIENT*_Brightness;
                 return col;
             }
             ENDCG
         }
     }
    
     FallBack "Diffuse"
}

What have you tried to make it work?

You can just add a second pass (before the already existing one) with the same vertex code for the curving, but with an additional increase. Most toon shader take the same approach.

Sorry I am a beginner for the shader, I was trying to add the outline (that additional increase) base on the vertex code after curving. Please advise me how to do that instead modify the same vertex?

Thanks a lot!!

Shader "Custom/ToonCurved" {
      Properties {
        _MainTex ("Base (RGB)", 2D) = "white" {}
        _QOffset ("Offset", Vector) = (0,0,0,0)
        _Dist ("Distance", Float) = 100.0
        _Alpha ("Alpha", Range(0.0,1.0)) = 1.0
        _OutlineColor ("Outline Color", Color) = (0,0,0,1)
        _Outline ("Outline width", Range (.002, 0.03)) = .005
        _Color ("Main Color", Color) = (0.5,0.5,0.5,1)
    }
   
    SubShader {
        Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
        LOD 100
        ZWrite On
        Blend SrcAlpha OneMinusSrcAlpha
       
       
        UsePass "Toon/Lit/FORWARD"
        Pass
        {
            Lighting Off

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            sampler2D _MainTex;
            float4 _QOffset;
            float _Dist;
            float _Alpha;
            uniform float4 _MainTex_ST;
            uniform float _Outline;
            uniform float4 _OutlineColor;
           
            struct v2f {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
                fixed4 color : COLOR;
            };

            v2f vert (appdata_base v)
            {
                v2f o;
                float4 vPos = mul (UNITY_MATRIX_MV, v.vertex);
                float zOff = vPos.z/_Dist;
                vPos += _QOffset*zOff*zOff;
                o.pos = mul (UNITY_MATRIX_P, vPos);
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
               
                float3 norm = normalize(mul ((float3x3)UNITY_MATRIX_IT_MV, v.normal));
                float2 offset = TransformViewToProjection(norm.xy);
                //o.pos.xy += offset * o.pos.z * _Outline;
                o.color = _OutlineColor;
                UNITY_TRANSFER_FOG(o,o.pos);
                return o;
            }

            half4 frag (v2f i) : COLOR
            {
                return tex2D(_MainTex, i.uv) * float4(1,1,1,_Alpha);
            }
            ENDCG
        }
       
    }
   

    Fallback "Toon/Lit"
}

Add another pass, not just the line :slight_smile:
Something like this:

Shader "Custom/ToonCurved"
{
    Properties
    {
        _MainTex("Base (RGB)", 2D) = "white" {}
        _QOffset("Offset", Vector) = (0,0,0,0)
        _Dist("Distance", Float) = 100.0
        _Alpha("Alpha", Range(0.0,1.0)) = 1.0
        _OutlineColor("Outline Color", Color) = (0,0,0,1)
        _Outline("Outline width", Range(.002, 0.03)) = .005
        _Color("Main Color", Color) = (0.5,0.5,0.5,1)
    }

        SubShader
        {
        Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" }
        LOD 100
        ZWrite On
        Blend SrcAlpha OneMinusSrcAlpha

        // Outline pass
        Pass
        {
            Lighting Off

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            float4 _QOffset;
            float _Dist;
            float _Alpha;
            uniform float _Outline;
            uniform float4 _OutlineColor;

            struct v2f
            {
                float4 pos : SV_POSITION;
                fixed4 color : COLOR;
            };

        v2f vert(appdata_base v)
        {
            v2f o;
            float4 vPos = mul(UNITY_MATRIX_MV, v.vertex);
            float zOff = vPos.z / _Dist;
            vPos += _QOffset*zOff*zOff;
            o.pos = mul(UNITY_MATRIX_P, vPos);

            float3 norm = normalize(mul((float3x3)UNITY_MATRIX_IT_MV, v.normal));
            float2 offset = TransformViewToProjection(norm.xy);
            o.pos.xy += offset * o.pos.z * _Outline;

            o.color = _OutlineColor;
            UNITY_TRANSFER_FOG(o,o.pos);
            return o;
        }

        half4 frag(v2f i) : COLOR
        {
            return i.color;
        }

            ENDCG
        }

        // Normal pass
        Pass
        {
            Lighting Off

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            sampler2D _MainTex;
            float4 _QOffset;
            float _Dist;
            float _Alpha;
            uniform float4 _MainTex_ST;
            uniform float _Outline;
            uniform float4 _OutlineColor;

            struct v2f
            {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
                fixed4 color : COLOR;
            };

            v2f vert(appdata_base v)
            {
                v2f o;
                float4 vPos = mul(UNITY_MATRIX_MV, v.vertex);
                float zOff = vPos.z / _Dist;
                vPos += _QOffset*zOff*zOff;
                o.pos = mul(UNITY_MATRIX_P, vPos);
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);

                o.color = _OutlineColor;
                UNITY_TRANSFER_FOG(o,o.pos);
                return o;
            }

            half4 frag(v2f i) : COLOR
            {
                return tex2D(_MainTex, i.uv) * float4(1,1,1,_Alpha);
            }

            ENDCG
        }
    }

    Fallback "Toon/Lit"
}

Note that I did not really test this, you might need to adjust some things here and there.

It works!!! Many thanks!!!

One more question, how can this shader have shadow?

Shader "Custom/ToonCurved" {
      Properties {
        _MainTex ("Base (RGB)", 2D) = "white" {}
        _QOffset ("Offset", Vector) = (0,0,0,0)
        _Dist ("Distance", Float) = 100.0
        _Alpha ("Alpha", Range(0.0,1.0)) = 1.0
        _OutlineColor ("Outline Color", Color) = (0,0,0,1)
        _Outline ("Outline width", Range (.002, 0.03)) = .005
        _Color ("Main Color", Color) = (0.5,0.5,0.5,1)
    }
  
    SubShader {
        Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
        LOD 100
        ZWrite On
        Blend SrcAlpha OneMinusSrcAlpha
      
        Pass
        {
             Name "OUTLINE"
            Tags { "LightMode" = "Always" }
            Cull Front
            ZWrite On
            ColorMask RGB
            Blend SrcAlpha OneMinusSrcAlpha
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            sampler2D _MainTex;
            float4 _QOffset;
            float _Dist;
            float _Alpha;
            uniform float4 _MainTex_ST;
            uniform float _Outline;
            uniform float4 _OutlineColor;
          
            struct v2f {
                float4 pos : SV_POSITION;
                fixed4 color : COLOR;
            };
           
            struct appdata {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };
            v2f vert (appdata v)
            {
                v2f o;
                float4 vPos = mul (UNITY_MATRIX_MV, v.vertex);
                float zOff = vPos.z/_Dist;
                vPos += _QOffset*zOff*zOff;
               
                o.pos = mul (UNITY_MATRIX_P, vPos);
              
                float3 norm = normalize(mul ((float3x3)UNITY_MATRIX_IT_MV, v.normal));
                float2 offset = TransformViewToProjection(norm.xy);
               
                o.pos.xy += offset * o.pos.z * _Outline;
                o.color = _OutlineColor;
                UNITY_TRANSFER_FOG(o,o.pos);
                return o;
            }
            half4 frag (v2f i) : COLOR
            {
                UNITY_APPLY_FOG(i.fogCoord, i.color);
                return i.color;
            }
            ENDCG
        }
       
       
         // Normal pass
        Pass
        {
            Lighting Off
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            sampler2D _MainTex;
            float4 _QOffset;
            float _Dist;
            float _Alpha;
            uniform float4 _MainTex_ST;
            uniform float _Outline;
            uniform float4 _OutlineColor;
            struct v2f
            {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
                fixed4 color : COLOR;
            };
            v2f vert(appdata_base v)
            {
                v2f o;
                float4 vPos = mul(UNITY_MATRIX_MV, v.vertex);
                float zOff = vPos.z / _Dist;
                vPos += _QOffset*zOff*zOff;
                o.pos = mul(UNITY_MATRIX_P, vPos);
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                o.color = _OutlineColor;
                UNITY_TRANSFER_FOG(o,o.pos);
                return o;
            }
            half4 frag(v2f i) : COLOR
            {
                return tex2D(_MainTex, i.uv) * float4(1,1,1,_Alpha);
            }
            ENDCG
        }
      
    }
  
    Fallback "Toon/Lit"
}

I’m not sure if this is still valid for Unity 5.x, but read this:

Thanks again! I tried some tutorials which are almost same with this, but it seems not working

Shader "Custom/ToonCurved" {
      Properties {
        _MainTex ("Base (RGB)", 2D) = "white" {}
        _QOffset ("Offset", Vector) = (0,0,0,0)
        _Dist ("Distance", Float) = 100.0
        _Alpha ("Alpha", Range(0.0,1.0)) = 1.0
        _OutlineColor ("Outline Color", Color) = (0,0,0,1)
        _Outline ("Outline width", Range (.002, 0.03)) = .005
        _Color ("Main Color", Color) = (0.5,0.5,0.5,1)
    }
    SubShader {
        Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
        Tags {"LightMode" = "ForwardBase"}
        LOD 100
    
        Pass
        {
            Name "OUTLINE"
            Cull Front
            ZWrite On
            ColorMask RGB
            Blend SrcAlpha OneMinusSrcAlpha
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            sampler2D _MainTex;
            float4 _QOffset;
            float _Dist;
            float _Alpha;
            uniform float4 _MainTex_ST;
            uniform float _Outline;
            uniform float4 _OutlineColor;
        
            struct v2f {
                float4 pos : SV_POSITION;
                fixed4 color : COLOR;
            };
          
            struct appdata {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };
            v2f vert (appdata v)
            {
                v2f o;
                float4 vPos = mul (UNITY_MATRIX_MV, v.vertex);
                float zOff = vPos.z/_Dist;
                vPos += _QOffset*zOff*zOff;
              
                o.pos = mul (UNITY_MATRIX_P, vPos);
            
                float3 norm = normalize(mul ((float3x3)UNITY_MATRIX_IT_MV, v.normal));
                float2 offset = TransformViewToProjection(norm.xy);
              
                o.pos.xy += offset * o.pos.z * _Outline;
                o.color = _OutlineColor;
                UNITY_TRANSFER_FOG(o,o.pos);
                return o;
            }
            half4 frag (v2f i) : COLOR
            {
                UNITY_APPLY_FOG(i.fogCoord, i.color);
                return i.color;
            }
            ENDCG
        }
      
      
         // Normal pass
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #pragma multi_compile_fwdbase
            #include "AutoLight.cginc"
            sampler2D _MainTex;
            float4 _QOffset;
            float _Dist;
            float _Alpha;
            uniform float4 _MainTex_ST;
            uniform float _Outline;
            uniform float4 _OutlineColor;
            struct v2f
            {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
                fixed4 color : COLOR;
                LIGHTING_COORDS(0,1)
            };
            v2f vert(appdata_base v)
            {
                v2f o;
                float4 vPos = mul(UNITY_MATRIX_MV, v.vertex);
                float zOff = vPos.z / _Dist;
                vPos += _QOffset*zOff*zOff;
                o.pos = mul(UNITY_MATRIX_P, vPos);
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                o.color = _OutlineColor;
                UNITY_TRANSFER_FOG(o,o.pos);
                TRANSFER_VERTEX_TO_FRAGMENT(o);
                return o;
            }
            fixed4 frag(v2f i) : COLOR
            {
                float attenuation = LIGHT_ATTENUATION(i);
                return tex2D(_MainTex, i.uv) * float4(1,1,1,_Alpha) * attenuation;
            }
            ENDCG
        }
    
    }
    Fallback "VertexLit"
}

Could be multiple things:

  • Tags {“LightMode”=“ForwardBase”} is a Pass Tag and should be used in a pass, not the subshader.

  • LIGHTING_COORDS(0,1) - you’re using 0 for your uv coordinates. use 1 and 2 instead.

  • Are shadows enabled? (see Quality Settings)

  • Are you using a directional light? (other light sources require a different Light mode and #pragma multi_compile_fwdadd instead of #pragma multi_compile_fwdbase)

Could be any or all of these things.

You can probably also save yourself a lot of trouble by just using a surface shader with the outline pass added to get shadows.