Search Unity

  1. Unity 2019.1 is now released.
    Dismiss Notice

Selection outline

Discussion in '5.5 Beta' started by IkaBika, Sep 3, 2016.

  1. IkaBika

    IkaBika

    Joined:
    Jul 23, 2013
    Posts:
    36
    Hi just migrated to Unity 5.5B and everything is working fine and seems stable so far.

    I got 1 question about editor selection outline feature. I use similar feature in my game and custom shader for that.

    If now its engine feature how can I access/use it ?

    Thanks in advance!
     
    LennartJohansen likes this.
  2. LeonhardP

    LeonhardP

    Unity Technologies

    Joined:
    Jul 4, 2016
    Posts:
    1,372
    Hi IkaBika,
    You can find the options for this in the editor under "Preferences" -> "Colors".
     
  3. LennartJohansen

    LennartJohansen

    Joined:
    Dec 1, 2014
    Posts:
    2,175
    But is it possible to use this run-time also? To highlight a gameobject ingame?

    Lennart
     
  4. IkaBika

    IkaBika

    Joined:
    Jul 23, 2013
    Posts:
    36
  5. Tim-C

    Tim-C

    Unity Technologies

    Joined:
    Feb 6, 2010
    Posts:
    2,087
    Here are the two files we use:

    This probably does too much compared to what you want to do (it does handling for picking and other things). But you can probably take this code and use it in your project with some modification.

    Basically:
    // Outline process:
    // 1. render selected objects into a mask buffer, with different colors for visible vs occluded ones (using existing Z buffer for testing)
    // 1a. objects also get an object ID which can be used to differntiate between differnt selection roots.
    // 2. prepass on object id to discover edges between roots
    // 2. blur the mask information in two separable passes, keeping the mask channels
    // 3. blend outline over existing scene image. blurred information & mask channels allow computing distance to selected
    // object edges, from which we create the outline pixels

    SceneViewSelected.cginc
    Code (csharp):
    1.  
    2. #ifndef DRAW_COLOR
    3. #define DRAW_COLOR 1
    4. #endif
    5.  
    6. #include "UnityCG.cginc"
    7.  
    8. sampler2D _MainTex;
    9. float4 _MainTex_ST;
    10. float _DoClip;
    11. fixed _Cutoff;
    12.  
    13. struct appdata_t
    14. {
    15.     float4 vertex   : POSITION;
    16.     float2 texcoord : TEXCOORD0;
    17. };
    18.  
    19. struct v2f
    20. {
    21.     float4 vertex        : SV_POSITION;
    22.     float2 texcoord      : TEXCOORD0;
    23. };
    24.  
    25. v2f vert (appdata_t IN)
    26. {
    27.     v2f OUT;
    28.     OUT.vertex = UnityObjectToClipPos(IN.vertex);
    29.     OUT.texcoord = TRANSFORM_TEX(IN.texcoord, _MainTex);
    30.     return OUT;
    31. }
    32.  
    33. fixed4 frag (v2f IN) : SV_Target
    34. {
    35.     if (_DoClip)
    36.     {
    37.         fixed4 col = tex2D( _MainTex, IN.texcoord);
    38.         clip(col.a - _Cutoff);
    39.     }
    40.     return DRAW_COLOR;
    41. }
    42.  
    SceneViewSelected.shader
    Code (csharp):
    1.  
    2. Shader "Hidden/SceneViewSelected"
    3. {
    4.     Properties
    5.     {
    6.         _MainTex ("Main Texture", 2D) = "white" {}
    7.         _Cutoff ("Alpha cutoff", Range(0,1)) = 0.01
    8.     }
    9.  
    10.     SubShader
    11.     {
    12.         CGINCLUDE
    13.         struct Input
    14.         {
    15.             float4 position : POSITION;
    16.             float2 uv : TEXCOORD0;
    17.         };
    18.  
    19.         struct Varying
    20.         {
    21.             float4 position : SV_POSITION;
    22.             float2 uv : TEXCOORD0;
    23.         };
    24.  
    25.         Varying vertex(Input input)
    26.         {
    27.             Varying output;
    28.  
    29.             output.position = mul(UNITY_MATRIX_MVP, input.position);
    30.             output.uv = input.uv;
    31.             return output;
    32.         }
    33.         ENDCG
    34.        
    35.         Tags { "RenderType"="Opaque" }
    36.  
    37.         // #0: things that are visible (pass depth). 1 in alpha, 1 in red (SM2.0)
    38.         Pass
    39.         {
    40.             Blend One Zero
    41.             ZTest LEqual
    42.             Cull Off
    43.             ZWrite Off
    44.             // push towards camera a bit, so that coord mismatch due to dynamic batching is not affecting us
    45.             Offset -0.02, 0
    46.  
    47.             CGPROGRAM
    48.             #pragma vertex vert
    49.             #pragma fragment frag
    50.             #pragma target 2.0
    51.  
    52.             float _ObjectId;
    53.  
    54.             #define DRAW_COLOR float4(_ObjectId, 1, 1, 1)
    55.             #include "SceneViewSelected.cginc"
    56.             ENDCG
    57.         }
    58.         // #1: things that are visible (pass depth). 1 in alpha, 1 in red (SM3.0)
    59.         Pass
    60.         {
    61.             Blend One Zero
    62.             ZTest LEqual
    63.             Cull Off
    64.             ZWrite Off
    65.             // push towards camera a bit, so that coord mismatch due to dynamic batching is not affecting us
    66.             Offset -0.02, 0
    67.  
    68.             CGPROGRAM
    69.             #pragma vertex vert
    70.             #pragma fragment frag
    71.             #pragma target 3.0
    72.  
    73.             float _ObjectId;
    74.  
    75.             #define DRAW_COLOR float4(_ObjectId, 1, 1, 1)
    76.             #include "SceneViewSelected.cginc"
    77.             ENDCG
    78.         }
    79.  
    80.         // #2: all the things, including the ones that fail the depth test. Additive blend, 1 in green, 1 in alpha (SM2.0)
    81.         Pass
    82.         {
    83.             Blend One One
    84.             BlendOp Max
    85.             ZTest Always
    86.             ZWrite Off
    87.             Cull Off
    88.             ColorMask GBA
    89.             // push towards camera a bit, so that coord mismatch due to dynamic batching is not affecting us
    90.             Offset -0.02, 0
    91.  
    92.             CGPROGRAM
    93.             #pragma vertex vert
    94.             #pragma fragment frag
    95.             #pragma target 2.0
    96.  
    97.             float _ObjectId;
    98.  
    99.             #define DRAW_COLOR float4(0, 0, 1, 1)
    100.             #include "SceneViewSelected.cginc"
    101.             ENDCG
    102.         }
    103.  
    104.         // #3: all the things, including the ones that fail the depth test. Additive blend, 1 in green, 1 in alpha
    105.         Pass
    106.         {
    107.             Blend One One
    108.             BlendOp Max
    109.             ZTest Always
    110.             ZWrite Off
    111.             Cull Off
    112.             ColorMask GBA
    113.             // push towards camera a bit, so that coord mismatch due to dynamic batching is not affecting us
    114.             Offset -0.02, 0
    115.  
    116.             CGPROGRAM
    117.             #pragma vertex vert
    118.             #pragma fragment frag
    119.             #pragma target 3.0
    120.  
    121.             float _ObjectId;
    122.  
    123.             #define DRAW_COLOR float4(0, 0, 1, 1)
    124.             #include "SceneViewSelected.cginc"
    125.             ENDCG
    126.         }
    127.  
    128.         // #4: final postprocessing pass
    129.         Pass
    130.         {
    131.             ZTest Always
    132.             Cull Off
    133.             ZWrite Off
    134.             Blend SrcAlpha OneMinusSrcAlpha
    135.            
    136.             CGPROGRAM
    137.             #pragma vertex vertex
    138.             #pragma fragment fragment
    139.             #pragma target 3.0
    140.             #include "UnityCG.cginc"
    141.  
    142.             sampler2D _MainTex;
    143.             float4 _MainTex_TexelSize;
    144.             half4 _OutlineColor;
    145.  
    146.             half4 fragment(Varying i) : SV_Target
    147.             {
    148.                 half4 col = tex2D(_MainTex, i.uv);
    149.                
    150.                 bool isSelected = col.a > 0.9;
    151.                 float alpha = saturate(col.b * 10);
    152.                 if (isSelected)
    153.                 {
    154.                     // outline color alpha controls how much tint the whole object gets
    155.                     alpha = _OutlineColor.a;
    156.                     if (any(i.uv - _MainTex_TexelSize.xy*2 < 0) || any(i.uv + _MainTex_TexelSize.xy*2 > 1))
    157.                         alpha = 1;
    158.                 }
    159.                 bool inFront = col.g > 0.0;
    160.                 if (!inFront)
    161.                 {
    162.                     alpha *= 0.3;
    163.                     if (isSelected) // no tinting at all for occluded selection
    164.                         alpha = 0;
    165.                 }
    166.                 float4 outlineColor = float4(_OutlineColor.rgb,alpha);
    167.                 return outlineColor;
    168.             }
    169.             ENDCG
    170.         }
    171.  
    172.         // #5: separable blur pass, either horizontal or vertical
    173.         Pass
    174.         {
    175.             ZTest Always
    176.             Cull Off
    177.             ZWrite Off
    178.            
    179.             CGPROGRAM
    180.             #pragma vertex vertex
    181.             #pragma fragment fragment
    182.             #pragma target 3.0
    183.             #include "UnityCG.cginc"
    184.  
    185.             float2 _BlurDirection;
    186.             sampler2D _MainTex;
    187.             float4 _MainTex_TexelSize;
    188.  
    189.             // 9-tap Gaussian kernel, that blurs green & blue channels,
    190.             // keeps red & alpha intact.
    191.             static const half4 kCurveWeights[9] = {
    192.                 half4(0,0.0204001988,0.0204001988,0),
    193.                 half4(0,0.0577929595,0.0577929595,0),
    194.                 half4(0,0.1215916882,0.1215916882,0),
    195.                 half4(0,0.1899858519,0.1899858519,0),
    196.                 half4(1,0.2204586031,0.2204586031,1),
    197.                 half4(0,0.1899858519,0.1899858519,0),
    198.                 half4(0,0.1215916882,0.1215916882,0),
    199.                 half4(0,0.0577929595,0.0577929595,0),
    200.                 half4(0,0.0204001988,0.0204001988,0)
    201.             };
    202.  
    203.             half4 fragment(Varying i) : SV_Target
    204.             {
    205.                 float2 step = _MainTex_TexelSize.xy * _BlurDirection;
    206.                 float2 uv = i.uv - step * 4;
    207.                 half4 col = 0;
    208.                 for (int tap = 0; tap < 9; ++tap)
    209.                 {
    210.                     col += tex2D(_MainTex, uv) * kCurveWeights[tap];
    211.                     uv += step;
    212.                 }
    213.                 return col;
    214.             }
    215.             ENDCG
    216.         }
    217.  
    218.         // #6: Compare object ids
    219.         Pass
    220.         {
    221.             ZTest Always
    222.             Cull Off
    223.             ZWrite Off
    224.            
    225.             CGPROGRAM
    226.             #pragma vertex vertex
    227.             #pragma fragment fragment
    228.             #pragma target 3.0
    229.             #include "UnityCG.cginc"
    230.  
    231.             sampler2D _MainTex;
    232.             float4 _MainTex_TexelSize;
    233.  
    234.             // 8 tap search around the current pixel to
    235.             // see if it borders with an object that has a
    236.             // different object id
    237.             static const half2 kOffsets[8] = {
    238.                 half2(-1,-1),
    239.                 half2(0,-1),
    240.                 half2(1,-1),
    241.                 half2(-1,0),
    242.                 half2(1,0),
    243.                 half2(-1,1),
    244.                 half2(0,1),
    245.                 half2(1,1)
    246.             };
    247.  
    248.             half4 fragment(Varying i) : SV_Target
    249.             {              
    250.                 float4 currentTexel = tex2D(_MainTex, i.uv);
    251.                 if (currentTexel.r == 0)
    252.                     return currentTexel;
    253.  
    254.                 // if the current texel borders with a
    255.                 // texel that has a differnt object id
    256.                 // set the alpha to 0. This implies an
    257.                 // edge.
    258.                 for (int tap = 0; tap < 8; ++tap)
    259.                 {
    260.                     float id = tex2D(_MainTex, i.uv + (kOffsets[tap] * _MainTex_TexelSize.xy)).r;
    261.                     if (id != 0 && id - currentTexel.r != 0)
    262.                     {
    263.                         currentTexel.a = 0;
    264.                     }
    265.                 }
    266.                 return currentTexel;
    267.             }
    268.             ENDCG
    269.         }
    270.     }
    271. }
    272.  
    273.  
     
  6. IkaBika

    IkaBika

    Joined:
    Jul 23, 2013
    Posts:
    36
    Oki thanks for sharing !
     
  7. Reanimate_L

    Reanimate_L

    Joined:
    Oct 10, 2009
    Posts:
    2,349
    @Tim-C What is the best way to do step 1, it's the only step that i cannot get it right/performant.
    and let's say, considering for mobile :D