using System.Collections; using System.Linq; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Serialization; using UnityEngine.UI; namespace Coffee.UIExtensions { /// /// UIEffectCapturedImage /// [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"; /// /// Desampling rate. /// 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. //################################ /// /// Effect factor between 0(no effect) and 1(complete effect). /// [System.Obsolete("Use effectFactor instead (UnityUpgradable) -> effectFactor")] public float toneLevel { get { return m_EffectFactor; } set { m_EffectFactor = Mathf.Clamp(value, 0, 1); } } /// /// Effect factor between 0(no effect) and 1(complete effect). /// public float effectFactor { get { return m_EffectFactor; } set { m_EffectFactor = Mathf.Clamp(value, 0, 1); } } /// /// Color effect factor between 0(no effect) and 1(complete effect). /// public float colorFactor { get { return m_ColorFactor; } set { m_ColorFactor = Mathf.Clamp(value, 0, 1); } } /// /// How far is the blurring from the graphic. /// [System.Obsolete("Use blurFactor instead (UnityUpgradable) -> blurFactor")] public float blur { get { return m_BlurFactor; } set { m_BlurFactor = Mathf.Clamp(value, 0, 4); } } /// /// How far is the blurring from the graphic. /// public float blurFactor { get { return m_BlurFactor; } set { m_BlurFactor = Mathf.Clamp(value, 0, 4); } } /// /// Tone effect mode. /// [System.Obsolete("Use effectMode instead (UnityUpgradable) -> effectMode")] public EffectMode toneMode { get { return m_EffectMode; } } /// /// Effect mode. /// public EffectMode effectMode { get { return m_EffectMode; } } /// /// Color effect mode. /// public ColorMode colorMode { get { return m_ColorMode; } } /// /// Blur effect mode. /// public BlurMode blurMode { get { return m_BlurMode; } } /// /// Color for the color effect. /// public Color effectColor { get { return m_EffectColor; } set { m_EffectColor = value; } } /// /// Effect material. /// public virtual Material effectMaterial { get { return m_EffectMaterial; } } /// /// Desampling rate of the generated RenderTexture. /// public DesamplingRate desamplingRate { get { return m_DesamplingRate; } set { m_DesamplingRate = value; } } /// /// Desampling rate of reduction buffer to apply effect. /// public DesamplingRate reductionRate { get { return m_ReductionRate; } set { m_ReductionRate = value; } } /// /// FilterMode for capturing. /// public FilterMode filterMode { get { return m_FilterMode; } set { m_FilterMode = value; } } /// /// Captured texture. /// public RenderTexture capturedTexture { get { return _rt; } } /// /// Blur iterations. /// [System.Obsolete("Use blurIterations instead (UnityUpgradable) -> blurIterations")] public int iterations { get { return m_BlurIterations; } set { m_BlurIterations = value; } } /// /// Blur iterations. /// public int blurIterations { get { return m_BlurIterations; } set { m_BlurIterations = value; } } /// /// Fits graphic size to screen. /// [System.Obsolete("Use fitToScreen instead (UnityUpgradable) -> fitToScreen")] public bool keepCanvasSize { get { return m_FitToScreen; } set { m_FitToScreen = value; } } /// /// Fits graphic size to screen on captured. /// public bool fitToScreen { get { return m_FitToScreen; } set { m_FitToScreen = value; } } /// /// Target RenderTexture to capture. /// [System.Obsolete] public RenderTexture targetTexture { get { return null; } set { } } /// /// Capture automatically on enable. /// public bool captureOnEnable { get { return m_CaptureOnEnable; } set { m_CaptureOnEnable = value; } } /// /// This function is called when the object becomes enabled and active. /// 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; } } /// /// This function is called when the MonoBehaviour will be destroyed. /// protected override void OnDestroy() { Release(); base.OnDestroy(); } /// /// Callback function when a UI element needs to generate vertices. /// 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); } } } /// /// Gets the size of the desampling. /// 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); } } /// /// Capture rendering result. /// 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().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().StartCoroutine(_CoUpdateTextureOnNextFrame()); } /// /// Release captured image. /// 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(); } /// /// Raises the before serialize event. /// public void OnBeforeSerialize() { } /// /// Raises the after deserialize event. /// public void OnAfterDeserialize() { UnityEditor.EditorApplication.delayCall += () => UpdateMaterial(true); } /// /// Raises the validate event. /// protected override void OnValidate() { base.OnValidate(); UnityEditor.EditorApplication.delayCall += () => UpdateMaterial(false); } /// /// Updates the material. /// /// If set to true ignore in play mode. 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; /// /// Release genarated objects. /// /// If set to true release cached RenderTexture. 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; } } /// /// Set texture on next frame. /// 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(); } } }