using System; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.PostProcessing; [Serializable] [PostProcess(typeof(OutlineRenderer), PostProcessEvent.AfterStack, "Custom/Outline")] public sealed class OutlineEffect : PostProcessEffectSettings { public UnityEngine.Rendering.PostProcessing.ColorParameter color = new UnityEngine.Rendering.PostProcessing.ColorParameter { value = Color.red }; [Range(0f, 5f), Tooltip("Outline size.")] public UnityEngine.Rendering.PostProcessing.FloatParameter size = new UnityEngine.Rendering.PostProcessing.FloatParameter { value = 2f }; [Range(0f, 1f), Tooltip("Outline softness.")] public UnityEngine.Rendering.PostProcessing.FloatParameter softness = new UnityEngine.Rendering.PostProcessing.FloatParameter { value = 0.5f }; public UnityEngine.Rendering.PostProcessing.BoolParameter downSample = new UnityEngine.Rendering.PostProcessing.BoolParameter { value = false }; public UnityEngine.Rendering.PostProcessing.BoolParameter drawOnTop = new UnityEngine.Rendering.PostProcessing.BoolParameter { value = true }; public ParameterOverride Tag = new ParameterOverride { value = "Outline" }; public override bool IsEnabledAndSupported(PostProcessRenderContext context) { bool objectsExist = false; try { objectsExist = GameObject.FindWithTag(Tag) != null; } catch (Exception) { } return enabled.value && objectsExist; } } public sealed class OutlineRenderer : PostProcessEffectRenderer { private Material m_mat; private Shader m_SeparableBlurShader; private Shader m_DilateShader; private Shader m_OutlineShader; private int m_objectsID; private int m_outlineColorID; private int m_ztestID; private int m_blurredID; private int m_tempID; private int m_offsetsID; public override void Init() { m_mat = new Material(Shader.Find("Hidden/Outline/UnlitColor")); m_mat.hideFlags = HideFlags.HideAndDontSave; m_SeparableBlurShader = Shader.Find("Hidden/Outline/SeparableBlur"); m_DilateShader = Shader.Find("Hidden/Outline/Dilate"); m_OutlineShader = Shader.Find("Hidden/Outline"); m_objectsID = Shader.PropertyToID("_ObjectsTex"); m_outlineColorID = Shader.PropertyToID("_OutlineColor"); m_ztestID = Shader.PropertyToID("_ZTest"); m_blurredID = Shader.PropertyToID("_BlurredTex"); m_tempID = Shader.PropertyToID("_Temp"); m_offsetsID = Shader.PropertyToID("_Offsets"); } public override void Render(PostProcessRenderContext context) { // 지정된 태그를 가진 모든 게임 오브젝트를 찾고 MeshRenderer가 있는지 확인 var renderers = GameObject.FindGameObjectsWithTag(settings.Tag) .Select((g) => g.GetComponent()) .Where(r => r != null); // MeshRenderer가 null이 아닌 경우만 선택 // 임시 렌더 텍스처 생성 context.command.GetTemporaryRT(m_objectsID, -1, -1, 24, FilterMode.Bilinear); var depthId = context.camera.actualRenderingPath == RenderingPath.Forward ? BuiltinRenderTextureType.Depth : BuiltinRenderTextureType.ResolvedDepth; context.command.SetRenderTarget(color: m_objectsID, depth: depthId); context.command.ClearRenderTarget(false, true, Color.clear); context.command.SetGlobalColor(m_outlineColorID, settings.color); int ztest = settings.drawOnTop ? (int)CompareFunction.Always : (int)CompareFunction.LessEqual; m_mat.SetInt(m_ztestID, ztest); // 객체를 그리기 foreach (var r in renderers) { context.command.DrawRenderer(r, m_mat); } int sample = settings.downSample.value ? 2 : 1; context.command.GetTemporaryRT(m_blurredID, -sample, -sample, 0, FilterMode.Bilinear); context.command.GetTemporaryRT(m_tempID, -sample, -sample, 0, FilterMode.Bilinear); context.command.Blit(m_objectsID, m_blurredID); // 수평 확장 float dilateSize = settings.size * (1 - settings.softness); var dilate = context.propertySheets.Get(m_DilateShader); dilate.properties.SetVector(m_offsetsID, new Vector4(dilateSize / context.width, 0, 0, 0)); context.command.BlitFullscreenTriangle(m_blurredID, m_tempID, dilate, 0); // 수직 확장 dilate.properties.SetVector(m_offsetsID, new Vector4(0, dilateSize / context.height, 0, 0)); context.command.BlitFullscreenTriangle(m_tempID, m_blurredID, dilate, 0); // 수평 블러 float blurSize = settings.size - dilateSize; var blur = context.propertySheets.Get(m_SeparableBlurShader); blur.properties.SetVector(m_offsetsID, new Vector4(blurSize / context.width, 0, 0, 0)); context.command.BlitFullscreenTriangle(m_blurredID, m_tempID, blur, 0); // 수직 블러 blur.properties.SetVector(m_offsetsID, new Vector4(0, blurSize / context.height, 0, 0)); context.command.BlitFullscreenTriangle(m_tempID, m_blurredID, blur, 0); var outline = context.propertySheets.Get(m_OutlineShader); context.command.BlitFullscreenTriangle(context.source, context.destination, outline, 0); } public override void Release() { GameObject.DestroyImmediate(m_mat); base.Release(); } }