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

Wesley13
• 阅读 522

问题背景:

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

效果图:

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

具体代码:

OutlineEffect.cs

实体高亮效果类:

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

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

  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类

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

 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物体的信息)

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

 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

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

  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

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

 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移除取消高亮物体的信息

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

 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,本来渲的图就很糙,不规则很毛糙,直接边缘检测模糊处理起来也很糙,效果很差,所以不得不再找其他方式

处理前效果:

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

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

模糊效果:

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

具体步骤:

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 }

效果:

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

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
陈占占 陈占占
2年前
PHP 利用confirm删除指定数据库的数据
完整的效果图方法一a标签href中的是你要删除记录html<ahref"PHPtest.php?name1"onclick"returnconfirm('是否要移除该小说?')"方法二下面这个方法是js代码,点击获取id,弹出提示框,确定是否删除,confirm好像可以返回true或者falsejavascriptfunctiond
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Wesley13 Wesley13
2年前
Unity横屏
Android下发现Unity里面的Player设置,并不能完全有效,比如打开了自动旋转,启动的时候还是会横屏,修改XML添加以下代码<applicationandroid:icon"@drawable/ic\_launcher"                    android:label"@string/app\_name"
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这