Before 优化 机场
This commit is contained in:
@@ -0,0 +1,226 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace Kamgam.UIToolkitBlurredBackground
|
||||
{
|
||||
/// <summary>
|
||||
/// This manager keeps track of whether or not the blur is needed and disables the
|
||||
/// rendering if not. This is done to save performance when no blurred UI is shown.
|
||||
/// </summary>
|
||||
public class BlurManager
|
||||
{
|
||||
static BlurManager _instance;
|
||||
public static BlurManager Instance // This is triggered by the UI Toolkit Elements
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = new BlurManager();
|
||||
#if UNITY_EDITOR
|
||||
if (!UnityEditor.EditorApplication.isPlaying && UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode)
|
||||
{
|
||||
// Delay start if in between play mode changes.
|
||||
// We need this because the UI Elements trigger the creation of the instance in between playmode changes.
|
||||
UnityEditor.EditorApplication.playModeStateChanged += delayedStart;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
_instance.Start();
|
||||
}
|
||||
|
||||
}
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
static void delayedStart(UnityEditor.PlayModeStateChange change)
|
||||
{
|
||||
if (change == UnityEditor.PlayModeStateChange.EnteredPlayMode && _instance != null)
|
||||
{
|
||||
_instance.Start();
|
||||
}
|
||||
UnityEditor.EditorApplication.playModeStateChanged -= delayedStart;
|
||||
}
|
||||
#endif
|
||||
|
||||
// -------------------
|
||||
|
||||
void Start()
|
||||
{
|
||||
// Register this classes Update() method in the Unity update loop for runtime and editor.
|
||||
BlurManagerUpdater.Init(Update);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Defines how often the blur will be applied. Use with caution. It drains performance quickly.")]
|
||||
/// </summary>
|
||||
[System.NonSerialized]
|
||||
protected int _iterations = 1;
|
||||
public int Iterations
|
||||
{
|
||||
get
|
||||
{
|
||||
return _iterations;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (value < 0)
|
||||
value = 0;
|
||||
|
||||
_iterations = value;
|
||||
Renderer.Iterations = value;
|
||||
}
|
||||
}
|
||||
|
||||
[System.NonSerialized]
|
||||
protected float _offset = 10f;
|
||||
public float Offset
|
||||
{
|
||||
get
|
||||
{
|
||||
return _offset;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (value < 0f)
|
||||
value = 0f;
|
||||
|
||||
_offset = value;
|
||||
Renderer.Offset = value;
|
||||
}
|
||||
}
|
||||
|
||||
[System.NonSerialized]
|
||||
protected Vector2Int _resolution = new Vector2Int(512, 512);
|
||||
public Vector2Int Resolution
|
||||
{
|
||||
get
|
||||
{
|
||||
return _resolution;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (value.x < 2 || value.y < 2)
|
||||
value = new Vector2Int(2, 2);
|
||||
|
||||
_resolution = value;
|
||||
Renderer.Resolution = value;
|
||||
}
|
||||
}
|
||||
|
||||
[System.NonSerialized]
|
||||
protected ShaderQuality _quality = ShaderQuality.Medium;
|
||||
public ShaderQuality Quality
|
||||
{
|
||||
get
|
||||
{
|
||||
return _quality;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_quality = value;
|
||||
Renderer.Quality = _quality;
|
||||
}
|
||||
}
|
||||
|
||||
public Texture GetBlurredTexture()
|
||||
{
|
||||
return Renderer.GetBlurredTexture();
|
||||
}
|
||||
|
||||
[System.NonSerialized]
|
||||
protected IBlurRenderer _renderer;
|
||||
public IBlurRenderer Renderer
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_renderer == null)
|
||||
{
|
||||
#if !KAMGAM_RENDER_PIPELINE_URP && !KAMGAM_RENDER_PIPELINE_HDRP
|
||||
_renderer = new BlurRendererBuiltIn(); // BuiltIn
|
||||
#elif KAMGAM_RENDER_PIPELINE_URP
|
||||
_renderer = new BlurRendererURP(); // URP
|
||||
#else
|
||||
_renderer = new BlurRendererHDRP(); // HDRP
|
||||
#endif
|
||||
_renderer.Iterations = Iterations;
|
||||
}
|
||||
return _renderer;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_renderer = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Keeps track of how many elements use the blurred texture. If none are using it then
|
||||
/// the rendering will be paused to save performance.
|
||||
/// </summary>
|
||||
protected List<VisualElement> _blurredBackgroundElements = new List<VisualElement>();
|
||||
|
||||
public void AttachElement(VisualElement ele)
|
||||
{
|
||||
if (!_blurredBackgroundElements.Contains(ele))
|
||||
{
|
||||
_blurredBackgroundElements.Add(ele);
|
||||
ele.MarkDirtyRepaint();
|
||||
}
|
||||
|
||||
if (Renderer != null)
|
||||
{
|
||||
Renderer.Active = shouldBeActive();
|
||||
}
|
||||
}
|
||||
|
||||
public void DetachElement(VisualElement ele)
|
||||
{
|
||||
if (_blurredBackgroundElements.Contains(ele))
|
||||
{
|
||||
_blurredBackgroundElements.Remove(ele);
|
||||
}
|
||||
|
||||
if (Renderer != null)
|
||||
{
|
||||
Renderer.Active = shouldBeActive();
|
||||
}
|
||||
}
|
||||
|
||||
protected bool shouldBeActive()
|
||||
{
|
||||
// Count the visible elements (display: none, visbility: hidden are not counted)
|
||||
int activeElements = 0;
|
||||
foreach (var ele in _blurredBackgroundElements)
|
||||
{
|
||||
if (ele.visible && ele.resolvedStyle.display != DisplayStyle.None && ele.enabledInHierarchy)
|
||||
{
|
||||
activeElements++;
|
||||
}
|
||||
}
|
||||
|
||||
return activeElements > 0 && _iterations > 0 && _offset > 0.0f;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
// Disable rendering is no elements with blurred background are visible.
|
||||
Renderer.Active = shouldBeActive();
|
||||
|
||||
// Keep the renderer in sync with the current main camera.
|
||||
if (Renderer.Active)
|
||||
{
|
||||
Renderer.Update();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b08d7248d7a9c674a83f58d7a2468d93
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Kamgam.UIToolkitBlurredBackground
|
||||
{
|
||||
/// <summary>
|
||||
/// This class ensure update is called in play and in edit mode.
|
||||
/// </summary>
|
||||
[HelpURL("https://kamgam.com/unity/UIToolkitBlurredBackgroundManual.pdf")]
|
||||
public partial class BlurManagerUpdater : MonoBehaviour
|
||||
{
|
||||
static BlurManagerUpdater _instance;
|
||||
static BlurManagerUpdater instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = Utils.FindRootObjectByType<BlurManagerUpdater>(includeInactive: true);
|
||||
if (_instance == null)
|
||||
{
|
||||
var go = new GameObject("UIToolkit BlurredBackground Updater");
|
||||
_instance = go.AddComponent<BlurManagerUpdater>();
|
||||
_instance.hideFlags = HideFlags.DontSave;
|
||||
Utils.SmartDontDestroyOnLoad(_instance.gameObject);
|
||||
}
|
||||
}
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
public Action OnUpdate;
|
||||
|
||||
public void Update()
|
||||
{
|
||||
OnUpdate?.Invoke();
|
||||
}
|
||||
|
||||
public static void Init(Action updateFunc)
|
||||
{
|
||||
#if !UNITY_EDITOR
|
||||
// Runtime
|
||||
instance.OnUpdate += updateFunc;
|
||||
#else
|
||||
// Editor
|
||||
_action = updateFunc;
|
||||
if (EditorApplication.isPlayingOrWillChangePlaymode)
|
||||
{
|
||||
instance.OnUpdate += updateFunc;
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorApplication.update -= updateInEditor;
|
||||
EditorApplication.update += updateInEditor;
|
||||
}
|
||||
|
||||
EditorApplication.playModeStateChanged -= onPlayModeChanged;
|
||||
EditorApplication.playModeStateChanged += onPlayModeChanged;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
static Action _action;
|
||||
static void updateInEditor()
|
||||
{
|
||||
if (!EditorApplication.isPlayingOrWillChangePlaymode) // Just to be extra sure.
|
||||
_action.Invoke();
|
||||
}
|
||||
|
||||
private static void onPlayModeChanged(PlayModeStateChange obj)
|
||||
{
|
||||
if (obj == PlayModeStateChange.ExitingPlayMode)
|
||||
{
|
||||
EditorApplication.update -= updateInEditor;
|
||||
EditorApplication.update += updateInEditor;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3ddba87dfe0cfee46aee8db9c2d00d23
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b8c07b9fd0b693348a94423905ba9385
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,153 @@
|
||||
#if !KAMGAM_RENDER_PIPELINE_HDRP && !KAMGAM_RENDER_PIPELINE_URP
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace Kamgam.UIToolkitBlurredBackground
|
||||
{
|
||||
/// <summary>
|
||||
/// Uses command buffers to hook into the rendering camera and extract a blurred image.
|
||||
/// </summary>
|
||||
public class BlurRendererBuiltIn : IBlurRenderer
|
||||
{
|
||||
public event Action OnPostRender;
|
||||
|
||||
protected BlurredBackgroundBufferBuiltIn _renderBuffer;
|
||||
public BlurredBackgroundBufferBuiltIn RenderBuffer
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_renderBuffer == null)
|
||||
{
|
||||
_renderBuffer = new BlurredBackgroundBufferBuiltIn(BlurredBackgroundBufferBuiltIn.CameraEventForBlur);
|
||||
}
|
||||
return _renderBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
protected bool _active;
|
||||
|
||||
/// <summary>
|
||||
/// Activate or deactivate the renderer. Disable to save performance (no rendering will be done).
|
||||
/// </summary>
|
||||
public bool Active
|
||||
{
|
||||
get => _active;
|
||||
set
|
||||
{
|
||||
if (value != _active)
|
||||
{
|
||||
_active = value;
|
||||
if (!_active)
|
||||
{
|
||||
RenderBuffer.Active = value;
|
||||
RenderBuffer.ClearBuffers();
|
||||
}
|
||||
else
|
||||
{
|
||||
var cam = RenderUtils.GetGameViewCamera();
|
||||
RenderBuffer.Active = value;
|
||||
RenderBuffer.AddBuffer(cam);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int Iterations
|
||||
{
|
||||
get
|
||||
{
|
||||
return RenderBuffer.Iterations;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
RenderBuffer.Iterations = value;
|
||||
}
|
||||
}
|
||||
|
||||
public float Offset
|
||||
{
|
||||
get
|
||||
{
|
||||
return RenderBuffer.Offset;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
RenderBuffer.Offset = value;
|
||||
}
|
||||
}
|
||||
|
||||
public Vector2Int Resolution
|
||||
{
|
||||
get
|
||||
{
|
||||
return RenderBuffer.Resolution;
|
||||
}
|
||||
set
|
||||
{
|
||||
RenderBuffer.Resolution = value;
|
||||
}
|
||||
}
|
||||
|
||||
public ShaderQuality Quality
|
||||
{
|
||||
get
|
||||
{
|
||||
return RenderBuffer.Quality;
|
||||
}
|
||||
set
|
||||
{
|
||||
RenderBuffer.Quality = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The material is used in screen space overlay canvases.
|
||||
/// </summary>
|
||||
public Material GetMaterial()
|
||||
{
|
||||
return RenderBuffer.Material;
|
||||
}
|
||||
|
||||
public Texture GetBlurredTexture()
|
||||
{
|
||||
return RenderBuffer.GetBlurredTexture();
|
||||
}
|
||||
|
||||
protected Color _additiveColor = new Color(0, 0, 0, 0);
|
||||
public Color AdditiveColor
|
||||
{
|
||||
get
|
||||
{
|
||||
return _additiveColor;
|
||||
}
|
||||
set
|
||||
{
|
||||
_additiveColor = value;
|
||||
|
||||
RenderBuffer.AdditiveColor = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called in the Update loop.
|
||||
/// </summary>
|
||||
public bool Update()
|
||||
{
|
||||
var gameCam = RenderUtils.GetGameViewCamera();
|
||||
_renderBuffer?.UpdateActiveCamera(gameCam);
|
||||
|
||||
OnPostRender?.Invoke();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
~BlurRendererBuiltIn()
|
||||
{
|
||||
_renderBuffer?.ClearBuffers();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d8410541ac426104aa8899f96f1b53fb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254328
|
||||
packageName: UI Toolkit Blurred Background - Fast translucent background image
|
||||
packageVersion: 1.0.4
|
||||
assetPath: Assets/Kamgam/UIToolkitBlurredBackground/Runtime/Scripts/Rendering/BlurRendererBuiltIn.cs
|
||||
uploadId: 644498
|
@@ -0,0 +1,216 @@
|
||||
#if KAMGAM_RENDER_PIPELINE_HDRP && !KAMGAM_RENDER_PIPELINE_URP
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering.HighDefinition;
|
||||
|
||||
namespace Kamgam.UIToolkitBlurredBackground
|
||||
{
|
||||
public class BlurRendererHDRP : IBlurRenderer
|
||||
{
|
||||
protected int _blurIterations;
|
||||
public int Iterations
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Pass != null)
|
||||
{
|
||||
return Pass.BlurIterations;
|
||||
}
|
||||
return _blurIterations;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_blurIterations = value;
|
||||
if (Pass != null)
|
||||
{
|
||||
Pass.BlurIterations = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected float _offset = 1.5f;
|
||||
/// <summary>
|
||||
/// This is only used in the performance shader. Default is 1.5f. You can increase this AND reduce the blur strength to imporve performance. However, the quality will start to degrade rapidly.
|
||||
/// </summary>
|
||||
public float Offset
|
||||
{
|
||||
get => _offset;
|
||||
set
|
||||
{
|
||||
_offset = value;
|
||||
if (Pass != null)
|
||||
{
|
||||
Pass.Offset = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Vector2Int _resolution = new Vector2Int(512, 512);
|
||||
/// <summary>
|
||||
/// The texture resolution of the blurred image. Default is 512 x 512. Please use 2^n values like 256, 512, 1024, 2048. Reducing this will increase performance but decrease quality. Every frame your rendered image will be copied, resized and then blurred [BlurStrength] times.
|
||||
/// </summary>
|
||||
public Vector2Int Resolution
|
||||
{
|
||||
get => _resolution;
|
||||
set
|
||||
{
|
||||
_resolution = value;
|
||||
if (Pass != null)
|
||||
{
|
||||
Pass.Resolution = _resolution;
|
||||
Pass.UpdateRenderTextureResolutions();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected ShaderQuality _quality = ShaderQuality.Medium;
|
||||
|
||||
/// <summary>
|
||||
/// The used shader variant. If you are having performance problems with the gaussian shader then try the perfrmance one. It's faster yet the quality is worse (especially for low shader strengths).
|
||||
/// </summary>
|
||||
public ShaderQuality Quality
|
||||
{
|
||||
get => _quality;
|
||||
set
|
||||
{
|
||||
_quality = value;
|
||||
if (Pass != null)
|
||||
{
|
||||
Pass.ShaderQuality = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected bool _active;
|
||||
public bool Active
|
||||
{
|
||||
get => _active;
|
||||
set
|
||||
{
|
||||
_active = value;
|
||||
if (Pass != null)
|
||||
{
|
||||
Pass.enabled = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected GameObject _passGameObject;
|
||||
protected CustomPassVolume _passVolume;
|
||||
protected BlurredBackgroundPassHDRP _pass;
|
||||
public BlurredBackgroundPassHDRP Pass
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_pass == null || _passVolume == null || _passGameObject == null)
|
||||
{
|
||||
_pass = null;
|
||||
_passVolume = null;
|
||||
_passGameObject = null;
|
||||
|
||||
var volumes = Utils.FindRootObjectsByType<CustomPassVolume>(includeInactive: true);
|
||||
foreach (var volume in volumes)
|
||||
{
|
||||
if (volume.isGlobal)
|
||||
{
|
||||
var type = typeof(BlurredBackgroundPassHDRP);
|
||||
var passes = volume.customPasses;
|
||||
foreach (var pass in volume.customPasses)
|
||||
{
|
||||
var uitkPass = pass as BlurredBackgroundPassHDRP;
|
||||
if (uitkPass != null)
|
||||
{
|
||||
_pass = uitkPass;
|
||||
_passVolume = volume;
|
||||
_passGameObject = volume.gameObject;
|
||||
goto EndOfLoop;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EndOfLoop:
|
||||
return _pass;
|
||||
}
|
||||
}
|
||||
|
||||
public BlurRendererHDRP()
|
||||
{
|
||||
var cam = RenderUtils.GetGameViewCamera();
|
||||
createPassIfNecessary(cam);
|
||||
}
|
||||
|
||||
void createPassIfNecessary(Camera cam = null)
|
||||
{
|
||||
if (Pass == null)
|
||||
{
|
||||
var go = new GameObject("UITK BlurredBackground Custom Pass Volume");
|
||||
go.hideFlags = HideFlags.DontSave;
|
||||
Utils.SmartDontDestroyOnLoad(go);
|
||||
|
||||
var volume = go.AddComponent<CustomPassVolume>();
|
||||
volume.injectionPoint = CustomPassInjectionPoint.AfterPostProcess;
|
||||
volume.priority = 0;
|
||||
if (cam == null)
|
||||
{
|
||||
volume.isGlobal = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
volume.isGlobal = false;
|
||||
volume.targetCamera = cam;
|
||||
}
|
||||
|
||||
|
||||
var pass = volume.AddPassOfType<BlurredBackgroundPassHDRP>();
|
||||
pass.enabled = true;
|
||||
pass.targetColorBuffer = CustomPass.TargetBuffer.Camera;
|
||||
pass.targetDepthBuffer = CustomPass.TargetBuffer.Camera;
|
||||
pass.clearFlags = UnityEngine.Rendering.ClearFlag.None;
|
||||
|
||||
// Important if HideFlags.HideAndDontSave is used or else the object will not be found by Pass.
|
||||
_pass = pass as BlurredBackgroundPassHDRP;
|
||||
_passVolume = volume;
|
||||
_passGameObject = go;
|
||||
|
||||
// Init pass variables
|
||||
_pass.ShaderQuality = Quality;
|
||||
_pass.Resolution = Resolution;
|
||||
_pass.Offset = Offset;
|
||||
_pass.BlurIterations = Iterations;
|
||||
}
|
||||
}
|
||||
|
||||
public Texture GetBlurredTexture()
|
||||
{
|
||||
if (Pass != null)
|
||||
return Pass.GetBlurredTexture();
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the pass objects if needed.
|
||||
/// </summary>
|
||||
/// <returns>Always false</returns>
|
||||
public bool Update()
|
||||
{
|
||||
// Create render pass if needed.
|
||||
// TODO: Investigate if adding the pass dynamically is possible in HDRP
|
||||
// see (URP): https://forum.unity.com/threads/urp-no-way-to-dynamically-access-modify-the-rendererfeatures-list-at-runtime.1342751/#post-8479169
|
||||
createPassIfNecessary();
|
||||
|
||||
// Keep camera up to date (in case camera stacking is used or the active camera changes at runtime).
|
||||
if ( _passVolume != null
|
||||
&& !_passVolume.isGlobal
|
||||
&& (_passVolume.targetCamera == null || !_passVolume.targetCamera.isActiveAndEnabled))
|
||||
{
|
||||
_passVolume.targetCamera = RenderUtils.GetGameViewCamera();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a08814fffe13b3c4d87f3b2391c2752b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,290 @@
|
||||
#if KAMGAM_RENDER_PIPELINE_URP
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor.SceneManagement;
|
||||
#endif
|
||||
|
||||
namespace Kamgam.UIToolkitBlurredBackground
|
||||
{
|
||||
public class BlurRendererURP : IBlurRenderer
|
||||
{
|
||||
public event System.Action OnPostRender;
|
||||
|
||||
protected BlurredBackgroundPassURP _screenSpacePass;
|
||||
public BlurredBackgroundPassURP ScreenSpacePass
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_screenSpacePass == null)
|
||||
{
|
||||
_screenSpacePass = new BlurredBackgroundPassURP();
|
||||
// NOTICE: This is now overridden in onBeginCameraRendering().
|
||||
_screenSpacePass.renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing;
|
||||
|
||||
_screenSpacePass.OnPostRender += onPostRender;
|
||||
}
|
||||
return _screenSpacePass;
|
||||
}
|
||||
}
|
||||
|
||||
protected bool _active;
|
||||
|
||||
/// <summary>
|
||||
/// Activate or deactivate the renderer. Disable to save performance (no rendering will be done).
|
||||
/// </summary>
|
||||
public bool Active
|
||||
{
|
||||
get => _active;
|
||||
set
|
||||
{
|
||||
if (value != _active)
|
||||
{
|
||||
_active = value;
|
||||
|
||||
ScreenSpacePass.Active = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected int _iterations = 1;
|
||||
public int Iterations
|
||||
{
|
||||
get
|
||||
{
|
||||
return _iterations;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_iterations = value;
|
||||
|
||||
ScreenSpacePass.Iterations = value;
|
||||
}
|
||||
}
|
||||
|
||||
protected float _offset = 1.5f;
|
||||
public float Offset
|
||||
{
|
||||
get
|
||||
{
|
||||
return _offset;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_offset = value;
|
||||
|
||||
ScreenSpacePass.Offset = value;
|
||||
}
|
||||
}
|
||||
|
||||
protected Vector2Int _resolution = new Vector2Int(512, 512);
|
||||
public Vector2Int Resolution
|
||||
{
|
||||
get
|
||||
{
|
||||
return _resolution;
|
||||
}
|
||||
set
|
||||
{
|
||||
_resolution = value;
|
||||
|
||||
ScreenSpacePass.Resolution = value;
|
||||
}
|
||||
}
|
||||
|
||||
protected ShaderQuality _quality = ShaderQuality.Medium;
|
||||
public ShaderQuality Quality
|
||||
{
|
||||
get
|
||||
{
|
||||
return _quality;
|
||||
}
|
||||
set
|
||||
{
|
||||
_quality = value;
|
||||
|
||||
ScreenSpacePass.Quality = value;
|
||||
}
|
||||
}
|
||||
|
||||
protected Color _additiveColor = new Color(0,0,0,0);
|
||||
public Color AdditiveColor
|
||||
{
|
||||
get
|
||||
{
|
||||
return _additiveColor;
|
||||
}
|
||||
set
|
||||
{
|
||||
_additiveColor = value;
|
||||
|
||||
ScreenSpacePass.AdditiveColor = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The material is used in screen space overlay canvases.
|
||||
/// </summary>
|
||||
public Material GetMaterial(RenderMode renderMode)
|
||||
{
|
||||
return ScreenSpacePass.Material;
|
||||
}
|
||||
|
||||
public Texture GetBlurredTexture()
|
||||
{
|
||||
return ScreenSpacePass.GetBlurredTexture();
|
||||
}
|
||||
|
||||
public BlurRendererURP()
|
||||
{
|
||||
RenderPipelineManager.beginCameraRendering += onBeginCameraRendering;
|
||||
|
||||
if (ScreenSpacePass != null)
|
||||
ScreenSpacePass.OnPostRender += onPostRender;
|
||||
|
||||
// Needed to avoid "Render Pipeline error : the XR layout still contains active passes. Executing XRSystem.EndLayout() right" Errors in Unity 2023
|
||||
// Also needed in normal URP to reset the render textures after play mode.
|
||||
#if UNITY_EDITOR
|
||||
UnityEditor.EditorApplication.playModeStateChanged += onPlayModeChanged;
|
||||
UnityEditor.SceneManagement.EditorSceneManager.sceneOpened += onSceneOpened;
|
||||
#endif
|
||||
}
|
||||
|
||||
~BlurRendererURP()
|
||||
{
|
||||
if (_screenSpacePass != null)
|
||||
_screenSpacePass.OnPostRender -= onPostRender;
|
||||
}
|
||||
|
||||
protected void clearRenderTargets()
|
||||
{
|
||||
_screenSpacePass?.ClearRenderTargets();
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
void onPlayModeChanged(UnityEditor.PlayModeStateChange obj)
|
||||
{
|
||||
if (obj == UnityEditor.PlayModeStateChange.ExitingPlayMode || obj == UnityEditor.PlayModeStateChange.EnteredEditMode)
|
||||
{
|
||||
clearRenderTargets();
|
||||
}
|
||||
}
|
||||
|
||||
void onSceneOpened(Scene scene, OpenSceneMode mode)
|
||||
{
|
||||
if (!UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode)
|
||||
{
|
||||
clearRenderTargets();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
const string Renderer2DTypeName = "Renderer2D";
|
||||
|
||||
private Camera[] _tmpAllCameras = new Camera[10];
|
||||
|
||||
void onBeginCameraRendering(ScriptableRenderContext context, Camera cam)
|
||||
{
|
||||
if ( cam == null
|
||||
|| !cam.isActiveAndEnabled)
|
||||
return;
|
||||
|
||||
// All of this is only to support multiple-camera setups with render textures.
|
||||
// The blur only needs to be done on one camera (usually the main camera). That's
|
||||
// why the stop on all other cameras.
|
||||
var mainCam = Camera.main;
|
||||
if (mainCam != null)
|
||||
{
|
||||
if (cam != mainCam)
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No main camera -> let's check if there are cameras that
|
||||
// are NOT rendering into render textures.
|
||||
Camera firstCamWithoutRenderTexture = null;
|
||||
int camCount = Camera.allCamerasCount;
|
||||
int maxCamCount = _tmpAllCameras.Length;
|
||||
// alloc new array if needed
|
||||
if(camCount > maxCamCount)
|
||||
{
|
||||
_tmpAllCameras = new Camera[camCount + 5];
|
||||
}
|
||||
Camera.GetAllCameras(_tmpAllCameras);
|
||||
for (int i = 0; i < maxCamCount; i++)
|
||||
{
|
||||
// Null out old references
|
||||
if(i >= camCount)
|
||||
{
|
||||
_tmpAllCameras[i] = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
var cCam = _tmpAllCameras[i];
|
||||
|
||||
if (cCam == null || !cCam.isActiveAndEnabled)
|
||||
continue;
|
||||
|
||||
if (cCam != null && cCam.targetTexture == null)
|
||||
{
|
||||
firstCamWithoutRenderTexture = cCam;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If there are some then use the first we an find. Which means we abort the blur pass on all others.
|
||||
if (firstCamWithoutRenderTexture != null && cam != firstCamWithoutRenderTexture)
|
||||
return;
|
||||
|
||||
// If there are only cameras with render textures then we ignore it.
|
||||
// This means that in setups with cameras that are only rendered in to textures
|
||||
// no blur will occur.
|
||||
if (firstCamWithoutRenderTexture == null)
|
||||
return;
|
||||
}
|
||||
|
||||
var data = cam.GetUniversalAdditionalCameraData();
|
||||
|
||||
if (data == null)
|
||||
return;
|
||||
|
||||
// Turns out the list is always empty and the enqueuing is a per frame action.
|
||||
|
||||
// Check if we are using the 2D renderer (skip check if already using "BeforeRenderingPostProcessing" event).
|
||||
if (cam.orthographic && ScreenSpacePass.renderPassEvent == RenderPassEvent.AfterRenderingPostProcessing)
|
||||
{
|
||||
if (cam.GetUniversalAdditionalCameraData().scriptableRenderer.GetType().Name.EndsWith(Renderer2DTypeName))
|
||||
{
|
||||
// If yes then change the event from AfterRenderingPostProcessing to BeforeRenderingPostProcessing.
|
||||
// Sadly accessing PostPro render results is not supported in URP 2D, see:
|
||||
// https://forum.unity.com/threads/urp-2d-how-to-access-camera-target-after-post-processing.1465124/
|
||||
// https://forum.unity.com/threads/7-3-1-renderpassevent-afterrenderingpostprocessing-is-broken.873604/#post-8422710
|
||||
ScreenSpacePass.renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing;
|
||||
}
|
||||
}
|
||||
|
||||
data.scriptableRenderer.EnqueuePass(ScreenSpacePass);
|
||||
}
|
||||
|
||||
protected void onPostRender()
|
||||
{
|
||||
OnPostRender?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Not needed in SRPs.
|
||||
/// </summary>
|
||||
public bool Update()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fa030b7761ae4ff499e9807b82ec3307
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,435 @@
|
||||
#if !KAMGAM_RENDER_PIPELINE_HDRP && !KAMGAM_RENDER_PIPELINE_URP
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace Kamgam.UIToolkitBlurredBackground
|
||||
{
|
||||
/// <summary>
|
||||
/// Uses command buffers to hook into the rendering camera and extract a blurred image.
|
||||
/// </summary>
|
||||
public class BlurredBackgroundBufferBuiltIn
|
||||
{
|
||||
public const string ShaderName = "Kamgam/UI Toolkit/BuiltIn/Blur Shader";
|
||||
|
||||
public const CameraEvent CameraEventForBlur = CameraEvent.AfterEverything;
|
||||
|
||||
protected Camera _camera;
|
||||
protected CameraEvent _cameraEvent;
|
||||
protected CommandBuffer _buffer;
|
||||
|
||||
protected bool _active;
|
||||
|
||||
/// <summary>
|
||||
/// Activate or deactivate the renderer. Disable to save performance (no rendering will be done).
|
||||
/// </summary>
|
||||
public bool Active
|
||||
{
|
||||
get => _active;
|
||||
set
|
||||
{
|
||||
if (value != _active)
|
||||
{
|
||||
_active = value;
|
||||
if (!_active)
|
||||
{
|
||||
ClearBuffers();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_camera != null)
|
||||
AddBuffer(_camera, _cameraEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected int _iterations = 1;
|
||||
public int Iterations
|
||||
{
|
||||
get => _iterations;
|
||||
set
|
||||
{
|
||||
if (value != _iterations)
|
||||
{
|
||||
_iterations = value;
|
||||
RecreateBuffers();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected float _offset = 10f;
|
||||
public float Offset
|
||||
{
|
||||
get => _offset;
|
||||
set
|
||||
{
|
||||
_offset = value;
|
||||
setOffset(value);
|
||||
}
|
||||
}
|
||||
|
||||
protected Vector2Int _resolution = new Vector2Int(512, 512);
|
||||
/// <summary>
|
||||
/// The texture resolution of the blurred image. Default is 512 x 512. Please use 2^n values like 256, 512, 1024, 2048, 4096. Reducing this will increase performance but decrease quality. Every frame your rendered image will be copied, resized and then blurred [BlurStrength] times.
|
||||
/// </summary>
|
||||
public Vector2Int Resolution
|
||||
{
|
||||
get => _resolution;
|
||||
set
|
||||
{
|
||||
_resolution = value;
|
||||
updateRenderTextureResolutions();
|
||||
setOffset(_offset); // We have to update offset here because the _worldMaterial offset depends on _resolution.
|
||||
}
|
||||
}
|
||||
|
||||
void updateRenderTextureResolutions()
|
||||
{
|
||||
if (_renderTargetBlurredA != null)
|
||||
{
|
||||
_renderTargetBlurredA.Release();
|
||||
_renderTargetBlurredA.width = _resolution.x;
|
||||
_renderTargetBlurredA.height = _resolution.y;
|
||||
_renderTargetBlurredA.Create();
|
||||
}
|
||||
|
||||
if (_renderTargetBlurredB != null)
|
||||
{
|
||||
_renderTargetBlurredB.Release();
|
||||
_renderTargetBlurredB.width = _resolution.x;
|
||||
_renderTargetBlurredB.height = _resolution.y;
|
||||
_renderTargetBlurredB.Create();
|
||||
}
|
||||
}
|
||||
|
||||
protected Shader _blurShader;
|
||||
public Shader BlurShader
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_blurShader == null)
|
||||
{
|
||||
_blurShader = Shader.Find(ShaderName);
|
||||
}
|
||||
|
||||
return _blurShader;
|
||||
}
|
||||
}
|
||||
|
||||
protected ShaderQuality _quality = ShaderQuality.Medium;
|
||||
public ShaderQuality Quality
|
||||
{
|
||||
get => _quality;
|
||||
set
|
||||
{
|
||||
if (_quality != value)
|
||||
{
|
||||
_quality = value;
|
||||
|
||||
setQualityOfMaterial(_material, _quality);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Color _additiveColor = new Color(0f, 0f, 0f, 0f);
|
||||
public Color AdditiveColor
|
||||
{
|
||||
get => _additiveColor;
|
||||
set
|
||||
{
|
||||
_additiveColor = value;
|
||||
setAdditiveColor(_material, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The material is used in screen space overlay canvases.
|
||||
/// </summary>
|
||||
[System.NonSerialized]
|
||||
protected Material _material;
|
||||
public Material Material
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_material == null)
|
||||
{
|
||||
// Create material with shader
|
||||
var shader = Shader.Find(ShaderName);
|
||||
if (shader != null)
|
||||
{
|
||||
_material = new Material(shader);
|
||||
_material.color = Color.white;
|
||||
_material.hideFlags = HideFlags.HideAndDontSave;
|
||||
|
||||
setQualityOfMaterial(_material, _quality);
|
||||
setFlipVerticalOfMaterial(_material, shouldFlipInShaderDependingOnProjectionParams());
|
||||
setAdditiveColor(_material, AdditiveColor);
|
||||
setOffset(_offset);
|
||||
}
|
||||
}
|
||||
return _material;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_material = value;
|
||||
}
|
||||
}
|
||||
|
||||
void setQualityOfMaterial(Material material, ShaderQuality quality)
|
||||
{
|
||||
if (material == null)
|
||||
return;
|
||||
|
||||
switch (quality)
|
||||
{
|
||||
case ShaderQuality.Low:
|
||||
material.SetKeyword(new LocalKeyword(material.shader, "_SAMPLES_LOW"), true);
|
||||
material.SetKeyword(new LocalKeyword(material.shader, "_SAMPLES_MEDIUM"), false);
|
||||
material.SetKeyword(new LocalKeyword(material.shader, "_SAMPLES_HIGH"), false);
|
||||
break;
|
||||
|
||||
case ShaderQuality.Medium:
|
||||
material.SetKeyword(new LocalKeyword(material.shader, "_SAMPLES_LOW"), false);
|
||||
material.SetKeyword(new LocalKeyword(material.shader, "_SAMPLES_MEDIUM"), true);
|
||||
material.SetKeyword(new LocalKeyword(material.shader, "_SAMPLES_HIGH"), false);
|
||||
break;
|
||||
|
||||
case ShaderQuality.High:
|
||||
material.SetKeyword(new LocalKeyword(material.shader, "_SAMPLES_LOW"), false);
|
||||
material.SetKeyword(new LocalKeyword(material.shader, "_SAMPLES_MEDIUM"), false);
|
||||
material.SetKeyword(new LocalKeyword(material.shader, "_SAMPLES_HIGH"), true);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public BlurredBackgroundBufferBuiltIn(CameraEvent evt)
|
||||
{
|
||||
if (evt != CameraEventForBlur)
|
||||
throw new System.Exception("Only " + CameraEventForBlur + " events are supported.");
|
||||
|
||||
_cameraEvent = evt;
|
||||
}
|
||||
|
||||
bool shouldFlipInShaderDependingOnProjectionParams()
|
||||
{
|
||||
// If I use DirectX (Win 10 Pc) or Vulkan (Win 10 Pc) or Metal (on an M1) it is flipped.
|
||||
// If I use OpenGL it works fine for all events (CameraEvent.AfterEverything and CameraEvent.BeforeForwardAlpha)
|
||||
// See: https://forum.unity.com/threads/command-buffer-blit-render-texture-result-is-upside-down.1463063/#post-9159080
|
||||
|
||||
// If on OpenGL then always enable flipping because OpenGL platforms do the flipping
|
||||
// correctly via _ProjectionParams in all cases.
|
||||
if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLCore
|
||||
#if !UNITY_2023_1_OR_NEWER
|
||||
|| SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLES2
|
||||
#endif
|
||||
|| SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLES3)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// On other platforms enable flipping via _ProjectionParams only if the
|
||||
// event is CameraEvent.AfterEverything (i.e. after post processing)
|
||||
return _cameraEvent == CameraEvent.AfterEverything;
|
||||
}
|
||||
|
||||
void setFlipVerticalOfMaterial(Material material, bool flip)
|
||||
{
|
||||
if (material == null)
|
||||
return;
|
||||
|
||||
material.SetFloat("_FlipVertical", flip ? 1f : 0f);
|
||||
}
|
||||
|
||||
void setAdditiveColor(Material material, Color color)
|
||||
{
|
||||
if (material == null)
|
||||
return;
|
||||
|
||||
material.SetColor("_AdditiveColor", color);
|
||||
}
|
||||
|
||||
void setOffset(float value)
|
||||
{
|
||||
if (_material != null)
|
||||
_material.SetVector("_BlurOffset", new Vector4(value, value, 0f, 0f));
|
||||
}
|
||||
|
||||
[System.NonSerialized]
|
||||
protected RenderTexture _renderTargetBlurredA;
|
||||
protected RenderTexture renderTargetBlurredA
|
||||
{
|
||||
get
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
releaseTexturesIfInWrongColorSpace();
|
||||
#endif
|
||||
|
||||
if (_renderTargetBlurredA == null)
|
||||
_renderTargetBlurredA = createRenderTexture();
|
||||
|
||||
return _renderTargetBlurredA;
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
protected void releaseTexturesIfInWrongColorSpace()
|
||||
{
|
||||
if (_renderTargetBlurredA != null)
|
||||
{
|
||||
// If the current sRGB settings does not match the color space then recreate the render textures.
|
||||
if ((_renderTargetBlurredA.sRGB && QualitySettings.activeColorSpace == ColorSpace.Gamma)
|
||||
|| (!_renderTargetBlurredA.sRGB && QualitySettings.activeColorSpace == ColorSpace.Linear))
|
||||
{
|
||||
_renderTargetBlurredA?.Release();
|
||||
_renderTargetBlurredA = null;
|
||||
_renderTargetBlurredB?.Release();
|
||||
_renderTargetBlurredB = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
[System.NonSerialized]
|
||||
protected RenderTexture _renderTargetBlurredB;
|
||||
protected RenderTexture renderTargetBlurredB
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_renderTargetBlurredB == null)
|
||||
_renderTargetBlurredB = createRenderTexture();
|
||||
|
||||
return _renderTargetBlurredB;
|
||||
}
|
||||
}
|
||||
|
||||
RenderTexture createRenderTexture()
|
||||
{
|
||||
var rw = QualitySettings.activeColorSpace == ColorSpace.Linear ? RenderTextureReadWrite.sRGB : RenderTextureReadWrite.Default;
|
||||
var texture = new RenderTexture(Resolution.x, Resolution.y, 0, RenderTextureFormat.Default, rw);
|
||||
texture.filterMode = FilterMode.Bilinear;
|
||||
texture.wrapMode = TextureWrapMode.Clamp;
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
public Texture GetBlurredTexture()
|
||||
{
|
||||
// Debugging textures
|
||||
//#if UNITY_EDITOR
|
||||
// var settings = UIToolkitBlurredBackgroundSettings.GetOrCreateSettings();
|
||||
// if (settings.DebugRenderTextureScreen != null && _cameraEvent == CameraEventForBlur)
|
||||
// {
|
||||
// if (renderTargetBlurredA.width == settings.DebugRenderTextureScreen.width)
|
||||
// {
|
||||
// Graphics.CopyTexture(renderTargetBlurredA, settings.DebugRenderTextureScreen);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// Debug.LogWarning("Debugging render texture width does not match blur render texture width. Debug texture will remain empty.");
|
||||
// }
|
||||
// }
|
||||
//#endif
|
||||
|
||||
return renderTargetBlurredA;
|
||||
}
|
||||
|
||||
public void ClearBuffers()
|
||||
{
|
||||
if (_camera != null && _buffer != null)
|
||||
_camera.RemoveCommandBuffer(_cameraEvent, _buffer);
|
||||
}
|
||||
|
||||
public void AddBuffer(Camera cam)
|
||||
{
|
||||
AddBuffer(cam, _cameraEvent);
|
||||
}
|
||||
|
||||
public void AddBuffer(Camera cam, CameraEvent evt)
|
||||
{
|
||||
if (cam == null)
|
||||
return;
|
||||
|
||||
// Seach for old buffers and remove them
|
||||
var buffers = cam.GetCommandBuffers(evt);
|
||||
foreach (var buf in buffers)
|
||||
{
|
||||
if (buf.name.StartsWith("Kamgam.UGUI Blur"))
|
||||
{
|
||||
cam.RemoveCommandBuffer(_cameraEvent, buf);
|
||||
buf.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
// Create buffer if needed
|
||||
// Debug.Log("Creating Command Buffer on " + cam);
|
||||
_buffer = createBuffer("Kamgam.UGUI Blur (" + evt + ")");
|
||||
cam.AddCommandBuffer(evt, _buffer);
|
||||
|
||||
// Done to avoid flipped (upside down) render results, see:
|
||||
// https://forum.unity.com/threads/commandbuffer-rendering-scene-flipped-upside-down-in-forward-rendering.415922/#post-3114571
|
||||
cam.forceIntoRenderTexture = true;
|
||||
}
|
||||
|
||||
public CommandBuffer createBuffer(string name)
|
||||
{
|
||||
CommandBuffer buf = new CommandBuffer();
|
||||
buf.name = name;
|
||||
|
||||
// copy screen into temporary RT
|
||||
int screenCopyID = Shader.PropertyToID("_ScreenCopyTexture");
|
||||
var desc = new RenderTextureDescriptor(-1, -1);
|
||||
desc.depthBufferBits = 0;
|
||||
desc.useMipMap = false;
|
||||
desc.autoGenerateMips = false;
|
||||
desc.colorFormat = RenderTextureFormat.Default;
|
||||
// Makes sure to properly support linear color space.
|
||||
desc.sRGB = QualitySettings.activeColorSpace == ColorSpace.Linear;
|
||||
buf.GetTemporaryRT(screenCopyID, desc, FilterMode.Bilinear);
|
||||
buf.Blit(BuiltinRenderTextureType.CurrentActive, screenCopyID);
|
||||
|
||||
// Copy from source to A (Sets _MainTex and scales the target down to our blur texture size).
|
||||
buf.Blit(screenCopyID, renderTargetBlurredA);
|
||||
|
||||
// 2 pass blur (A > B > A)
|
||||
int iterations = Iterations * 2 - 1; // Necessary do compensate for flipping of Material (iterations need
|
||||
// to be odd or else the image is upside down if shouldFlip() is true).
|
||||
for (int i = 0; i < iterations; i++)
|
||||
{
|
||||
buf.Blit(renderTargetBlurredA, renderTargetBlurredB, Material, 0);
|
||||
buf.Blit(renderTargetBlurredB, renderTargetBlurredA, Material, 1);
|
||||
}
|
||||
|
||||
buf.ReleaseTemporaryRT(screenCopyID);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
public void UpdateActiveCamera(Camera cam)
|
||||
{
|
||||
if (cam != null && _camera != cam)
|
||||
{
|
||||
// Debug.Log("Setting new camera: " + cam);
|
||||
|
||||
ClearBuffers();
|
||||
_camera = cam;
|
||||
AddBuffer(_camera, _cameraEvent);
|
||||
}
|
||||
}
|
||||
|
||||
public void RecreateBuffers()
|
||||
{
|
||||
ClearBuffers();
|
||||
|
||||
if (_camera != null)
|
||||
AddBuffer(_camera);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a4abb7b676f12cc4c96ef4b45ef6bb3d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,300 @@
|
||||
#if !KAMGAM_RENDER_PIPELINE_URP && KAMGAM_RENDER_PIPELINE_HDRP
|
||||
// Based on: https://github.com/alelievr/HDRP-Custom-Passes/blob/2021.2/Assets/CustomPasses/CopyPass/CopyPass.cs#L67
|
||||
// as recommended by antoinel_unity in https://forum.unity.com/threads/custom-pass-into-render-texture-into-custom-aov.1146872/#post-7362314
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.HighDefinition;
|
||||
|
||||
namespace Kamgam.UIToolkitBlurredBackground
|
||||
{
|
||||
public class BlurredBackgroundPassHDRP : CustomPass
|
||||
{
|
||||
public const string ShaderName = "Kamgam/UI Toolkit/HDRP/Blur Shader";
|
||||
|
||||
[System.NonSerialized]
|
||||
protected Material _material;
|
||||
public Material Material
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_material == null)
|
||||
{
|
||||
// Create material with shader
|
||||
var shader = Shader.Find(ShaderName);
|
||||
if (shader != null)
|
||||
{
|
||||
_material = CoreUtils.CreateEngineMaterial(shader);
|
||||
_material.color = Color.white;
|
||||
|
||||
switch (_shaderQuality)
|
||||
{
|
||||
case ShaderQuality.Low:
|
||||
_material.SetKeyword(new LocalKeyword(shader, "_SAMPLES_LOW"), true);
|
||||
_material.SetKeyword(new LocalKeyword(shader, "_SAMPLES_MEDIUM"), false);
|
||||
_material.SetKeyword(new LocalKeyword(shader, "_SAMPLES_HIGH"), false);
|
||||
break;
|
||||
|
||||
case ShaderQuality.Medium:
|
||||
_material.SetKeyword(new LocalKeyword(shader, "_SAMPLES_LOW"), false);
|
||||
_material.SetKeyword(new LocalKeyword(shader, "_SAMPLES_MEDIUM"), true);
|
||||
_material.SetKeyword(new LocalKeyword(shader, "_SAMPLES_HIGH"), false);
|
||||
break;
|
||||
|
||||
case ShaderQuality.High:
|
||||
_material.SetKeyword(new LocalKeyword(shader, "_SAMPLES_LOW"), false);
|
||||
_material.SetKeyword(new LocalKeyword(shader, "_SAMPLES_MEDIUM"), false);
|
||||
_material.SetKeyword(new LocalKeyword(shader, "_SAMPLES_HIGH"), true);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
setOffset(Offset);
|
||||
}
|
||||
}
|
||||
return _material;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_material = value;
|
||||
}
|
||||
}
|
||||
|
||||
void setOffset(float value)
|
||||
{
|
||||
if (_material != null)
|
||||
_material.SetVector("_BlurOffset", new Vector4(value, value, 0f, 0f));
|
||||
}
|
||||
|
||||
[System.NonSerialized]
|
||||
protected int _blurIterations = 0;
|
||||
public int BlurIterations
|
||||
{
|
||||
get => _blurIterations;
|
||||
set
|
||||
{
|
||||
if (_blurIterations != value)
|
||||
{
|
||||
_blurIterations = value;
|
||||
enabled = _blurIterations > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected float _offset = 1.5f;
|
||||
/// <summary>
|
||||
/// This is only used in the performance shader. Default is 1.5f. You can increase this AND reduce the blur strength to imporve performance. However, the quality will start to degrade rapidly.
|
||||
/// </summary>
|
||||
public float Offset
|
||||
{
|
||||
get => _offset;
|
||||
set
|
||||
{
|
||||
_offset = value;
|
||||
setOffset(value);
|
||||
}
|
||||
}
|
||||
|
||||
protected ShaderQuality _shaderQuality = ShaderQuality.Medium;
|
||||
|
||||
/// <summary>
|
||||
/// The used shader quality. The higher the more performance it will cost.
|
||||
/// </summary>
|
||||
public ShaderQuality ShaderQuality
|
||||
{
|
||||
get => _shaderQuality;
|
||||
set
|
||||
{
|
||||
_shaderQuality = value;
|
||||
_material = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The used resolution of the render texture.
|
||||
/// </summary>
|
||||
[System.NonSerialized]
|
||||
public Vector2Int Resolution = new Vector2Int(512, 512);
|
||||
|
||||
public void UpdateRenderTextureResolutions()
|
||||
{
|
||||
if (_renderTargetBlurredA != null)
|
||||
{
|
||||
_renderTargetBlurredA.Release();
|
||||
_renderTargetBlurredA.width = Resolution.x;
|
||||
_renderTargetBlurredA.height = Resolution.y;
|
||||
_renderTargetBlurredA.Create();
|
||||
}
|
||||
|
||||
if (_renderTargetBlurredB != null)
|
||||
{
|
||||
_renderTargetBlurredB.Release();
|
||||
_renderTargetBlurredB.width = Resolution.x;
|
||||
_renderTargetBlurredB.height = Resolution.y;
|
||||
_renderTargetBlurredB.Create();
|
||||
}
|
||||
}
|
||||
|
||||
[System.NonSerialized]
|
||||
protected RenderTexture _renderTargetBlurredA;
|
||||
public RenderTexture RenderTargetBlurredA
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_renderTargetBlurredA == null)
|
||||
{
|
||||
_renderTargetBlurredA = createRenderTexture();
|
||||
|
||||
if (_renderTargetHandleA != null)
|
||||
{
|
||||
_renderTargetHandleA.Release();
|
||||
_renderTargetHandleA = null;
|
||||
}
|
||||
}
|
||||
|
||||
return _renderTargetBlurredA;
|
||||
}
|
||||
}
|
||||
|
||||
[System.NonSerialized]
|
||||
protected RenderTexture _renderTargetBlurredB;
|
||||
public RenderTexture RenderTargetBlurredB
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_renderTargetBlurredB == null)
|
||||
{
|
||||
_renderTargetBlurredB = createRenderTexture();
|
||||
|
||||
if (_renderTargetHandleB != null)
|
||||
{
|
||||
_renderTargetHandleB.Release();
|
||||
_renderTargetHandleB = null;
|
||||
}
|
||||
}
|
||||
|
||||
return _renderTargetBlurredB;
|
||||
}
|
||||
}
|
||||
|
||||
[System.NonSerialized]
|
||||
protected RTHandle _renderTargetHandleA;
|
||||
public RTHandle RenderTargetHandleA
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_renderTargetHandleA == null)
|
||||
_renderTargetHandleA = RTHandles.Alloc(RenderTargetBlurredA);
|
||||
|
||||
return _renderTargetHandleA;
|
||||
}
|
||||
}
|
||||
|
||||
[System.NonSerialized]
|
||||
protected RTHandle _renderTargetHandleB;
|
||||
public RTHandle RenderTargetHandleB
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_renderTargetHandleB == null)
|
||||
_renderTargetHandleB = RTHandles.Alloc(RenderTargetBlurredB);
|
||||
|
||||
return _renderTargetHandleB;
|
||||
}
|
||||
}
|
||||
|
||||
RenderTexture createRenderTexture()
|
||||
{
|
||||
var texture = new RenderTexture(Resolution.x, Resolution.y, 16);
|
||||
texture.filterMode = FilterMode.Bilinear;
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
[System.NonSerialized]
|
||||
public bool AreTexturesSwapped;
|
||||
|
||||
public Texture GetBlurredTexture()
|
||||
{
|
||||
return AreTexturesSwapped ? RenderTargetBlurredB : RenderTargetBlurredA;
|
||||
}
|
||||
|
||||
protected override bool executeInSceneView => false;
|
||||
|
||||
protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd)
|
||||
{
|
||||
name = "UITK Blurred Background";
|
||||
}
|
||||
|
||||
protected override void Execute(CustomPassContext ctx)
|
||||
{
|
||||
if (Material == null || BlurIterations == 0 || Offset == 0)
|
||||
return;
|
||||
|
||||
var source = ctx.cameraColorBuffer;
|
||||
|
||||
// First pass is just a copy with the right scale (plus downsampling).
|
||||
// From: ctx.cmd.Blit(RenderTargetBlurredB, RenderTargetBlurredA, Material);
|
||||
//
|
||||
// Sadly the API for copying, scaling AND using a material is not exposed.
|
||||
//
|
||||
// TODO: Investigate if this breaks XR compatibility.
|
||||
// Solution leads: Use 2DArray and SAMPLE_TEXTURE2D_X in the shader and maybe use Blit_Texture() or Blit_Identifier to pass the material.
|
||||
// See: https://github.com/Unity-Technologies/UnityCsReference/blob/master/Runtime/Export/Graphics/RenderingCommandBuffer.cs#L901
|
||||
// and: https://github.com/Unity-Technologies/UnityCsReference/blob/master/Runtime/Export/Graphics/RenderingCommandBuffer.bindings.cs#L614
|
||||
var scale = RTHandles.rtHandleProperties.rtHandleScale;
|
||||
ctx.cmd.Blit(source, RenderTargetBlurredA, new Vector2(scale.x, scale.y), Vector2.zero, 0, 0);
|
||||
AreTexturesSwapped = false;
|
||||
|
||||
// All other blur passes play ping pong between A and B
|
||||
for (int i = 0; i < BlurIterations; i++)
|
||||
{
|
||||
if (AreTexturesSwapped)
|
||||
{
|
||||
ctx.cmd.Blit(RenderTargetBlurredB, RenderTargetBlurredA, Material, 0);
|
||||
ctx.cmd.Blit(RenderTargetBlurredA, RenderTargetBlurredB, Material, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx.cmd.Blit(RenderTargetBlurredA, RenderTargetBlurredB, Material, 0);
|
||||
ctx.cmd.Blit(RenderTargetBlurredB, RenderTargetBlurredA, Material, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Cleanup()
|
||||
{
|
||||
CoreUtils.Destroy(_material);
|
||||
|
||||
if (_renderTargetBlurredA != null)
|
||||
{
|
||||
_renderTargetBlurredA.Release();
|
||||
_renderTargetBlurredA = null;
|
||||
}
|
||||
|
||||
if (_renderTargetBlurredB != null)
|
||||
{
|
||||
_renderTargetBlurredB.Release();
|
||||
_renderTargetBlurredB = null;
|
||||
}
|
||||
|
||||
if (_renderTargetHandleA != null)
|
||||
{
|
||||
_renderTargetHandleA.Release();
|
||||
_renderTargetHandleA = null;
|
||||
}
|
||||
|
||||
if (_renderTargetHandleB != null)
|
||||
{
|
||||
_renderTargetHandleB.Release();
|
||||
_renderTargetHandleB = null;
|
||||
}
|
||||
|
||||
base.Cleanup();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d1931dd95fbdff24f98ab13d7b914752
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,470 @@
|
||||
#if KAMGAM_RENDER_PIPELINE_URP
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
#if UNITY_6000_0_OR_NEWER
|
||||
using UnityEngine.Rendering.RenderGraphModule;
|
||||
#endif
|
||||
using UnityEngine.Rendering.Universal;
|
||||
|
||||
namespace Kamgam.UIToolkitBlurredBackground
|
||||
{
|
||||
public class BlurredBackgroundPassURP : ScriptableRenderPass
|
||||
{
|
||||
public System.Action OnPostRender;
|
||||
|
||||
public bool Active = false;
|
||||
|
||||
protected int _iterations;
|
||||
public int Iterations
|
||||
{
|
||||
get => _iterations;
|
||||
set
|
||||
{
|
||||
if (_iterations != value)
|
||||
{
|
||||
_iterations = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected float _offset = 1.5f;
|
||||
public float Offset
|
||||
{
|
||||
get => _offset;
|
||||
set
|
||||
{
|
||||
_offset = value;
|
||||
setOffset(value);
|
||||
}
|
||||
}
|
||||
|
||||
protected Color _additiveColor = new Color(0f, 0f, 0f, 0f);
|
||||
public Color AdditiveColor
|
||||
{
|
||||
get => _additiveColor;
|
||||
set
|
||||
{
|
||||
_additiveColor = value;
|
||||
setAdditiveColor(_material, value);
|
||||
}
|
||||
}
|
||||
|
||||
void setAdditiveColor(Material material, Color color)
|
||||
{
|
||||
if (material == null)
|
||||
return;
|
||||
|
||||
material.SetColor("_AdditiveColor", color);
|
||||
}
|
||||
|
||||
protected Vector2Int _resolution = new Vector2Int(512, 512);
|
||||
/// <summary>
|
||||
/// The texture resolution of the blurred image. Default is 512 x 512. Please use 2^n values like 256, 512, 1024, 2048. Reducing this will increase performance but decrease quality. Every frame your rendered image will be copied, resized and then blurred [BlurStrength] times.
|
||||
/// </summary>
|
||||
public Vector2Int Resolution
|
||||
{
|
||||
get => _resolution;
|
||||
set
|
||||
{
|
||||
_resolution = value;
|
||||
updateRenderTextureResolutions();
|
||||
}
|
||||
}
|
||||
|
||||
void updateRenderTextureResolutions()
|
||||
{
|
||||
if (_renderTargetBlurredA != null)
|
||||
{
|
||||
_renderTargetBlurredA.Release();
|
||||
_renderTargetBlurredA.width = _resolution.x;
|
||||
_renderTargetBlurredA.height = _resolution.y;
|
||||
_renderTargetBlurredA.Create();
|
||||
}
|
||||
|
||||
if (_renderTargetBlurredB != null)
|
||||
{
|
||||
_renderTargetBlurredB.Release();
|
||||
_renderTargetBlurredB.width = _resolution.x;
|
||||
_renderTargetBlurredB.height = _resolution.y;
|
||||
_renderTargetBlurredB.Create();
|
||||
}
|
||||
}
|
||||
|
||||
public const string ShaderName = "Kamgam/UI Toolkit/URP/Blur Shader";
|
||||
|
||||
protected ShaderQuality _quality = ShaderQuality.Medium;
|
||||
public ShaderQuality Quality
|
||||
{
|
||||
get => _quality;
|
||||
set
|
||||
{
|
||||
_quality = value;
|
||||
_material = null;
|
||||
}
|
||||
}
|
||||
|
||||
[System.NonSerialized]
|
||||
protected Material _material;
|
||||
public Material Material
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_material == null)
|
||||
{
|
||||
// Create material with shader
|
||||
var shader = Shader.Find(ShaderName);
|
||||
if (shader != null)
|
||||
{
|
||||
_material = new Material(shader);
|
||||
_material.color = Color.white;
|
||||
|
||||
switch (_quality)
|
||||
{
|
||||
case ShaderQuality.Low:
|
||||
_material.SetKeyword(new LocalKeyword(shader, "_SAMPLES_LOW"), true);
|
||||
_material.SetKeyword(new LocalKeyword(shader, "_SAMPLES_MEDIUM"), false);
|
||||
_material.SetKeyword(new LocalKeyword(shader, "_SAMPLES_HIGH"), false);
|
||||
break;
|
||||
|
||||
case ShaderQuality.Medium:
|
||||
_material.SetKeyword(new LocalKeyword(shader, "_SAMPLES_LOW"), false);
|
||||
_material.SetKeyword(new LocalKeyword(shader, "_SAMPLES_MEDIUM"), true);
|
||||
_material.SetKeyword(new LocalKeyword(shader, "_SAMPLES_HIGH"), false);
|
||||
break;
|
||||
|
||||
case ShaderQuality.High:
|
||||
_material.SetKeyword(new LocalKeyword(shader, "_SAMPLES_LOW"), false);
|
||||
_material.SetKeyword(new LocalKeyword(shader, "_SAMPLES_MEDIUM"), false);
|
||||
_material.SetKeyword(new LocalKeyword(shader, "_SAMPLES_HIGH"), true);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
setOffset(_offset);
|
||||
setAdditiveColor(_material,AdditiveColor);
|
||||
}
|
||||
}
|
||||
return _material;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_material = value;
|
||||
}
|
||||
}
|
||||
|
||||
void setOffset(float value)
|
||||
{
|
||||
if (_material != null)
|
||||
_material.SetVector("_BlurOffset", new Vector4(value, value, 0f, 0f));
|
||||
}
|
||||
|
||||
[System.NonSerialized]
|
||||
protected RenderTexture _renderTargetBlurredA;
|
||||
public RenderTexture RenderTargetBlurredA
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_renderTargetBlurredA == null)
|
||||
{
|
||||
_renderTargetBlurredA = createRenderTexture();
|
||||
|
||||
if (_renderTargetHandleA != null)
|
||||
{
|
||||
_renderTargetHandleA.Release();
|
||||
_renderTargetHandleA = null;
|
||||
}
|
||||
}
|
||||
|
||||
return _renderTargetBlurredA;
|
||||
}
|
||||
}
|
||||
|
||||
[System.NonSerialized]
|
||||
protected RenderTexture _renderTargetBlurredB;
|
||||
public RenderTexture RenderTargetBlurredB
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_renderTargetBlurredB == null)
|
||||
{
|
||||
_renderTargetBlurredB = createRenderTexture();
|
||||
|
||||
if (_renderTargetHandleB != null)
|
||||
{
|
||||
_renderTargetHandleB.Release();
|
||||
_renderTargetHandleB = null;
|
||||
}
|
||||
}
|
||||
|
||||
return _renderTargetBlurredB;
|
||||
}
|
||||
}
|
||||
|
||||
[System.NonSerialized]
|
||||
protected RTHandle _renderTargetHandleA;
|
||||
public RTHandle RenderTargetHandleA
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_renderTargetHandleA == null)
|
||||
_renderTargetHandleA = RTHandles.Alloc(RenderTargetBlurredA);
|
||||
|
||||
return _renderTargetHandleA;
|
||||
}
|
||||
}
|
||||
|
||||
[System.NonSerialized]
|
||||
protected RTHandle _renderTargetHandleB;
|
||||
public RTHandle RenderTargetHandleB
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_renderTargetHandleB == null)
|
||||
_renderTargetHandleB = RTHandles.Alloc(RenderTargetBlurredB);
|
||||
|
||||
return _renderTargetHandleB;
|
||||
}
|
||||
}
|
||||
|
||||
RenderTexture createRenderTexture()
|
||||
{
|
||||
var texture = new RenderTexture(Resolution.x, Resolution.y, 0);
|
||||
texture.filterMode = FilterMode.Bilinear;
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
public void ClearRenderTargets()
|
||||
{
|
||||
if (_renderTargetHandleA != null)
|
||||
{
|
||||
_renderTargetHandleA.Release();
|
||||
_renderTargetHandleA = null;
|
||||
}
|
||||
if (_renderTargetBlurredA != null)
|
||||
{
|
||||
_renderTargetBlurredA.Release();
|
||||
_renderTargetBlurredA = null;
|
||||
}
|
||||
|
||||
if (_renderTargetHandleB != null)
|
||||
{
|
||||
_renderTargetHandleB.Release();
|
||||
_renderTargetHandleB = null;
|
||||
}
|
||||
if (_renderTargetBlurredB != null)
|
||||
{
|
||||
_renderTargetBlurredB.Release();
|
||||
_renderTargetBlurredB = null;
|
||||
}
|
||||
}
|
||||
|
||||
public Texture GetBlurredTexture()
|
||||
{
|
||||
return RenderTargetBlurredA;
|
||||
}
|
||||
|
||||
|
||||
// Actual Render Pass stuff starts here:
|
||||
// -------------------------------------------------------------
|
||||
|
||||
#region PASS_RENDER_NON_GRAPH_PATH
|
||||
|
||||
// Turns out profiling scopes should NOT be mixed with CommandBuffers, see:
|
||||
// https://forum.unity.com/threads/how-to-use-profilingscope-correctly.1366812/#post-8621289
|
||||
// ProfilingSampler _profilingSampler = new ProfilingSampler("UGUI Blurred Background Pass");
|
||||
|
||||
#if KAMGAM_RENDER_PIPELINE_URP_13
|
||||
RTHandle _cameraColorTarget;
|
||||
#endif
|
||||
|
||||
#if UNITY_6000_0_OR_NEWER
|
||||
[System.Obsolete]
|
||||
#endif
|
||||
public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
|
||||
{
|
||||
ConfigureInput(ScriptableRenderPassInput.Color);
|
||||
|
||||
#if KAMGAM_RENDER_PIPELINE_URP_13
|
||||
_cameraColorTarget = renderingData.cameraData.renderer.cameraColorTargetHandle;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if UNITY_6000_0_OR_NEWER
|
||||
[System.Obsolete]
|
||||
#endif
|
||||
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
|
||||
{
|
||||
if (!Active || _iterations == 0 || Offset <= 0f)
|
||||
return;
|
||||
|
||||
// Do not render while switching play modes.
|
||||
#if UNITY_EDITOR
|
||||
if (EditorPlayState.State != EditorPlayState.PlayState.Playing && EditorPlayState.State != EditorPlayState.PlayState.Editing)
|
||||
return;
|
||||
#endif
|
||||
|
||||
// Skip rendering in scene view or preview. Why? Because rendering in these
|
||||
// makes the scene view flicker if not in play mode.
|
||||
// See: https://forum.unity.com/threads/urp-custom-pass-blit-flickering-in-scene-view.1461932/
|
||||
#if UNITY_EDITOR
|
||||
if ( renderingData.cameraData.cameraType == CameraType.SceneView
|
||||
|| renderingData.cameraData.cameraType == CameraType.Preview)
|
||||
return;
|
||||
#endif
|
||||
|
||||
|
||||
#if !KAMGAM_RENDER_PIPELINE_URP_13
|
||||
var source = renderingData.cameraData.renderer.cameraColorTarget;
|
||||
#else
|
||||
var source = renderingData.cameraData.renderer.cameraColorTargetHandle;
|
||||
|
||||
// Check if source is null, if yes then try to fetch it from the set target. Otherwise abort.
|
||||
if (renderingData.cameraData.cameraType != CameraType.Game || source == null)
|
||||
{
|
||||
source = _cameraColorTarget;
|
||||
if (source == null)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
// TODO: Investigate: This is happening in URP 14 though it has no effect (everything works).
|
||||
// Logger.LogWarning("Camera color target source is null. Will skip blur rendering. Please investigate this issue.");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
CommandBuffer cmd = CommandBufferPool.Get(name: "UGUI Blurred Background Pass");
|
||||
|
||||
cmd.Clear();
|
||||
|
||||
// Notice: Do not use cmd.Blit() in SPRs, see:
|
||||
// https://forum.unity.com/threads/how-to-blit-in-urp-documentation-unity-blog-post-on-every-blit-function.1211508/#post-7735527
|
||||
// Blit Implementation can be found here:
|
||||
// https://github.com/Unity-Technologies/Graphics/blob/b57fcac51bb88e1e589b01e32fd610c991f16de9/Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blitter.cs#L221
|
||||
|
||||
// First pass scales down the image
|
||||
Blit(cmd, source, RenderTargetHandleA);
|
||||
|
||||
// 2 pass blur A > B, B > A
|
||||
for (int i = 0; i < Iterations; i++)
|
||||
{
|
||||
// Blur horizontal (pass 0)
|
||||
Blit(cmd, RenderTargetHandleA, RenderTargetHandleB, Material, 0);
|
||||
// Blur vertical (pass 1)
|
||||
Blit(cmd, RenderTargetHandleB, RenderTargetHandleA, Material, 1);
|
||||
}
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
cmd.Clear();
|
||||
|
||||
CommandBufferPool.Release(cmd);
|
||||
|
||||
OnPostRender?.Invoke();
|
||||
}
|
||||
|
||||
public override void OnCameraCleanup(CommandBuffer cmd)
|
||||
{
|
||||
base.OnCameraCleanup(cmd);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#if UNITY_6000_0_OR_NEWER
|
||||
#region PASS_RENDER_GRAPH_PATH
|
||||
|
||||
// The custom copy color pass data that will be passed at render graph execution to the lambda we set with "SetRenderFunc" during render graph setup
|
||||
private class CopyPassData
|
||||
{
|
||||
public TextureHandle inputTexture;
|
||||
}
|
||||
|
||||
// The custom main pass data that will be passed at render graph execution to the lambda we set with "SetRenderFunc" during render graph setup
|
||||
private class BlurPassData
|
||||
{
|
||||
public Material material;
|
||||
public TextureHandle inputTexture;
|
||||
public int pass;
|
||||
}
|
||||
|
||||
RenderTargetInfo getRenderTargetInfo(RenderTexture texture)
|
||||
{
|
||||
RenderTargetInfo info = new RenderTargetInfo();
|
||||
info.format = texture.descriptor.graphicsFormat;
|
||||
info.width = texture.width;
|
||||
info.height = texture.height;
|
||||
info.volumeDepth = texture.volumeDepth;
|
||||
info.bindMS = texture.bindTextureMS;
|
||||
return info;
|
||||
}
|
||||
|
||||
// Here you can implement the rendering logic for the render graph path
|
||||
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
|
||||
{
|
||||
// This works
|
||||
var infoA = getRenderTargetInfo(RenderTargetBlurredA);
|
||||
var targetA = renderGraph.ImportTexture(RenderTargetHandleA, infoA);
|
||||
var infoB = getRenderTargetInfo(RenderTargetBlurredB);
|
||||
var targetB = renderGraph.ImportTexture(RenderTargetHandleB, infoB);
|
||||
|
||||
// This does not. Wth?!?
|
||||
// see: https://forum.unity.com/threads/introduction-of-render-graph-in-the-universal-render-pipeline-urp.1500833/page-7#post-9822162
|
||||
//var targetA = renderGraph.ImportTexture(RenderTargetHandleA, getRenderTargetInfo(RenderTargetBlurredA));
|
||||
//var targetB = renderGraph.ImportTexture(RenderTargetHandleB, getRenderTargetInfo(RenderTargetBlurredB));
|
||||
|
||||
UniversalResourceData resourcesData = frameData.Get<UniversalResourceData>();
|
||||
|
||||
// Color buffer copy pass
|
||||
// * This pass makes a temporary copy of the active color target for sampling
|
||||
// * This is needed as GPU graphics pipelines don't allow to sample the texture bound as the active color target
|
||||
// * This copy can be avoided if you won't need to sample the color target or will only need to render/blend on top of it
|
||||
using (var builder = renderGraph.AddRasterRenderPass<CopyPassData>("UITKBlurredBackground_CopyColor", out var passData, profilingSampler))
|
||||
{
|
||||
passData.inputTexture = resourcesData.activeColorTexture;
|
||||
builder.UseTexture(resourcesData.activeColorTexture, AccessFlags.Read);
|
||||
builder.SetRenderAttachment(targetA, 0, AccessFlags.WriteAll);
|
||||
builder.SetRenderFunc((CopyPassData data, RasterGraphContext context) => ExecuteCopyColorPass(data, context));
|
||||
}
|
||||
|
||||
// Blur horizontal pass
|
||||
using (var builder = renderGraph.AddRasterRenderPass<BlurPassData>("UITKBlurredBackground_BlurHori", out var passData, profilingSampler))
|
||||
{
|
||||
passData.material = Material;
|
||||
passData.inputTexture = targetA;
|
||||
passData.pass = 0;
|
||||
builder.UseTexture(targetA, AccessFlags.Read);
|
||||
builder.SetRenderAttachment(targetB, 0, AccessFlags.WriteAll);
|
||||
builder.SetRenderFunc((BlurPassData data, RasterGraphContext context) => ExecuteBlurPass(data, context));
|
||||
}
|
||||
|
||||
// Blur vertical pass
|
||||
using (var builder = renderGraph.AddRasterRenderPass<BlurPassData>("UITKBlurredBackground_BlurVerti", out var passData, profilingSampler))
|
||||
{
|
||||
passData.material = Material;
|
||||
passData.inputTexture = targetB;
|
||||
passData.pass = 1;
|
||||
builder.UseTexture(targetB, AccessFlags.Read);
|
||||
builder.SetRenderAttachment(targetA, 0, AccessFlags.WriteAll);
|
||||
builder.SetRenderFunc((BlurPassData data, RasterGraphContext context) => ExecuteBlurPass(data, context));
|
||||
}
|
||||
|
||||
OnPostRender?.Invoke();
|
||||
}
|
||||
|
||||
private static void ExecuteCopyColorPass(CopyPassData data, RasterGraphContext context)
|
||||
{
|
||||
Blitter.BlitTexture(context.cmd, data.inputTexture, new Vector4(1, 1, 0, 0), 0.0f, bilinear: true);
|
||||
}
|
||||
|
||||
private static void ExecuteBlurPass(BlurPassData data, RasterGraphContext context)
|
||||
{
|
||||
Blitter.BlitTexture(context.cmd, data.inputTexture, new Vector4(1, 1, 0, 0), data.material, data.pass);
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 533e2914ca6dfeb499952168ff6d3c93
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,31 @@
|
||||
using UnityEngine;
|
||||
namespace Kamgam.UIToolkitBlurredBackground
|
||||
{
|
||||
public interface IBlurRenderer
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines how often the blur will be applied. Use with caution."
|
||||
/// </summary>
|
||||
int Iterations { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defines how far out the sampling foes and thus the blur strength for each pass.
|
||||
/// </summary>
|
||||
float Offset { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The square texture resolution of the blurred image. Default is 512 x 512. Please use 2^n values like 256, 512, 1024, 2048. Reducing this will increase performance but decrease quality. Every frame your rendered image will be copied, resized and then blurred [BlurStrength] times.
|
||||
/// </summary>
|
||||
Vector2Int Resolution { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defines how may samples are taken per pass. The higher the quality the more texels will be sampled and the lower the performance will be.
|
||||
/// </summary>
|
||||
ShaderQuality Quality { get; set; }
|
||||
|
||||
bool Active { get; set; }
|
||||
|
||||
Texture GetBlurredTexture();
|
||||
bool Update();
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3fa6c55ccce6d3348bbe45a160dadac3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,64 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
namespace Kamgam.UIToolkitBlurredBackground
|
||||
{
|
||||
public static class RenderUtils
|
||||
{
|
||||
static Camera _cachedGameViewCam;
|
||||
|
||||
static Camera[] _tmpAllCameras = new Camera[10];
|
||||
|
||||
public static Camera GetGameViewCamera()
|
||||
{
|
||||
var cam = Camera.main;
|
||||
if (cam == null)
|
||||
{
|
||||
// Fetch cameras
|
||||
int allCamerasCount = Camera.allCamerasCount;
|
||||
// Alloc new array only if needed
|
||||
if (allCamerasCount > _tmpAllCameras.Length)
|
||||
{
|
||||
_tmpAllCameras = new Camera[allCamerasCount + 5];
|
||||
}
|
||||
Camera.GetAllCameras(_tmpAllCameras);
|
||||
|
||||
// We sort by depth and start from the back because we assume
|
||||
// that among cameras with equal depth the last takes precedence.
|
||||
float maxDepth = float.MinValue;
|
||||
for (int i = _tmpAllCameras.Length - 1; i >= 0; i--)
|
||||
{
|
||||
// Null out old references
|
||||
if (i >= allCamerasCount)
|
||||
{
|
||||
_tmpAllCameras[i] = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
var cCam = _tmpAllCameras[i];
|
||||
|
||||
if (!cCam.isActiveAndEnabled)
|
||||
continue;
|
||||
|
||||
// Only take full screen cameras that are not rendering into render textures
|
||||
if (cCam.depth > maxDepth && cCam.targetTexture == null && cCam.rect.width >= 1f && cCam.rect.height >= 1f)
|
||||
{
|
||||
maxDepth = cCam.depth;
|
||||
cam = cCam;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// cache game view camera
|
||||
if (cam != null && cam.cameraType == CameraType.Game)
|
||||
_cachedGameViewCam = cam;
|
||||
|
||||
if (cam == null)
|
||||
return _cachedGameViewCam;
|
||||
|
||||
return cam;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 82b708f16d7af5f4e98cc285c4f83173
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
|
||||
namespace Kamgam.UIToolkitBlurredBackground
|
||||
{
|
||||
public enum ShaderQuality { Low, Medium, High };
|
||||
|
||||
public static class ShaderQualityTools
|
||||
{
|
||||
public static ShaderQuality FromString(string str)
|
||||
{
|
||||
if(Enum.TryParse(str, out ShaderQuality result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f6a1f3f8717848e48b3fe3cce70d9b87
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,88 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Kamgam.UIToolkitBlurredBackground
|
||||
{
|
||||
public enum SquareResolution
|
||||
{
|
||||
_32,
|
||||
_64,
|
||||
_128,
|
||||
_256,
|
||||
_512,
|
||||
_1024,
|
||||
_2048,
|
||||
_4096
|
||||
};
|
||||
|
||||
public static class SquareResolutionsUtils
|
||||
{
|
||||
public static Vector2Int ToResolution(this SquareResolution res)
|
||||
{
|
||||
switch (res)
|
||||
{
|
||||
case SquareResolution._32:
|
||||
return new Vector2Int(32, 32);
|
||||
|
||||
case SquareResolution._64:
|
||||
return new Vector2Int(64, 64);
|
||||
|
||||
case SquareResolution._128:
|
||||
return new Vector2Int(128, 128);
|
||||
|
||||
case SquareResolution._256:
|
||||
return new Vector2Int(256, 256);
|
||||
|
||||
case SquareResolution._512:
|
||||
return new Vector2Int(512, 512);
|
||||
|
||||
case SquareResolution._1024:
|
||||
return new Vector2Int(1024, 1024);
|
||||
|
||||
case SquareResolution._2048:
|
||||
return new Vector2Int(2048, 2048);
|
||||
|
||||
case SquareResolution._4096:
|
||||
return new Vector2Int(4096, 4096);
|
||||
|
||||
default:
|
||||
return new Vector2Int(512, 512);
|
||||
}
|
||||
}
|
||||
|
||||
public static SquareResolution FromResolution(this Vector2Int res)
|
||||
{
|
||||
if (res.x >= 4096 && res.y >= 4096)
|
||||
{
|
||||
return SquareResolution._4096;
|
||||
}
|
||||
else if (res.x >= 2048 && res.y >= 2048)
|
||||
{
|
||||
return SquareResolution._2048;
|
||||
}
|
||||
else if (res.x >= 1024 && res.y >= 1024)
|
||||
{
|
||||
return SquareResolution._1024;
|
||||
}
|
||||
else if (res.x >= 512 && res.y >= 512)
|
||||
{
|
||||
return SquareResolution._512;
|
||||
}
|
||||
else if (res.x >= 256 && res.y >= 256)
|
||||
{
|
||||
return SquareResolution._256;
|
||||
}
|
||||
else if (res.x >= 128 && res.y >= 128)
|
||||
{
|
||||
return SquareResolution._128;
|
||||
}
|
||||
else if (res.x >= 64 && res.y >= 64)
|
||||
{
|
||||
return SquareResolution._64;
|
||||
}
|
||||
else
|
||||
{
|
||||
return SquareResolution._32;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1d1c2d493da892c40aff77e085a20f4e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,128 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
namespace Kamgam.UIToolkitBlurredBackground
|
||||
{
|
||||
public static class Utils
|
||||
{
|
||||
public static void SmartDestroy(UnityEngine.Object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
GameObject.DestroyImmediate(obj);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
GameObject.Destroy(obj);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SmartDontDestroyOnLoad(GameObject go)
|
||||
{
|
||||
if (go == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (EditorApplication.isPlaying)
|
||||
{
|
||||
GameObject.DontDestroyOnLoad(go);
|
||||
}
|
||||
#else
|
||||
GameObject.DontDestroyOnLoad(go);
|
||||
#endif
|
||||
}
|
||||
|
||||
private static List<GameObject> _tmpSceneObjects = new List<GameObject>();
|
||||
|
||||
public static List<T> FindRootObjectsByType<T>(bool includeInactive) where T : Component
|
||||
{
|
||||
var results = new List<T>();
|
||||
FindRootObjectsByType(includeInactive, results);
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A simple replacement for GameObject.FindObjectsOfType<T>. It checks the ROOT objects in ALL opened or loaded scenes.
|
||||
/// </summary>
|
||||
/// <param name="includeInactive"></param>
|
||||
/// <param name="results">A list that will be cleared and then filled with the results.</param>
|
||||
/// <returns></returns>
|
||||
public static void FindRootObjectsByType<T>(bool includeInactive, IList<T> results) where T : Component
|
||||
{
|
||||
if (results == null)
|
||||
{
|
||||
results = new List<T>();
|
||||
}
|
||||
else
|
||||
{
|
||||
results.Clear();
|
||||
}
|
||||
|
||||
for (int i = 0; i < UnityEngine.SceneManagement.SceneManager.sceneCount; i++)
|
||||
{
|
||||
var scene = UnityEngine.SceneManagement.SceneManager.GetSceneAt(i);
|
||||
if (!scene.IsValid())
|
||||
continue;
|
||||
|
||||
scene.GetRootGameObjects(_tmpSceneObjects);
|
||||
|
||||
foreach (var obj in _tmpSceneObjects)
|
||||
{
|
||||
var comp = obj.GetComponent<T>();
|
||||
if (comp == null)
|
||||
continue;
|
||||
|
||||
if (!includeInactive && !comp.gameObject.activeInHierarchy)
|
||||
continue;
|
||||
|
||||
results.Add(comp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A simple replacement for GameObject.FindObjectsOfType<T>. It checks the ROOT objects in ALL opened or loaded scenes.
|
||||
/// </summary>
|
||||
/// <param name="includeInactive"></param>
|
||||
/// <returns></returns>
|
||||
public static T FindRootObjectByType<T>(bool includeInactive) where T : Component
|
||||
{
|
||||
for (int i = 0; i < UnityEngine.SceneManagement.SceneManager.sceneCount; i++)
|
||||
{
|
||||
var scene = UnityEngine.SceneManagement.SceneManager.GetSceneAt(i);
|
||||
if (!scene.IsValid())
|
||||
continue;
|
||||
|
||||
if (!scene.isLoaded)
|
||||
continue;
|
||||
|
||||
scene.GetRootGameObjects(_tmpSceneObjects);
|
||||
|
||||
foreach (var obj in _tmpSceneObjects)
|
||||
{
|
||||
var comp = obj.GetComponent<T>();
|
||||
if (comp == null)
|
||||
continue;
|
||||
|
||||
if (!includeInactive && !comp.gameObject.activeInHierarchy)
|
||||
continue;
|
||||
|
||||
return comp;
|
||||
}
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 684574d6b49de6d498b01a6544168214
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d0a1dc9c0782ef24daa7bf404d5f8cf8
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,730 @@
|
||||
#if UNITY_6000_0_OR_NEWER
|
||||
using Unity.Properties;
|
||||
#endif
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace Kamgam.UIToolkitBlurredBackground
|
||||
{
|
||||
/// <summary>
|
||||
/// The blurred background works by adding an additional mesh on top of the default mesh via OnGenerateVisualContent().
|
||||
/// </summary>
|
||||
#if UNITY_6000_0_OR_NEWER
|
||||
[UxmlElement]
|
||||
#endif
|
||||
public partial class BlurredBackground : VisualElement
|
||||
{
|
||||
public static Color BackgroundColorDefault = new Color(0, 0, 0, 0);
|
||||
|
||||
#if !UNITY_6000_0_OR_NEWER
|
||||
public new class UxmlFactory : UxmlFactory<BlurredBackground, UxmlTraits> { }
|
||||
public new class UxmlTraits : VisualElement.UxmlTraits
|
||||
{
|
||||
UxmlFloatAttributeDescription m_BlurStrength =
|
||||
new UxmlFloatAttributeDescription { name = "Blur-Strength", defaultValue = 15f };
|
||||
|
||||
UxmlEnumAttributeDescription<ShaderQuality> m_BlurQuality =
|
||||
new UxmlEnumAttributeDescription<ShaderQuality> { name = "Blur-Quality", defaultValue = ShaderQuality.Medium };
|
||||
|
||||
UxmlIntAttributeDescription m_BlurIterations =
|
||||
new UxmlIntAttributeDescription { name = "Blur-Iterations", defaultValue = 1 };
|
||||
|
||||
UxmlEnumAttributeDescription<SquareResolution> m_BlurResolution =
|
||||
new UxmlEnumAttributeDescription<SquareResolution> { name = "Blur-Resolution", defaultValue = SquareResolution._512 };
|
||||
|
||||
UxmlColorAttributeDescription m_BlurTint =
|
||||
new UxmlColorAttributeDescription { name = "Blur-Tint", defaultValue = new Color(1f, 1f, 1f, 1f) };
|
||||
|
||||
UxmlFloatAttributeDescription m_BlurMeshCornerOverlap =
|
||||
new UxmlFloatAttributeDescription { name = "Blur-Mesh-Corner-Overlap", defaultValue = 0.3f };
|
||||
|
||||
UxmlIntAttributeDescription m_BlurMeshCornerSegments =
|
||||
new UxmlIntAttributeDescription { name = "Blur-Mesh-Corner-Segments", defaultValue = 12 };
|
||||
|
||||
UxmlColorAttributeDescription m_BackgroundColor =
|
||||
new UxmlColorAttributeDescription { name = "Background-Color", defaultValue = BackgroundColorDefault };
|
||||
|
||||
public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)
|
||||
{
|
||||
base.Init(ve, bag, cc);
|
||||
var bg = ve as BlurredBackground;
|
||||
|
||||
// Delay in edito to avoid "SendMessage cannot be called during Awake, CheckConsistency, or OnValidate" warnings.
|
||||
#if UNITY_EDITOR
|
||||
UnityEditor.EditorApplication.delayCall += () =>
|
||||
{
|
||||
#endif
|
||||
bg.BlurStrength = m_BlurStrength.GetValueFromBag(bag, cc);
|
||||
bg.BlurQuality = m_BlurQuality.GetValueFromBag(bag, cc);
|
||||
bg.BlurIterations = m_BlurIterations.GetValueFromBag(bag, cc);
|
||||
bg.BlurResolution = m_BlurResolution.GetValueFromBag(bag, cc);
|
||||
bg.BlurTint = m_BlurTint.GetValueFromBag(bag, cc);
|
||||
bg.BlurMeshCornerOverlap = m_BlurMeshCornerOverlap.GetValueFromBag(bag, cc);
|
||||
bg.BlurMeshCornerSegments = m_BlurMeshCornerSegments.GetValueFromBag(bag, cc);
|
||||
bg.BackgroundColor = m_BackgroundColor.GetValueFromBag(bag, cc);
|
||||
#if UNITY_EDITOR
|
||||
};
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Cache to have a value to return in GET if no style is defined.
|
||||
[System.NonSerialized]
|
||||
private int? _cachedBlurIterations;
|
||||
|
||||
#if UNITY_6000_0_OR_NEWER
|
||||
[UxmlAttribute("Blur-Iterations")]
|
||||
[CreateProperty]
|
||||
#endif
|
||||
public int BlurIterations
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_cachedBlurIterations.HasValue)
|
||||
_cachedBlurIterations = BlurManager.Instance.Iterations;
|
||||
|
||||
return BlurredBackgroundStyles.ResolveStyle(BlurredBackgroundStyles.Iterations, this, _cachedBlurIterations.Value);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_cachedBlurIterations = value;
|
||||
|
||||
int newValue = BlurredBackgroundStyles.ResolveStyle(BlurredBackgroundStyles.Iterations, this, value);
|
||||
if (newValue != BlurManager.Instance.Iterations)
|
||||
{
|
||||
if (newValue < 0)
|
||||
newValue = 0;
|
||||
|
||||
BlurManager.Instance.Iterations = newValue;
|
||||
|
||||
MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[System.NonSerialized]
|
||||
private float? _cachedBlurStrength;
|
||||
|
||||
#if UNITY_6000_0_OR_NEWER
|
||||
[UxmlAttribute("Blur-Strength")]
|
||||
[CreateProperty]
|
||||
#endif
|
||||
public float BlurStrength
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_cachedBlurStrength.HasValue)
|
||||
_cachedBlurStrength = BlurManager.Instance.Offset;
|
||||
|
||||
return BlurredBackgroundStyles.ResolveStyle(BlurredBackgroundStyles.Strength, this, _cachedBlurStrength.Value);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_cachedBlurStrength = value;
|
||||
|
||||
float newValue = BlurredBackgroundStyles.ResolveStyle(BlurredBackgroundStyles.Strength, this, value);
|
||||
if (newValue != BlurManager.Instance.Offset)
|
||||
{
|
||||
if (newValue < 0f)
|
||||
newValue = 0f;
|
||||
|
||||
BlurManager.Instance.Offset = newValue;
|
||||
|
||||
MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Vector2Int _blurResolutionSize = new Vector2Int(512, 512);
|
||||
public Vector2Int BlurResolutionSize
|
||||
{
|
||||
get
|
||||
{
|
||||
return _blurResolutionSize;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
|
||||
if (value != _blurResolutionSize)
|
||||
{
|
||||
if (value.x < 2 || value.y < 2)
|
||||
value = new Vector2Int(2, 2);
|
||||
|
||||
BlurManager.Instance.Resolution = value;
|
||||
|
||||
MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[System.NonSerialized]
|
||||
private Vector2Int? _cachedBlurResolution;
|
||||
|
||||
#if UNITY_6000_0_OR_NEWER
|
||||
[UxmlAttribute("Blur-Resolution")]
|
||||
[CreateProperty]
|
||||
#endif
|
||||
public SquareResolution BlurResolution
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_cachedBlurResolution.HasValue)
|
||||
_cachedBlurResolution = _blurResolutionSize;
|
||||
|
||||
if (customStyle.TryGetValue(BlurredBackgroundStyles.Resolution, out var width))
|
||||
{
|
||||
return SquareResolutionsUtils.FromResolution(new Vector2Int(width, width));
|
||||
}
|
||||
else
|
||||
{
|
||||
return SquareResolutionsUtils.FromResolution(_cachedBlurResolution.Value);
|
||||
}
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
var newResolution = SquareResolutionsUtils.ToResolution(value);
|
||||
_cachedBlurResolution = newResolution;
|
||||
|
||||
if (customStyle.TryGetValue(BlurredBackgroundStyles.Resolution, out var newWidth))
|
||||
{
|
||||
BlurResolutionSize = new Vector2Int(newWidth, newWidth);
|
||||
}
|
||||
else
|
||||
{
|
||||
BlurResolutionSize = newResolution;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[System.NonSerialized]
|
||||
private ShaderQuality? _cachedShaderQuality;
|
||||
|
||||
#if UNITY_6000_0_OR_NEWER
|
||||
[UxmlAttribute("Blur-Quality")]
|
||||
[CreateProperty]
|
||||
#endif
|
||||
public ShaderQuality BlurQuality
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_cachedShaderQuality.HasValue)
|
||||
_cachedShaderQuality = BlurManager.Instance.Quality;
|
||||
|
||||
if (customStyle.TryGetValue(BlurredBackgroundStyles.Quality, out var qualityString))
|
||||
{
|
||||
return ShaderQualityTools.FromString(qualityString);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _cachedShaderQuality.Value;
|
||||
}
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_cachedShaderQuality = value;
|
||||
|
||||
ShaderQuality newValue = value;
|
||||
if (customStyle.TryGetValue(BlurredBackgroundStyles.Quality, out var qualityString))
|
||||
{
|
||||
newValue = ShaderQualityTools.FromString(qualityString);
|
||||
}
|
||||
|
||||
if (newValue != BlurManager.Instance.Quality)
|
||||
{
|
||||
BlurManager.Instance.Quality = value;
|
||||
MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Color _blurTint = new Color(1f, 1f, 1f, 1f);
|
||||
#if UNITY_6000_0_OR_NEWER
|
||||
[UxmlAttribute("Blur-Tint")]
|
||||
[CreateProperty]
|
||||
#endif
|
||||
public Color BlurTint
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlurredBackgroundStyles.ResolveStyle(BlurredBackgroundStyles.Tint, this, _blurTint);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
var newValue = BlurredBackgroundStyles.ResolveStyle(BlurredBackgroundStyles.Tint, this, value);
|
||||
if (newValue != _blurTint)
|
||||
{
|
||||
_blurTint = newValue;
|
||||
MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected int _blurMeshCornerSegments = 12;
|
||||
#if UNITY_6000_0_OR_NEWER
|
||||
[UxmlAttribute("Blur-Mesh-Corner-Segments")]
|
||||
[CreateProperty]
|
||||
#endif
|
||||
public int BlurMeshCornerSegments
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlurredBackgroundStyles.ResolveStyle(BlurredBackgroundStyles.MeshCornerSegments, this, _blurMeshCornerSegments);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
var newValue = BlurredBackgroundStyles.ResolveStyle(BlurredBackgroundStyles.MeshCornerSegments, this, value);
|
||||
if (newValue != _blurMeshCornerSegments)
|
||||
{
|
||||
if (newValue < 1)
|
||||
newValue = 1;
|
||||
|
||||
_blurMeshCornerSegments = newValue;
|
||||
MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected float _blurMeshCornerOverlap = 0.3f;
|
||||
#if UNITY_6000_0_OR_NEWER
|
||||
[UxmlAttribute("Blur-Mesh-Corner-Overlap")]
|
||||
[CreateProperty]
|
||||
#endif
|
||||
public float BlurMeshCornerOverlap
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlurredBackgroundStyles.ResolveStyle(BlurredBackgroundStyles.MeshCornerOverlap, this, _blurMeshCornerOverlap);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
var newValue = BlurredBackgroundStyles.ResolveStyle(BlurredBackgroundStyles.MeshCornerOverlap, this, value);
|
||||
if (newValue != _blurMeshCornerOverlap)
|
||||
{
|
||||
if (newValue < 0f)
|
||||
newValue = 0f;
|
||||
|
||||
_blurMeshCornerOverlap = newValue;
|
||||
MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Color _defaultBackgroundColor = BackgroundColorDefault;
|
||||
#if UNITY_6000_0_OR_NEWER
|
||||
[UxmlAttribute("Background-Color")]
|
||||
[CreateProperty]
|
||||
#endif
|
||||
public Color BackgroundColor
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlurredBackgroundStyles.ResolveStyle(BlurredBackgroundStyles.BackgroundColor, this, _defaultBackgroundColor);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
var newValue = BlurredBackgroundStyles.ResolveStyle(BlurredBackgroundStyles.BackgroundColor, this, value);
|
||||
_defaultBackgroundColor = newValue;
|
||||
style.backgroundColor = _defaultBackgroundColor;
|
||||
}
|
||||
}
|
||||
|
||||
// Mesh Data
|
||||
Vertex[] _vertices;
|
||||
ushort[] _indices;
|
||||
|
||||
protected VisualElement rootParent;
|
||||
|
||||
public BlurredBackground()
|
||||
{
|
||||
generateVisualContent = OnGenerateVisualContent;
|
||||
|
||||
RegisterCallback<AttachToPanelEvent>(attach);
|
||||
RegisterCallback<DetachFromPanelEvent>(detach);
|
||||
}
|
||||
|
||||
void attach(AttachToPanelEvent evt)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
UnityEditor.EditorApplication.delayCall += () =>
|
||||
{
|
||||
#endif
|
||||
BlurManager.Instance.AttachElement(this);
|
||||
#if UNITY_EDITOR
|
||||
};
|
||||
#endif
|
||||
}
|
||||
|
||||
void detach(DetachFromPanelEvent evt)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
UnityEditor.EditorApplication.delayCall += () =>
|
||||
{
|
||||
#endif
|
||||
BlurManager.Instance.DetachElement(this);
|
||||
#if UNITY_EDITOR
|
||||
};
|
||||
#endif
|
||||
}
|
||||
|
||||
public virtual void OnGenerateVisualContent(MeshGenerationContext mgc)
|
||||
{
|
||||
// Remember: "generateVisualContent is an addition to the default rendering, it's not a replacement"
|
||||
// See: https://forum.unity.com/threads/hp-bars-at-runtime-image-masking-or-fill.1076486/#post-6948578
|
||||
|
||||
if (BlurManager.Instance == null)
|
||||
return;
|
||||
|
||||
// If no blur is required then do not even draw the mesh.
|
||||
if (BlurIterations <= 0 || BlurManager.Instance.Offset <= 0f || contentRect.width == 0 || contentRect.height == 0)
|
||||
return;
|
||||
|
||||
Rect contentRectAbs = contentRect;
|
||||
|
||||
if (contentRectAbs.width + resolvedStyle.paddingLeft + resolvedStyle.paddingRight < 0.01f || contentRectAbs.height + resolvedStyle.paddingTop + resolvedStyle.paddingBottom < 0.01f)
|
||||
return;
|
||||
|
||||
// Clamp content
|
||||
if (resolvedStyle.borderLeftWidth < 0) contentRectAbs.xMin -= resolvedStyle.borderLeftWidth;
|
||||
if (resolvedStyle.borderRightWidth < 0) contentRectAbs.xMax += resolvedStyle.borderRightWidth;
|
||||
if (resolvedStyle.borderTopWidth < 0) contentRectAbs.yMin -= resolvedStyle.borderTopWidth;
|
||||
if (resolvedStyle.borderBottomWidth < 0) contentRectAbs.yMax += resolvedStyle.borderBottomWidth;
|
||||
|
||||
|
||||
// Mesh generation
|
||||
|
||||
// clamp to positive
|
||||
float borderLeft = Mathf.Clamp(resolvedStyle.borderLeftWidth, 0, resolvedStyle.width * 0.5f);
|
||||
float borderRight = Mathf.Clamp(resolvedStyle.borderRightWidth, 0, resolvedStyle.width * 0.5f);
|
||||
float borderTop = Mathf.Clamp(resolvedStyle.borderTopWidth, 0, resolvedStyle.height * 0.5f);
|
||||
float borderBottom = Mathf.Clamp(resolvedStyle.borderBottomWidth, 0, resolvedStyle.height * 0.5f);
|
||||
|
||||
float radiusTopLeft = Mathf.Max(0, resolvedStyle.borderTopLeftRadius);
|
||||
float radiusTopRight = Mathf.Max(0, resolvedStyle.borderTopRightRadius);
|
||||
float radiusBottomLeft = Mathf.Max(0, resolvedStyle.borderBottomLeftRadius);
|
||||
float radiusBottomRight = Mathf.Max(0, resolvedStyle.borderBottomRightRadius);
|
||||
|
||||
float paddingLeft = Mathf.Max(0, resolvedStyle.paddingLeft);
|
||||
float paddingRight = Mathf.Max(0, resolvedStyle.paddingRight);
|
||||
float paddingTop = Mathf.Max(0, resolvedStyle.paddingTop);
|
||||
float paddingBottom = Mathf.Max(0, resolvedStyle.paddingBottom);
|
||||
|
||||
contentRectAbs.xMin -= paddingLeft;
|
||||
contentRectAbs.xMax += paddingRight;
|
||||
contentRectAbs.yMin -= paddingTop;
|
||||
contentRectAbs.yMax += paddingBottom;
|
||||
|
||||
// Calc inner rect
|
||||
// It only starts to curve on the inside once the radius is > the bigger border width
|
||||
Vector2 topLeftCornerSize = new Vector2(
|
||||
Mathf.Clamp(radiusTopLeft - borderLeft, 0, resolvedStyle.width * 0.5f - borderLeft),
|
||||
Mathf.Clamp(radiusTopLeft - borderTop, 0, resolvedStyle.height * 0.5f - borderTop)
|
||||
);
|
||||
|
||||
Vector2 topRightCornerSize = new Vector2(
|
||||
Mathf.Clamp(radiusTopRight - borderRight, 0, resolvedStyle.width * 0.5f - borderRight),
|
||||
Mathf.Clamp(radiusTopRight - borderTop, 0, resolvedStyle.height * 0.5f - borderTop)
|
||||
);
|
||||
|
||||
Vector2 bottomLeftCornerSize = new Vector2(
|
||||
Mathf.Clamp(radiusBottomLeft - borderLeft, 0, resolvedStyle.width * 0.5f - borderLeft),
|
||||
Mathf.Clamp(radiusBottomLeft - borderBottom, 0, resolvedStyle.height * 0.5f - borderBottom)
|
||||
);
|
||||
|
||||
Vector2 bottomRightCornerSize = new Vector2(
|
||||
Mathf.Clamp(radiusBottomRight - borderRight, 0, resolvedStyle.width * 0.5f - borderRight),
|
||||
Mathf.Clamp(radiusBottomRight - borderBottom, 0, resolvedStyle.height * 0.5f - borderBottom)
|
||||
);
|
||||
|
||||
|
||||
// Calc inner quad with corner radius taken into account
|
||||
Vector2 innerTopLeft = new Vector2(contentRectAbs.xMin + topLeftCornerSize.x, contentRectAbs.yMin + topLeftCornerSize.y);
|
||||
Vector2 innerTopRight = new Vector2(contentRectAbs.xMax - topRightCornerSize.x, contentRectAbs.yMin + topRightCornerSize.y);
|
||||
Vector2 innerBottomLeft = new Vector2(contentRectAbs.xMin + bottomLeftCornerSize.x, contentRectAbs.yMax - bottomLeftCornerSize.y);
|
||||
Vector2 innerBottomRight = new Vector2(contentRectAbs.xMax - bottomRightCornerSize.x, contentRectAbs.yMax - bottomRightCornerSize.y);
|
||||
|
||||
int verticesPerCorner = BlurMeshCornerSegments;
|
||||
|
||||
// Calc total number of vertices
|
||||
// 4 Vertices for the inner rectangle
|
||||
// + verticesPerCorner + 2 for each full corner
|
||||
// + 1 for a corner with a radius on one side
|
||||
// + 0 vertices for a corner without any border radius
|
||||
int numVertices = 4; // <- start value
|
||||
|
||||
// Calc total number of indices
|
||||
// 6 Vertices for the inner quad (2 tris)
|
||||
// + (verticesPerCorner + 1) * 3 for each full corner
|
||||
// + 0 for a corner with a radius on one side
|
||||
// + 0 vertices for a corner without any border radius
|
||||
// Sides
|
||||
// + see below
|
||||
int numIndices = 6; // <- start value
|
||||
|
||||
// Top Left Corner
|
||||
if (topLeftCornerSize.x > 0 && topLeftCornerSize.y > 0)
|
||||
{
|
||||
numVertices += verticesPerCorner + 2;
|
||||
numIndices += (verticesPerCorner + 1) * 3;
|
||||
}
|
||||
else if (topLeftCornerSize.x > 0 || topLeftCornerSize.y > 0)
|
||||
{
|
||||
numVertices += 1;
|
||||
}
|
||||
|
||||
// Top Right Corner
|
||||
if (topRightCornerSize.x > 0 && topRightCornerSize.y > 0)
|
||||
{
|
||||
numVertices += verticesPerCorner + 2;
|
||||
numIndices += (verticesPerCorner + 1) * 3;
|
||||
}
|
||||
else if (topRightCornerSize.x > 0 || topRightCornerSize.y > 0)
|
||||
{
|
||||
numVertices += 1;
|
||||
}
|
||||
|
||||
// Bottom Left Corner
|
||||
if (bottomLeftCornerSize.x > 0 && bottomLeftCornerSize.y > 0)
|
||||
{
|
||||
numVertices += verticesPerCorner + 2;
|
||||
numIndices += (verticesPerCorner + 1) * 3;
|
||||
}
|
||||
else if (bottomLeftCornerSize.x > 0 || bottomLeftCornerSize.y > 0)
|
||||
{
|
||||
numVertices += 1;
|
||||
}
|
||||
|
||||
// Bottom Right Corner
|
||||
if (bottomRightCornerSize.x > 0 && bottomRightCornerSize.y > 0)
|
||||
{
|
||||
numVertices += verticesPerCorner + 2;
|
||||
numIndices += (verticesPerCorner + 1) * 3;
|
||||
}
|
||||
else if (bottomRightCornerSize.x > 0 || bottomRightCornerSize.y > 0)
|
||||
{
|
||||
numVertices += 1;
|
||||
}
|
||||
|
||||
// Sides (indices)
|
||||
// + 6 for a side where the corners form a rectangle
|
||||
// + 3 for a side where the corners form a triangle
|
||||
// + 0 for a side between two 0 vertex corners
|
||||
// Top
|
||||
if (topLeftCornerSize.y > 0 && topRightCornerSize.y > 0)
|
||||
numIndices += 6;
|
||||
else if (topLeftCornerSize.y > 0 || topRightCornerSize.y > 0)
|
||||
numIndices += 3;
|
||||
// Right
|
||||
if (topRightCornerSize.x > 0 && bottomRightCornerSize.x > 0)
|
||||
numIndices += 6;
|
||||
else if (topRightCornerSize.x > 0 || bottomRightCornerSize.x > 0)
|
||||
numIndices += 3;
|
||||
// Bottom
|
||||
if (bottomRightCornerSize.y > 0 && bottomLeftCornerSize.y > 0)
|
||||
numIndices += 6;
|
||||
else if (bottomRightCornerSize.y > 0 || bottomLeftCornerSize.y > 0)
|
||||
numIndices += 3;
|
||||
// Left
|
||||
if (bottomLeftCornerSize.x > 0 && topLeftCornerSize.x > 0)
|
||||
numIndices += 6;
|
||||
else if (bottomLeftCornerSize.x > 0 || topLeftCornerSize.x > 0)
|
||||
numIndices += 3;
|
||||
|
||||
if (_vertices == null || _vertices.Length != numVertices)
|
||||
{
|
||||
_vertices = new Vertex[numVertices];
|
||||
_indices = new ushort[numIndices];
|
||||
}
|
||||
|
||||
// keep track of indices
|
||||
ushort v = 0;
|
||||
ushort i = 0;
|
||||
|
||||
// Center rect
|
||||
ushort innerBottomLeftVertex = v;
|
||||
_vertices[v++].position = new Vector3(innerBottomLeft.x, innerBottomLeft.y, Vertex.nearZ);
|
||||
ushort innerTopLeftVertex = v;
|
||||
_vertices[v++].position = new Vector3(innerTopLeft.x, innerTopLeft.y, Vertex.nearZ);
|
||||
ushort innerTopRightVertex = v;
|
||||
_vertices[v++].position = new Vector3(innerTopRight.x, innerTopRight.y, Vertex.nearZ);
|
||||
ushort innerBottomRightVertex = v;
|
||||
_vertices[v++].position = new Vector3(innerBottomRight.x, innerBottomRight.y, Vertex.nearZ);
|
||||
_indices[i++] = 0;
|
||||
_indices[i++] = 1;
|
||||
_indices[i++] = 2;
|
||||
_indices[i++] = 2;
|
||||
_indices[i++] = 3;
|
||||
_indices[i++] = 0;
|
||||
|
||||
ushort bottomLeftLeftVertex, bottomLeftBottomVertex, bottomRightRightVertex, bottomRightBottomVertex,
|
||||
topLeftLeftVertex, topLeftTopVertex, topRightTopVertex, topRightRightVertex;
|
||||
|
||||
// We add an overlap to make the new mesh overlap the borders a little to reduce gaps.
|
||||
float overlapWidth = BlurMeshCornerOverlap;
|
||||
|
||||
// Sides (indices)
|
||||
// + 2 tris for a side where the corners form a rectangle
|
||||
// + 1 tri for a side where the corners form a triangle
|
||||
// Top
|
||||
createSide(topLeftCornerSize, topRightCornerSize, cornerSizeNotZeroY, ref v, ref i, innerTopLeftVertex, innerTopRightVertex,
|
||||
new Vector3(innerTopLeft.x, innerTopLeft.y - topLeftCornerSize.y - overlapWidth, Vertex.nearZ),
|
||||
new Vector3(innerTopRight.x, innerTopRight.y - topRightCornerSize.y - overlapWidth, Vertex.nearZ),
|
||||
out topLeftTopVertex, out topRightTopVertex
|
||||
);
|
||||
// Right
|
||||
createSide(topRightCornerSize, bottomRightCornerSize, cornerSizeNotZeroX, ref v, ref i, innerTopRightVertex, innerBottomRightVertex,
|
||||
new Vector3(innerTopRight.x + topRightCornerSize.x + overlapWidth, innerTopRight.y, Vertex.nearZ),
|
||||
new Vector3(innerBottomRight.x + bottomRightCornerSize.x + overlapWidth, innerBottomRight.y, Vertex.nearZ),
|
||||
out topRightRightVertex, out bottomRightRightVertex
|
||||
);
|
||||
// Bottom
|
||||
createSide(bottomRightCornerSize, bottomLeftCornerSize, cornerSizeNotZeroY, ref v, ref i, innerBottomRightVertex, innerBottomLeftVertex,
|
||||
new Vector3(innerBottomRight.x, innerBottomRight.y + bottomRightCornerSize.y + overlapWidth, Vertex.nearZ),
|
||||
new Vector3(innerBottomLeft.x, innerBottomLeft.y + bottomLeftCornerSize.y + overlapWidth, Vertex.nearZ),
|
||||
out bottomRightBottomVertex, out bottomLeftBottomVertex
|
||||
);
|
||||
// Left
|
||||
createSide(bottomLeftCornerSize, topLeftCornerSize, cornerSizeNotZeroX, ref v, ref i, innerBottomLeftVertex, innerTopLeftVertex,
|
||||
new Vector3(innerBottomLeft.x - bottomLeftCornerSize.x - overlapWidth, innerBottomLeft.y, Vertex.nearZ),
|
||||
new Vector3(innerTopLeft.x - topLeftCornerSize.x - overlapWidth, innerTopLeft.y, Vertex.nearZ),
|
||||
out bottomLeftLeftVertex, out topLeftLeftVertex
|
||||
);
|
||||
|
||||
if (verticesPerCorner > 0)
|
||||
{
|
||||
createCorner(topLeftCornerSize, innerTopLeft, verticesPerCorner, ref v, ref i, innerTopLeftVertex, topLeftLeftVertex, topLeftTopVertex, 2);
|
||||
createCorner(topRightCornerSize, innerTopRight, verticesPerCorner, ref v, ref i, innerTopRightVertex, topRightTopVertex, topRightRightVertex, 3);
|
||||
createCorner(bottomRightCornerSize, innerBottomRight, verticesPerCorner, ref v, ref i, innerBottomRightVertex, bottomRightRightVertex, bottomRightBottomVertex, 0);
|
||||
createCorner(bottomLeftCornerSize, innerBottomLeft, verticesPerCorner, ref v, ref i, innerBottomLeftVertex, bottomLeftBottomVertex, bottomLeftLeftVertex, 1);
|
||||
}
|
||||
|
||||
MeshWriteData mwd = mgc.Allocate(_vertices.Length, _indices.Length, BlurManager.Instance.GetBlurredTexture());
|
||||
|
||||
// UVs
|
||||
if (rootParent == null)
|
||||
{
|
||||
rootParent = GetDocumentRoot(this);
|
||||
}
|
||||
|
||||
for (int n = 0; n < _vertices.Length; n++)
|
||||
{
|
||||
_vertices[n].tint = BlurTint;
|
||||
|
||||
var uv = this.LocalToWorld(_vertices[n].position);
|
||||
uv.x /= rootParent.worldBound.width;
|
||||
uv.y /= rootParent.worldBound.height;
|
||||
uv.y = 1f - uv.y;
|
||||
|
||||
_vertices[n].uv = uv;
|
||||
}
|
||||
|
||||
mwd.SetAllVertices(_vertices);
|
||||
mwd.SetAllIndices(_indices);
|
||||
}
|
||||
|
||||
private void createCorner(Vector2 cornerSize, Vector2 innerPos, int verticesPerCorner, ref ushort v, ref ushort i, ushort innerVertex, ushort startVertex, ushort endVertex, int quadrantOffset)
|
||||
{
|
||||
if (cornerSize.x > 0 && cornerSize.y > 0)
|
||||
{
|
||||
ushort center = innerVertex;
|
||||
ushort last = startVertex;
|
||||
|
||||
float offset = Mathf.PI * 0.5f * quadrantOffset;
|
||||
float stepSizeInQuadrant = 1f / (verticesPerCorner + 1) * Mathf.PI * 0.5f;
|
||||
|
||||
for (int c = 1; c < verticesPerCorner + 1; c++)
|
||||
{
|
||||
float x = Mathf.Cos(offset + stepSizeInQuadrant * c);
|
||||
float y = Mathf.Sin(offset + stepSizeInQuadrant * c);
|
||||
// We also add an overlap to make the new mesh overlap the borders a little to reduce gaps.
|
||||
float overlapWidth = BlurMeshCornerOverlap;
|
||||
_vertices[v++].position = new Vector3(innerPos.x + x * (cornerSize.x + overlapWidth), innerPos.y + y * (cornerSize.y + overlapWidth), Vertex.nearZ);
|
||||
|
||||
_indices[i++] = center;
|
||||
_indices[i++] = last;
|
||||
_indices[i++] = (ushort)(v - 1);
|
||||
last = _indices[i - 1];
|
||||
}
|
||||
|
||||
// End at the existing vertex
|
||||
_indices[i++] = center;
|
||||
_indices[i++] = last;
|
||||
_indices[i++] = endVertex;
|
||||
}
|
||||
}
|
||||
|
||||
void createSide(
|
||||
Vector2 firstCornerSize, Vector2 secondCornerSize,
|
||||
System.Func<Vector2, bool> cornerSizeNotZeroFunc,
|
||||
ref ushort v, ref ushort i,
|
||||
ushort firstOuterVertex, ushort secondOuterVertex,
|
||||
Vector3 newVertexAPos, Vector3 newVertexBPos,
|
||||
out ushort newVertexA, out ushort newVertexB)
|
||||
{
|
||||
newVertexA = 0;
|
||||
newVertexB = 0;
|
||||
|
||||
if (cornerSizeNotZeroFunc(firstCornerSize) && cornerSizeNotZeroFunc(secondCornerSize))
|
||||
{
|
||||
newVertexA = v;
|
||||
_vertices[v++].position = newVertexAPos;
|
||||
newVertexB = v;
|
||||
_vertices[v++].position = newVertexBPos;
|
||||
_indices[i++] = newVertexA;
|
||||
_indices[i++] = newVertexB;
|
||||
_indices[i++] = firstOuterVertex;
|
||||
_indices[i++] = newVertexB;
|
||||
_indices[i++] = secondOuterVertex;
|
||||
_indices[i++] = firstOuterVertex;
|
||||
}
|
||||
else if (cornerSizeNotZeroFunc(firstCornerSize) || cornerSizeNotZeroFunc(secondCornerSize))
|
||||
{
|
||||
if (cornerSizeNotZeroFunc(firstCornerSize))
|
||||
{
|
||||
newVertexA = v;
|
||||
_vertices[v++].position = newVertexAPos;
|
||||
_indices[i++] = newVertexA;
|
||||
_indices[i++] = secondOuterVertex;
|
||||
_indices[i++] = firstOuterVertex;
|
||||
}
|
||||
else
|
||||
{
|
||||
newVertexB = v;
|
||||
_vertices[v++].position = newVertexBPos;
|
||||
_indices[i++] = newVertexB;
|
||||
_indices[i++] = secondOuterVertex;
|
||||
_indices[i++] = firstOuterVertex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool cornerSizeNotZeroX(Vector2 cornerSize)
|
||||
{
|
||||
return cornerSize.x > 0;
|
||||
}
|
||||
|
||||
bool cornerSizeNotZeroY(Vector2 cornerSize)
|
||||
{
|
||||
return cornerSize.y > 0;
|
||||
}
|
||||
|
||||
public VisualElement GetDocumentRoot(VisualElement ele)
|
||||
{
|
||||
while (ele.parent != null)
|
||||
{
|
||||
ele = ele.parent;
|
||||
}
|
||||
|
||||
return ele;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9a05c9b06e01cf048bcd4d1956224659
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,70 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace Kamgam.UIToolkitBlurredBackground
|
||||
{
|
||||
public static class BlurredBackgroundStyles
|
||||
{
|
||||
public static CustomStyleProperty<float> Strength = new CustomStyleProperty<float>("--blur-strength");
|
||||
public static CustomStyleProperty<int> Iterations = new CustomStyleProperty<int>("--blur-iterations");
|
||||
/// <summary>
|
||||
/// low, medium, high
|
||||
/// </summary>
|
||||
public static CustomStyleProperty<string> Quality = new CustomStyleProperty<string>("--blur-quality");
|
||||
public static CustomStyleProperty<int> Resolution = new CustomStyleProperty<int>("--blur-resolution");
|
||||
public static CustomStyleProperty<Color> Tint = new CustomStyleProperty<Color>("--blur-tint");
|
||||
public static CustomStyleProperty<float> MeshCornerOverlap = new CustomStyleProperty<float>("--blur-mesh-corner-overlap");
|
||||
public static CustomStyleProperty<int> MeshCornerSegments = new CustomStyleProperty<int>("--blur-mesh-corner-segments");
|
||||
public static CustomStyleProperty<Color> BackgroundColor = new CustomStyleProperty<Color>("--blur-background-color");
|
||||
|
||||
public static float ResolveStyle(CustomStyleProperty<float> property, VisualElement element, float defaultValue)
|
||||
{
|
||||
if (element.customStyle.TryGetValue(property, out var value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public static int ResolveStyle(CustomStyleProperty<int> property, VisualElement element, int defaultValue)
|
||||
{
|
||||
if (element.customStyle.TryGetValue(property, out var value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public static bool ResolveStyle(CustomStyleProperty<bool> property, VisualElement element, bool defaultValue)
|
||||
{
|
||||
if (element.customStyle.TryGetValue(property, out var value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public static Color ResolveStyle(CustomStyleProperty<Color> property, VisualElement element, Color defaultValue)
|
||||
{
|
||||
if (element.customStyle.TryGetValue(property, out var value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public static string ResolveStyle(CustomStyleProperty<string> property, VisualElement element, string defaultValue)
|
||||
{
|
||||
if (element.customStyle.TryGetValue(property, out var value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1708b53bd35204045b5cbef7db10b070
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user