问题背景:
最近要实现选中实体的高亮效果,要那种类似于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 }
效果:
来源:oschina
链接:https://my.oschina.net/u/4258325/blog/4263124