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

具体代码:
OutlineEffect.cs
实体高亮效果类:
轮廓边总控制类,该脚本需要挂载到场景相机上

1 1 using UnityEngine; 2 2 using System.Collections.Generic; 3 3 using UnityEngine.Rendering; 4 4 5 5 namespace Tx3d.Framework 6 6 { 7 7 [DisallowMultipleComponent] 8 8 [RequireComponent(typeof(Camera))] 9 9 [ExecuteInEditMode] 10 10 public class OutlineEffect : MonoBehaviour 11 11 { 12 12 public static OutlineEffect Instance { get; private set; } 13 13 14 14 private readonly LinkedSet<Outline> outlines = new LinkedSet<Outline>(); 15 15 16 16 [Range(1.0f, 6.0f)] 17 17 public float lineThickness = 1.0f; 18 18 [Range(0, 10)] 19 19 public float lineIntensity = 1.2f; 20 20 [Range(0, 1)] 21 21 public float fillAmount = 0.108f; 22 22 23 23 public Color lineColor0 = Color.yellow; 24 24 public Color lineColor1 = Color.green; 25 25 public Color lineColor2 = Color.blue; 26 26 public Color lineColor3 = Color.cyan; 27 27 28 28 public bool additiveRendering = false; 29 29 30 30 public bool backfaceCulling = true; 31 31 32 32 [Header("These settings can affect performance!")] 33 33 public bool cornerOutlines = false; 34 34 public bool addLinesBetweenColors = false; 35 35 36 36 [Header("Advanced settings")] 37 37 public bool scaleWithScreenSize = true; 38 38 [Range(0.1f, .9f)] 39 39 public float alphaCutoff = .5f; 40 40 public bool flipY = false; 41 41 public Camera sourceCamera; 42 42 public bool autoEnableOutlines = true; 43 43 44 44 [HideInInspector] 45 45 public Camera outlineCamera; 46 46 Material outline1Material; 47 47 Material outline2Material; 48 48 Material outline3Material; 49 49 Material outline4Material; 50 50 Material outlineEraseMaterial; 51 51 Shader outlineShader; 52 52 Shader outlineBufferShader; 53 53 [HideInInspector] 54 54 public Material outlineShaderMaterial; 55 55 [HideInInspector] 56 56 public RenderTexture renderTexture; 57 57 [HideInInspector] 58 58 public RenderTexture extraRenderTexture; 59 59 60 60 CommandBuffer commandBuffer; 61 61 62 62 Material GetMaterialFromID(int ID) 63 63 { 64 64 if (ID == 0) 65 65 return outline1Material; 66 66 else if (ID == 1) 67 67 return outline2Material; 68 68 else if (ID == 2) 69 69 return outline3Material; 70 70 else if (ID == 3) 71 71 return outline4Material; 72 72 else 73 73 return outline1Material; 74 74 } 75 75 List<Material> materialBuffer = new List<Material>(); 76 76 Material CreateMaterial(Color emissionColor) 77 77 { 78 78 Material m = new Material(outlineBufferShader); 79 79 m.SetColor("_Color", emissionColor); 80 80 m.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha); 81 81 m.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha); 82 82 m.SetInt("_ZWrite", 0); 83 83 m.DisableKeyword("_ALPHATEST_ON"); 84 84 m.EnableKeyword("_ALPHABLEND_ON"); 85 85 m.DisableKeyword("_ALPHAPREMULTIPLY_ON"); 86 86 m.renderQueue = 3000; 87 87 return m; 88 88 } 89 89 90 90 private void Awake() 91 91 { 92 92 if (Instance != null) 93 93 { 94 94 Destroy(this); 95 95 throw new System.Exception("you can only have one outline camera in the scene"); 96 96 } 97 97 98 98 Instance = this; 99 99 } 100100 101101 void Start() 102102 { 103103 CreateMaterialsIfNeeded(); 104104 UpdateMaterialsPublicProperties(); 105105 106106 if (sourceCamera == null) 107107 { 108108 sourceCamera = GetComponent<Camera>(); 109109 110110 if (sourceCamera == null) 111111 sourceCamera = Camera.main; 112112 } 113113 114114 if (outlineCamera == null) 115115 { 116116 foreach (Camera c in GetComponentsInChildren<Camera>()) 117117 { 118118 if (c.name == "Outline Camera") 119119 { 120120 outlineCamera = c; 121121 c.enabled = false; 122122 123123 break; 124124 } 125125 } 126126 127127 if (outlineCamera == null) 128128 { 129129 GameObject cameraGameObject = new GameObject("Outline Camera"); 130130 cameraGameObject.transform.parent = sourceCamera.transform; 131131 outlineCamera = cameraGameObject.AddComponent<Camera>(); 132132 outlineCamera.enabled = false; 133133 } 134134 } 135135 136136 renderTexture = new RenderTexture(sourceCamera.pixelWidth, sourceCamera.pixelHeight, 16, RenderTextureFormat.Default); 137137 extraRenderTexture = new RenderTexture(sourceCamera.pixelWidth, sourceCamera.pixelHeight, 16, RenderTextureFormat.Default); 138138 UpdateOutlineCameraFromSource(); 139139 140140 commandBuffer = new CommandBuffer(); 141141 outlineCamera.AddCommandBuffer(CameraEvent.BeforeImageEffects, commandBuffer); 142142 } 143143 144144 bool RenderTheNextFrame; 145145 public void OnPreRender() 146146 { 147147 if (commandBuffer == null) 148148 return; 149149 150150 // the first frame during which there are no outlines, we still need to render 151151 // to clear out any outlines that were being rendered on the previous frame 152152 if (outlines.Count == 0) 153153 { 154154 if (!RenderTheNextFrame) 155155 return; 156156 157157 RenderTheNextFrame = false; 158158 } 159159 else 160160 { 161161 RenderTheNextFrame = true; 162162 } 163163 164164 CreateMaterialsIfNeeded(); 165165 166166 if (renderTexture == null || renderTexture.width != sourceCamera.pixelWidth || renderTexture.height != sourceCamera.pixelHeight) 167167 { 168168 renderTexture = new RenderTexture(sourceCamera.pixelWidth, sourceCamera.pixelHeight, 16, RenderTextureFormat.Default); 169169 extraRenderTexture = new RenderTexture(sourceCamera.pixelWidth, sourceCamera.pixelHeight, 16, RenderTextureFormat.Default); 170170 outlineCamera.targetTexture = renderTexture; 171171 } 172172 UpdateMaterialsPublicProperties(); 173173 UpdateOutlineCameraFromSource(); 174174 outlineCamera.targetTexture = renderTexture; 175175 commandBuffer.SetRenderTarget(renderTexture); 176176 177177 commandBuffer.Clear(); 178178 179179 foreach (Outline outline in outlines) 180180 { 181181 LayerMask l = sourceCamera.cullingMask; 182182 183183 // if (outline != null && l == (l | (1 << outline.gameObject.layer))) 184184 if (outline != null) 185185 { 186186 for (int v = 0; v < outline.SharedMaterials.Length; v++) 187187 { 188188 Material m = null; 189189 190190 if (outline.SharedMaterials[v].mainTexture != null && outline.SharedMaterials[v]) 191191 { 192192 foreach (Material g in materialBuffer) 193193 { 194194 if (g.mainTexture == outline.SharedMaterials[v].mainTexture) 195195 { 196196 if (outline.eraseRenderer && g.color == outlineEraseMaterial.color) 197197 m = g; 198198 else if (g.color == GetMaterialFromID(outline.color).color) 199199 m = g; 200200 } 201201 } 202202 203203 if (m == null) 204204 { 205205 if (outline.eraseRenderer) 206206 m = new Material(outlineEraseMaterial); 207207 else 208208 m = new Material(GetMaterialFromID(outline.color)); 209209 m.mainTexture = outline.SharedMaterials[v].mainTexture; 210210 materialBuffer.Add(m); 211211 } 212212 } 213213 else 214214 { 215215 if (outline.eraseRenderer) 216216 m = outlineEraseMaterial; 217217 else 218218 m = GetMaterialFromID(outline.color); 219219 } 220220 221221 if (backfaceCulling) 222222 m.SetInt("_Culling", (int)UnityEngine.Rendering.CullMode.Back); 223223 else 224224 m.SetInt("_Culling", (int)UnityEngine.Rendering.CullMode.Off); 225225 226226 commandBuffer.DrawRenderer(outline.Renderer, m, 0, 0); 227227 MeshFilter mL = outline.MeshFilter; 228228 if (mL) 229229 { 230230 if (mL.sharedMesh != null) 231231 { 232232 for (int i = 1; i < mL.sharedMesh.subMeshCount; i++) 233233 commandBuffer.DrawRenderer(outline.Renderer, m, i, 0); 234234 } 235235 } 236236 SkinnedMeshRenderer sMR = outline.SkinnedMeshRenderer; 237237 if (sMR) 238238 { 239239 if (sMR.sharedMesh != null) 240240 { 241241 for (int i = 1; i < sMR.sharedMesh.subMeshCount; i++) 242242 commandBuffer.DrawRenderer(outline.Renderer, m, i, 0); 243243 } 244244 } 245245 } 246246 } 247247 } 248248 249249 outlineCamera.Render(); 250250 } 251251 252252 private void OnEnable() 253253 { 254254 //if (autoEnableOutlines) 255255 //{ 256256 // Outline[] o = FindObjectsOfType<Outline>(); 257257 258258 // foreach (Outline oL in o) 259259 // { 260260 // oL.enabled = false; 261261 // oL.enabled = true; 262262 // } 263263 //} 264264 } 265265 266266 void OnDestroy() 267267 { 268268 if (renderTexture != null) 269269 renderTexture.Release(); 270270 if (extraRenderTexture != null) 271271 extraRenderTexture.Release(); 272272 DestroyMaterials(); 273273 } 274274 275275 void OnRenderImage(RenderTexture source, RenderTexture destination) 276276 { 277277 if (outlineShaderMaterial != null) 278278 { 279279 outlineShaderMaterial.SetTexture("_OutlineSource", renderTexture); 280280 281281 if (addLinesBetweenColors) 282282 { 283283 Graphics.Blit(source, extraRenderTexture, outlineShaderMaterial, 0); 284284 outlineShaderMaterial.SetTexture("_OutlineSource", extraRenderTexture); 285285 } 286286 Graphics.Blit(source, destination, outlineShaderMaterial, 1); 287287 } 288288 } 289289 290290 private void CreateMaterialsIfNeeded() 291291 { 292292 if (outlineShader == null) 293293 outlineShader = Resources.Load<Shader>("Shaders/Outline/OutlineShader"); 294294 if (outlineBufferShader == null) 295295 { 296296 outlineBufferShader = Resources.Load<Shader>("Shaders/Outline/OutlineBufferShader"); 297297 } 298298 if (outlineShaderMaterial == null) 299299 { 300300 outlineShaderMaterial = new Material(outlineShader); 301301 outlineShaderMaterial.hideFlags = HideFlags.HideAndDontSave; 302302 UpdateMaterialsPublicProperties(); 303303 } 304304 if (outlineEraseMaterial == null) 305305 outlineEraseMaterial = CreateMaterial(new Color(0, 0, 0, 0)); 306306 if (outline1Material == null) 307307 outline1Material = CreateMaterial(new Color(1, 0, 0, 0)); 308308 if (outline2Material == null) 309309 outline2Material = CreateMaterial(new Color(0, 1, 0, 0)); 310310 if (outline3Material == null) 311311 outline3Material = CreateMaterial(new Color(0, 0, 1, 0)); 312312 if (outline4Material == null) 313313 outline4Material = CreateMaterial(new Color(0, 0, 0, 1)); 314314 } 315315 316316 private void DestroyMaterials() 317317 { 318318 foreach (Material m in materialBuffer) 319319 DestroyImmediate(m); 320320 materialBuffer.Clear(); 321321 DestroyImmediate(outlineShaderMaterial); 322322 DestroyImmediate(outlineEraseMaterial); 323323 DestroyImmediate(outline1Material); 324324 DestroyImmediate(outline2Material); 325325 DestroyImmediate(outline3Material); 326326 outlineShader = null; 327327 outlineBufferShader = null; 328328 outlineShaderMaterial = null; 329329 outlineEraseMaterial = null; 330330 outline1Material = null; 331331 outline2Material = null; 332332 outline3Material = null; 333333 outline4Material = null; 334334 } 335335 336336 public void UpdateMaterialsPublicProperties() 337337 { 338338 if (outlineShaderMaterial) 339339 { 340340 float scalingFactor = 1; 341341 if (scaleWithScreenSize) 342342 { 343343 // If Screen.height gets bigger, outlines gets thicker 344344 scalingFactor = Screen.height / 360.0f; 345345 } 346346 347347 // If scaling is too small (height less than 360 pixels), make sure you still render the outlines, but render them with 1 thickness 348348 if (scaleWithScreenSize && scalingFactor < 1) 349349 { 350350 if (UnityEngine.XR.XRSettings.isDeviceActive && sourceCamera.stereoTargetEye != StereoTargetEyeMask.None) 351351 { 352352 outlineShaderMaterial.SetFloat("_LineThicknessX", (1 / 1000.0f) * (1.0f / UnityEngine.XR.XRSettings.eyeTextureWidth) * 1000.0f); 353353 outlineShaderMaterial.SetFloat("_LineThicknessY", (1 / 1000.0f) * (1.0f / UnityEngine.XR.XRSettings.eyeTextureHeight) * 1000.0f); 354354 } 355355 else 356356 { 357357 outlineShaderMaterial.SetFloat("_LineThicknessX", (1 / 1000.0f) * (1.0f / Screen.width) * 1000.0f); 358358 outlineShaderMaterial.SetFloat("_LineThicknessY", (1 / 1000.0f) * (1.0f / Screen.height) * 1000.0f); 359359 } 360360 } 361361 else 362362 { 363363 if (UnityEngine.XR.XRSettings.isDeviceActive && sourceCamera.stereoTargetEye != StereoTargetEyeMask.None) 364364 { 365365 outlineShaderMaterial.SetFloat("_LineThicknessX", scalingFactor * (lineThickness / 1000.0f) * (1.0f / UnityEngine.XR.XRSettings.eyeTextureWidth) * 1000.0f); 366366 outlineShaderMaterial.SetFloat("_LineThicknessY", scalingFactor * (lineThickness / 1000.0f) * (1.0f / UnityEngine.XR.XRSettings.eyeTextureHeight) * 1000.0f); 367367 } 368368 else 369369 { 370370 outlineShaderMaterial.SetFloat("_LineThicknessX", scalingFactor * (lineThickness / 1000.0f) * (1.0f / Screen.width) * 1000.0f); 371371 outlineShaderMaterial.SetFloat("_LineThicknessY", scalingFactor * (lineThickness / 1000.0f) * (1.0f / Screen.height) * 1000.0f); 372372 } 373373 } 374374 outlineShaderMaterial.SetFloat("_LineIntensity", lineIntensity); 375375 outlineShaderMaterial.SetFloat("_FillAmount", fillAmount); 376376 outlineShaderMaterial.SetColor("_LineColor1", lineColor0 * lineColor0); 377377 outlineShaderMaterial.SetColor("_LineColor2", lineColor1 * lineColor1); 378378 outlineShaderMaterial.SetColor("_LineColor3", lineColor2 * lineColor2); 379379 outlineShaderMaterial.SetColor("_LineColor4", lineColor3 * lineColor3); 380380 if (flipY) 381381 outlineShaderMaterial.SetInt("_FlipY", 1); 382382 else 383383 outlineShaderMaterial.SetInt("_FlipY", 0); 384384 if (!additiveRendering) 385385 outlineShaderMaterial.SetInt("_Dark", 1); 386386 else 387387 outlineShaderMaterial.SetInt("_Dark", 0); 388388 if (cornerOutlines) 389389 outlineShaderMaterial.SetInt("_CornerOutlines", 1); 390390 else 391391 outlineShaderMaterial.SetInt("_CornerOutlines", 0); 392392 393393 Shader.SetGlobalFloat("_OutlineAlphaCutoff", alphaCutoff); 394394 } 395395 } 396396 397397 void UpdateOutlineCameraFromSource() 398398 { 399399 outlineCamera.CopyFrom(sourceCamera); 400400 outlineCamera.renderingPath = RenderingPath.Forward; 401401 outlineCamera.backgroundColor = new Color(0.0f, 0.0f, 0.0f, 0.0f); 402402 outlineCamera.clearFlags = CameraClearFlags.SolidColor; 403403 outlineCamera.rect = new Rect(0, 0, 1, 1); 404404 outlineCamera.cullingMask = 0; 405405 outlineCamera.targetTexture = renderTexture; 406406 outlineCamera.enabled = false; 407407 #if UNITY_EDITOR 408408 outlineCamera.allowHDR = false; 409409 #else 410410 outlineCamera.allowHDR = false; 411411 #endif 412412 } 413413 414414 public void AddOutline(Outline outline) 415415 => outlines.Add(outline); 416416 417417 public void RemoveOutline(Outline outline) 418418 => outlines.Remove(outline); 419419 } 420420 }
View Code
LinkedSet.cs
实体高亮效果的集合相关逻辑类:
辅助OutlineEffect类

1 1 using System; 2 2 using System.Collections.Generic; 3 3 4 4 namespace Tx3d 5 5 { 6 6 /// <summary> 7 7 /// 具有列表的快速迭代时间、无重复和快速删除/包含HashSet时间的集合。 8 8 /// </summary> 9 9 public class LinkedSet<T> : IEnumerable<T> 1010 { 1111 private LinkedList<T> list; 1212 private Dictionary<T, LinkedListNode<T>> dictionary; 1313 1414 public LinkedSet() 1515 { 1616 list = new LinkedList<T>(); 1717 dictionary = new Dictionary<T, LinkedListNode<T>>(); 1818 } 1919 2020 public LinkedSet(IEqualityComparer<T> comparer) 2121 { 2222 list = new LinkedList<T>(); 2323 dictionary = new Dictionary<T, LinkedListNode<T>>(comparer); 2424 } 2525 2626 /// <summary> 2727 /// 如果项在LinkedSet中不存在,则返回true 2828 /// </summary> 2929 public bool Add(T t) 3030 { 3131 if (dictionary.ContainsKey(t)) 3232 return false; 3333 3434 LinkedListNode<T> node = list.AddLast(t); 3535 dictionary.Add(t, node); 3636 return true; 3737 } 3838 3939 /// <summary> 4040 /// 如果项之前确实存在于LinkedSet中,则返回true 4141 /// </summary> 4242 public bool Remove(T t) 4343 { 4444 LinkedListNode<T> node; 4545 4646 if (dictionary.TryGetValue(t, out node)) 4747 { 4848 dictionary.Remove(t); 4949 list.Remove(node); 5050 return true; 5151 } 5252 else 5353 { 5454 return false; 5555 } 5656 } 5757 5858 public void Clear() 5959 { 6060 list.Clear(); 6161 dictionary.Clear(); 6262 } 6363 6464 public bool Contains(T t) 6565 => dictionary.ContainsKey(t); 6666 6767 public int Count 6868 => list.Count; 6969 7070 public IEnumerator<T> GetEnumerator() 7171 => list.GetEnumerator(); 7272 7373 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 7474 => list.GetEnumerator(); 7575 } 7676 }
View Code
Outline.cs
外边框高亮基本信息类:
该信息类需要的mesh渲染器所在的物体上的信息(谁有MeshRenderer和MeshFilter物体的信息)

1 1 /// <summary> 2 2 /// 外边高亮基本信息类 3 3 /// </summary> 4 4 public class Outline 5 5 { 6 6 public Renderer Renderer { get; set; } 7 7 public SkinnedMeshRenderer SkinnedMeshRenderer { get; set; } 8 8 public MeshFilter MeshFilter { get; set; } 9 9 1010 public int color; 1111 public bool eraseRenderer; 1212 1313 private Material[] _SharedMaterials; 1414 public Material[] SharedMaterials 1515 { 1616 get 1717 { 1818 if (_SharedMaterials == null) 1919 _SharedMaterials = Renderer.sharedMaterials; 2020 2121 return _SharedMaterials; 2222 } 2323 } 2424 }
View Code
OutlineEffect.shader
计算所需要的shader

1 1 Shader "Hidden/OutlineEffect" 2 2 { 3 3 Properties 4 4 { 5 5 _MainTex ("Base (RGB)", 2D) = "white" {} 6 6 7 7 } 8 8 SubShader 9 9 { 10 10 Pass 11 11 { 12 12 Tags{ "RenderType" = "Opaque" } 13 13 LOD 200 14 14 ZTest Always 15 15 ZWrite Off 16 16 Cull Off 17 17 18 18 CGPROGRAM 19 19 20 20 #pragma vertex vert 21 21 #pragma fragment frag 22 22 #pragma target 3.0 23 23 #include "UnityCG.cginc" 24 24 25 25 sampler2D _MainTex; 26 26 float4 _MainTex_ST; 27 27 sampler2D _OutlineSource; 28 28 29 29 struct v2f 30 30 { 31 31 float4 position : SV_POSITION; 32 32 float2 uv : TEXCOORD0; 33 33 }; 34 34 35 35 v2f vert(appdata_img v) 36 36 { 37 37 v2f o; 38 38 o.position = UnityObjectToClipPos(v.vertex); 39 39 o.uv = v.texcoord; 40 40 41 41 return o; 42 42 } 43 43 44 44 float _LineThicknessX; 45 45 float _LineThicknessY; 46 46 int _FlipY; 47 47 uniform float4 _MainTex_TexelSize; 48 48 49 49 half4 frag(v2f input) : COLOR 50 50 { 51 51 float2 uv = input.uv; 52 52 if (_FlipY == 1) 53 53 uv.y = uv.y; 54 54 #if UNITY_UV_STARTS_AT_TOP 55 55 if (_MainTex_TexelSize.y < 0) 56 56 uv.y = 1 - uv.y; 57 57 #endif 58 58 59 59 //half4 originalPixel = tex2D(_MainTex,input.uv, UnityStereoScreenSpaceUVAdjust(input.uv, _MainTex_ST)); 60 60 half4 outlineSource = tex2D(_OutlineSource, UnityStereoScreenSpaceUVAdjust(uv, _MainTex_ST)); 61 61 62 62 const float h = .95f; 63 63 64 64 half4 sample1 = tex2D(_OutlineSource, uv + float2(_LineThicknessX,0.0)); 65 65 half4 sample2 = tex2D(_OutlineSource, uv + float2(-_LineThicknessX,0.0)); 66 66 half4 sample3 = tex2D(_OutlineSource, uv + float2(.0,_LineThicknessY)); 67 67 half4 sample4 = tex2D(_OutlineSource, uv + float2(.0,-_LineThicknessY)); 68 68 69 69 bool red = sample1.r > h || sample2.r > h || sample3.r > h || sample4.r > h; 70 70 bool green = sample1.g > h || sample2.g > h || sample3.g > h || sample4.g > h; 71 71 bool blue = sample1.b > h || sample2.b > h || sample3.b > h || sample4.b > h; 72 72 73 73 if ((red && blue) || (green && blue) || (red && green)) 74 74 return float4(0,0,0,0); 75 75 else 76 76 return outlineSource; 77 77 } 78 78 79 79 ENDCG 80 80 } 81 81 82 82 Pass 83 83 { 84 84 Tags { "RenderType"="Opaque" } 85 85 LOD 200 86 86 ZTest Always 87 87 ZWrite Off 88 88 Cull Off 89 89 90 90 CGPROGRAM 91 91 92 92 #pragma vertex vert 93 93 #pragma fragment frag 94 94 #pragma target 3.0 95 95 #include "UnityCG.cginc" 96 96 97 97 sampler2D _MainTex; 98 98 float4 _MainTex_ST; 99 99 sampler2D _OutlineSource; 100100 101101 struct v2f { 102102 float4 position : SV_POSITION; 103103 float2 uv : TEXCOORD0; 104104 }; 105105 106106 v2f vert(appdata_img v) 107107 { 108108 v2f o; 109109 o.position = UnityObjectToClipPos(v.vertex); 110110 o.uv = v.texcoord; 111111 112112 return o; 113113 } 114114 115115 float _LineThicknessX; 116116 float _LineThicknessY; 117117 float _LineIntensity; 118118 half4 _LineColor1; 119119 half4 _LineColor2; 120120 half4 _LineColor3; 121121 half4 _LineColor4; 122122 int _FlipY; 123123 int _Dark; 124124 float _FillAmount; 125125 int _CornerOutlines; 126126 uniform float4 _MainTex_TexelSize; 127127 128128 half4 frag (v2f input) : COLOR 129129 { 130130 float2 uv = input.uv; 131131 if (_FlipY == 1) 132132 uv.y = 1 - uv.y; 133133 #if UNITY_UV_STARTS_AT_TOP 134134 if (_MainTex_TexelSize.y < 0) 135135 uv.y = 1 - uv.y; 136136 #endif 137137 138138 half4 originalPixel = tex2D(_MainTex, UnityStereoScreenSpaceUVAdjust(input.uv, _MainTex_ST)); 139139 half4 outlineSource = tex2D(_OutlineSource, UnityStereoScreenSpaceUVAdjust(uv, _MainTex_ST)); 140140 141141 const float h = .95f; 142142 half4 outline = 0; 143143 bool hasOutline = false; 144144 145145 half4 sample1 = tex2D(_OutlineSource, uv + float2(_LineThicknessX,0.0)); 146146 half4 sample2 = tex2D(_OutlineSource, uv + float2(-_LineThicknessX,0.0)); 147147 half4 sample3 = tex2D(_OutlineSource, uv + float2(.0,_LineThicknessY)); 148148 half4 sample4 = tex2D(_OutlineSource, uv + float2(.0,-_LineThicknessY)); 149149 150150 bool outside = outlineSource.a < h; 151151 bool outsideDark = outside && _Dark; 152152 153153 if (_CornerOutlines) 154154 { 155155 // TODO: Conditional compile 156156 half4 sample5 = tex2D(_OutlineSource, uv + float2(_LineThicknessX, _LineThicknessY)); 157157 half4 sample6 = tex2D(_OutlineSource, uv + float2(-_LineThicknessX, -_LineThicknessY)); 158158 half4 sample7 = tex2D(_OutlineSource, uv + float2(_LineThicknessX, -_LineThicknessY)); 159159 half4 sample8 = tex2D(_OutlineSource, uv + float2(-_LineThicknessX, _LineThicknessY)); 160160 161161 if (sample1.r > h || sample2.r > h || sample3.r > h || sample4.r > h || 162162 sample5.r > h || sample6.r > h || sample7.r > h || sample8.r > h) 163163 { 164164 outline = _LineColor1 * _LineIntensity * _LineColor1.a; 165165 if (outsideDark) 166166 originalPixel *= 1 - _LineColor1.a; 167167 hasOutline = true; 168168 } 169169 else if (sample1.g > h || sample2.g > h || sample3.g > h || sample4.g > h || 170170 sample5.g > h || sample6.g > h || sample7.g > h || sample8.g > h) 171171 { 172172 outline = _LineColor2 * _LineIntensity * _LineColor2.a; 173173 if (outsideDark) 174174 originalPixel *= 1 - _LineColor2.a; 175175 hasOutline = true; 176176 } 177177 else if (sample1.b > h || sample2.b > h || sample3.b > h || sample4.b > h || 178178 sample5.b > h || sample6.b > h || sample7.b > h || sample8.b > h) 179179 { 180180 outline = _LineColor3 * _LineIntensity * _LineColor3.a; 181181 if (outsideDark) 182182 originalPixel *= 1 - _LineColor3.a; 183183 hasOutline = true; 184184 } 185185 else if (sample1.a > h || sample2.a > h || sample3.a > h || sample4.a > h || 186186 sample5.a > h || sample6.a > h || sample7.a > h || sample8.a > h) 187187 { 188188 outline = _LineColor4 * _LineIntensity * _LineColor4.a; 189189 if (outsideDark) 190190 originalPixel *= 1 - _LineColor4.a; 191191 hasOutline = true; 192192 } 193193 194194 if (!outside) 195195 outline *= _FillAmount; 196196 } 197197 else 198198 { 199199 if (sample1.r > h || sample2.r > h || sample3.r > h || sample4.r > h) 200200 { 201201 outline = _LineColor1 * _LineIntensity * _LineColor1.a; 202202 if (outsideDark) 203203 originalPixel *= 1 - _LineColor1.a; 204204 hasOutline = true; 205205 } 206206 else if (sample1.g > h || sample2.g > h || sample3.g > h || sample4.g > h) 207207 { 208208 outline = _LineColor2 * _LineIntensity * _LineColor2.a; 209209 if (outsideDark) 210210 originalPixel *= 1 - _LineColor2.a; 211211 hasOutline = true; 212212 } 213213 else if (sample1.b > h || sample2.b > h || sample3.b > h || sample4.b > h) 214214 { 215215 outline = _LineColor3 * _LineIntensity * _LineColor3.a; 216216 if (outsideDark) 217217 originalPixel *= 1 - _LineColor3.a; 218218 hasOutline = true; 219219 } 220220 else if (sample1.a > h || sample2.a > h || sample3.a > h || sample4.a > h) 221221 { 222222 outline = _LineColor4 * _LineIntensity * _LineColor4.a; 223223 if (outsideDark) 224224 originalPixel *= 1 - _LineColor4.a; 225225 hasOutline = true; 226226 } 227227 228228 if (!outside) 229229 outline *= _FillAmount; 230230 } 231231 232232 //return outlineSource; 233233 if (hasOutline) 234234 return lerp(originalPixel + outline, outline, _FillAmount); 235235 else 236236 return originalPixel; 237237 } 238238 239239 ENDCG 240240 } 241241 } 242242 243243 FallBack "Diffuse" 244244 }
View Code
OutlineBufferEffect.shader
计算所需要的shader

1 1 Shader "Hidden/OutlineBufferEffect" { 2 2 Properties 3 3 { 4 4 [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {} 5 5 _Color ("Tint", Color) = (1,1,1,1) 6 6 [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0 7 7 } 8 8 9 9 SubShader 1010 { 1111 Tags 1212 { 1313 "Queue" = "Transparent" 1414 "IgnoreProjector" = "True" 1515 "RenderType" = "Transparent" 1616 "PreviewType" = "Plane" 1717 "CanUseSpriteAtlas" = "True" 1818 } 1919 2020 // Change this stuff in OutlineEffect.cs instead! 2121 //ZWrite Off 2222 //Blend One OneMinusSrcAlpha 2323 Cull [_Culling] 2424 Lighting Off 2525 2626 CGPROGRAM 2727 2828 #pragma surface surf Lambert vertex:vert nofog noshadow noambient nolightmap novertexlights noshadowmask nometa //keepalpha 2929 #pragma multi_compile _ PIXELSNAP_ON 3030 3131 sampler2D _MainTex; 3232 fixed4 _Color; 3333 float _OutlineAlphaCutoff; 3434 3535 struct Input 3636 { 3737 float2 uv_MainTex; 3838 //fixed4 color; 3939 }; 4040 4141 void vert(inout appdata_full v, out Input o) 4242 { 4343 #if defined(PIXELSNAP_ON) 4444 v.vertex = UnityPixelSnap(v.vertex); 4545 #endif 4646 4747 UNITY_INITIALIZE_OUTPUT(Input, o); 4848 //o.color = v.color; 4949 } 5050 5151 void surf(Input IN, inout SurfaceOutput o) 5252 { 5353 fixed4 c = tex2D(_MainTex, IN.uv_MainTex);// * IN.color; 5454 if (c.a < _OutlineAlphaCutoff) discard; 5555 5656 float alpha = c.a * 99999999; 5757 5858 o.Albedo = _Color * alpha; 5959 o.Alpha = alpha; 6060 o.Emission = o.Albedo; 6161 } 6262 6363 ENDCG 6464 } 6565 6666 Fallback "Transparent/VertexLit" 6767 }
View Code
//测试代码
其中outline是上面的信息对象,通过OutlineEffect中的AddOutline函数以及 RemoveOutline函数对场景物体进行管理,将需要高亮的物体的mesh信息构建的基本信息类并使用AddOutline函数添加进去,才可以实现高亮,取消高亮即调用RemoveOutline移除取消高亮物体的信息

1 1 // 实体是否高亮 2 2 public bool Highlight 3 3 { 4 4 get => highlight; 5 5 set 6 6 { 7 7 highlight = value; 8 8 9 9 if (highlight) 1010 { 1111 if (gameObject != null) 1212 { 1313 outline = outline ?? new Outline(); 1414 outline.Renderer = gameObject.GetComponent<Renderer>(); 1515 outline.MeshFilter = gameObject.GetComponent<MeshFilter>(); 1616 outline.SkinnedMeshRenderer = gameObject.GetComponent<SkinnedMeshRenderer>(); 1717 OutlineEffect.Instance?.AddOutline(outline); 1818 } 1919 } 2020 else 2121 { 2222 OutlineEffect.Instance?.RemoveOutline(outline); 2323 } 2424 } 2525 }
View Code
ok,实现了,但是这里的shader是摘得,因为我还在shader的学习阶段,记录下功能吧也算是
最新:
按照上述方式实现外轮廓,会有很严重的锯齿,而且抗锯齿操作,由于OutlineCamera的 Renderertexture,本来渲的图就很糙,不规则很毛糙,直接边缘检测模糊处理起来也很糙,效果很差,所以不得不再找其他方式
处理前效果:

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

具体步骤:
1.将outlineCamera的RendererTexture全部模糊。
2.再将轮廓线颜色再与主纹理混合
注:
1.r为1的地区,rbg=0,因为该区域应该事自己的颜色为非轮廓颜色
2.OnRenderImage函数中只有在最后返回时才能动主Camera的 RenderTexture。
主要代码:
1 1 RenderTexture temp=null; 2 2 3 3 private void OnRenderImage(RenderTexture source, RenderTexture destination) 4 4 { 5 5 if (outlineShaderMaterial != null) 6 6 { 7 7 outlineShaderMaterial.SetTexture("_OutlineSource", renderTexture); 8 8 9 9 if (temp==null) 1010 { 1111 temp = new RenderTexture(source.width,source.height, 16, RenderTextureFormat.Default); 1212 } 1313 1414 ////高斯模糊轮廓逻辑 1515 1616 if (outlines.Count != 0) 1717 { 1818 if (golMaterial != null) 1919 { 2020 int rtW = source.width / downSample; 2121 int rtH = source.height / downSample; 2222 2323 RenderTexture buffer0 = RenderTexture.GetTemporary(rtW, rtH, 0); 2424 buffer0.filterMode = FilterMode.Bilinear; 2525 2626 //轮廓颜色 2727 golMaterial.SetColor("_TargetColor", lineColor0); 2828 2929 //将OutlineCamera的RendererTexture Copy 给buffer0 3030 Graphics.Blit(renderTexture, buffer0); 3131 3232 for (int i = 0; i < iterations; i++) 3333 { 3434 golMaterial.SetFloat("_BlurSize", 1.0f + i * blurSpread); 3535 3636 RenderTexture buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0); 3737 3838 // Render the vertical pass 3939 Graphics.Blit(buffer0, buffer1, golMaterial, 0); 4040 4141 RenderTexture.ReleaseTemporary(buffer0); 4242 buffer0 = buffer1; 4343 buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0); 4444 4545 // Render the horizontal pass 4646 Graphics.Blit(buffer0, buffer1, golMaterial, 1); 4747 4848 RenderTexture.ReleaseTemporary(buffer0); 4949 buffer0 = buffer1; 5050 } 5151 5252 //将模糊完的纹理传给混合Shader,去混合,buffer0混合完的纹理 5353 golMaterial.SetTexture("_OutlineSource", buffer0); 5454 5555 //混合纹理输出 5656 Graphics.Blit(source, destination, golMaterial, 2); 5757 RenderTexture.ReleaseTemporary(buffer0); 5858 } 5959 } 6060 else 6161 { 6262 Graphics.Blit(source, destination); 6363 } 6464 } 6565 }
高斯模糊Shader
1 1 Shader "Unlit/MyShader" 2 2 { 3 3 Properties 4 4 { 5 5 _MainTex ("Texture", 2D) = "white" {} 6 6 _BlurSize("Blur Size",Float) =1.0 7 7 _TargetColor ("_TargetColor", Color) = (1,1,1,1) 8 8 } 9 9 SubShader 10 10 { 11 11 CGINCLUDE 12 12 13 13 #include "UnityCG.cginc" 14 14 15 15 sampler2D _MainTex; 16 16 sampler2D _OutlineSource; 17 17 half4 _MainTex_TexelSize; 18 18 float4 _MainTex_ST; 19 19 float _BlurSize; 20 20 fixed4 _TargetColor; 21 21 float _HighlightFlicker=0.0f; 22 22 23 23 struct v2f 24 24 { 25 25 float4 pos : SV_POSITION; 26 26 half2 uv[5] : TEXCOORD0; 27 27 }; 28 28 29 29 30 30 struct v2 31 31 { 32 32 float4 pos : SV_POSITION; 33 33 half2 uv: TEXCOORD0; 34 34 }; 35 35 36 36 v2f vertBlurVertical (appdata_img v) 37 37 { 38 38 v2f o; 39 39 o.pos = UnityObjectToClipPos(v.vertex); 40 40 41 41 half2 uv=v.texcoord; 42 42 o.uv[0]=uv; 43 43 o.uv[1]=uv + float2(0.0,_MainTex_TexelSize.y * 1.0) * _BlurSize; 44 44 o.uv[2]=uv - float2(0.0,_MainTex_TexelSize.y * 1.0) * _BlurSize; 45 45 o.uv[3]=uv + float2(0.0,_MainTex_TexelSize.y * 2.0) * _BlurSize; 46 46 o.uv[4]=uv - float2(0.0,_MainTex_TexelSize.y * 2.0) * _BlurSize; 47 47 48 48 return o; 49 49 } 50 50 51 51 v2f vertBlurHorizontal(appdata_img v) 52 52 { 53 53 v2f o; 54 54 o.pos = UnityObjectToClipPos(v.vertex); 55 55 56 56 half2 uv = v.texcoord; 57 57 58 58 o.uv[0] = uv; 59 59 o.uv[1] = uv + float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize; 60 60 o.uv[2] = uv - float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize; 61 61 o.uv[3] = uv + float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize; 62 62 o.uv[4] = uv - float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize; 63 63 64 64 return o; 65 65 } 66 66 67 67 //融合 68 68 v2 vertBlur(appdata_img v) 69 69 { 70 70 v2 o; 71 71 o.pos = UnityObjectToClipPos(v.vertex); 72 72 o.uv = v.texcoord; 73 73 return o; 74 74 } 75 75 76 76 fixed4 fragBlur(v2f i) : SV_Target 77 77 { 78 78 float weight[3] = {0.4026, 0.2442, 0.0545}; 79 79 80 80 fixed3 sum = tex2D(_MainTex, i.uv[0]).rgb * weight[0]; 81 81 fixed4 originalPixel = tex2D(_MainTex, UnityStereoScreenSpaceUVAdjust(i.uv[0], _MainTex_ST)); 82 82 83 83 for (int it = 1; it < 3; it++) { 84 84 sum += tex2D(_MainTex, i.uv[it*2-1]).rgb * weight[it]; 85 85 sum += tex2D(_MainTex, i.uv[it*2]).rgb * weight[it]; 86 86 } 87 87 88 88 return fixed4(sum, 1.0); 89 89 } 90 90 91 91 //融合 92 92 fixed4 frag(v2 i) : SV_Target 93 93 { 94 94 fixed3 sum = tex2D(_OutlineSource, i.uv).rgb; 95 95 fixed4 originalPixel = tex2D(_MainTex, UnityStereoScreenSpaceUVAdjust(i.uv, _MainTex_ST)); 96 96 // sum = sum.r > 0.95 ? 0 : sum; 97 97 fixed temp = 1.0 - abs((sum.r - 0.5) / 0.5); 98 98 fixed4 target=(1.0- temp)*originalPixel+_TargetColor*(temp); //闪烁 99 99 if(_HighlightFlicker==1.0f) 100100 target= (1.0- temp)*originalPixel+abs(sin(_Time.y * 1.5f))*_TargetColor*(temp); 101101 else 102102 target= (1.0- temp)*originalPixel+_TargetColor*(temp); 103103 return target; 104104 } 105105 106106 ENDCG 107107 108108 ZTest Always Cull Off ZWrite Off 109109 110110 Pass 111111 { 112112 NAME "GAUSSIAN_BLUR_VERTICAL" 113113 114114 CGPROGRAM 115115 116116 #pragma vertex vertBlurVertical 117117 #pragma fragment fragBlur 118118 119119 ENDCG 120120 } 121121 122122 Pass 123123 { 124124 NAME "GAUSSIAN_BLUR_HORIZONTAL" 125125 126126 CGPROGRAM 127127 128128 #pragma vertex vertBlurHorizontal 129129 #pragma fragment fragBlur 130130 131131 ENDCG 132132 } 133133 134134 Pass 135135 { 136136 NAME "GAUSSIAN_BLUR" 137137 138138 CGPROGRAM 139139 140140 #pragma vertex vertBlur 141141 #pragma fragment frag 142142 143143 ENDCG 144144 } 145145 } 146146 FallBack "Diffuse" 147147 }
效果:
