Search Unity

Trying to make a silhouette outline shader

Discussion in 'Shaders' started by AnomalusUndrdog, Jul 9, 2011.

  1. AnomalusUndrdog

    AnomalusUndrdog

    Joined:
    Jul 3, 2009
    Posts:
    1,553
    I'm trying to make an outline shader that only shows outline from the model's silhouette, to be used as indication that the character has been selected.

    The current problem with this one is the outline doesn't show in the feet, because of the offset, it gets hidden by the ground. The offset is there to get rid of outline lines that are not in the silhouette (i.e. protrusions facing the camera)

    I want the outline to be above the terrain, but below the character. The queue tag doesn't seem to work.



    Code (csharp):
    1.  
    2. Shader "Outlined/Diffuse" {
    3.     Properties {
    4.         _Color ("Main Color", Color) = (.5,.5,.5,1)
    5.         _OutlineColor ("Outline Color", Color) = (0,0,0,1)
    6.         _Outline ("Outline width", Range (.002, 0.03)) = .005
    7.         _MainTex ("Base (RGB)", 2D) = "white" { }
    8.     }
    9.    
    10. CGINCLUDE
    11. #include "UnityCG.cginc"
    12.  
    13. struct appdata {
    14.     float4 vertex : POSITION;
    15.     float3 normal : NORMAL;
    16. };
    17.  
    18. struct v2f {
    19.     float4 pos : POSITION;
    20.     float4 color : COLOR;
    21. };
    22.  
    23. uniform float _Outline;
    24. uniform float4 _OutlineColor;
    25.  
    26. v2f vert(appdata v) {
    27.     // just make a copy of incoming vertex data but scaled according to normal direction
    28.     v2f o;
    29.     o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
    30.  
    31.     float3 norm   = mul ((float3x3)UNITY_MATRIX_IT_MV, v.normal);
    32.     float2 offset = TransformViewToProjection(norm.xy);
    33.  
    34.     o.pos.xy += offset * o.pos.z * _Outline;
    35.     o.color = _OutlineColor;
    36.     return o;
    37. }
    38. ENDCG
    39.  
    40.     SubShader {
    41.         Tags {"Queue" = "Overlay" }
    42. CGPROGRAM
    43. #pragma surface surf Lambert
    44.  
    45. sampler2D _MainTex;
    46. fixed4 _Color;
    47.  
    48. struct Input {
    49.     float2 uv_MainTex;
    50. };
    51.  
    52. void surf (Input IN, inout SurfaceOutput o) {
    53.     fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
    54.     o.Albedo = c.rgb;
    55.     o.Alpha = c.a;
    56. }
    57. ENDCG
    58.  
    59.         // note that a vertex shader is specified here but its using the one above
    60.         Pass {
    61.             Name "OUTLINE"
    62.             Tags { "LightMode" = "Always" }
    63.             Cull Front
    64.             ZWrite On
    65.             ZTest LEqual
    66.             ColorMask RGB
    67.             Blend SrcAlpha OneMinusSrcAlpha
    68.             Offset 15,15
    69.  
    70.             CGPROGRAM
    71.             #pragma vertex vert
    72.             #pragma fragment frag
    73.             half4 frag(v2f i) :COLOR { return i.color; }
    74.             ENDCG
    75.         }
    76.     }
    77.    
    78.     SubShader {
    79.         Tags {"Queue" = "Overlay" }
    80. CGPROGRAM
    81. #pragma surface surf Lambert
    82.  
    83. sampler2D _MainTex;
    84. fixed4 _Color;
    85.  
    86. struct Input {
    87.     float2 uv_MainTex;
    88. };
    89.  
    90. void surf (Input IN, inout SurfaceOutput o) {
    91.     fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
    92.     o.Albedo = c.rgb;
    93.     o.Alpha = c.a;
    94. }
    95. ENDCG
    96.  
    97.         Pass {
    98.             Name "OUTLINE"
    99.             Tags { "LightMode" = "Always" }
    100.             Cull Front
    101.             ZWrite On
    102.             ColorMask RGB
    103.             Blend SrcAlpha OneMinusSrcAlpha
    104.  
    105.             CGPROGRAM
    106.             #pragma vertex vert
    107.             #pragma exclude_renderers gles xbox360 ps3
    108.             ENDCG
    109.             SetTexture [_MainTex] { combine primary }
    110.         }
    111.     }
    112.    
    113.     Fallback "Diffuse"
    114. }
    115.  
     
  2. AnomalusUndrdog

    AnomalusUndrdog

    Joined:
    Jul 3, 2009
    Posts:
    1,553
    A good friend in IRC said the problem is that the outline is reading the depth buffer, and that's what causes it to clip against other geometry.

    Somehow I want the outline to stop considering other geometry other than the model its supposed to outline.
     
  3. Martin-Kraus

    Martin-Kraus

    Joined:
    Feb 18, 2011
    Posts:
    617
    Use "ZTest Always" and "ZWrite Off" to render the outline in the Transparent queue and then render the character on top of it in the Overlay queue. (With or without "ZTest Always" depending on what you prefer.)
     
  4. AnomalusUndrdog

    AnomalusUndrdog

    Joined:
    Jul 3, 2009
    Posts:
    1,553
    Also another problem with my current set up is you'll have to tweak the depth offset values depending on the size of your object. You'll want it to be below the model its outlining, and above objects that the model is positionally behind; it has to be in-between.

    P.S. positionally is not a word
     
  5. AnomalusUndrdog

    AnomalusUndrdog

    Joined:
    Jul 3, 2009
    Posts:
    1,553
    Here it is with zwrite off and ztest always:


    Here it is with zwrite off only:


    I'm not sure if you can specify render queue per pass. I added it in but I didn't see any difference.


    Here is what I want to happen, done using a two camera trick:


    Unfortunately, that method does this:


    EDIT: I realize the picture doesn't show it, but the character is supposed to be behind that slope. What happened was the character effectively shows on top always, no matter what is supposed to obstruct your view of it.

    I'm fine if the outline has to show "always on top no matter what"[1] but certainly the character itself shouldn't be "always on top".

    ______________________________________________

    1: Though I would prefer the outline to be "always on top of only things that are farther than the character, but also be on top of the portion of the ground he's standing in".

    On the other hand, showing outline always on top no matter what can also be useful to show a selected character when he's behind walls, or when he's crawling under ducts for example. So I'd like to explore both options.
     
    Last edited: Jul 9, 2011
  6. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    Use the Offset command in shaders to push it in front of the geometry. It will only need to offset by a small ish amount to get his feet over. Do the same for the above model too. That could work. It will cause problems clipping with small rocks etc though.
     
  7. AnomalusUndrdog

    AnomalusUndrdog

    Joined:
    Jul 3, 2009
    Posts:
    1,553
    Please read the thread entirely first.

    Here is what it looks like with depth offset of positive values, which is almost what I want, except the feet don't show outline (this is also the first pic I posted):


    Here it is with depth offset with negative values, notice it has lines on the belt, nose, and other areas which I do not want:


    Here is what it looks like without any offset:
     
  8. AnomalusUndrdog

    AnomalusUndrdog

    Joined:
    Jul 3, 2009
    Posts:
    1,553
    An interesting experiment, I was able to make the shader show the outline only, but still suffers from the problem that the feet doesn't show outline.

     
  9. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    well if speed is not an issue you can solve it with render to texture.
     
  10. AnomalusUndrdog

    AnomalusUndrdog

    Joined:
    Jul 3, 2009
    Posts:
    1,553
    The problem is I don't have Pro.
     
  11. AnomalusUndrdog

    AnomalusUndrdog

    Joined:
    Jul 3, 2009
    Posts:
    1,553
  12. sirival

    sirival

    Joined:
    Sep 14, 2011
    Posts:
    146
    Hi there,

    Nice shader thanks for sharing!

    I wanted to ask you if it is possible to show the outline of the character when he is behind objects instead of rendering a solid color for the whole obstructed part of the character.

    Thanks!
     
  13. SilentWarrior

    SilentWarrior

    Joined:
    Aug 3, 2010
    Posts:
    107
    +1

    Also, if it could be possible to make it such that it doesnt render the outline over renderers with this shader, for example, if a tank has hull, turret, gunbarrel, then it will render outlines (and worst, solid filled color) for the back of the gunbarrel that is inside the turret and for the base of the turret that is slighty inside the tank.
     
  14. jpenca

    jpenca

    Joined:
    Jan 22, 2012
    Posts:
    4
    hej, this shader is really nice, just what we need.

    when we're trying to use it on an Android device (or with OpenGL ES2 emulation enabled in the editor), it doesn't render correctly though. we're using the Silhouette Only variant:

    disabled ES2 emulation:


    enabled ES2 emulation:




    My knowledge of shader things is limited, could this have something to do with the precision of depth testing, or some feature used by this shader that's not available in ES? Maybe someone can hint towards a way to fix this. thanks!
     
  15. BenJones

    BenJones

    Joined:
    Dec 14, 2012
    Posts:
    11
    This is really cool! How did you make the shader just render the outlines?
     
  16. AnomalusUndrdog

    AnomalusUndrdog

    Joined:
    Jul 3, 2009
    Posts:
    1,553
    Never saw that before. One guess is it could be z-fighting or whatever they call it. The shader has an `Offset -8, -8` that's commented out. Try enabling it and play around with the values.
     
  17. AnomalusUndrdog

    AnomalusUndrdog

    Joined:
    Jul 3, 2009
    Posts:
    1,553
    Check the wiki link I gave (http://wiki.unity3d.com/index.php?title=Silhouette-Outlined_Diffuse) it's there.

     
  18. BenJones

    BenJones

    Joined:
    Dec 14, 2012
    Posts:
    11
    Awesome, Thanks so much!
     
  19. Dragonian

    Dragonian

    Joined:
    Dec 1, 2010
    Posts:
    37
    Hi guys, great work.

    I am currently exploring ways to create a Silhouette outline of a gameObject that is not a Post Effect, and have successfully got it working in the unity editor, but we are getting z-sorting artifacts on the mobile device.

    I am approaching this problem by duplicating the gameObjects in question, extruding the faces out using the vertex normals, then applying a simple colour shader to this expanded geometry.
    For the mask we are using the original non-extruded gameObjects and applying our mask shader to mask the extruded gameObject yielding an outline effect.

    The problem is that when viewed from certain camera angles, the outline game objects faces inside the mask object are not being masked.

    Initially we solved this problem by using Offset -1, -1000 which works in the Unity editor, how ever, Offset fails on iOS.

    to illustrate:
    Wireframe of object setup, inner object is being used as mask object, outer object is our extruded outline object


    Screenshot of results inside unity editor:


    Screen shot of results on iOS:


    Here are our shaders:
    Code (csharp):
    1. Shader "Mask"  {
    2.    
    3.     SubShader {
    4.         Tags {"Queue" = "Geometry-10" }      
    5.        
    6.         Pass {
    7.             Lighting Off
    8.            Offset -1000, 0
    9.             ColorMask 0
    10.             ZWrite On
    11.         }
    12.     }
    13. }
    Code (csharp):
    1. Shader "Back Colour Only"{
    2. Properties {
    3.         _Color ("Main Color", Color) = (1,1,1,1)
    4.     }
    5.     SubShader {
    6.         Tags {"Queue" = "Geometry" }
    7.        
    8.             Pass {
    9.             Cull Front
    10.             Color [_Color]
    11.             ZWrite On
    12.             Offset 1000, 0
    13.             Lighting Off
    14.         }
    15.        
    16.     }
    17. }
    18.  
    Have also tried the above solutions, which has the same issues I'm getting here.
    My shader knowledge is rather limited, and if someone has any insight into what I'm doing wrong, would be greatly appreciated.

    Cheers all
     
    Last edited: Dec 20, 2012
  20. Dragonian

    Dragonian

    Joined:
    Dec 1, 2010
    Posts:
    37
    Hi guys

    solved my problem another way.

    Cheers all.
     
  21. aaronsullivan

    aaronsullivan

    Joined:
    Nov 10, 2005
    Posts:
    986
    Great! What did you end up doing to fix it?

    I'm interested in a silhouette-style outline solution for iOS and Android and I'm very new to shaders. I ended up auto-generating real geometry for each stage's environment in a previous project.
    (And boy has it been a long time since I've been in the forums! I miss it.)
     
  22. guru20

    guru20

    Joined:
    Jul 30, 2013
    Posts:
    239
    I, too, would love to know what was done to fix the Android...

    I would also be interested in the outline (but not filled silhouette) of hidden portion, as previous commenters suggested...
     
  23. Boolean

    Boolean

    Joined:
    Nov 5, 2013
    Posts:
    1
    Hey, So I'm trying to make a shader to give a character an outline when they are behind objects. I have it mostly working, I think, but have run into an issue with more complex models. Basically when the model is being outlined it does not occluded itself anymore.

    See in the picture you can see parts of the back and the ears which should be behind the model.
    $OcclusionErr.png

    Im trying to learn shaders, but Im not the most technically adept person when it comes to code.
    The way Im doing the "outline" is using the example rim lighting code Unity offers in their documentation here

    And here is my code
    Code (csharp):
    1. //http://forum.unity3d.com/threads/96393-Achieving-a-multi-pass-effect-with-a-Surface-Shader
    2. //http://answers.unity3d.com/questions/11175/how-to-make-an-outline-of-an-object-behind-a-wall.html
    3.  
    4. Shader "Custom/NewOcclusionOutline" {
    5.     Properties {
    6.         _MainTex ("Base (RGB)", 2D) = "white" {}
    7.         _RimCol ("Rim Colour" , Color) = (1,0,0,1)
    8.         _RimPow ("Rim Power", Float) = 1.0
    9.     }
    10.     SubShader {
    11.         Pass {
    12.                 Name "Behind"
    13.                 Tags { "RenderType"="transparent" "Queue" = "Transparent" }
    14.                 Blend SrcAlpha OneMinusSrcAlpha
    15.                 ZTest Greater               // here the check is for the pixel being greater or closer to the camera, in which
    16.                 Cull Back                   // case the model is behind something, so this pass runs
    17.                 ZWrite Off
    18.                 LOD 200                    
    19.                
    20.                 CGPROGRAM
    21.                 #pragma vertex vert
    22.                 #pragma fragment frag
    23.                 #include "UnityCG.cginc"
    24.                
    25.                 struct v2f {
    26.                     float4 pos : SV_POSITION;
    27.                     float2 uv : TEXCOORD0;
    28.                     float3 normal : TEXCOORD1;      // Normal needed for rim lighting
    29.                     float3 viewDir : TEXCOORD2;     // as is view direction.
    30.                 };
    31.                
    32.                 sampler2D _MainTex;
    33.                 float4 _RimCol;
    34.                 float _RimPow;
    35.                
    36.                 float4 _MainTex_ST;
    37.                
    38.                 v2f vert (appdata_tan v)
    39.                 {
    40.                     v2f o;
    41.                     o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
    42.                     o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
    43.                     o.normal = normalize(v.normal);
    44.                     o.viewDir = normalize(ObjSpaceViewDir(v.vertex));       //this could also be WorldSpaceViewDir, which would
    45.                     return o;                                               //return the World space view direction.
    46.                 }
    47.                
    48.                 half4 frag (v2f i) : COLOR
    49.                 {
    50.                     half Rim = 1 - saturate(dot(normalize(i.viewDir), i.normal));       //Calculates where the model view falloff is       
    51.                                                                                                                                        //for rim lighting.
    52.                    
    53.                     half4 RimOut = _RimCol * pow(Rim, _RimPow);
    54.                     return RimOut;
    55.                 }
    56.                 ENDCG
    57.             }
    58.            
    59.             Pass {
    60.                 Name "Regular"
    61.                 Tags { "RenderType"="Opaque" }
    62.                 ZTest LEqual                // this checks for depth of the pixel being less than or equal to the shader
    63.                 ZWrite On                   // and if the depth is ok, it renders the main texture.
    64.                 Cull Back
    65.                 LOD 200
    66.                
    67.                 CGPROGRAM
    68.                 #pragma vertex vert
    69.                 #pragma fragment frag
    70.                 #include "UnityCG.cginc"
    71.                
    72.                 struct v2f {
    73.                     float4 pos : SV_POSITION;
    74.                     float2 uv : TEXCOORD0;
    75.                 };
    76.                
    77.                 sampler2D _MainTex;
    78.                 float4 _MainTex_ST;
    79.                
    80.                 v2f vert (appdata_base v)
    81.                 {
    82.                     v2f o;
    83.                     o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
    84.                     o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
    85.                     return o;
    86.                 }
    87.                
    88.                 half4 frag (v2f i) : COLOR
    89.                 {
    90.                     half4 texcol = tex2D(_MainTex,i.uv);
    91.                     return texcol;
    92.                 }
    93.                 ENDCG
    94.             }          
    95.         }
    96.     FallBack "VertexLit"
    97. }
    Sorry for the inane code comments...

    Any ideas?
     
  24. reefwirrax

    reefwirrax

    Joined:
    Sep 20, 2013
    Posts:
    137
    another way is to superimpose a 2nd copy of the character who is red and a little bit larger in front and put the blend so that the real character shines through the larger copy everywhere except the edges.
     
  25. Jiraiyahsama

    Jiraiyahsama

    Joined:
    Sep 9, 2013
    Posts:
    14
    Hi
    I'm not sure why, but i get this error in Unity 4.3.3
    ---------------------------------------------------------------------------
    $jwHXU6U.jpg
    --------------------------------------------------------------------------------------------------------------------------------------------

    Can you change it so that it works in 4.3.3?
    Just so you know, the outline only works ok, but the main one not
    thanks
     
    Last edited: Mar 5, 2014
  26. haroon121

    haroon121

    Joined:
    Jan 2, 2015
    Posts:
    1
    Is this possible to have only outline visible in Silhouette Outlined Diffuse when occluded by object in front, current one blends complete shape.
     
  27. SBull

    SBull

    Joined:
    Jun 1, 2013
    Posts:
    6
    I just found this shader through a tutorial on YouTube and I am loving it, but have one question. I'm wondering if it is possible to have the outline effect around a mesh, but not allow the silhouette to show through another object. Basically, I am trying to use the outline to increase a character's visibility, but still need them to be able to hide behind objects. Is there anyway this could be done?
     
  28. chrismarch

    chrismarch

    Joined:
    Jul 24, 2013
    Posts:
    472
    LiterallyJeff likes this.
  29. SBull

    SBull

    Joined:
    Jun 1, 2013
    Posts:
    6
    Ah. That is perfect. Thank you very much.
     
  30. scifresh

    scifresh

    Joined:
    Oct 8, 2013
    Posts:
    23
    hello there, i have a complex model that has different parts with different shaders. take unity's the blacksmith example. the challenger character has a lot of small body parts. so how could I just outline the the model, not the small parts seperately.
     
  31. chrismarch

    chrismarch

    Joined:
    Jul 24, 2013
    Posts:
    472
    One way to do it is to render the complex model once with a flat color shader in the color of the outline you desire to a render texture, and then blur or scale it so that you can that you can use the rendertexture in an image effect behind the model
     
  32. scifresh

    scifresh

    Joined:
    Oct 8, 2013
    Posts:
    23
    how would i do this ? i have little experience in shader programming.
     
  33. okaybj

    okaybj

    Joined:
    Nov 15, 2018
    Posts:
    33
    hey I'm using the outline only shader as a second material on some of my meshes and it works fine in the unity desktop editor for android but when i build the apk and test on my actual device the thickness of the lines are WAY bigger. unity 2019.4.22f1 btw. Any fix?