552 lines
18 KiB
C#
552 lines
18 KiB
C#
using System.Collections;
|
|
using System.Linq;
|
|
using UnityEngine;
|
|
using UnityEngine.Rendering;
|
|
using UnityEngine.Serialization;
|
|
using UnityEngine.UI;
|
|
|
|
namespace Coffee.UIExtensions
|
|
{
|
|
/// <summary>
|
|
/// UIEffectCapturedImage
|
|
/// </summary>
|
|
[AddComponentMenu("UI/UIEffect/UIEffectCapturedImage", 200)]
|
|
public class UIEffectCapturedImage : RawImage
|
|
#if UNITY_EDITOR
|
|
, ISerializationCallbackReceiver
|
|
#endif
|
|
{
|
|
|
|
//################################
|
|
// Constant or Static Members.
|
|
//################################
|
|
public const string shaderName = "UI/Hidden/UI-EffectCapture";
|
|
|
|
/// <summary>
|
|
/// Desampling rate.
|
|
/// </summary>
|
|
public enum DesamplingRate
|
|
{
|
|
None = 0,
|
|
x1 = 1,
|
|
x2 = 2,
|
|
x4 = 4,
|
|
x8 = 8,
|
|
}
|
|
|
|
|
|
//################################
|
|
// Serialize Members.
|
|
//################################
|
|
[Tooltip("Effect factor between 0(no effect) and 1(complete effect).")]
|
|
[FormerlySerializedAs("m_ToneLevel")]
|
|
[SerializeField][Range(0, 1)] float m_EffectFactor = 1;
|
|
|
|
[Tooltip("Color effect factor between 0(no effect) and 1(complete effect).")]
|
|
[SerializeField][Range(0, 1)] float m_ColorFactor = 1;
|
|
|
|
[Tooltip("How far is the blurring from the graphic.")]
|
|
[FormerlySerializedAs("m_Blur")]
|
|
[SerializeField][Range(0, 1)] float m_BlurFactor = 1;
|
|
|
|
[Tooltip("Effect mode.")]
|
|
[FormerlySerializedAs("m_ToneMode")]
|
|
[SerializeField] EffectMode m_EffectMode = EffectMode.None;
|
|
|
|
[Tooltip("Color effect mode.")]
|
|
[SerializeField] ColorMode m_ColorMode = ColorMode.Multiply;
|
|
|
|
[Tooltip("Blur effect mode.")]
|
|
[SerializeField] BlurMode m_BlurMode = BlurMode.DetailBlur;
|
|
|
|
[Tooltip("Color for the color effect.")]
|
|
[SerializeField] Color m_EffectColor = Color.white;
|
|
|
|
[Tooltip("Desampling rate of the generated RenderTexture.")]
|
|
[SerializeField] DesamplingRate m_DesamplingRate = DesamplingRate.x1;
|
|
|
|
[Tooltip("Desampling rate of reduction buffer to apply effect.")]
|
|
[SerializeField] DesamplingRate m_ReductionRate = DesamplingRate.x1;
|
|
|
|
[Tooltip("FilterMode for capturing.")]
|
|
[SerializeField] FilterMode m_FilterMode = FilterMode.Bilinear;
|
|
|
|
[Tooltip("Effect material.")]
|
|
[SerializeField] Material m_EffectMaterial = null;
|
|
|
|
[Tooltip("Blur iterations.")]
|
|
[FormerlySerializedAs("m_Iterations")]
|
|
[SerializeField][Range(1, 8)] int m_BlurIterations = 3;
|
|
|
|
[Tooltip("Fits graphic size to screen on captured.")]
|
|
[FormerlySerializedAs("m_KeepCanvasSize")]
|
|
[SerializeField] bool m_FitToScreen = true;
|
|
|
|
[Tooltip("Capture automatically on enable.")]
|
|
[SerializeField] bool m_CaptureOnEnable = false;
|
|
|
|
|
|
//################################
|
|
// Public Members.
|
|
//################################
|
|
/// <summary>
|
|
/// Effect factor between 0(no effect) and 1(complete effect).
|
|
/// </summary>
|
|
[System.Obsolete("Use effectFactor instead (UnityUpgradable) -> effectFactor")]
|
|
public float toneLevel { get { return m_EffectFactor; } set { m_EffectFactor = Mathf.Clamp(value, 0, 1); } }
|
|
|
|
/// <summary>
|
|
/// Effect factor between 0(no effect) and 1(complete effect).
|
|
/// </summary>
|
|
public float effectFactor { get { return m_EffectFactor; } set { m_EffectFactor = Mathf.Clamp(value, 0, 1); } }
|
|
|
|
/// <summary>
|
|
/// Color effect factor between 0(no effect) and 1(complete effect).
|
|
/// </summary>
|
|
public float colorFactor { get { return m_ColorFactor; } set { m_ColorFactor = Mathf.Clamp(value, 0, 1); } }
|
|
|
|
/// <summary>
|
|
/// How far is the blurring from the graphic.
|
|
/// </summary>
|
|
[System.Obsolete("Use blurFactor instead (UnityUpgradable) -> blurFactor")]
|
|
public float blur { get { return m_BlurFactor; } set { m_BlurFactor = Mathf.Clamp(value, 0, 4); } }
|
|
|
|
/// <summary>
|
|
/// How far is the blurring from the graphic.
|
|
/// </summary>
|
|
public float blurFactor { get { return m_BlurFactor; } set { m_BlurFactor = Mathf.Clamp(value, 0, 4); } }
|
|
|
|
/// <summary>
|
|
/// Tone effect mode.
|
|
/// </summary>
|
|
[System.Obsolete("Use effectMode instead (UnityUpgradable) -> effectMode")]
|
|
public EffectMode toneMode { get { return m_EffectMode; } }
|
|
|
|
/// <summary>
|
|
/// Effect mode.
|
|
/// </summary>
|
|
public EffectMode effectMode { get { return m_EffectMode; } }
|
|
|
|
/// <summary>
|
|
/// Color effect mode.
|
|
/// </summary>
|
|
public ColorMode colorMode { get { return m_ColorMode; } }
|
|
|
|
/// <summary>
|
|
/// Blur effect mode.
|
|
/// </summary>
|
|
public BlurMode blurMode { get { return m_BlurMode; } }
|
|
|
|
/// <summary>
|
|
/// Color for the color effect.
|
|
/// </summary>
|
|
public Color effectColor { get { return m_EffectColor; } set { m_EffectColor = value; } }
|
|
|
|
/// <summary>
|
|
/// Effect material.
|
|
/// </summary>
|
|
public virtual Material effectMaterial { get { return m_EffectMaterial; } }
|
|
|
|
/// <summary>
|
|
/// Desampling rate of the generated RenderTexture.
|
|
/// </summary>
|
|
public DesamplingRate desamplingRate { get { return m_DesamplingRate; } set { m_DesamplingRate = value; } }
|
|
|
|
/// <summary>
|
|
/// Desampling rate of reduction buffer to apply effect.
|
|
/// </summary>
|
|
public DesamplingRate reductionRate { get { return m_ReductionRate; } set { m_ReductionRate = value; } }
|
|
|
|
/// <summary>
|
|
/// FilterMode for capturing.
|
|
/// </summary>
|
|
public FilterMode filterMode { get { return m_FilterMode; } set { m_FilterMode = value; } }
|
|
|
|
/// <summary>
|
|
/// Captured texture.
|
|
/// </summary>
|
|
public RenderTexture capturedTexture { get { return _rt; } }
|
|
|
|
/// <summary>
|
|
/// Blur iterations.
|
|
/// </summary>
|
|
[System.Obsolete("Use blurIterations instead (UnityUpgradable) -> blurIterations")]
|
|
public int iterations { get { return m_BlurIterations; } set { m_BlurIterations = value; } }
|
|
|
|
/// <summary>
|
|
/// Blur iterations.
|
|
/// </summary>
|
|
public int blurIterations { get { return m_BlurIterations; } set { m_BlurIterations = value; } }
|
|
|
|
/// <summary>
|
|
/// Fits graphic size to screen.
|
|
/// </summary>
|
|
[System.Obsolete("Use fitToScreen instead (UnityUpgradable) -> fitToScreen")]
|
|
public bool keepCanvasSize { get { return m_FitToScreen; } set { m_FitToScreen = value; } }
|
|
|
|
/// <summary>
|
|
/// Fits graphic size to screen on captured.
|
|
/// </summary>
|
|
public bool fitToScreen { get { return m_FitToScreen; } set { m_FitToScreen = value; } }
|
|
|
|
/// <summary>
|
|
/// Target RenderTexture to capture.
|
|
/// </summary>
|
|
[System.Obsolete]
|
|
public RenderTexture targetTexture { get { return null; } set { } }
|
|
|
|
/// <summary>
|
|
/// Capture automatically on enable.
|
|
/// </summary>
|
|
public bool captureOnEnable { get { return m_CaptureOnEnable; } set { m_CaptureOnEnable = value; } }
|
|
|
|
/// <summary>
|
|
/// This function is called when the object becomes enabled and active.
|
|
/// </summary>
|
|
protected override void OnEnable()
|
|
{
|
|
base.OnEnable();
|
|
if (m_CaptureOnEnable && Application.isPlaying)
|
|
{
|
|
Capture();
|
|
}
|
|
}
|
|
|
|
protected override void OnDisable()
|
|
{
|
|
base.OnDisable();
|
|
if (m_CaptureOnEnable && Application.isPlaying)
|
|
{
|
|
_Release(false);
|
|
texture = null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This function is called when the MonoBehaviour will be destroyed.
|
|
/// </summary>
|
|
protected override void OnDestroy()
|
|
{
|
|
Release();
|
|
base.OnDestroy();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Callback function when a UI element needs to generate vertices.
|
|
/// </summary>
|
|
protected override void OnPopulateMesh(VertexHelper vh)
|
|
{
|
|
// When not displaying, clear vertex.
|
|
if (texture == null || color.a < 1 / 255f || canvasRenderer.GetAlpha() < 1 / 255f)
|
|
{
|
|
vh.Clear();
|
|
}
|
|
else
|
|
{
|
|
base.OnPopulateMesh(vh);
|
|
int count = vh.currentVertCount;
|
|
UIVertex vt = default(UIVertex);
|
|
Color c = color;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
vh.PopulateUIVertex(ref vt, i);
|
|
vt.color = c;
|
|
vh.SetUIVertex(vt, i);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the size of the desampling.
|
|
/// </summary>
|
|
public void GetDesamplingSize(DesamplingRate rate, out int w, out int h)
|
|
{
|
|
#if UNITY_EDITOR
|
|
if (!Application.isPlaying)
|
|
{
|
|
var res = UnityEditor.UnityStats.screenRes.Split('x');
|
|
w = int.Parse(res[0]);
|
|
h = int.Parse(res[1]);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
w = Screen.width;
|
|
h = Screen.height;
|
|
}
|
|
|
|
if (rate == DesamplingRate.None)
|
|
return;
|
|
|
|
float aspect = (float)w / h;
|
|
if (w < h)
|
|
{
|
|
h = Mathf.ClosestPowerOfTwo(h / (int)rate);
|
|
w = Mathf.CeilToInt(h * aspect);
|
|
}
|
|
else
|
|
{
|
|
w = Mathf.ClosestPowerOfTwo(w / (int)rate);
|
|
h = Mathf.CeilToInt(w / aspect);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Capture rendering result.
|
|
/// </summary>
|
|
public void Capture()
|
|
{
|
|
// Fit to screen.
|
|
var rootCanvas = canvas.rootCanvas;
|
|
if (m_FitToScreen)
|
|
{
|
|
var rootTransform = rootCanvas.transform as RectTransform;
|
|
var size = rootTransform.rect.size;
|
|
rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, size.x);
|
|
rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, size.y);
|
|
rectTransform.position = rootTransform.position;
|
|
}
|
|
|
|
// Cache some ids.
|
|
if (s_CopyId == 0)
|
|
{
|
|
s_CopyId = Shader.PropertyToID("_UIEffectCapturedImage_ScreenCopyId");
|
|
s_EffectId1 = Shader.PropertyToID("_UIEffectCapturedImage_EffectId1");
|
|
s_EffectId2 = Shader.PropertyToID("_UIEffectCapturedImage_EffectId2");
|
|
|
|
s_EffectFactorId = Shader.PropertyToID("_EffectFactor");
|
|
s_ColorFactorId = Shader.PropertyToID("_ColorFactor");
|
|
s_CommandBuffer = new CommandBuffer();
|
|
}
|
|
|
|
|
|
// If size of result RT has changed, release it.
|
|
int w, h;
|
|
GetDesamplingSize(m_DesamplingRate, out w, out h);
|
|
if (_rt && (_rt.width != w || _rt.height != h))
|
|
{
|
|
_Release(ref _rt);
|
|
}
|
|
|
|
// Generate RT for result.
|
|
if (_rt == null)
|
|
{
|
|
_rt = RenderTexture.GetTemporary(w, h, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Default);
|
|
_rt.filterMode = m_FilterMode;
|
|
_rt.useMipMap = false;
|
|
_rt.wrapMode = TextureWrapMode.Clamp;
|
|
_rtId = new RenderTargetIdentifier(_rt);
|
|
}
|
|
SetupCommandBuffer();
|
|
}
|
|
|
|
void SetupCommandBuffer()
|
|
{
|
|
// Material for effect.
|
|
Material mat = m_EffectMaterial;
|
|
|
|
if (s_CommandBuffer == null)
|
|
{
|
|
s_CommandBuffer = new CommandBuffer();
|
|
}
|
|
|
|
// [1] Capture from back buffer (back buffer -> copied screen).
|
|
int w, h;
|
|
GetDesamplingSize(DesamplingRate.None, out w, out h);
|
|
s_CommandBuffer.GetTemporaryRT(s_CopyId, w, h, 0, m_FilterMode);
|
|
#if UNITY_EDITOR
|
|
s_CommandBuffer.Blit(Resources.FindObjectsOfTypeAll<RenderTexture>().FirstOrDefault(x => x.name == "GameView RT"), s_CopyId);
|
|
#else
|
|
s_CommandBuffer.Blit(BuiltinRenderTextureType.BindableTexture, s_CopyId);
|
|
#endif
|
|
|
|
// Set properties for effect.
|
|
s_CommandBuffer.SetGlobalVector(s_EffectFactorId, new Vector4(m_EffectFactor, 0));
|
|
s_CommandBuffer.SetGlobalVector(s_ColorFactorId, new Vector4(m_EffectColor.r, m_EffectColor.g, m_EffectColor.b, m_EffectColor.a));
|
|
|
|
// [2] Apply base effect with reduction buffer (copied screen -> effect1).
|
|
GetDesamplingSize(m_ReductionRate, out w, out h);
|
|
s_CommandBuffer.GetTemporaryRT(s_EffectId1, w, h, 0, m_FilterMode);
|
|
s_CommandBuffer.Blit(s_CopyId, s_EffectId1, mat, 0);
|
|
s_CommandBuffer.ReleaseTemporaryRT(s_CopyId);
|
|
|
|
// Iterate blurring operation.
|
|
if (m_BlurMode != BlurMode.None)
|
|
{
|
|
s_CommandBuffer.GetTemporaryRT(s_EffectId2, w, h, 0, m_FilterMode);
|
|
for (int i = 0; i < m_BlurIterations; i++)
|
|
{
|
|
// [3] Apply blurring with reduction buffer (effect1 -> effect2, or effect2 -> effect1).
|
|
s_CommandBuffer.SetGlobalVector(s_EffectFactorId, new Vector4(m_BlurFactor, 0));
|
|
s_CommandBuffer.Blit(s_EffectId1, s_EffectId2, mat, 1);
|
|
s_CommandBuffer.SetGlobalVector(s_EffectFactorId, new Vector4(0, m_BlurFactor));
|
|
s_CommandBuffer.Blit(s_EffectId2, s_EffectId1, mat, 1);
|
|
}
|
|
s_CommandBuffer.ReleaseTemporaryRT(s_EffectId2);
|
|
}
|
|
|
|
// [4] Copy to result RT.
|
|
s_CommandBuffer.Blit(s_EffectId1, _rtId);
|
|
s_CommandBuffer.ReleaseTemporaryRT(s_EffectId1);
|
|
|
|
#if UNITY_EDITOR
|
|
if (!Application.isPlaying)
|
|
{
|
|
Graphics.ExecuteCommandBuffer(s_CommandBuffer);
|
|
|
|
UpdateTexture();
|
|
return;
|
|
}
|
|
#endif
|
|
// Execute command buffer.
|
|
canvas.rootCanvas.GetComponent<CanvasScaler>().StartCoroutine(_CoUpdateTextureOnNextFrame());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Release captured image.
|
|
/// </summary>
|
|
public void Release()
|
|
{
|
|
_Release(true);
|
|
texture = null;
|
|
_SetDirty();
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
protected override void Reset()
|
|
{
|
|
// Set parameters as 'Medium'.
|
|
m_BlurIterations = 3;
|
|
m_FilterMode = FilterMode.Bilinear;
|
|
m_DesamplingRate = DesamplingRate.x1;
|
|
m_ReductionRate = DesamplingRate.x1;
|
|
base.Reset();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Raises the before serialize event.
|
|
/// </summary>
|
|
public void OnBeforeSerialize()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Raises the after deserialize event.
|
|
/// </summary>
|
|
public void OnAfterDeserialize()
|
|
{
|
|
UnityEditor.EditorApplication.delayCall += () => UpdateMaterial(true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Raises the validate event.
|
|
/// </summary>
|
|
protected override void OnValidate()
|
|
{
|
|
base.OnValidate();
|
|
UnityEditor.EditorApplication.delayCall += () => UpdateMaterial(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates the material.
|
|
/// </summary>
|
|
/// <param name="ignoreInPlayMode">If set to <c>true</c> ignore in play mode.</param>
|
|
protected void UpdateMaterial(bool ignoreInPlayMode)
|
|
{
|
|
if (!this || ignoreInPlayMode && Application.isPlaying)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var mat = MaterialResolver.GetOrGenerateMaterialVariant(Shader.Find(shaderName), m_EffectMode, m_ColorMode, m_BlurMode);
|
|
if (m_EffectMaterial != mat)
|
|
{
|
|
material = null;
|
|
m_EffectMaterial = mat;
|
|
_SetDirty();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
//################################
|
|
// Private Members.
|
|
//################################
|
|
RenderTexture _rt;
|
|
RenderTargetIdentifier _rtId;
|
|
|
|
static int s_CopyId;
|
|
static int s_EffectId1;
|
|
static int s_EffectId2;
|
|
static int s_EffectFactorId;
|
|
static int s_ColorFactorId;
|
|
static CommandBuffer s_CommandBuffer;
|
|
|
|
/// <summary>
|
|
/// Release genarated objects.
|
|
/// </summary>
|
|
/// <param name="releaseRT">If set to <c>true</c> release cached RenderTexture.</param>
|
|
void _Release(bool releaseRT)
|
|
{
|
|
if (releaseRT)
|
|
{
|
|
texture = null;
|
|
_Release(ref _rt);
|
|
}
|
|
|
|
if (s_CommandBuffer != null)
|
|
{
|
|
s_CommandBuffer.Clear();
|
|
|
|
if (releaseRT)
|
|
{
|
|
s_CommandBuffer.Release();
|
|
s_CommandBuffer = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
[System.Diagnostics.Conditional("UNITY_EDITOR")]
|
|
void _SetDirty()
|
|
{
|
|
#if UNITY_EDITOR
|
|
if (!Application.isPlaying)
|
|
{
|
|
UnityEditor.EditorUtility.SetDirty(this);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void _Release(ref RenderTexture obj)
|
|
{
|
|
if (obj)
|
|
{
|
|
obj.Release();
|
|
RenderTexture.ReleaseTemporary(obj);
|
|
obj = null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set texture on next frame.
|
|
/// </summary>
|
|
IEnumerator _CoUpdateTextureOnNextFrame()
|
|
{
|
|
yield return new WaitForEndOfFrame();
|
|
UpdateTexture();
|
|
}
|
|
|
|
void UpdateTexture()
|
|
{
|
|
#if !UNITY_EDITOR
|
|
// Execute command buffer.
|
|
Graphics.ExecuteCommandBuffer (s_CommandBuffer);
|
|
#endif
|
|
_Release(false);
|
|
texture = capturedTexture;
|
|
_SetDirty();
|
|
}
|
|
|
|
}
|
|
} |