• Unity中实现网格轮廓效果,选中边框效果(附带高斯模糊实现模式,处理了锯齿情况)


    问题背景:

    最近要实现选中实体的高亮效果,要那种类似于unity中Outline的效果,网格轮廓高亮效果。

    效果图:

    具体代码:

    OutlineEffect.cs

    实体高亮效果类:

    轮廓边总控制类,该脚本需要挂载到场景相机上

      1 using UnityEngine;
      2 using System.Collections.Generic;
      3 using UnityEngine.Rendering;
      4 
      5 namespace Tx3d.Framework
      6 {
      7     [DisallowMultipleComponent]
      8     [RequireComponent(typeof(Camera))]
      9     [ExecuteInEditMode]
     10     public class OutlineEffect : MonoBehaviour
     11     {
     12         public static OutlineEffect Instance { get; private set; }
     13 
     14         private readonly LinkedSet<Outline> outlines = new LinkedSet<Outline>();
     15 
     16         [Range(1.0f, 6.0f)]
     17         public float lineThickness = 1.0f;
     18         [Range(0, 10)]
     19         public float lineIntensity = 1.2f;
     20         [Range(0, 1)]
     21         public float fillAmount = 0.108f;
     22 
     23         public Color lineColor0 = Color.yellow;
     24         public Color lineColor1 = Color.green;
     25         public Color lineColor2 = Color.blue;
     26         public Color lineColor3 = Color.cyan;
     27 
     28         public bool additiveRendering = false;
     29 
     30         public bool backfaceCulling = true;
     31 
     32         [Header("These settings can affect performance!")]
     33         public bool cornerOutlines = false;
     34         public bool addLinesBetweenColors = false;
     35 
     36         [Header("Advanced settings")]
     37         public bool scaleWithScreenSize = true;
     38         [Range(0.1f, .9f)]
     39         public float alphaCutoff = .5f;
     40         public bool flipY = false;
     41         public Camera sourceCamera;
     42         public bool autoEnableOutlines = true;
     43 
     44         [HideInInspector]
     45         public Camera outlineCamera;
     46         Material outline1Material;
     47         Material outline2Material;
     48         Material outline3Material;
     49         Material outline4Material;
     50         Material outlineEraseMaterial;
     51         Shader outlineShader;
     52         Shader outlineBufferShader;
     53         [HideInInspector]
     54         public Material outlineShaderMaterial;
     55         [HideInInspector]
     56         public RenderTexture renderTexture;
     57         [HideInInspector]
     58         public RenderTexture extraRenderTexture;
     59 
     60         CommandBuffer commandBuffer;
     61 
     62         Material GetMaterialFromID(int ID)
     63         {
     64             if (ID == 0)
     65                 return outline1Material;
     66             else if (ID == 1)
     67                 return outline2Material;
     68             else if (ID == 2)
     69                 return outline3Material;
     70             else if (ID == 3)
     71                 return outline4Material;
     72             else
     73                 return outline1Material;
     74         }
     75         List<Material> materialBuffer = new List<Material>();
     76         Material CreateMaterial(Color emissionColor)
     77         {
     78             Material m = new Material(outlineBufferShader);
     79             m.SetColor("_Color", emissionColor);
     80             m.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
     81             m.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
     82             m.SetInt("_ZWrite", 0);
     83             m.DisableKeyword("_ALPHATEST_ON");
     84             m.EnableKeyword("_ALPHABLEND_ON");
     85             m.DisableKeyword("_ALPHAPREMULTIPLY_ON");
     86             m.renderQueue = 3000;
     87             return m;
     88         }
     89 
     90         private void Awake()
     91         {
     92             if (Instance != null)
     93             {
     94                 Destroy(this);
     95                 throw new System.Exception("you can only have one outline camera in the scene");
     96             }
     97 
     98             Instance = this;
     99         }
    100 
    101         void Start()
    102         {
    103             CreateMaterialsIfNeeded();
    104             UpdateMaterialsPublicProperties();
    105 
    106             if (sourceCamera == null)
    107             {
    108                 sourceCamera = GetComponent<Camera>();
    109 
    110                 if (sourceCamera == null)
    111                     sourceCamera = Camera.main;
    112             }
    113 
    114             if (outlineCamera == null)
    115             {
    116                 foreach (Camera c in GetComponentsInChildren<Camera>())
    117                 {
    118                     if (c.name == "Outline Camera")
    119                     {
    120                         outlineCamera = c;
    121                         c.enabled = false;
    122 
    123                         break;
    124                     }
    125                 }
    126 
    127                 if (outlineCamera == null)
    128                 {
    129                     GameObject cameraGameObject = new GameObject("Outline Camera");
    130                     cameraGameObject.transform.parent = sourceCamera.transform;
    131                     outlineCamera = cameraGameObject.AddComponent<Camera>();
    132                     outlineCamera.enabled = false;
    133                 }
    134             }
    135 
    136             renderTexture = new RenderTexture(sourceCamera.pixelWidth, sourceCamera.pixelHeight, 16, RenderTextureFormat.Default);
    137             extraRenderTexture = new RenderTexture(sourceCamera.pixelWidth, sourceCamera.pixelHeight, 16, RenderTextureFormat.Default);
    138             UpdateOutlineCameraFromSource();
    139 
    140             commandBuffer = new CommandBuffer();
    141             outlineCamera.AddCommandBuffer(CameraEvent.BeforeImageEffects, commandBuffer);
    142         }
    143 
    144         bool RenderTheNextFrame;
    145         public void OnPreRender()
    146         {
    147             if (commandBuffer == null)
    148                 return;
    149 
    150             // the first frame during which there are no outlines, we still need to render 
    151             // to clear out any outlines that were being rendered on the previous frame
    152             if (outlines.Count == 0)
    153             {
    154                 if (!RenderTheNextFrame)
    155                     return;
    156 
    157                 RenderTheNextFrame = false;
    158             }
    159             else
    160             {
    161                 RenderTheNextFrame = true;
    162             }
    163 
    164             CreateMaterialsIfNeeded();
    165 
    166             if (renderTexture == null || renderTexture.width != sourceCamera.pixelWidth || renderTexture.height != sourceCamera.pixelHeight)
    167             {
    168                 renderTexture = new RenderTexture(sourceCamera.pixelWidth, sourceCamera.pixelHeight, 16, RenderTextureFormat.Default);
    169                 extraRenderTexture = new RenderTexture(sourceCamera.pixelWidth, sourceCamera.pixelHeight, 16, RenderTextureFormat.Default);
    170                 outlineCamera.targetTexture = renderTexture;
    171             }
    172             UpdateMaterialsPublicProperties();
    173             UpdateOutlineCameraFromSource();
    174             outlineCamera.targetTexture = renderTexture;
    175             commandBuffer.SetRenderTarget(renderTexture);
    176 
    177             commandBuffer.Clear();
    178 
    179             foreach (Outline outline in outlines)
    180             {
    181                 LayerMask l = sourceCamera.cullingMask;
    182 
    183                 // if (outline != null && l == (l | (1 << outline.gameObject.layer)))
    184                 if (outline != null)
    185                 {
    186                     for (int v = 0; v < outline.SharedMaterials.Length; v++)
    187                     {
    188                         Material m = null;
    189 
    190                         if (outline.SharedMaterials[v].mainTexture != null && outline.SharedMaterials[v])
    191                         {
    192                             foreach (Material g in materialBuffer)
    193                             {
    194                                 if (g.mainTexture == outline.SharedMaterials[v].mainTexture)
    195                                 {
    196                                     if (outline.eraseRenderer && g.color == outlineEraseMaterial.color)
    197                                         m = g;
    198                                     else if (g.color == GetMaterialFromID(outline.color).color)
    199                                         m = g;
    200                                 }
    201                             }
    202 
    203                             if (m == null)
    204                             {
    205                                 if (outline.eraseRenderer)
    206                                     m = new Material(outlineEraseMaterial);
    207                                 else
    208                                     m = new Material(GetMaterialFromID(outline.color));
    209                                 m.mainTexture = outline.SharedMaterials[v].mainTexture;
    210                                 materialBuffer.Add(m);
    211                             }
    212                         }
    213                         else
    214                         {
    215                             if (outline.eraseRenderer)
    216                                 m = outlineEraseMaterial;
    217                             else
    218                                 m = GetMaterialFromID(outline.color);
    219                         }
    220 
    221                         if (backfaceCulling)
    222                             m.SetInt("_Culling", (int)UnityEngine.Rendering.CullMode.Back);
    223                         else
    224                             m.SetInt("_Culling", (int)UnityEngine.Rendering.CullMode.Off);
    225 
    226                         commandBuffer.DrawRenderer(outline.Renderer, m, 0, 0);
    227                         MeshFilter mL = outline.MeshFilter;
    228                         if (mL)
    229                         {
    230                             if (mL.sharedMesh != null)
    231                             {
    232                                 for (int i = 1; i < mL.sharedMesh.subMeshCount; i++)
    233                                     commandBuffer.DrawRenderer(outline.Renderer, m, i, 0);
    234                             }
    235                         }
    236                         SkinnedMeshRenderer sMR = outline.SkinnedMeshRenderer;
    237                         if (sMR)
    238                         {
    239                             if (sMR.sharedMesh != null)
    240                             {
    241                                 for (int i = 1; i < sMR.sharedMesh.subMeshCount; i++)
    242                                     commandBuffer.DrawRenderer(outline.Renderer, m, i, 0);
    243                             }
    244                         }
    245                     }
    246                 }
    247             }
    248 
    249             outlineCamera.Render();
    250         }
    251 
    252         private void OnEnable()
    253         {
    254             //if (autoEnableOutlines)
    255             //{
    256             //    Outline[] o = FindObjectsOfType<Outline>();
    257 
    258             //    foreach (Outline oL in o)
    259             //    {
    260             //        oL.enabled = false;
    261             //        oL.enabled = true;
    262             //    }
    263             //}
    264         }
    265 
    266         void OnDestroy()
    267         {
    268             if (renderTexture != null)
    269                 renderTexture.Release();
    270             if (extraRenderTexture != null)
    271                 extraRenderTexture.Release();
    272             DestroyMaterials();
    273         }
    274 
    275         void OnRenderImage(RenderTexture source, RenderTexture destination)
    276         {
    277             if (outlineShaderMaterial != null)
    278             {
    279                 outlineShaderMaterial.SetTexture("_OutlineSource", renderTexture);
    280 
    281                 if (addLinesBetweenColors)
    282                 {
    283                     Graphics.Blit(source, extraRenderTexture, outlineShaderMaterial, 0);
    284                     outlineShaderMaterial.SetTexture("_OutlineSource", extraRenderTexture);
    285                 }
    286                 Graphics.Blit(source, destination, outlineShaderMaterial, 1);
    287             }
    288         }
    289 
    290         private void CreateMaterialsIfNeeded()
    291         {
    292             if (outlineShader == null)
    293                 outlineShader = Resources.Load<Shader>("Shaders/Outline/OutlineShader");
    294             if (outlineBufferShader == null)
    295             {
    296                 outlineBufferShader = Resources.Load<Shader>("Shaders/Outline/OutlineBufferShader");
    297             }
    298             if (outlineShaderMaterial == null)
    299             {
    300                 outlineShaderMaterial = new Material(outlineShader);
    301                 outlineShaderMaterial.hideFlags = HideFlags.HideAndDontSave;
    302                 UpdateMaterialsPublicProperties();
    303             }
    304             if (outlineEraseMaterial == null)
    305                 outlineEraseMaterial = CreateMaterial(new Color(0, 0, 0, 0));
    306             if (outline1Material == null)
    307                 outline1Material = CreateMaterial(new Color(1, 0, 0, 0));
    308             if (outline2Material == null)
    309                 outline2Material = CreateMaterial(new Color(0, 1, 0, 0));
    310             if (outline3Material == null)
    311                 outline3Material = CreateMaterial(new Color(0, 0, 1, 0));
    312             if (outline4Material == null)
    313                 outline4Material = CreateMaterial(new Color(0, 0, 0, 1));
    314         }
    315 
    316         private void DestroyMaterials()
    317         {
    318             foreach (Material m in materialBuffer)
    319                 DestroyImmediate(m);
    320             materialBuffer.Clear();
    321             DestroyImmediate(outlineShaderMaterial);
    322             DestroyImmediate(outlineEraseMaterial);
    323             DestroyImmediate(outline1Material);
    324             DestroyImmediate(outline2Material);
    325             DestroyImmediate(outline3Material);
    326             outlineShader = null;
    327             outlineBufferShader = null;
    328             outlineShaderMaterial = null;
    329             outlineEraseMaterial = null;
    330             outline1Material = null;
    331             outline2Material = null;
    332             outline3Material = null;
    333             outline4Material = null;
    334         }
    335 
    336         public void UpdateMaterialsPublicProperties()
    337         {
    338             if (outlineShaderMaterial)
    339             {
    340                 float scalingFactor = 1;
    341                 if (scaleWithScreenSize)
    342                 {
    343                     // If Screen.height gets bigger, outlines gets thicker
    344                     scalingFactor = Screen.height / 360.0f;
    345                 }
    346 
    347                 // If scaling is too small (height less than 360 pixels), make sure you still render the outlines, but render them with 1 thickness
    348                 if (scaleWithScreenSize && scalingFactor < 1)
    349                 {
    350                     if (UnityEngine.XR.XRSettings.isDeviceActive && sourceCamera.stereoTargetEye != StereoTargetEyeMask.None)
    351                     {
    352                         outlineShaderMaterial.SetFloat("_LineThicknessX", (1 / 1000.0f) * (1.0f / UnityEngine.XR.XRSettings.eyeTextureWidth) * 1000.0f);
    353                         outlineShaderMaterial.SetFloat("_LineThicknessY", (1 / 1000.0f) * (1.0f / UnityEngine.XR.XRSettings.eyeTextureHeight) * 1000.0f);
    354                     }
    355                     else
    356                     {
    357                         outlineShaderMaterial.SetFloat("_LineThicknessX", (1 / 1000.0f) * (1.0f / Screen.width) * 1000.0f);
    358                         outlineShaderMaterial.SetFloat("_LineThicknessY", (1 / 1000.0f) * (1.0f / Screen.height) * 1000.0f);
    359                     }
    360                 }
    361                 else
    362                 {
    363                     if (UnityEngine.XR.XRSettings.isDeviceActive && sourceCamera.stereoTargetEye != StereoTargetEyeMask.None)
    364                     {
    365                         outlineShaderMaterial.SetFloat("_LineThicknessX", scalingFactor * (lineThickness / 1000.0f) * (1.0f / UnityEngine.XR.XRSettings.eyeTextureWidth) * 1000.0f);
    366                         outlineShaderMaterial.SetFloat("_LineThicknessY", scalingFactor * (lineThickness / 1000.0f) * (1.0f / UnityEngine.XR.XRSettings.eyeTextureHeight) * 1000.0f);
    367                     }
    368                     else
    369                     {
    370                         outlineShaderMaterial.SetFloat("_LineThicknessX", scalingFactor * (lineThickness / 1000.0f) * (1.0f / Screen.width) * 1000.0f);
    371                         outlineShaderMaterial.SetFloat("_LineThicknessY", scalingFactor * (lineThickness / 1000.0f) * (1.0f / Screen.height) * 1000.0f);
    372                     }
    373                 }
    374                 outlineShaderMaterial.SetFloat("_LineIntensity", lineIntensity);
    375                 outlineShaderMaterial.SetFloat("_FillAmount", fillAmount);
    376                 outlineShaderMaterial.SetColor("_LineColor1", lineColor0 * lineColor0);
    377                 outlineShaderMaterial.SetColor("_LineColor2", lineColor1 * lineColor1);
    378                 outlineShaderMaterial.SetColor("_LineColor3", lineColor2 * lineColor2);
    379                 outlineShaderMaterial.SetColor("_LineColor4", lineColor3 * lineColor3);
    380                 if (flipY)
    381                     outlineShaderMaterial.SetInt("_FlipY", 1);
    382                 else
    383                     outlineShaderMaterial.SetInt("_FlipY", 0);
    384                 if (!additiveRendering)
    385                     outlineShaderMaterial.SetInt("_Dark", 1);
    386                 else
    387                     outlineShaderMaterial.SetInt("_Dark", 0);
    388                 if (cornerOutlines)
    389                     outlineShaderMaterial.SetInt("_CornerOutlines", 1);
    390                 else
    391                     outlineShaderMaterial.SetInt("_CornerOutlines", 0);
    392 
    393                 Shader.SetGlobalFloat("_OutlineAlphaCutoff", alphaCutoff);
    394             }
    395         }
    396 
    397         void UpdateOutlineCameraFromSource()
    398         {
    399             outlineCamera.CopyFrom(sourceCamera);
    400             outlineCamera.renderingPath = RenderingPath.Forward;
    401             outlineCamera.backgroundColor = new Color(0.0f, 0.0f, 0.0f, 0.0f);
    402             outlineCamera.clearFlags = CameraClearFlags.SolidColor;
    403             outlineCamera.rect = new Rect(0, 0, 1, 1);
    404             outlineCamera.cullingMask = 0;
    405             outlineCamera.targetTexture = renderTexture;
    406             outlineCamera.enabled = false;
    407 #if UNITY_EDITOR
    408             outlineCamera.allowHDR = false;
    409 #else
    410             outlineCamera.allowHDR = false;
    411 #endif
    412         }
    413 
    414         public void AddOutline(Outline outline)
    415             => outlines.Add(outline);
    416 
    417         public void RemoveOutline(Outline outline)
    418             => outlines.Remove(outline);
    419     }
    420 }
    View Code

    LinkedSet.cs

    实体高亮效果的集合相关逻辑类:

    辅助OutlineEffect类

     1 using System;
     2 using System.Collections.Generic;
     3 
     4 namespace Tx3d
     5 {
     6     /// <summary>
     7     ///  具有列表的快速迭代时间、无重复和快速删除/包含HashSet时间的集合。
     8     /// </summary>
     9     public class LinkedSet<T> : IEnumerable<T>
    10     {
    11         private LinkedList<T> list;
    12         private Dictionary<T, LinkedListNode<T>> dictionary;
    13 
    14         public LinkedSet()
    15         {
    16             list = new LinkedList<T>();
    17             dictionary = new Dictionary<T, LinkedListNode<T>>();
    18         }
    19 
    20         public LinkedSet(IEqualityComparer<T> comparer)
    21         {
    22             list = new LinkedList<T>();
    23             dictionary = new Dictionary<T, LinkedListNode<T>>(comparer);
    24         }
    25 
    26         /// <summary>
    27         /// 如果项在LinkedSet中不存在,则返回true
    28         /// </summary>
    29         public bool Add(T t)
    30         {
    31             if (dictionary.ContainsKey(t))
    32                 return false;
    33 
    34             LinkedListNode<T> node = list.AddLast(t);
    35             dictionary.Add(t, node);
    36             return true;
    37         }
    38 
    39         /// <summary>
    40         /// 如果项之前确实存在于LinkedSet中,则返回true
    41         /// </summary>
    42         public bool Remove(T t)
    43         {
    44             LinkedListNode<T> node;
    45 
    46             if (dictionary.TryGetValue(t, out node))
    47             {
    48                 dictionary.Remove(t);
    49                 list.Remove(node);
    50                 return true;
    51             }
    52             else
    53             {
    54                 return false;
    55             }
    56         }
    57 
    58         public void Clear()
    59         {
    60             list.Clear();
    61             dictionary.Clear();
    62         }
    63 
    64         public bool Contains(T t)
    65             => dictionary.ContainsKey(t);
    66 
    67         public int Count
    68             => list.Count;
    69 
    70         public IEnumerator<T> GetEnumerator()
    71             => list.GetEnumerator();
    72 
    73         System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    74             => list.GetEnumerator();
    75     }
    76 }
    View Code

    Outline.cs

    外边框高亮基本信息类:

    该信息类需要的mesh渲染器所在的物体上的信息(谁有MeshRenderer和MeshFilter物体的信息)

     1     /// <summary>
     2     /// 外边高亮基本信息类
     3     /// </summary>
     4     public class Outline
     5     {
     6         public Renderer Renderer { get; set; }
     7         public SkinnedMeshRenderer SkinnedMeshRenderer { get; set; }
     8         public MeshFilter MeshFilter { get; set; }
     9 
    10         public int color;
    11         public bool eraseRenderer;
    12 
    13         private Material[] _SharedMaterials;
    14         public Material[] SharedMaterials
    15         {
    16             get
    17             {
    18                 if (_SharedMaterials == null)
    19                     _SharedMaterials = Renderer.sharedMaterials;
    20 
    21                 return _SharedMaterials;
    22             }
    23         }
    24     }
    View Code

    OutlineEffect.shader

    计算所需要的shader

      1 Shader "Hidden/OutlineEffect" 
      2 {
      3     Properties 
      4     {
      5         _MainTex ("Base (RGB)", 2D) = "white" {}
      6         
      7     }
      8     SubShader 
      9     {
     10         Pass
     11         {
     12             Tags{ "RenderType" = "Opaque" }
     13             LOD 200
     14             ZTest Always
     15             ZWrite Off
     16             Cull Off
     17 
     18             CGPROGRAM
     19 
     20             #pragma vertex vert
     21             #pragma fragment frag
     22             #pragma target 3.0
     23             #include "UnityCG.cginc"
     24 
     25             sampler2D _MainTex;
     26             float4 _MainTex_ST;
     27             sampler2D _OutlineSource;
     28 
     29             struct v2f
     30             {
     31                 float4 position : SV_POSITION;
     32                 float2 uv : TEXCOORD0;
     33             };
     34 
     35             v2f vert(appdata_img v)
     36             {
     37                 v2f o;
     38                 o.position = UnityObjectToClipPos(v.vertex);
     39                 o.uv = v.texcoord;
     40 
     41                 return o;
     42             }
     43 
     44             float _LineThicknessX;
     45             float _LineThicknessY;
     46             int _FlipY;
     47             uniform float4 _MainTex_TexelSize;
     48 
     49             half4 frag(v2f input) : COLOR
     50             {
     51                 float2 uv = input.uv;
     52                 if (_FlipY == 1)
     53                     uv.y = uv.y;
     54                 #if UNITY_UV_STARTS_AT_TOP
     55                 if (_MainTex_TexelSize.y < 0)
     56                     uv.y = 1 - uv.y;
     57                 #endif
     58 
     59                 //half4 originalPixel = tex2D(_MainTex,input.uv, UnityStereoScreenSpaceUVAdjust(input.uv, _MainTex_ST));
     60                 half4 outlineSource = tex2D(_OutlineSource, UnityStereoScreenSpaceUVAdjust(uv, _MainTex_ST));
     61 
     62                 const float h = .95f;
     63 
     64                 half4 sample1 = tex2D(_OutlineSource, uv + float2(_LineThicknessX,0.0));
     65                 half4 sample2 = tex2D(_OutlineSource, uv + float2(-_LineThicknessX,0.0));
     66                 half4 sample3 = tex2D(_OutlineSource, uv + float2(.0,_LineThicknessY));
     67                 half4 sample4 = tex2D(_OutlineSource, uv + float2(.0,-_LineThicknessY));
     68 
     69                 bool red = sample1.r > h || sample2.r > h || sample3.r > h || sample4.r > h;
     70                 bool green = sample1.g > h || sample2.g > h || sample3.g > h || sample4.g > h;
     71                 bool blue = sample1.b > h || sample2.b > h || sample3.b > h || sample4.b > h;
     72                  
     73                 if ((red && blue) || (green && blue) || (red && green))
     74                     return float4(0,0,0,0);
     75                 else
     76                     return outlineSource;
     77             }
     78 
     79             ENDCG
     80         }
     81 
     82         Pass
     83         {
     84             Tags { "RenderType"="Opaque" }
     85             LOD 200
     86             ZTest Always
     87             ZWrite Off
     88             Cull Off
     89             
     90             CGPROGRAM
     91 
     92             #pragma vertex vert
     93             #pragma fragment frag
     94             #pragma target 3.0
     95             #include "UnityCG.cginc"
     96 
     97             sampler2D _MainTex;
     98             float4 _MainTex_ST;
     99             sampler2D _OutlineSource;
    100 
    101             struct v2f {
    102                float4 position : SV_POSITION;
    103                float2 uv : TEXCOORD0;
    104             };
    105             
    106             v2f vert(appdata_img v)
    107             {
    108                    v2f o;
    109                 o.position = UnityObjectToClipPos(v.vertex);
    110                 o.uv = v.texcoord;
    111                 
    112                    return o;
    113             }
    114 
    115             float _LineThicknessX;
    116             float _LineThicknessY;
    117             float _LineIntensity;
    118             half4 _LineColor1;
    119             half4 _LineColor2;
    120             half4 _LineColor3;
    121             half4 _LineColor4;
    122             int _FlipY;
    123             int _Dark;
    124             float _FillAmount;
    125             int _CornerOutlines;
    126             uniform float4 _MainTex_TexelSize;
    127 
    128             half4 frag (v2f input) : COLOR
    129             {    
    130                 float2 uv = input.uv;
    131                 if (_FlipY == 1)
    132                     uv.y = 1 - uv.y;
    133                 #if UNITY_UV_STARTS_AT_TOP
    134                     if (_MainTex_TexelSize.y < 0)
    135                         uv.y = 1 - uv.y;
    136                 #endif
    137 
    138                 half4 originalPixel = tex2D(_MainTex, UnityStereoScreenSpaceUVAdjust(input.uv, _MainTex_ST));
    139                 half4 outlineSource = tex2D(_OutlineSource, UnityStereoScreenSpaceUVAdjust(uv, _MainTex_ST));
    140                                 
    141                 const float h = .95f;
    142                 half4 outline = 0;
    143                 bool hasOutline = false;
    144 
    145                 half4 sample1 = tex2D(_OutlineSource, uv + float2(_LineThicknessX,0.0));
    146                 half4 sample2 = tex2D(_OutlineSource, uv + float2(-_LineThicknessX,0.0));
    147                 half4 sample3 = tex2D(_OutlineSource, uv + float2(.0,_LineThicknessY));
    148                 half4 sample4 = tex2D(_OutlineSource, uv + float2(.0,-_LineThicknessY));
    149                 
    150                 bool outside = outlineSource.a < h;
    151                 bool outsideDark = outside && _Dark;
    152 
    153                 if (_CornerOutlines)
    154                 {
    155                     // TODO: Conditional compile
    156                     half4 sample5 = tex2D(_OutlineSource, uv + float2(_LineThicknessX, _LineThicknessY));
    157                     half4 sample6 = tex2D(_OutlineSource, uv + float2(-_LineThicknessX, -_LineThicknessY));
    158                     half4 sample7 = tex2D(_OutlineSource, uv + float2(_LineThicknessX, -_LineThicknessY));
    159                     half4 sample8 = tex2D(_OutlineSource, uv + float2(-_LineThicknessX, _LineThicknessY));
    160 
    161                     if (sample1.r > h || sample2.r > h || sample3.r > h || sample4.r > h ||
    162                         sample5.r > h || sample6.r > h || sample7.r > h || sample8.r > h)
    163                     {
    164                         outline = _LineColor1 * _LineIntensity * _LineColor1.a;
    165                         if (outsideDark)
    166                             originalPixel *= 1 - _LineColor1.a;
    167                         hasOutline = true;
    168                     }
    169                     else if (sample1.g > h || sample2.g > h || sample3.g > h || sample4.g > h ||
    170                         sample5.g > h || sample6.g > h || sample7.g > h || sample8.g > h)
    171                     {
    172                         outline = _LineColor2 * _LineIntensity * _LineColor2.a;
    173                         if (outsideDark)
    174                             originalPixel *= 1 - _LineColor2.a;
    175                         hasOutline = true;
    176                     }
    177                     else if (sample1.b > h || sample2.b > h || sample3.b > h || sample4.b > h ||
    178                         sample5.b > h || sample6.b > h || sample7.b > h || sample8.b > h)
    179                     {
    180                         outline = _LineColor3 * _LineIntensity * _LineColor3.a;
    181                         if (outsideDark)
    182                             originalPixel *= 1 - _LineColor3.a;
    183                         hasOutline = true;
    184                     }
    185                     else if (sample1.a > h || sample2.a > h || sample3.a > h || sample4.a > h ||
    186                         sample5.a > h || sample6.a > h || sample7.a > h || sample8.a > h)
    187                     {
    188                         outline = _LineColor4 * _LineIntensity * _LineColor4.a;
    189                         if (outsideDark)
    190                             originalPixel *= 1 - _LineColor4.a;
    191                         hasOutline = true;
    192                     }
    193 
    194                     if (!outside)
    195                         outline *= _FillAmount;
    196                 }
    197                 else
    198                 {
    199                     if (sample1.r > h || sample2.r > h || sample3.r > h || sample4.r > h)
    200                     {
    201                         outline = _LineColor1 * _LineIntensity * _LineColor1.a;
    202                         if (outsideDark)
    203                             originalPixel *= 1 - _LineColor1.a;
    204                         hasOutline = true;
    205                     }
    206                     else if (sample1.g > h || sample2.g > h || sample3.g > h || sample4.g > h)
    207                     {
    208                         outline = _LineColor2 * _LineIntensity * _LineColor2.a;
    209                         if (outsideDark)
    210                             originalPixel *= 1 - _LineColor2.a;
    211                         hasOutline = true;
    212                     }
    213                     else if (sample1.b > h || sample2.b > h || sample3.b > h || sample4.b > h)
    214                     {
    215                         outline = _LineColor3 * _LineIntensity * _LineColor3.a;
    216                         if (outsideDark)
    217                             originalPixel *= 1 - _LineColor3.a;
    218                         hasOutline = true;
    219                     }
    220                     else if (sample1.a > h || sample2.a > h || sample3.a > h || sample4.a > h)
    221                     {
    222                         outline = _LineColor4 * _LineIntensity * _LineColor4.a;
    223                         if (outsideDark)
    224                             originalPixel *= 1 - _LineColor4.a;
    225                         hasOutline = true;
    226                     }
    227 
    228                     if (!outside)
    229                         outline *= _FillAmount;
    230                 }                    
    231                     
    232                 //return outlineSource;        
    233                 if (hasOutline)
    234                     return lerp(originalPixel + outline, outline, _FillAmount);
    235                 else
    236                     return originalPixel;
    237             }
    238             
    239             ENDCG
    240         }
    241     } 
    242 
    243     FallBack "Diffuse"
    244 }
    View Code

    OutlineBufferEffect.shader

    计算所需要的shader

     1 Shader "Hidden/OutlineBufferEffect" {
     2     Properties
     3     {
     4         [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
     5         _Color ("Tint", Color) = (1,1,1,1)
     6         [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
     7     }
     8 
     9     SubShader
    10     { 
    11         Tags
    12         {
    13             "Queue" = "Transparent"
    14             "IgnoreProjector" = "True"
    15             "RenderType" = "Transparent"
    16             "PreviewType" = "Plane"
    17             "CanUseSpriteAtlas" = "True"
    18         }
    19 
    20         // Change this stuff in OutlineEffect.cs instead!
    21         //ZWrite Off
    22         //Blend One OneMinusSrcAlpha
    23         Cull [_Culling]
    24         Lighting Off
    25             
    26         CGPROGRAM
    27 
    28         #pragma surface surf Lambert vertex:vert nofog noshadow noambient nolightmap novertexlights noshadowmask nometa //keepalpha
    29         #pragma multi_compile _ PIXELSNAP_ON
    30 
    31         sampler2D _MainTex;
    32         fixed4 _Color;
    33         float _OutlineAlphaCutoff;
    34 
    35         struct Input
    36         {
    37             float2 uv_MainTex;
    38             //fixed4 color;
    39         };
    40 
    41         void vert(inout appdata_full v, out Input o)
    42         {
    43             #if defined(PIXELSNAP_ON)
    44             v.vertex = UnityPixelSnap(v.vertex);
    45             #endif
    46 
    47             UNITY_INITIALIZE_OUTPUT(Input, o);
    48             //o.color = v.color;
    49         }
    50 
    51         void surf(Input IN, inout SurfaceOutput o)
    52         {
    53             fixed4 c = tex2D(_MainTex, IN.uv_MainTex);// * IN.color;
    54             if (c.a < _OutlineAlphaCutoff) discard;
    55 
    56             float alpha = c.a * 99999999;
    57 
    58             o.Albedo = _Color * alpha;
    59             o.Alpha = alpha;
    60             o.Emission = o.Albedo;
    61         }
    62 
    63         ENDCG        
    64     }
    65 
    66     Fallback "Transparent/VertexLit"
    67 }
    View Code

    //测试代码

    其中outline是上面的信息对象,通过OutlineEffect中的AddOutline函数以及 RemoveOutline函数对场景物体进行管理,将需要高亮的物体的mesh信息构建的基本信息类并使用AddOutline函数添加进去,才可以实现高亮,取消高亮即调用RemoveOutline移除取消高亮物体的信息

     1  // 实体是否高亮
     2         public bool Highlight
     3         {
     4             get => highlight;
     5             set
     6             {
     7                 highlight = value;
     8 
     9                 if (highlight)
    10                 {
    11                     if (gameObject != null)
    12                     {
    13                         outline = outline ?? new Outline();
    14                         outline.Renderer = gameObject.GetComponent<Renderer>();
    15                         outline.MeshFilter = gameObject.GetComponent<MeshFilter>();
    16                         outline.SkinnedMeshRenderer = gameObject.GetComponent<SkinnedMeshRenderer>();
    17                         OutlineEffect.Instance?.AddOutline(outline);
    18                     }
    19                 }
    20                 else
    21                 {
    22                     OutlineEffect.Instance?.RemoveOutline(outline);
    23                 }
    24             }
    25         }
    View Code

    ok,实现了,但是这里的shader是摘得,因为我还在shader的学习阶段,记录下功能吧也算是

    最新:

    按照上述方式实现外轮廓,会有很严重的锯齿,而且抗锯齿操作,由于OutlineCamera的 Renderertexture,本来渲的图就很糙,不规则很毛糙,直接边缘检测模糊处理起来也很糙,效果很差,所以不得不再找其他方式

    处理前效果:

    解决方案:高斯模糊,纵向模糊以及横向模糊两种模糊解决这个问题。

    模糊效果:

    具体步骤:

    1.将outlineCamera的RendererTexture全部模糊。

    2.再将轮廓线颜色再与主纹理混合

    注:

    1.r为1的地区,rbg=0,因为该区域应该事自己的颜色为非轮廓颜色

    2.OnRenderImage函数中只有在最后返回时才能动主Camera的 RenderTexture。

    主要代码:

     1      RenderTexture temp=null;
     2      
     3  private  void OnRenderImage(RenderTexture source, RenderTexture destination)
     4         {
     5             if (outlineShaderMaterial != null)
     6             {
     7                 outlineShaderMaterial.SetTexture("_OutlineSource", renderTexture);
     8 
     9                 if (temp==null)
    10                 {
    11                     temp = new RenderTexture(source.width,source.height, 16, RenderTextureFormat.Default);
    12                 }
    13 
    14                 ////高斯模糊轮廓逻辑
    15 
    16                 if (outlines.Count != 0)
    17                 {
    18                     if (golMaterial != null)
    19                     {
    20                         int rtW = source.width / downSample;
    21                         int rtH = source.height / downSample;
    22 
    23                         RenderTexture buffer0 = RenderTexture.GetTemporary(rtW, rtH, 0);
    24                         buffer0.filterMode = FilterMode.Bilinear;
    25                         
    26                         //轮廓颜色
    27                         golMaterial.SetColor("_TargetColor", lineColor0);
    28 
    29                         //将OutlineCamera的RendererTexture Copy 给buffer0
    30                         Graphics.Blit(renderTexture, buffer0);
    31 
    32                         for (int i = 0; i < iterations; i++)
    33                         {
    34                             golMaterial.SetFloat("_BlurSize", 1.0f + i * blurSpread);
    35 
    36                             RenderTexture buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);
    37 
    38                             // Render the vertical pass
    39                             Graphics.Blit(buffer0, buffer1, golMaterial, 0);
    40 
    41                             RenderTexture.ReleaseTemporary(buffer0);
    42                             buffer0 = buffer1;
    43                             buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);
    44 
    45                             // Render the horizontal pass
    46                             Graphics.Blit(buffer0, buffer1, golMaterial, 1);
    47 
    48                             RenderTexture.ReleaseTemporary(buffer0);
    49                             buffer0 = buffer1;
    50                         }
    51 
    52                         //将模糊完的纹理传给混合Shader,去混合,buffer0混合完的纹理
    53                         golMaterial.SetTexture("_OutlineSource", buffer0);
    54                         
    55                         //混合纹理输出
    56                         Graphics.Blit(source, destination, golMaterial, 2);
    57                         RenderTexture.ReleaseTemporary(buffer0);
    58                     }
    59                 }
    60                 else
    61                 {
    62                     Graphics.Blit(source, destination);
    63                 }
    64             }
    65         }

    高斯模糊Shader

      1 Shader "Unlit/MyShader"
      2 {
      3     Properties
      4     {
      5         _MainTex ("Texture", 2D) = "white" {}
      6         _BlurSize("Blur Size",Float) =1.0
      7         _TargetColor ("_TargetColor", Color) = (1,1,1,1)
      8     }
      9     SubShader
     10     {
     11           CGINCLUDE
     12 
     13             #include "UnityCG.cginc"
     14             
     15             sampler2D _MainTex;
     16             sampler2D _OutlineSource;
     17             half4 _MainTex_TexelSize;
     18             float4 _MainTex_ST;
     19             float _BlurSize;
     20             fixed4 _TargetColor;
     21             float _HighlightFlicker=0.0f;
     22 
     23             struct v2f
     24             {
     25                 float4 pos : SV_POSITION;
     26                 half2 uv[5] : TEXCOORD0;
     27             };
     28 
     29             
     30             struct v2
     31             {
     32                 float4 pos : SV_POSITION;
     33                 half2  uv: TEXCOORD0;
     34             };
     35 
     36             v2f vertBlurVertical (appdata_img  v)
     37             {
     38                 v2f o;
     39                 o.pos = UnityObjectToClipPos(v.vertex);
     40 
     41                 half2 uv=v.texcoord;
     42                 o.uv[0]=uv;
     43                 o.uv[1]=uv + float2(0.0,_MainTex_TexelSize.y * 1.0) * _BlurSize;
     44                 o.uv[2]=uv - float2(0.0,_MainTex_TexelSize.y * 1.0) * _BlurSize;
     45                 o.uv[3]=uv + float2(0.0,_MainTex_TexelSize.y * 2.0) * _BlurSize;
     46                 o.uv[4]=uv - float2(0.0,_MainTex_TexelSize.y * 2.0) * _BlurSize;
     47 
     48                 return o;
     49             }
     50 
     51             v2f vertBlurHorizontal(appdata_img v) 
     52             {
     53                v2f o;
     54                o.pos = UnityObjectToClipPos(v.vertex);
     55             
     56                half2 uv = v.texcoord;
     57             
     58                o.uv[0] = uv;
     59                o.uv[1] = uv + float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
     60                o.uv[2] = uv - float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
     61                o.uv[3] = uv + float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;
     62                o.uv[4] = uv - float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;
     63                      
     64                return o;
     65             }
     66             
     67             //融合
     68             v2 vertBlur(appdata_img v) 
     69             {
     70                v2 o;
     71                o.pos = UnityObjectToClipPos(v.vertex);
     72                o.uv = v.texcoord;     
     73                return o;
     74             }
     75 
     76             fixed4 fragBlur(v2f i) : SV_Target 
     77             {
     78                float weight[3] = {0.4026, 0.2442, 0.0545};
     79             
     80                fixed3 sum = tex2D(_MainTex, i.uv[0]).rgb * weight[0];
     81                fixed4 originalPixel = tex2D(_MainTex, UnityStereoScreenSpaceUVAdjust(i.uv[0], _MainTex_ST));
     82                
     83                for (int it = 1; it < 3; it++) {
     84                    sum += tex2D(_MainTex, i.uv[it*2-1]).rgb * weight[it];
     85                    sum += tex2D(_MainTex, i.uv[it*2]).rgb * weight[it];
     86                }
     87             
     88              return fixed4(sum, 1.0);
     89             } 
     90             
     91             //融合
     92             fixed4 frag(v2 i) : SV_Target 
     93             {
     94              fixed3 sum = tex2D(_OutlineSource, i.uv).rgb;
     95              fixed4 originalPixel = tex2D(_MainTex, UnityStereoScreenSpaceUVAdjust(i.uv, _MainTex_ST));
     96             // sum = sum.r > 0.95 ? 0 : sum;
     97              fixed temp = 1.0 - abs((sum.r - 0.5) / 0.5);
     98              fixed4 target=(1.0- temp)*originalPixel+_TargetColor*(temp);

    //闪烁
    99 if(_HighlightFlicker==1.0f) 100 target= (1.0- temp)*originalPixel+abs(sin(_Time.y * 1.5f))*_TargetColor*(temp); 101 else 102 target= (1.0- temp)*originalPixel+_TargetColor*(temp); 103 return target; 104 } 105 106 ENDCG 107 108 ZTest Always Cull Off ZWrite Off 109 110 Pass 111 { 112 NAME "GAUSSIAN_BLUR_VERTICAL" 113 114 CGPROGRAM 115 116 #pragma vertex vertBlurVertical 117 #pragma fragment fragBlur 118 119 ENDCG 120 } 121 122 Pass 123 { 124 NAME "GAUSSIAN_BLUR_HORIZONTAL" 125 126 CGPROGRAM 127 128 #pragma vertex vertBlurHorizontal 129 #pragma fragment fragBlur 130 131 ENDCG 132 } 133 134 Pass 135 { 136 NAME "GAUSSIAN_BLUR" 137 138 CGPROGRAM 139 140 #pragma vertex vertBlur 141 #pragma fragment frag 142 143 ENDCG 144 } 145 } 146 FallBack "Diffuse" 147 }

    效果:

  • 相关阅读:
    绝对路径相对路径
    LN项目重构之职责链模式
    年度回忆录(2011.072011.12)
    协议学习建议
    UBUNTU下制作软盘映
    从汇编看c语言函数调用
    计算机底层入门知识杂记(一)——计算机启动流程解析
    自己动手写操作体统 pmtest1.asm 详细解释
    汇编函数与C函数的相互调用
    嵌入式linux驱动开发班
  • 原文地址:https://www.cnblogs.com/answer-yj/p/12035181.html
Copyright © 2020-2023  润新知