120 lines
5.4 KiB
C#
120 lines
5.4 KiB
C#
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<string> Tag = new ParameterOverride<string> { 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<OutlineEffect>
|
|
{
|
|
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<MeshRenderer>())
|
|
.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();
|
|
}
|
|
}
|