using System.Collections.Generic; using UnityEngine; [RequireComponent(typeof(Camera))] [AddComponentMenu("Image Effects/Outline Glow Effect")] public class OutlineGlowEffectScript : MonoBehaviour { private static Dictionary renderers; private static OutlineGlowEffectScript instance; public int SecondCameraLayer = 31; public int BlurSteps = 2; public float BlurSpread = 0.6f; public bool QuarterResolutionSecondRender = true; public bool SmootherOutlines = true; public bool SplitObjects; public bool UseObjectColors; public bool UseObjectBlurSettings; public bool UseObjectOutlineStrength; public bool SeeThrough; public bool DepthTest; public float MinZ = 0.1f; public Color OutlineColor = Color.cyan; public float OutlineStrength = 3f; public bool DrawObjectsOnTop; public GameObject[] TopDrawObjects; public Shader DephtPassShader; public Shader SecondPassShader; public Shader TopDrawShader; public Shader BlurPassShader; public Shader MixPassShader; private Material e_Mat; private Material d_Mat; private Material m_Mat; private Camera scam; private GameObject camObject; private Transform scam_transform; private float InternalBlurSpread = 0.6f; private List temp_renderers; public static OutlineGlowEffectScript Instance { get { return instance; } } private void Start() { if (renderers == null) { renderers = new Dictionary(); } if (instance == null) { instance = this; } if (!SystemInfo.supportsImageEffects || !SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.Depth)) { base.enabled = false; } else if (DepthTest) { GetComponent().depthTextureMode |= DepthTextureMode.DepthNormals; } } public int AddRenderer(OutlineGlowRenderer renderer) { for (int i = 0; i < 2147483646; i++) { if (!renderers.ContainsKey(i)) { renderers.Add(i, renderer); return i; } } return -1; } public void RemoveRenderer(int id) { if (renderers.ContainsKey(id)) { renderers.Remove(id); } } private void CreateMaterials() { if (d_Mat == null && SecondPassShader != null) { d_Mat = new Material(SecondPassShader); } if (e_Mat == null && BlurPassShader != null && BlurPassShader.isSupported) { e_Mat = new Material(BlurPassShader); } if (m_Mat == null && MixPassShader != null && MixPassShader.isSupported) { m_Mat = new Material(MixPassShader); } } private void CreateCamera() { if (scam == null) { if (camObject != null) { Object.DestroyImmediate(camObject); } GameObject gameObject = new GameObject(); scam = gameObject.AddComponent(); scam_transform = scam.transform; scam.gameObject.name = "Outline Glow Cam"; camObject = gameObject; camObject.hideFlags = HideFlags.HideAndDontSave; } } public void FourTapCone(RenderTexture source, RenderTexture dest, int iteration) { float num = 0.5f + (float)iteration * InternalBlurSpread; Graphics.BlitMultiTap(source, dest, e_Mat, new Vector2(0f - num, 0f - num), new Vector2(0f - num, num), new Vector2(num, num), new Vector2(num, 0f - num)); } private void DownSample4x(RenderTexture source, RenderTexture dest) { float num = 1f * InternalBlurSpread; Graphics.BlitMultiTap(source, dest, e_Mat, new Vector2(0f - num, 0f - num), new Vector2(0f - num, num), new Vector2(num, num), new Vector2(num, 0f - num)); } private void OnRenderImage(RenderTexture source, RenderTexture destination) { foreach (OutlineGlowRenderer value in renderers.Values) { value.UpdateDistance(); } CreateMaterials(); if (!BlurPassShader.isSupported || !MixPassShader.isSupported) { base.enabled = false; return; } if (DepthTest) { GetComponent().depthTextureMode |= DepthTextureMode.DepthNormals; } CreateCamera(); SecondCameraLayer = Mathf.Clamp(SecondCameraLayer, 0, 31); BlurSteps = Mathf.Clamp(BlurSteps, 1, 6); BlurSpread = Mathf.Clamp(BlurSpread, 0f, 1.5f); InternalBlurSpread = BlurSpread; scam.enabled = false; int num = 2; int num2 = 1; if (QuarterResolutionSecondRender) { num2 = 2; num = 4; } bool flag = true; if (TopDrawObjects != null) { flag = true; for (int i = 0; i < TopDrawObjects.Length; i++) { if (TopDrawObjects[i] != null) { flag = false; break; } } } if (!DepthTest) { if (!SplitObjects) { RenderTexture renderTexture = ((!DrawObjectsOnTop || flag) ? RenderTexture.GetTemporary(source.width / num2, source.height / num2, 0, RenderTextureFormat.Default) : RenderTexture.GetTemporary(source.width / num2, source.height / num2, 24, RenderTextureFormat.Default)); scam.renderingPath = RenderingPath.VertexLit; scam_transform.position = GetComponent().transform.position; scam_transform.rotation = GetComponent().transform.rotation; scam.fieldOfView = GetComponent().fieldOfView; scam.near = GetComponent().near; scam.far = GetComponent().far; scam.targetTexture = renderTexture; scam.backgroundColor = Color.black; scam.clearFlags = CameraClearFlags.Color; scam.cullingMask = 1 << SecondCameraLayer; scam.hdr = false; scam.depthTextureMode = DepthTextureMode.None; if (DrawObjectsOnTop && !flag) { GameObject[] array = new GameObject[TopDrawObjects.Length]; int[] array2 = new int[TopDrawObjects.Length]; int num3 = 0; for (int j = 0; j < TopDrawObjects.Length; j++) { if (TopDrawObjects[j] != null) { array[num3] = TopDrawObjects[j]; array2[num3] = TopDrawObjects[j].layer; num3++; } } for (int k = 0; k < array.Length; k++) { array[k].layer = SecondCameraLayer; } scam.RenderWithShader(TopDrawShader, string.Empty); for (int l = 0; l < array.Length; l++) { if (array[l] != null) { array[l].layer = array2[l]; } } scam.clearFlags = CameraClearFlags.Nothing; } foreach (OutlineGlowRenderer value2 in renderers.Values) { value2.SetLayer(SecondCameraLayer); } scam.RenderWithShader(SecondPassShader, string.Empty); foreach (OutlineGlowRenderer value3 in renderers.Values) { value3.ResetLayer(); } RenderTexture temporary = RenderTexture.GetTemporary(source.width / num, source.height / num, 0); RenderTexture temporary2 = RenderTexture.GetTemporary(source.width / num, source.height / num, 0); DownSample4x(renderTexture, temporary); bool flag2 = true; for (int m = 0; m < BlurSteps; m++) { if (flag2) { FourTapCone(temporary, temporary2, m); } else { FourTapCone(temporary2, temporary, m); } flag2 = !flag2; } m_Mat.SetTexture("_WhiteTex", renderTexture); m_Mat.SetColor("_OutlineColor", OutlineColor); m_Mat.SetFloat("_Mult", OutlineStrength); m_Mat.SetVector("_TexSize", new Vector4(1f / (float)source.width, 1f / (float)source.height, 0f, 0f)); if (flag2) { m_Mat.SetTexture("_BlurTex", temporary); } else { m_Mat.SetTexture("_BlurTex", temporary2); } if (SmootherOutlines && QuarterResolutionSecondRender) { Graphics.Blit(source, destination, m_Mat, 1); } else { Graphics.Blit(source, destination, m_Mat, 0); } RenderTexture.ReleaseTemporary(temporary); RenderTexture.ReleaseTemporary(temporary2); RenderTexture.ReleaseTemporary(renderTexture); return; } RenderTexture renderTexture2 = ((!DrawObjectsOnTop || flag) ? RenderTexture.GetTemporary(source.width / num2, source.height / num2, 0, RenderTextureFormat.Default) : RenderTexture.GetTemporary(source.width / num2, source.height / num2, 24, RenderTextureFormat.Default)); scam.renderingPath = RenderingPath.VertexLit; scam_transform.position = GetComponent().transform.position; scam_transform.rotation = GetComponent().transform.rotation; scam.fieldOfView = GetComponent().fieldOfView; scam.near = GetComponent().near; scam.far = GetComponent().far; scam.targetTexture = renderTexture2; scam.backgroundColor = Color.black; scam.clearFlags = CameraClearFlags.Color; scam.cullingMask = 1 << SecondCameraLayer; scam.depthTextureMode = DepthTextureMode.None; RenderTexture temporary3 = RenderTexture.GetTemporary(source.width / num, source.height / num, 0); RenderTexture temporary4 = RenderTexture.GetTemporary(source.width / num, source.height / num, 0); RenderTexture temporary5 = RenderTexture.GetTemporary(source.width, source.height, 0); RenderTexture temporary6 = RenderTexture.GetTemporary(source.width, source.height, 0); Graphics.Blit(source, temporary5, m_Mat, 2); Graphics.Blit(source, temporary6, m_Mat, 2); bool flag3 = false; if (temp_renderers == null) { temp_renderers = new List(); } else { temp_renderers.Clear(); } temp_renderers.AddRange(renderers.Values); temp_renderers.Sort(new OutlineGlowRendererSort()); foreach (OutlineGlowRenderer temp_renderer in temp_renderers) { if (DrawObjectsOnTop && !flag) { GameObject[] array3 = new GameObject[TopDrawObjects.Length]; int[] array4 = new int[TopDrawObjects.Length]; int num4 = 0; for (int n = 0; n < TopDrawObjects.Length; n++) { if (TopDrawObjects[n] != null) { array3[num4] = TopDrawObjects[n]; array4[num4] = TopDrawObjects[n].layer; num4++; } } for (int num5 = 0; num5 < array3.Length; num5++) { array3[num5].layer = SecondCameraLayer; } scam.RenderWithShader(TopDrawShader, string.Empty); for (int num6 = 0; num6 < array3.Length; num6++) { if (array3[num6] != null) { array3[num6].layer = array4[num6]; } } scam.clearFlags = CameraClearFlags.Nothing; } temp_renderer.SetLayer(SecondCameraLayer); scam.RenderWithShader(SecondPassShader, string.Empty); scam.clearFlags = CameraClearFlags.Color; DownSample4x(renderTexture2, temporary3); bool flag4 = true; int num7 = BlurSteps; if (UseObjectBlurSettings) { temp_renderer.ObjectBlurSteps = Mathf.Clamp(temp_renderer.ObjectBlurSteps, 1, 6); temp_renderer.ObjectBlurSpread = Mathf.Clamp(temp_renderer.ObjectBlurSpread, 0f, 1.5f); num7 = temp_renderer.ObjectBlurSteps; InternalBlurSpread = temp_renderer.ObjectBlurSpread; } else { InternalBlurSpread = BlurSpread; } for (int num8 = 0; num8 < num7; num8++) { if (flag4) { FourTapCone(temporary3, temporary4, num8); } else { FourTapCone(temporary4, temporary3, num8); } flag4 = !flag4; } m_Mat.SetTexture("_WhiteTex", renderTexture2); if (UseObjectColors) { m_Mat.SetColor("_OutlineColor", temp_renderer.OutlineColor); } else { m_Mat.SetColor("_OutlineColor", OutlineColor); } if (UseObjectOutlineStrength) { m_Mat.SetFloat("_Mult", temp_renderer.ObjectOutlineStrength); } else { m_Mat.SetFloat("_Mult", OutlineStrength); } m_Mat.SetVector("_TexSize", new Vector4(1f / (float)source.width, 1f / (float)source.height, 0f, 0f)); if (flag4) { m_Mat.SetTexture("_BlurTex", temporary3); } else { m_Mat.SetTexture("_BlurTex", temporary4); } if (SmootherOutlines && QuarterResolutionSecondRender) { if (flag3) { if (!SeeThrough) { Graphics.Blit(temporary5, temporary6, m_Mat, 5); } else { Graphics.Blit(temporary5, temporary6, m_Mat, 1); } } else if (!SeeThrough) { Graphics.Blit(temporary6, temporary5, m_Mat, 5); } else { Graphics.Blit(temporary6, temporary5, m_Mat, 1); } } else if (flag3) { if (!SeeThrough) { Graphics.Blit(temporary5, temporary6, m_Mat, 4); } else { Graphics.Blit(temporary5, temporary6, m_Mat, 0); } } else if (!SeeThrough) { Graphics.Blit(temporary6, temporary5, m_Mat, 4); } else { Graphics.Blit(temporary6, temporary5, m_Mat, 0); } temp_renderer.ResetLayer(); flag3 = !flag3; } if (flag3) { m_Mat.SetTexture("_AddTex", temporary5); Graphics.Blit(source, destination, m_Mat, 3); } else { m_Mat.SetTexture("_AddTex", temporary6); Graphics.Blit(source, destination, m_Mat, 3); } RenderTexture.ReleaseTemporary(temporary3); RenderTexture.ReleaseTemporary(temporary4); RenderTexture.ReleaseTemporary(temporary5); RenderTexture.ReleaseTemporary(temporary6); RenderTexture.ReleaseTemporary(renderTexture2); return; } m_Mat.SetFloat("_MinZ", MinZ); if (!SplitObjects) { RenderTexture temporary7 = RenderTexture.GetTemporary(source.width / num2, source.height / num2, 24, RenderTextureFormat.Default); RenderTexture temporary8 = RenderTexture.GetTemporary(temporary7.width, temporary7.height, 24, RenderTextureFormat.Default); RenderTexture renderTexture3 = null; if (QuarterResolutionSecondRender) { renderTexture3 = RenderTexture.GetTemporary(temporary7.width, temporary7.height, 0); m_Mat.SetVector("_DTexelOffset", new Vector4(0.5f / (float)source.width, 0.5f / (float)source.height, -0.5f / (float)source.width, 0.5f / (float)source.height)); Graphics.Blit(source, renderTexture3, m_Mat, 11); } scam.renderingPath = RenderingPath.Forward; scam_transform.position = GetComponent().transform.position; scam_transform.rotation = GetComponent().transform.rotation; scam.fieldOfView = GetComponent().fieldOfView; scam.near = GetComponent().near; scam.far = GetComponent().far; scam.targetTexture = temporary7; scam.backgroundColor = Color.black; scam.clearFlags = CameraClearFlags.Color; scam.cullingMask = 1 << SecondCameraLayer; scam.hdr = false; scam.depthTextureMode = DepthTextureMode.None; if (DrawObjectsOnTop && !flag) { GameObject[] array5 = new GameObject[TopDrawObjects.Length]; int[] array6 = new int[TopDrawObjects.Length]; int num9 = 0; for (int num10 = 0; num10 < TopDrawObjects.Length; num10++) { if (TopDrawObjects[num10] != null) { array5[num9] = TopDrawObjects[num10]; array6[num9] = TopDrawObjects[num10].layer; TopDrawObjects[num10].layer = SecondCameraLayer; num9++; } } scam.RenderWithShader(TopDrawShader, string.Empty); for (int num11 = 0; num11 < array5.Length; num11++) { if (array5[num11] != null) { array5[num11].layer = array6[num11]; } } scam.clearFlags = CameraClearFlags.Nothing; } foreach (OutlineGlowRenderer value4 in renderers.Values) { value4.SetLayer(SecondCameraLayer); } scam.RenderWithShader(SecondPassShader, string.Empty); scam.targetTexture = temporary8; scam.RenderWithShader(DephtPassShader, "RenderType"); scam.targetTexture = temporary7; m_Mat.SetTexture("_SecDepth", temporary8); foreach (OutlineGlowRenderer value5 in renderers.Values) { value5.ResetLayer(); } RenderTexture temporary9 = RenderTexture.GetTemporary(source.width / num, source.height / num, 0); RenderTexture temporary10 = RenderTexture.GetTemporary(source.width / num, source.height / num, 0); RenderTexture temporary11 = RenderTexture.GetTemporary(temporary7.width, temporary7.height, 0); if (!QuarterResolutionSecondRender) { m_Mat.SetVector("_DTexelOffset", new Vector4(1f / (float)temporary7.width, 1f / (float)temporary7.height, -1f / (float)temporary7.width, 1f / (float)temporary7.height)); Graphics.Blit(temporary7, temporary11, m_Mat, 12); Graphics.Blit(temporary11, temporary7, m_Mat, 10); } else { m_Mat.SetVector("_DTexelOffset", new Vector4(1f / (float)temporary7.width, 1f / (float)temporary7.height, -1f / (float)temporary7.width, 1f / (float)temporary7.height)); m_Mat.SetTexture("_DSD", renderTexture3); Graphics.Blit(temporary7, temporary11, m_Mat, 13); Graphics.Blit(temporary11, temporary7, m_Mat, 10); } DownSample4x(temporary7, temporary9); bool flag5 = true; for (int num12 = 0; num12 < BlurSteps; num12++) { if (flag5) { FourTapCone(temporary9, temporary10, num12); } else { FourTapCone(temporary10, temporary9, num12); } flag5 = !flag5; } m_Mat.SetTexture("_WhiteTex", temporary7); m_Mat.SetColor("_OutlineColor", OutlineColor); m_Mat.SetFloat("_Mult", OutlineStrength); m_Mat.SetVector("_TexSize", new Vector4(1f / (float)source.width, 1f / (float)source.height, 0f, 0f)); if (flag5) { m_Mat.SetTexture("_BlurTex", temporary9); } else { m_Mat.SetTexture("_BlurTex", temporary10); } if (SmootherOutlines && QuarterResolutionSecondRender) { Graphics.Blit(source, destination, m_Mat, 1); } else { Graphics.Blit(source, destination, m_Mat, 0); } RenderTexture.ReleaseTemporary(temporary9); RenderTexture.ReleaseTemporary(temporary10); RenderTexture.ReleaseTemporary(temporary7); RenderTexture.ReleaseTemporary(temporary11); RenderTexture.ReleaseTemporary(temporary8); if (renderTexture3 != null) { RenderTexture.ReleaseTemporary(renderTexture3); } return; } RenderTexture temporary12 = RenderTexture.GetTemporary(source.width / num2, source.height / num2, 0, RenderTextureFormat.Default); RenderTexture temporary13 = RenderTexture.GetTemporary(source.width, source.height, 24, RenderTextureFormat.Default); scam.renderingPath = RenderingPath.VertexLit; scam_transform.position = GetComponent().transform.position; scam_transform.rotation = GetComponent().transform.rotation; scam.fieldOfView = GetComponent().fieldOfView; scam.near = GetComponent().near; scam.far = GetComponent().far; scam.targetTexture = temporary12; scam.backgroundColor = Color.black; scam.clearFlags = CameraClearFlags.Color; scam.cullingMask = 1 << SecondCameraLayer; scam.depthTextureMode = DepthTextureMode.None; RenderTexture temporary14 = RenderTexture.GetTemporary(source.width / num, source.height / num, 0); RenderTexture temporary15 = RenderTexture.GetTemporary(source.width / num, source.height / num, 0); RenderTexture temporary16 = RenderTexture.GetTemporary(source.width, source.height, 0); RenderTexture temporary17 = RenderTexture.GetTemporary(source.width, source.height, 0); RenderTexture temporary18 = RenderTexture.GetTemporary(temporary12.width, temporary12.height, 0); Graphics.Blit(source, temporary16, m_Mat, 2); Graphics.Blit(source, temporary17, m_Mat, 2); bool flag6 = false; if (temp_renderers == null) { temp_renderers = new List(); } else { temp_renderers.Clear(); } temp_renderers.AddRange(renderers.Values); temp_renderers.Sort(new OutlineGlowRendererSort()); foreach (OutlineGlowRenderer temp_renderer2 in temp_renderers) { if (DrawObjectsOnTop && !flag) { GameObject[] array7 = new GameObject[TopDrawObjects.Length]; int[] array8 = new int[TopDrawObjects.Length]; int num13 = 0; for (int num14 = 0; num14 < TopDrawObjects.Length; num14++) { if (TopDrawObjects[num14] != null) { array7[num13] = TopDrawObjects[num14]; array8[num13] = TopDrawObjects[num14].layer; TopDrawObjects[num14].layer = SecondCameraLayer; num13++; } } scam.RenderWithShader(TopDrawShader, string.Empty); for (int num15 = 0; num15 < array7.Length; num15++) { if (array7[num15] != null) { array7[num15].layer = array8[num15]; } } scam.clearFlags = CameraClearFlags.Nothing; } temp_renderer2.SetLayer(SecondCameraLayer); scam.RenderWithShader(SecondPassShader, string.Empty); scam.clearFlags = CameraClearFlags.Color; scam.targetTexture = temporary13; scam.RenderWithShader(DephtPassShader, "RenderType"); scam.targetTexture = temporary12; m_Mat.SetTexture("_SecDepth", temporary13); Graphics.Blit(temporary12, temporary18, m_Mat, 8); Graphics.Blit(temporary18, temporary12, m_Mat, 10); DownSample4x(temporary12, temporary14); bool flag7 = true; int num16 = BlurSteps; if (UseObjectBlurSettings) { temp_renderer2.ObjectBlurSteps = Mathf.Clamp(temp_renderer2.ObjectBlurSteps, 1, 6); temp_renderer2.ObjectBlurSpread = Mathf.Clamp(temp_renderer2.ObjectBlurSpread, 0f, 1.5f); num16 = temp_renderer2.ObjectBlurSteps; InternalBlurSpread = temp_renderer2.ObjectBlurSpread; } else { InternalBlurSpread = BlurSpread; } for (int num17 = 0; num17 < num16; num17++) { if (flag7) { FourTapCone(temporary14, temporary15, num17); } else { FourTapCone(temporary15, temporary14, num17); } flag7 = !flag7; } m_Mat.SetTexture("_WhiteTex", temporary12); if (UseObjectColors) { m_Mat.SetColor("_OutlineColor", temp_renderer2.OutlineColor); } else { m_Mat.SetColor("_OutlineColor", OutlineColor); } if (UseObjectOutlineStrength) { m_Mat.SetFloat("_Mult", temp_renderer2.ObjectOutlineStrength); } else { m_Mat.SetFloat("_Mult", OutlineStrength); } m_Mat.SetVector("_TexSize", new Vector4(1f / (float)source.width, 1f / (float)source.height, 0f, 0f)); if (flag7) { m_Mat.SetTexture("_BlurTex", temporary14); } else { m_Mat.SetTexture("_BlurTex", temporary15); } if (SmootherOutlines && QuarterResolutionSecondRender) { if (flag6) { if (!SeeThrough) { Graphics.Blit(temporary16, temporary17, m_Mat, 5); } else { Graphics.Blit(temporary16, temporary17, m_Mat, 1); } } else if (!SeeThrough) { Graphics.Blit(temporary17, temporary16, m_Mat, 5); } else { Graphics.Blit(temporary17, temporary16, m_Mat, 1); } } else if (flag6) { if (!SeeThrough) { Graphics.Blit(temporary16, temporary17, m_Mat, 4); } else { Graphics.Blit(temporary16, temporary17, m_Mat, 0); } } else if (!SeeThrough) { Graphics.Blit(temporary17, temporary16, m_Mat, 4); } else { Graphics.Blit(temporary17, temporary16, m_Mat, 0); } temp_renderer2.ResetLayer(); flag6 = !flag6; } if (flag6) { m_Mat.SetTexture("_AddTex", temporary16); Graphics.Blit(source, destination, m_Mat, 3); } else { m_Mat.SetTexture("_AddTex", temporary17); Graphics.Blit(source, destination, m_Mat, 3); } RenderTexture.ReleaseTemporary(temporary14); RenderTexture.ReleaseTemporary(temporary15); RenderTexture.ReleaseTemporary(temporary16); RenderTexture.ReleaseTemporary(temporary17); RenderTexture.ReleaseTemporary(temporary12); RenderTexture.ReleaseTemporary(temporary13); RenderTexture.ReleaseTemporary(temporary18); } }