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 }
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 }
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 }
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 }
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 }
//测试代码
其中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 }
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 }