Before 优化 机场
This commit is contained in:
@@ -0,0 +1 @@
|
||||
1.2.3
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 256161b2a56b37d48a9951048ed423b8
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 88da447c6ce2d6745be197dfb3e77920
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,111 @@
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEditor.Callbacks;
|
||||
|
||||
namespace Kamgam.UIToolkitBlurredBackground
|
||||
{
|
||||
public static class EditorPlayState
|
||||
{
|
||||
public enum PlayState
|
||||
{
|
||||
NotListening = 0,
|
||||
Editing = 1,
|
||||
FromEditToPlay = 2,
|
||||
Playing = 3,
|
||||
FromPlayToEdit = 4
|
||||
}
|
||||
|
||||
static string _editorPlayStateKey;
|
||||
static string EditorPlayStateKey
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(_editorPlayStateKey))
|
||||
{
|
||||
_editorPlayStateKey = typeof(EditorPlayState).FullName + ".State";
|
||||
}
|
||||
|
||||
return _editorPlayStateKey;
|
||||
}
|
||||
}
|
||||
|
||||
static PlayState _state = PlayState.NotListening;
|
||||
public static PlayState State
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_state == PlayState.NotListening)
|
||||
StartListening();
|
||||
|
||||
return _state;
|
||||
}
|
||||
}
|
||||
|
||||
[DidReloadScripts]
|
||||
[InitializeOnLoadMethod]
|
||||
public static void StartListening()
|
||||
{
|
||||
if (_state != PlayState.NotListening)
|
||||
return;
|
||||
|
||||
EditorApplication.playModeStateChanged -= onPlayModeChanged;
|
||||
EditorApplication.playModeStateChanged += onPlayModeChanged;
|
||||
|
||||
int state = SessionState.GetInt(EditorPlayStateKey, -1);
|
||||
if (state >= 0)
|
||||
{
|
||||
_state = (PlayState)state;
|
||||
}
|
||||
else
|
||||
{
|
||||
_state = EditorApplication.isPlayingOrWillChangePlaymode ? PlayState.Playing : PlayState.Editing;
|
||||
}
|
||||
}
|
||||
|
||||
private static void onPlayModeChanged(PlayModeStateChange change)
|
||||
{
|
||||
if (change == PlayModeStateChange.EnteredPlayMode)
|
||||
{
|
||||
_state = PlayState.Playing;
|
||||
}
|
||||
if (change == PlayModeStateChange.ExitingPlayMode)
|
||||
{
|
||||
_state = PlayState.FromPlayToEdit;
|
||||
}
|
||||
if (change == PlayModeStateChange.EnteredEditMode)
|
||||
{
|
||||
_state = PlayState.Editing;
|
||||
}
|
||||
if (change == PlayModeStateChange.ExitingEditMode)
|
||||
{
|
||||
_state = PlayState.FromEditToPlay;
|
||||
}
|
||||
|
||||
SessionState.SetInt(EditorPlayStateKey, (int)_state);
|
||||
}
|
||||
|
||||
public static void StopListening()
|
||||
{
|
||||
_state = PlayState.NotListening;
|
||||
SessionState.EraseInt(EditorPlayStateKey);
|
||||
|
||||
EditorApplication.playModeStateChanged -= onPlayModeChanged;
|
||||
}
|
||||
|
||||
public static bool IsInBetween
|
||||
{
|
||||
get => _state == PlayState.FromEditToPlay || _state == PlayState.FromPlayToEdit;
|
||||
}
|
||||
|
||||
public static bool IsEditing
|
||||
{
|
||||
get => _state == PlayState.Editing;
|
||||
}
|
||||
|
||||
public static bool IsPlaying
|
||||
{
|
||||
get => _state == PlayState.Playing;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1b74da72089888e458571f6b6303e93d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 343da767e1329d94783b9866b9bf7764
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,363 @@
|
||||
#if UNITY_EDITOR
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Callbacks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Kamgam.UIToolkitBlurredBackground
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows you to register a callback before compilation
|
||||
/// which is then executed automatically after compilation.<br />
|
||||
/// <br />
|
||||
/// Methods registered are usually executed in FIFO order. Though there
|
||||
/// is no guarantee that this will always be the case.
|
||||
/// <example>
|
||||
/// CrossCompileCallbacks.RegisterCallback(testCallbackA); // It can find the type automatically.
|
||||
/// CrossCompileCallbacks.RegisterCallback(typeof(YourClass), "testCallbackA");
|
||||
/// </example>
|
||||
/// </summary>
|
||||
public static class CrossCompileCallbacks
|
||||
{
|
||||
/// <summary>
|
||||
/// If set to true then the callbacks will not be called immediately but
|
||||
/// within the next editor update cycle.<br />
|
||||
/// Use this to avoid "Calling ... from assembly reloading callbacks are not supported." errors.
|
||||
/// </summary>
|
||||
public static bool DelayExecutionAfterCompilation
|
||||
{
|
||||
get => SessionState.GetBool(typeName() + ".DelayExecution", false);
|
||||
set => SessionState.SetBool(typeName() + ".DelayExecution", value);
|
||||
}
|
||||
|
||||
static string typeName() => typeof(CrossCompileCallbacks).FullName;
|
||||
|
||||
const string _maxIndexKey = ".MaxIndex";
|
||||
static string maxIndexKey() => typeName() + _maxIndexKey;
|
||||
|
||||
const string _lastReleasedIndexKey = ".LastReleasedIndex";
|
||||
static string lastReleasedIndexKey() => typeName() + _lastReleasedIndexKey;
|
||||
|
||||
const string _indexTypeKey = ".Index[{0}].Type";
|
||||
static string indexTypeKey(int index) => string.Format(typeName() + _indexTypeKey, index);
|
||||
|
||||
const string _indexMethodKey = ".Index[{0}].Method";
|
||||
static string indexMethodKey(int index) => string.Format(typeName() + _indexMethodKey, index);
|
||||
|
||||
|
||||
static int getMaxIndex()
|
||||
{
|
||||
return SessionState.GetInt(maxIndexKey(), -1);
|
||||
}
|
||||
|
||||
static int getNextIndex()
|
||||
{
|
||||
int maxIndex;
|
||||
|
||||
// Try to reuse an old index (update max index if necessary)
|
||||
int reusableIndex = SessionState.GetInt(lastReleasedIndexKey(), -1);
|
||||
if (reusableIndex >= 0)
|
||||
{
|
||||
SessionState.SetInt(lastReleasedIndexKey(), -1);
|
||||
|
||||
maxIndex = getMaxIndex();
|
||||
if(maxIndex < reusableIndex)
|
||||
SessionState.SetInt(maxIndexKey(), reusableIndex);
|
||||
|
||||
return reusableIndex;
|
||||
}
|
||||
|
||||
// New index needed (increase max index).
|
||||
maxIndex = SessionState.GetInt(maxIndexKey(), -1);
|
||||
maxIndex++;
|
||||
SessionState.SetInt(maxIndexKey(), maxIndex);
|
||||
|
||||
return maxIndex;
|
||||
}
|
||||
|
||||
public static void ReleaseIndex(int index)
|
||||
{
|
||||
if (index < 0)
|
||||
return;
|
||||
|
||||
SessionState.SetInt(lastReleasedIndexKey(), index);
|
||||
SessionState.EraseString(indexTypeKey(index));
|
||||
SessionState.EraseString(indexMethodKey(index));
|
||||
|
||||
// Decrease or erase max index if needed.
|
||||
int maxIndex = getMaxIndex();
|
||||
if(index == maxIndex)
|
||||
{
|
||||
maxIndex--;
|
||||
if(maxIndex < 0)
|
||||
SessionState.EraseInt(maxIndexKey());
|
||||
else
|
||||
SessionState.SetInt(maxIndexKey(), maxIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ReleaseAllOnType(Type type)
|
||||
{
|
||||
if (type == null)
|
||||
return;
|
||||
|
||||
int maxIndex = getMaxIndex();
|
||||
for (int i = maxIndex; i >= 0; i--)
|
||||
{
|
||||
string typeName;
|
||||
GetCallbackInfo(i, out typeName, out _);
|
||||
|
||||
if(typeName == type.FullName)
|
||||
{
|
||||
ReleaseIndex(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a callback and returns an index >= 0 on success and -1 on failure.
|
||||
/// </summary>
|
||||
/// <param name="callback">A static method without any parameters.</param>
|
||||
/// <returns></returns>
|
||||
public static int RegisterCallback(System.Action callback)
|
||||
{
|
||||
if (callback == null)
|
||||
return -1;
|
||||
|
||||
var methodInfo = callback.GetMethodInfo();
|
||||
if (methodInfo == null)
|
||||
return -1;
|
||||
|
||||
if (!methodInfo.IsStatic)
|
||||
{
|
||||
Debug.Log("Method needs to be static.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return RegisterCallback(methodInfo.DeclaringType, methodInfo.Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a callback and returns an index >= 0 on success and -1 on failure.
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="staticMethodName">A static method without any parameters.</param>
|
||||
/// <returns></returns>
|
||||
public static int RegisterCallback(Type type, string staticMethodName)
|
||||
{
|
||||
if (type == null || string.IsNullOrEmpty(staticMethodName))
|
||||
{
|
||||
Debug.Assert(type != null);
|
||||
Debug.Assert(staticMethodName != null);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check if methods has any parameters (that's not supported)
|
||||
try
|
||||
{
|
||||
var flags = BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public;
|
||||
var methodInfo = type.GetMethod(staticMethodName, flags);
|
||||
if (methodInfo == null)
|
||||
{
|
||||
Debug.LogError("No static method '" + staticMethodName + "' found in '" + type.FullName + "'.");
|
||||
return -1;
|
||||
}
|
||||
if (methodInfo.GetParameters().Length > 0)
|
||||
{
|
||||
Debug.Assert(methodInfo.GetParameters().Length == 0);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.LogError($"CrossCompileCallbacks: Error while checking '{staticMethodName}' method parameters. Error:\n" + e.Message);
|
||||
}
|
||||
|
||||
int index = getNextIndex();
|
||||
SessionState.SetString(indexTypeKey(index), type.FullName);
|
||||
SessionState.SetString(indexMethodKey(index), staticMethodName);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
public static void GetCallbackInfo(int index, out string typeName, out string methodName)
|
||||
{
|
||||
typeName = SessionState.GetString(indexTypeKey(index), null);
|
||||
methodName = SessionState.GetString(indexMethodKey(index), null);
|
||||
}
|
||||
|
||||
[DidReloadScripts(-1)]
|
||||
static void onAfterCompilation()
|
||||
{
|
||||
if (DelayExecutionAfterCompilation)
|
||||
{
|
||||
EditorApplication.delayCall -= delayedExecuteRegisteredCallbacks;
|
||||
EditorApplication.delayCall += delayedExecuteRegisteredCallbacks;
|
||||
}
|
||||
else
|
||||
{
|
||||
executeRegisteredCallbacks();
|
||||
}
|
||||
}
|
||||
|
||||
static void delayedExecuteRegisteredCallbacks()
|
||||
{
|
||||
EditorApplication.delayCall -= delayedExecuteRegisteredCallbacks;
|
||||
executeRegisteredCallbacks();
|
||||
}
|
||||
|
||||
static void executeRegisteredCallbacks()
|
||||
{
|
||||
int maxIndex = getMaxIndex();
|
||||
for (int i = maxIndex; i >= 0; i--)
|
||||
{
|
||||
string typeName;
|
||||
string methodName;
|
||||
GetCallbackInfo(i, out typeName, out methodName);
|
||||
|
||||
try
|
||||
{
|
||||
ReleaseIndex(i);
|
||||
|
||||
if (string.IsNullOrEmpty(typeName) || string.IsNullOrEmpty(methodName))
|
||||
continue;
|
||||
|
||||
var methodInfo = findStaticMethod(typeName, methodName);
|
||||
methodInfo.Invoke(null, null);
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
string errorMsg = e.Message;
|
||||
if(errorMsg.Contains("invocation") && e.InnerException != null)
|
||||
{
|
||||
errorMsg += "\n" + e.InnerException.Message;
|
||||
}
|
||||
Debug.LogError($"CrossCompileCallbacks: Calling '{typeName}.{methodName}' failed. Error:\n" + errorMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static MethodInfo findStaticMethod(string fullTypeName, string methodName)
|
||||
{
|
||||
var type = findType(fullTypeName);
|
||||
if (type == null)
|
||||
return null;
|
||||
|
||||
var flags = BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public;
|
||||
var methodInfo = type.GetMethod(methodName, flags);
|
||||
|
||||
return methodInfo;
|
||||
}
|
||||
|
||||
static Type findType(string fullTypeName)
|
||||
{
|
||||
Debug.Assert(fullTypeName != null);
|
||||
|
||||
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
|
||||
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
Type t = assembly.GetType(fullTypeName, throwOnError: false);
|
||||
if (t != null)
|
||||
return t;
|
||||
}
|
||||
|
||||
throw new ArgumentException("Type " + fullTypeName + " doesn't exist in the current app domain.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Utility method to store a static parameterless Action
|
||||
/// in the SessionState for retrieval at a later time.
|
||||
/// </summary>
|
||||
/// <param name="sessionStorageKey"></param>
|
||||
/// <param name="action"></param>
|
||||
/// <returns></returns>
|
||||
public static bool StoreAction(string sessionStorageKey, System.Action action)
|
||||
{
|
||||
if (action == null)
|
||||
return false;
|
||||
|
||||
var methodInfo = action.GetMethodInfo();
|
||||
if (methodInfo == null)
|
||||
return false;
|
||||
|
||||
if (!methodInfo.IsStatic)
|
||||
{
|
||||
Debug.Log("Method '"+ methodInfo.Name + "'needs to be static.");
|
||||
return false;
|
||||
}
|
||||
|
||||
SessionState.SetString(sessionStorageKey + ".Type", methodInfo.DeclaringType.FullName);
|
||||
SessionState.SetString(sessionStorageKey + ".Method", methodInfo.Name);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the Action from the SessionState.
|
||||
/// </summary>
|
||||
/// <param name="sessionStorageKey"></param>
|
||||
/// <returns></returns>
|
||||
public static System.Action GetStoredAction(string sessionStorageKey)
|
||||
{
|
||||
var typeName = SessionState.GetString(sessionStorageKey + ".Type", null);
|
||||
var methodName = SessionState.GetString(sessionStorageKey + ".Method", null);
|
||||
|
||||
if (string.IsNullOrEmpty(typeName) || string.IsNullOrEmpty(methodName))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var type = findType(typeName);
|
||||
if (type == null)
|
||||
return null;
|
||||
|
||||
var methodInfo = findStaticMethod(typeName, methodName);
|
||||
if (methodInfo == null)
|
||||
return null;
|
||||
|
||||
return (Action) Delegate.CreateDelegate(typeof(Action), methodInfo);
|
||||
}
|
||||
|
||||
public static void ClearStoredAction(string sessionStorageKey)
|
||||
{
|
||||
SessionState.EraseString(sessionStorageKey + ".Type");
|
||||
SessionState.EraseString(sessionStorageKey + ".Method");
|
||||
}
|
||||
|
||||
// Testing
|
||||
/*
|
||||
[DidReloadScripts]
|
||||
static void StartTest()
|
||||
{
|
||||
Debug.Log("CrossCompileCallbacks: Starting test.");
|
||||
RegisterCallback(testCallbackA);
|
||||
RegisterCallback(typeof(CrossCompileCallbacks), "testCallbackB");
|
||||
|
||||
var action = GetStoredAction("storedActionA");
|
||||
ClearStoredAction("storedActionA");
|
||||
if (action != null)
|
||||
action.Invoke();
|
||||
StoreAction("storedActionA", storedActionA);
|
||||
}
|
||||
|
||||
static void testCallbackA()
|
||||
{
|
||||
Debug.Log("Test callback A executed.");
|
||||
}
|
||||
|
||||
static void testCallbackB()
|
||||
{
|
||||
Debug.Log("Test callback B executed.");
|
||||
}
|
||||
|
||||
static void storedActionA()
|
||||
{
|
||||
Debug.Log("Stored action A executed.");
|
||||
}
|
||||
//*/
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aa14d306c727c71409d46d6aa8335138
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,235 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEditor.Build;
|
||||
using UnityEditor.SceneManagement;
|
||||
#endif
|
||||
namespace Kamgam.UIToolkitBlurredBackground
|
||||
{
|
||||
public class Installer
|
||||
#if UNITY_EDITOR
|
||||
: IActiveBuildTargetChanged
|
||||
#endif
|
||||
{
|
||||
public const string AssetName = "UI Toolkit Blurred Background";
|
||||
public const string Version = "1.2.3";
|
||||
public const string Define = "KAMGAM_UI_TOOLKIT_BLURRED_BACKGROUND";
|
||||
public const string ManualUrl = "https://kamgam.com/unity/UIToolkitBlurredBackgroundManual.pdf";
|
||||
public const string AssetLink = "https://assetstore.unity.com/packages/slug/254328";
|
||||
|
||||
public static string _assetRootPathDefault = "Assets/Kamgam/UIToolkitBlurredBackground/";
|
||||
public static string AssetRootPath
|
||||
{
|
||||
get
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (System.IO.File.Exists(_assetRootPathDefault))
|
||||
{
|
||||
return _assetRootPathDefault;
|
||||
}
|
||||
|
||||
// The the tool was moved then search for the installer script and derive the root
|
||||
// path from there. Used Assets/ as ultimate fallback.
|
||||
string finalPath = "Assets/";
|
||||
string assetRootPathRelative = _assetRootPathDefault.Replace("Assets/", "");
|
||||
var installerGUIDS = AssetDatabase.FindAssets("t:Script Installer");
|
||||
foreach (var guid in installerGUIDS)
|
||||
{
|
||||
var path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
if (path.Contains(assetRootPathRelative))
|
||||
{
|
||||
int index = path.IndexOf(assetRootPathRelative);
|
||||
return path.Substring(0, index) + assetRootPathRelative;
|
||||
}
|
||||
}
|
||||
|
||||
return finalPath;
|
||||
#else
|
||||
return _assetRootPathDefault;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
public static string ExamplePath = AssetRootPath + "Examples/UIToolkitBlurredBackgroundDemo.unity";
|
||||
|
||||
public static Version GetVersion() => new Version(Version);
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[UnityEditor.Callbacks.DidReloadScripts(998001)]
|
||||
public static void InstallIfNeeded()
|
||||
{
|
||||
bool versionChanged = VersionHelper.UpgradeVersion(GetVersion, out Version oldVersion, out Version newVersion);
|
||||
if (versionChanged)
|
||||
{
|
||||
if (versionChanged)
|
||||
{
|
||||
Debug.Log(AssetName + " version changed from " + oldVersion + " to " + newVersion);
|
||||
|
||||
if (AddDefineSymbol())
|
||||
{
|
||||
CrossCompileCallbacks.RegisterCallback(onPostImport);
|
||||
}
|
||||
else
|
||||
{
|
||||
onPostImport();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int callbackOrder => 0;
|
||||
|
||||
public void OnActiveBuildTargetChanged(BuildTarget previousTarget, BuildTarget newTarget)
|
||||
{
|
||||
Logger.LogMessage($"Build target changed from {previousTarget} to {newTarget}. Refreshing define symbols.");
|
||||
AddDefineSymbol();
|
||||
}
|
||||
|
||||
[MenuItem("Tools/" + AssetName + "/Debug/Add Defines", priority = 501)]
|
||||
private static void AddDefineSymbolMenu()
|
||||
{
|
||||
AddDefineSymbol();
|
||||
}
|
||||
|
||||
private static bool AddDefineSymbol()
|
||||
{
|
||||
bool didChange = false;
|
||||
|
||||
foreach (BuildTargetGroup targetGroup in System.Enum.GetValues(typeof(BuildTargetGroup)))
|
||||
{
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
if (targetGroup == BuildTargetGroup.Unknown || targetGroup == BuildTargetGroup.GameCoreScarlett)
|
||||
continue;
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
try
|
||||
{
|
||||
#if UNITY_2023_1_OR_NEWER
|
||||
string currentDefineSymbols = PlayerSettings.GetScriptingDefineSymbols(NamedBuildTarget.FromBuildTargetGroup(targetGroup));
|
||||
#else
|
||||
string currentDefineSymbols = PlayerSettings.GetScriptingDefineSymbolsForGroup(targetGroup);
|
||||
#endif
|
||||
|
||||
if (currentDefineSymbols.Contains(Define))
|
||||
continue;
|
||||
|
||||
#if UNITY_2023_1_OR_NEWER
|
||||
PlayerSettings.SetScriptingDefineSymbols(NamedBuildTarget.FromBuildTargetGroup(targetGroup), currentDefineSymbols + ";" + Define);
|
||||
#else
|
||||
PlayerSettings.SetScriptingDefineSymbolsForGroup(targetGroup, currentDefineSymbols + ";" + Define);
|
||||
#endif
|
||||
// Logger.LogMessage($"{Define} symbol has been added for {targetGroup}.");
|
||||
|
||||
didChange = true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// There are many obsolete defines in the enum, skip them silently.
|
||||
}
|
||||
}
|
||||
|
||||
return didChange;
|
||||
}
|
||||
|
||||
[MenuItem("Tools/" + AssetName + "/Debug/Remove Defines", priority = 502)]
|
||||
private static void RemoveDefineSymbol()
|
||||
{
|
||||
foreach (BuildTargetGroup targetGroup in System.Enum.GetValues(typeof(BuildTargetGroup)))
|
||||
{
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
if (targetGroup == BuildTargetGroup.Unknown || targetGroup == BuildTargetGroup.GameCoreScarlett)
|
||||
continue;
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
try
|
||||
{
|
||||
#if UNITY_2023_1_OR_NEWER
|
||||
string currentDefineSymbols = PlayerSettings.GetScriptingDefineSymbols(NamedBuildTarget.FromBuildTargetGroup(targetGroup));
|
||||
#else
|
||||
string currentDefineSymbols = PlayerSettings.GetScriptingDefineSymbolsForGroup(targetGroup);
|
||||
#endif
|
||||
|
||||
if (currentDefineSymbols.Contains(Define))
|
||||
{
|
||||
currentDefineSymbols = currentDefineSymbols.Replace(";" + Define, "");
|
||||
#if UNITY_2023_1_OR_NEWER
|
||||
PlayerSettings.SetScriptingDefineSymbols(NamedBuildTarget.FromBuildTargetGroup(targetGroup), currentDefineSymbols);
|
||||
#else
|
||||
PlayerSettings.SetScriptingDefineSymbolsForGroup(targetGroup, currentDefineSymbols);
|
||||
#endif
|
||||
Logger.LogMessage($"{Define} symbol has been removed for {targetGroup}.");
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// There are many obsolete defines in the enum, skip them silently.
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void onPostImport()
|
||||
{
|
||||
// Import packages and then show welcome screen.
|
||||
PackageImporter.ImportDelayed(showWelcomeMessage);
|
||||
}
|
||||
|
||||
static void showWelcomeMessage()
|
||||
{
|
||||
bool openExample = EditorUtility.DisplayDialog(
|
||||
AssetName,
|
||||
"Thank you for choosing " + AssetName + ".\n\n" +
|
||||
"Please start by reading the manual.\n\n" +
|
||||
"You'll find the asset options under Tools > UGUI Blurred Background > ...\n\n" +
|
||||
"It would be great if you could find the time to leave a review.\n\n" +
|
||||
"I have prepared some examples for you.",
|
||||
"Open Example", "Open manual (web)"
|
||||
);
|
||||
|
||||
if (openExample)
|
||||
OpenExample();
|
||||
else
|
||||
OpenManual();
|
||||
|
||||
UIToolkitBlurredBackgroundSettings.GetOrCreateSettings().AddShaderBeforeBuild = true;
|
||||
}
|
||||
|
||||
|
||||
[MenuItem("Tools/" + AssetName + "/Manual", priority = 101)]
|
||||
public static void OpenManual()
|
||||
{
|
||||
Application.OpenURL(ManualUrl);
|
||||
}
|
||||
|
||||
[MenuItem("Tools/" + AssetName + "/Open Example Scene", priority = 103)]
|
||||
public static void OpenExample()
|
||||
{
|
||||
EditorApplication.delayCall += () =>
|
||||
{
|
||||
var scene = AssetDatabase.LoadAssetAtPath<SceneAsset>(ExamplePath);
|
||||
EditorGUIUtility.PingObject(scene);
|
||||
EditorSceneManager.OpenScene(ExamplePath);
|
||||
};
|
||||
}
|
||||
|
||||
[MenuItem("Tools/" + AssetName + "/Please leave a review :-)", priority = 510)]
|
||||
public static void LeaveReview()
|
||||
{
|
||||
Application.OpenURL(AssetLink + "?aid=1100lqC54&pubref=asset");
|
||||
}
|
||||
|
||||
[MenuItem("Tools/" + AssetName + "/More Asset by KAMGAM", priority = 511)]
|
||||
public static void MoreAssets()
|
||||
{
|
||||
Application.OpenURL("https://assetstore.unity.com/publishers/37829?aid=1100lqC54&pubref=asset");
|
||||
}
|
||||
|
||||
[MenuItem("Tools/" + AssetName + "/Version " + Version, priority = 512)]
|
||||
public static void LogVersion()
|
||||
{
|
||||
Debug.Log(AssetName + " v" + Version);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a0c8e1d2030d4f249a58791bb732838b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,78 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Kamgam.UIToolkitBlurredBackground
|
||||
{
|
||||
public class Logger
|
||||
{
|
||||
public delegate void LogCallback(string msg, LogLevel logLevel);
|
||||
|
||||
public const string Prefix = "UITK Blurred BG: ";
|
||||
public static LogLevel CurrentLogLevel = LogLevel.Warning;
|
||||
|
||||
/// <summary>
|
||||
/// Optional: leave as is or set to NULL to not use it.<br />
|
||||
/// Set this to a function which returns the log level (from settings for example).<br />
|
||||
/// This will be called before every log.
|
||||
/// <example>
|
||||
/// [RuntimeInitializeOnLoadMethod]
|
||||
/// private static void HookUpToLogger()
|
||||
/// {
|
||||
/// Logger.OnGetLogLevel = () => GetOrCreateSettings().LogLevel;
|
||||
/// }
|
||||
/// </example>
|
||||
/// </summary>
|
||||
public static System.Func<LogLevel> OnGetLogLevel = null;
|
||||
|
||||
public enum LogLevel
|
||||
{
|
||||
Log = 0,
|
||||
Warning = 1,
|
||||
Error = 2,
|
||||
Message = 3,
|
||||
NoLogs = 99
|
||||
}
|
||||
|
||||
public static bool IsLogLevelVisible(LogLevel logLevel)
|
||||
{
|
||||
return (int)logLevel >= (int)CurrentLogLevel;
|
||||
}
|
||||
|
||||
public static void UpdateCurrentLogLevel()
|
||||
{
|
||||
if (OnGetLogLevel != null)
|
||||
{
|
||||
CurrentLogLevel = OnGetLogLevel();
|
||||
}
|
||||
}
|
||||
|
||||
const string changeHint = "\nYou can change the verbosity of logs in the Settings under Tools > UI Toolkit Blurred Background > Settings : LogLevel";
|
||||
|
||||
public static void Log(string message)
|
||||
{
|
||||
UpdateCurrentLogLevel();
|
||||
if(IsLogLevelVisible(LogLevel.Log))
|
||||
Debug.Log(Prefix + message + changeHint);
|
||||
}
|
||||
|
||||
public static void LogWarning(string message)
|
||||
{
|
||||
UpdateCurrentLogLevel();
|
||||
if (IsLogLevelVisible(LogLevel.Warning))
|
||||
Debug.LogWarning(Prefix + message + changeHint);
|
||||
}
|
||||
|
||||
public static void LogError(string message)
|
||||
{
|
||||
UpdateCurrentLogLevel();
|
||||
if (IsLogLevelVisible(LogLevel.Error))
|
||||
Debug.LogError(Prefix + message + changeHint);
|
||||
}
|
||||
|
||||
public static void LogMessage(string message)
|
||||
{
|
||||
UpdateCurrentLogLevel();
|
||||
if (IsLogLevelVisible(LogLevel.Message))
|
||||
Debug.Log(Prefix + message + changeHint);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7e0bd73b28c8cc543bdc4b01080f358a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,268 @@
|
||||
#if UNITY_EDITOR
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace Kamgam.UIToolkitBlurredBackground
|
||||
{
|
||||
public static class PackageImporter
|
||||
{
|
||||
public enum RenderPiplelineType
|
||||
{
|
||||
URP = 0, HDRP = 1, BuiltIn = 2
|
||||
}
|
||||
|
||||
public enum PackageType
|
||||
{
|
||||
// match them 1:1 to RenderPiplelineType
|
||||
URP = 0, HDRP = 1, BuiltIn = 2
|
||||
}
|
||||
|
||||
private class Package
|
||||
{
|
||||
public PackageType PackageType;
|
||||
public string PackagePath;
|
||||
|
||||
public Package(PackageType packageType, string packagePath)
|
||||
{
|
||||
PackageType = packageType;
|
||||
PackagePath = packagePath;
|
||||
}
|
||||
}
|
||||
|
||||
static List<Package> Packages = new List<Package>()
|
||||
{
|
||||
new Package( PackageType.URP, "Assets/Kamgam/UIToolkitBlurredBackground/Packages/UITKBlurredBackgroundURP.unitypackage" ),
|
||||
new Package( PackageType.HDRP, "Assets/Kamgam/UIToolkitBlurredBackground/Packages/UITKBlurredBackgroundHDRP.unitypackage" )
|
||||
};
|
||||
|
||||
static Package getPackageFor(RenderPiplelineType renderPipeline)
|
||||
{
|
||||
foreach (var pkg in Packages)
|
||||
{
|
||||
if ((int)pkg.PackageType == (int)renderPipeline)
|
||||
return pkg;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static Package getPackageFor(PackageType packageType)
|
||||
{
|
||||
foreach (var pkg in Packages)
|
||||
{
|
||||
if (pkg.PackageType == packageType)
|
||||
return pkg;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static System.Action _onComplete;
|
||||
|
||||
#region Start Import Delayed
|
||||
static double startPackageImportAt;
|
||||
|
||||
public static void ImportDelayed(System.Action onComplete)
|
||||
{
|
||||
// Some assets may not be loaded at this time. Thus we wait for them to be imported.
|
||||
_onComplete = onComplete;
|
||||
EditorApplication.update -= onEditorUpdate;
|
||||
EditorApplication.update += onEditorUpdate;
|
||||
startPackageImportAt = EditorApplication.timeSinceStartup + 3; // wait N seconds
|
||||
}
|
||||
|
||||
static void onEditorUpdate()
|
||||
{
|
||||
// wait for the time to reach startPackageImportAt
|
||||
if (startPackageImportAt - EditorApplication.timeSinceStartup < 0)
|
||||
{
|
||||
EditorApplication.update -= onEditorUpdate;
|
||||
ImportFixes();
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
static int _crossCompileCallbackID = -1;
|
||||
|
||||
const string PackagesToImportKey = "Kamgam.SettingsGenerator.PackagesToImport";
|
||||
|
||||
[MenuItem("Tools/UI Toolkit Blurred Background/Debug/Import packages", priority = 500)]
|
||||
public static void ImportFixes()
|
||||
{
|
||||
// Don't import during play mode.
|
||||
if (EditorApplication.isPlaying)
|
||||
return;
|
||||
|
||||
Debug.Log("PackageImporter: Importing..");
|
||||
|
||||
var packagesToImport = initializePackagesToImport();
|
||||
startImportingNextPackage(packagesToImport);
|
||||
}
|
||||
|
||||
static List<PackageType> initializePackagesToImport()
|
||||
{
|
||||
var packages = new List<PackageType>();
|
||||
|
||||
// render pipeline packages
|
||||
var createdForRenderPipleline = RenderPiplelineType.BuiltIn;
|
||||
var currentRenderPipline = GetCurrentRenderPiplelineType();
|
||||
var package = getPackageFor(currentRenderPipline);
|
||||
if (package == null)
|
||||
{
|
||||
Debug.Log("PackageImporter: Render Pipline seems okay, no import needed.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("PackageImporter: Scheduling upgrade from '" + createdForRenderPipleline.ToString() + "' to '" + currentRenderPipline.ToString() + "'.");
|
||||
packages.Add((PackageType)currentRenderPipline);
|
||||
}
|
||||
|
||||
setPackagesToImportList(packages);
|
||||
|
||||
if (packages.Count == 0)
|
||||
{
|
||||
onPackageImportDone(_onComplete);
|
||||
}
|
||||
|
||||
return packages;
|
||||
}
|
||||
|
||||
static void startImportingNextPackage(List<PackageType> packagesToImport)
|
||||
{
|
||||
if (packagesToImport.Count > 0)
|
||||
{
|
||||
var package = getPackageFor(packagesToImport[0]);
|
||||
removePackageToImportList(package.PackageType);
|
||||
startImportingPackage(package);
|
||||
}
|
||||
}
|
||||
|
||||
static void startImportingPackage(Package package)
|
||||
{
|
||||
// AssetDatabase.importPackageCompleted callbacks are lost after a recompile.
|
||||
// Therefore, if the package includes any scripts then these will not be called.
|
||||
// See: https://forum.unity.com/threads/assetdatabase-importpackage-callbacks-dont-work.544031/#post-3716791
|
||||
|
||||
// We use CrossCompileCallbacks to register a callback for after compilation.
|
||||
_crossCompileCallbackID = CrossCompileCallbacks.RegisterCallback(onPackageImportedAfterRecompile);
|
||||
// We also have to store the external callback (if there is one)
|
||||
CrossCompileCallbacks.StoreAction(typeof(PackageImporter).FullName + ".importedCallack", _onComplete);
|
||||
// Delay to avoid "Calling ... from assembly reloading callbacks are not supported." errors.
|
||||
CrossCompileCallbacks.DelayExecutionAfterCompilation = true;
|
||||
|
||||
// If the package does not contain any scripts the we can still use the normal callbacks.
|
||||
AssetDatabase.importPackageCompleted -= onPackageImported;
|
||||
AssetDatabase.importPackageCompleted += onPackageImported;
|
||||
|
||||
// import package
|
||||
Debug.Log("PackageImporter: Importing '" + package.PackagePath + "'.");
|
||||
AssetDatabase.ImportPackage(package.PackagePath, interactive: false);
|
||||
AssetDatabase.SaveAssets();
|
||||
}
|
||||
|
||||
static void setPackagesToImportList(List<PackageType> packages)
|
||||
{
|
||||
var packagesAsInts = packages.Select(p => (int)p).ToArray();
|
||||
SessionState.SetIntArray(PackagesToImportKey, packagesAsInts);
|
||||
}
|
||||
|
||||
static List<PackageType> getPackagesToImportList()
|
||||
{
|
||||
var packagesAsInts = SessionState.GetIntArray(PackagesToImportKey, new int[] { });
|
||||
var packages = packagesAsInts.Select(p => (PackageType)p).ToList();
|
||||
return packages;
|
||||
}
|
||||
|
||||
static void removePackageToImportList(PackageType package)
|
||||
{
|
||||
var packages = getPackagesToImportList();
|
||||
packages.Remove(package);
|
||||
setPackagesToImportList(packages);
|
||||
}
|
||||
|
||||
// This is only execute if the package did not contain any script files.
|
||||
static void onPackageImported(string packageName)
|
||||
{
|
||||
Debug.Log("PackageImporter: Package '" + packageName + "' imported.");
|
||||
|
||||
// There was no recompile. Thus we clear the registered callback.
|
||||
CrossCompileCallbacks.ReleaseIndex(_crossCompileCallbackID);
|
||||
|
||||
// Check if it is one of our packages.
|
||||
// Abort if not.
|
||||
bool isFixerPackage = false;
|
||||
foreach (var pkg in Packages)
|
||||
{
|
||||
if (pkg.PackagePath.Contains(packageName))
|
||||
isFixerPackage = true;
|
||||
}
|
||||
if (!isFixerPackage)
|
||||
return;
|
||||
|
||||
AssetDatabase.importPackageCompleted -= onPackageImported;
|
||||
|
||||
onPackageImportDone(_onComplete);
|
||||
_onComplete = null;
|
||||
}
|
||||
|
||||
static void onPackageImportedAfterRecompile()
|
||||
{
|
||||
Debug.Log("PackageImporter: Recompile detected. Assuming package import is done.");
|
||||
|
||||
// The registered callback is already cleared by now.
|
||||
// Now we let's retrieve that stored extenal callback and hand it over.
|
||||
var onComplete = CrossCompileCallbacks.GetStoredAction(typeof(PackageImporter).FullName + ".importedCallack");
|
||||
onPackageImportDone(onComplete);
|
||||
}
|
||||
|
||||
static void onPackageImportDone(System.Action onComplete)
|
||||
{
|
||||
// Check for more packages to import
|
||||
Debug.Log("PackageImporter: package imported. Looking for next package.");
|
||||
|
||||
var packagesToImport = getPackagesToImportList();
|
||||
|
||||
if (packagesToImport.Count > 0)
|
||||
{
|
||||
// Make sure the onComplete callback is retained across multiple package loads.
|
||||
_onComplete = onComplete;
|
||||
|
||||
// Start importing
|
||||
startImportingNextPackage(packagesToImport);
|
||||
}
|
||||
else
|
||||
{
|
||||
AssetDatabase.SaveAssets();
|
||||
onComplete?.Invoke();
|
||||
|
||||
Debug.Log("PackageImporter: Done (no more packages to import).");
|
||||
}
|
||||
}
|
||||
|
||||
public static RenderPiplelineType GetCurrentRenderPiplelineType()
|
||||
{
|
||||
var currentRP = GraphicsSettings.currentRenderPipeline;
|
||||
|
||||
// null if built-in
|
||||
if (currentRP != null)
|
||||
{
|
||||
if (currentRP.GetType().Name.Contains("Universal"))
|
||||
{
|
||||
return RenderPiplelineType.URP;
|
||||
}
|
||||
else
|
||||
{
|
||||
return RenderPiplelineType.HDRP;
|
||||
}
|
||||
}
|
||||
|
||||
return RenderPiplelineType.BuiltIn;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f365eb8588ba4cf41bfbc9d123cf0739
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,80 @@
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEditor.Build;
|
||||
using UnityEditor.Build.Reporting;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace Kamgam.UIToolkitBlurredBackground
|
||||
{
|
||||
/// <summary>
|
||||
/// Since we do not add any objects directly referencing the materials/shaders we
|
||||
/// need to make sure the shaders are added to builds so they can be found at runtime.
|
||||
/// </summary>
|
||||
public static class SetupShaders
|
||||
{
|
||||
public class SetupShadersOnBuild : IPreprocessBuildWithReport
|
||||
{
|
||||
public int callbackOrder => int.MinValue + 10;
|
||||
|
||||
public void OnPreprocessBuild(BuildReport report)
|
||||
{
|
||||
if (UIToolkitBlurredBackgroundSettings.GetOrCreateSettings().AddShaderBeforeBuild)
|
||||
SetupShaders.AddShaders();
|
||||
}
|
||||
}
|
||||
|
||||
[MenuItem("Tools/UI Toolkit Blurred Background/Debug/Add shaders to always included shader", priority = 401)]
|
||||
public static void AddShaders()
|
||||
{
|
||||
#if !KAMGAM_RENDER_PIPELINE_URP && !KAMGAM_RENDER_PIPELINE_HDRP
|
||||
// BuiltIn
|
||||
AddShaders(BlurredBackgroundBufferBuiltIn.ShaderName);
|
||||
#elif KAMGAM_RENDER_PIPELINE_URP
|
||||
// URP
|
||||
AddShaders(BlurredBackgroundPassURP.ShaderName);
|
||||
#else
|
||||
// HDRP
|
||||
AddShaders(BlurredBackgroundPassHDRP.ShaderName);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void AddShaders(string shaderName)
|
||||
{
|
||||
// Thanks to: https://forum.unity.com/threads/modify-always-included-shaders-with-pre-processor.509479/#post-3509413
|
||||
|
||||
var shader = Shader.Find(shaderName);
|
||||
if (shader == null)
|
||||
return;
|
||||
|
||||
var graphicsSettingsObj = AssetDatabase.LoadAssetAtPath<GraphicsSettings>("ProjectSettings/GraphicsSettings.asset");
|
||||
var serializedObject = new SerializedObject(graphicsSettingsObj);
|
||||
var arrayProp = serializedObject.FindProperty("m_AlwaysIncludedShaders");
|
||||
bool hasShader = false;
|
||||
for (int i = 0; i < arrayProp.arraySize; ++i)
|
||||
{
|
||||
var arrayElem = arrayProp.GetArrayElementAtIndex(i);
|
||||
if (shader == arrayElem.objectReferenceValue)
|
||||
{
|
||||
hasShader = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasShader)
|
||||
{
|
||||
int arrayIndex = arrayProp.arraySize;
|
||||
arrayProp.InsertArrayElementAtIndex(arrayIndex);
|
||||
var arrayElem = arrayProp.GetArrayElementAtIndex(arrayIndex);
|
||||
arrayElem.objectReferenceValue = shader;
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
|
||||
AssetDatabase.SaveAssets();
|
||||
|
||||
Debug.Log("Added the '"+ shaderName + "' shader to always included shaders (see Project Settings > Graphics). UI Toolkit Blurred Background requires it to render the blur. Hope that's okay.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bd28bf476ff557e4ba94bcad28feaf45
|
||||
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/Editor/Settings/SetupShaders.cs
|
||||
uploadId: 644498
|
@@ -0,0 +1,206 @@
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEditor.Compilation;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Kamgam.UIToolkitBlurredBackground
|
||||
{
|
||||
// Create a new type of Settings Asset.
|
||||
public class UIToolkitBlurredBackgroundSettings : ScriptableObject
|
||||
{
|
||||
public enum ShaderVariant { Performance, Gaussian };
|
||||
|
||||
public const string Version = "1.2.0";
|
||||
public const string SettingsFilePath = "Assets/UIToolkitBlurredBackgroundSettings.asset";
|
||||
|
||||
[SerializeField, Tooltip(_logLevelTooltip)]
|
||||
public Logger.LogLevel LogLevel;
|
||||
public const string _logLevelTooltip = "Any log above this log level will not be shown. To turn off all logs choose 'NoLogs'";
|
||||
|
||||
public const string _addShaderBeforeBuildTooltip = "Should the blur shader be added to the list of always included shaders before a build is started?\n\n" +
|
||||
"Disable only if you do not use any blurred images in your project but you still want to keep the asset around.";
|
||||
[Tooltip(_addShaderBeforeBuildTooltip)]
|
||||
public bool AddShaderBeforeBuild = true;
|
||||
|
||||
[RuntimeInitializeOnLoadMethod]
|
||||
static void bindLoggerLevelToSetting()
|
||||
{
|
||||
// Notice: This does not yet create a setting instance!
|
||||
Logger.OnGetLogLevel = () => GetOrCreateSettings().LogLevel;
|
||||
}
|
||||
|
||||
static UIToolkitBlurredBackgroundSettings cachedSettings;
|
||||
|
||||
public static UIToolkitBlurredBackgroundSettings GetOrCreateSettings()
|
||||
{
|
||||
if (cachedSettings == null)
|
||||
{
|
||||
string typeName = typeof(UIToolkitBlurredBackgroundSettings).Name;
|
||||
|
||||
cachedSettings = AssetDatabase.LoadAssetAtPath<UIToolkitBlurredBackgroundSettings>(SettingsFilePath);
|
||||
|
||||
// Still not found? Then search for it.
|
||||
if (cachedSettings == null)
|
||||
{
|
||||
string[] results = AssetDatabase.FindAssets("t:" + typeName);
|
||||
if (results.Length > 0)
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(results[0]);
|
||||
cachedSettings = AssetDatabase.LoadAssetAtPath<UIToolkitBlurredBackgroundSettings>(path);
|
||||
}
|
||||
}
|
||||
|
||||
if (cachedSettings != null)
|
||||
{
|
||||
SessionState.EraseBool(typeName + "WaitingForReload");
|
||||
}
|
||||
|
||||
// Still not found? Then create settings.
|
||||
if (cachedSettings == null)
|
||||
{
|
||||
// Are the settings waiting for a recompile to finish? If yes then return null;
|
||||
// This is important if an external script tries to access the settings before they
|
||||
// are deserialized after a re-compile.
|
||||
bool isWaitingForReloadAfterCompilation = SessionState.GetBool(typeName + "WaitingForReload", false);
|
||||
if (isWaitingForReloadAfterCompilation)
|
||||
{
|
||||
Debug.LogWarning(typeName + " is waiting for assembly reload.");
|
||||
return null;
|
||||
}
|
||||
|
||||
cachedSettings = ScriptableObject.CreateInstance<UIToolkitBlurredBackgroundSettings>();
|
||||
cachedSettings.LogLevel = Logger.LogLevel.Warning;
|
||||
cachedSettings.AddShaderBeforeBuild = true;
|
||||
|
||||
AssetDatabase.CreateAsset(cachedSettings, SettingsFilePath);
|
||||
AssetDatabase.SaveAssets();
|
||||
|
||||
Logger.OnGetLogLevel = () => cachedSettings.LogLevel;
|
||||
}
|
||||
}
|
||||
|
||||
return cachedSettings;
|
||||
}
|
||||
|
||||
internal static SerializedObject GetSerializedSettings()
|
||||
{
|
||||
return new SerializedObject(GetOrCreateSettings());
|
||||
}
|
||||
|
||||
[MenuItem("Tools/UI Toolkit Blurred Background/Settings", priority = 101)]
|
||||
public static void OpenSettings()
|
||||
{
|
||||
var settings = UIToolkitBlurredBackgroundSettings.GetOrCreateSettings();
|
||||
if (settings != null)
|
||||
{
|
||||
Selection.activeObject = settings;
|
||||
EditorGUIUtility.PingObject(settings);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorUtility.DisplayDialog("Error", "UI Toolkit Blurred Background Settings could not be found or created.", "Ok");
|
||||
}
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
EditorUtility.SetDirty(this);
|
||||
#if UNITY_2021_2_OR_NEWER
|
||||
AssetDatabase.SaveAssetIfDirty(this);
|
||||
#else
|
||||
AssetDatabase.SaveAssets();
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[CustomEditor(typeof(UIToolkitBlurredBackgroundSettings))]
|
||||
public class UIToolkitBlurredBackgroundSettingsEditor : Editor
|
||||
{
|
||||
public UIToolkitBlurredBackgroundSettings settings;
|
||||
|
||||
public void OnEnable()
|
||||
{
|
||||
settings = target as UIToolkitBlurredBackgroundSettings;
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
EditorGUILayout.LabelField("Version: " + UIToolkitBlurredBackgroundSettings.Version);
|
||||
base.OnInspectorGUI();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static class UIToolkitBlurredBackgroundSettingsProvider
|
||||
{
|
||||
[SettingsProvider]
|
||||
public static UnityEditor.SettingsProvider CreateUIToolkitBlurredBackgroundSettingsProvider()
|
||||
{
|
||||
var provider = new UnityEditor.SettingsProvider("Project/UI Toolkit Blurred Background", SettingsScope.Project)
|
||||
{
|
||||
label = "UI Toolkit Blurred Background",
|
||||
guiHandler = (searchContext) =>
|
||||
{
|
||||
var settings = UIToolkitBlurredBackgroundSettings.GetSerializedSettings();
|
||||
|
||||
var style = new GUIStyle(GUI.skin.label);
|
||||
style.wordWrap = true;
|
||||
|
||||
EditorGUILayout.LabelField("Version: " + UIToolkitBlurredBackgroundSettings.Version);
|
||||
if (drawButton(" Open Manual ", icon: "_Help"))
|
||||
{
|
||||
Installer.OpenManual();
|
||||
}
|
||||
|
||||
var settingsObj = settings.targetObject as UIToolkitBlurredBackgroundSettings;
|
||||
|
||||
drawField("LogLevel", "Log Level", UIToolkitBlurredBackgroundSettings._logLevelTooltip, settings, style);
|
||||
drawField("AddShaderBeforeBuild", "Add Shader Before Build", UIToolkitBlurredBackgroundSettings._addShaderBeforeBuildTooltip, settings, style);
|
||||
|
||||
settings.ApplyModifiedProperties();
|
||||
},
|
||||
|
||||
// Populate the search keywords to enable smart search filtering and label highlighting.
|
||||
keywords = new System.Collections.Generic.HashSet<string>(new[] { "shader", "triplanar", "rendering" })
|
||||
};
|
||||
|
||||
return provider;
|
||||
}
|
||||
|
||||
static void drawField(string propertyName, string label, string tooltip, SerializedObject settings, GUIStyle style)
|
||||
{
|
||||
EditorGUILayout.PropertyField(settings.FindProperty(propertyName), new GUIContent(label));
|
||||
if (!string.IsNullOrEmpty(tooltip))
|
||||
{
|
||||
GUILayout.BeginVertical(EditorStyles.helpBox);
|
||||
GUILayout.Label(tooltip, style);
|
||||
GUILayout.EndVertical();
|
||||
}
|
||||
GUILayout.Space(10);
|
||||
}
|
||||
|
||||
static bool drawButton(string text, string tooltip = null, string icon = null, params GUILayoutOption[] options)
|
||||
{
|
||||
GUIContent content;
|
||||
|
||||
// icon
|
||||
if (!string.IsNullOrEmpty(icon))
|
||||
content = EditorGUIUtility.IconContent(icon);
|
||||
else
|
||||
content = new GUIContent();
|
||||
|
||||
// text
|
||||
content.text = text;
|
||||
|
||||
// tooltip
|
||||
if (!string.IsNullOrEmpty(tooltip))
|
||||
content.tooltip = tooltip;
|
||||
|
||||
return GUILayout.Button(content, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9535b8a7472136f4f82f99c6192cc973
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,173 @@
|
||||
#if UNITY_EDITOR
|
||||
using System;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Kamgam.UIToolkitBlurredBackground
|
||||
{
|
||||
public static class VersionHelper
|
||||
{
|
||||
public static string VersionFileName = "." + typeof(VersionHelper).FullName + ".txt";
|
||||
public static Version DefaultVersion = new Version(0, 0, 0, 1);
|
||||
|
||||
public delegate bool UpgradeVersionDelegate(Version oldVersion, Version newVersion);
|
||||
|
||||
public static Version Parse(string version)
|
||||
{
|
||||
if (string.IsNullOrEmpty(version))
|
||||
return DefaultVersion;
|
||||
|
||||
if (Version.TryParse(version, out var versionObj))
|
||||
return versionObj;
|
||||
else
|
||||
return VersionHelper.DefaultVersion;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns false if upgrading was not necessary, True otherwise.
|
||||
/// </summary>
|
||||
/// <param name="getVersionFunc">Change this to return the version of the software.<br />
|
||||
/// This is a separate method because your version may be stored in another class or a file.<br />
|
||||
/// The return value if this is compared against the install version marker.</param>
|
||||
/// <param name="upgradeVersionFunc">Use this to execute some custom code before upgrading
|
||||
/// the installed version info. If this returns false then the installed version will NOT be changed.</param>
|
||||
/// <returns></returns>
|
||||
public static bool UpgradeVersion(Func<Version> getVersionFunc, UpgradeVersionDelegate upgradeVersionFunc = null)
|
||||
{
|
||||
return UpgradeVersion(getVersionFunc, out _, out _, upgradeVersionFunc);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns false if upgrading was not necessary, True otherwise.<br />
|
||||
/// Upgrades the version number only if the version info file path is valid. Otherwise it will abort.
|
||||
/// </summary>
|
||||
/// <param name="getVersionFunc">Change this to return the version of the software.<br />
|
||||
/// This is a separate method because your version may be stored in another class or a file.<br />
|
||||
/// The return value if this is compared against the install version marker.</param>
|
||||
/// <param name="oldVersion"></param>
|
||||
/// <param name="newVersion"></param>
|
||||
/// <param name="upgradeVersionFunc">Use this to execute some custom code before upgrading
|
||||
/// the installed version info. If this returns false then the installed version will NOT be changed.</param>
|
||||
/// <returns>Returns false if upgrading was not necessary (or impossible). Returns true if an upgrade is needed (and possible).</returns>
|
||||
public static bool UpgradeVersion(Func<Version> getVersionFunc, out Version oldVersion, out Version newVersion, UpgradeVersionDelegate upgradeVersionFunc = null)
|
||||
{
|
||||
oldVersion = GetInstalledVersion();
|
||||
newVersion = getVersionFunc();
|
||||
|
||||
// Abort upgrades if version info can not be retrieved.
|
||||
if (!VersionInfoPathIsValid())
|
||||
{
|
||||
// We abort if the dir is not found because we assume the
|
||||
// user has moved the asset and thus any upgrade attempts
|
||||
// will probably fail anyways.
|
||||
Logger.LogWarning(
|
||||
"Could not find version info directory: '" + getVersionFileDir() + "'. Aborting upgrade. Did you move the asset?\n" +
|
||||
"If you want auto-upgrades to work again then please restore the asset to the original directory (" + Installer.AssetRootPath + ").");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Notice: this also cover downgrades.
|
||||
if (oldVersion != newVersion)
|
||||
{
|
||||
if (upgradeVersionFunc != null)
|
||||
{
|
||||
bool upgradeSucceeded = upgradeVersionFunc(oldVersion, newVersion);
|
||||
if (upgradeSucceeded)
|
||||
SetInstalledVersion(newVersion);
|
||||
return upgradeSucceeded;
|
||||
}
|
||||
else
|
||||
{
|
||||
SetInstalledVersion(newVersion);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool VersionInfoPathIsValid()
|
||||
{
|
||||
return System.IO.Directory.Exists(getVersionFileDir());
|
||||
}
|
||||
|
||||
public static void SetInstalledVersion(Version version)
|
||||
{
|
||||
if (version == null)
|
||||
return;
|
||||
|
||||
if (!VersionInfoPathIsValid())
|
||||
return;
|
||||
|
||||
string versionString = version.ToString();
|
||||
string filePath = getVersionFilePath();
|
||||
string tmpPath = filePath + ".tmp";
|
||||
|
||||
if (File.Exists(tmpPath))
|
||||
{
|
||||
File.Delete(tmpPath);
|
||||
}
|
||||
|
||||
File.WriteAllText(tmpPath, versionString);
|
||||
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
File.Delete(filePath);
|
||||
}
|
||||
|
||||
File.Move(tmpPath, filePath);
|
||||
}
|
||||
|
||||
public static Version GetInstalledVersion()
|
||||
{
|
||||
string filePath = getVersionFilePath();
|
||||
|
||||
if (!File.Exists(filePath))
|
||||
{
|
||||
return DefaultVersion;
|
||||
}
|
||||
|
||||
string version = File.ReadAllText(filePath);
|
||||
return Parse(version);
|
||||
}
|
||||
|
||||
static string getVersionFilePath()
|
||||
{
|
||||
string dir = getVersionFileDir();
|
||||
return dir + VersionFileName;
|
||||
}
|
||||
|
||||
static string getVersionFileDir()
|
||||
{
|
||||
string dir = Installer.AssetRootPath.Trim();
|
||||
|
||||
// fix empty dir path
|
||||
if (string.IsNullOrEmpty(dir))
|
||||
{
|
||||
dir = "Assets/";
|
||||
}
|
||||
|
||||
// Fix missing ending slash
|
||||
if (!dir.EndsWith("/") && !dir.EndsWith("\\"))
|
||||
{
|
||||
dir = dir + "/";
|
||||
}
|
||||
|
||||
return getBasePath() + dir;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the path to project root (the parent dir of Assets).
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
static string getBasePath()
|
||||
{
|
||||
// Unity Editor: <path to project folder>/Assets
|
||||
// See: https://docs.unity3d.com/ScriptReference/Application-dataPath.html
|
||||
string basePath = Application.dataPath.Replace("/Assets", "/");
|
||||
basePath = basePath.Replace("\\Assets", "\\");
|
||||
return basePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8f7fae563c3916c40a7026d274ee36e7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a72ae1d367ff0ec4e91ddf230e6a5e2e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b37e42fabe04c1347b481cf77b3d9c9f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2bda2a26e9068334093b9ae177d4f386
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,201 @@
|
||||
// This shader uses some optimized gaussian sampling for better quality with decent performance.
|
||||
// _BlurOffset property is ignored in this shader. It exists only to maintain a consistent API.
|
||||
// See: https://www.rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/
|
||||
|
||||
Shader "Kamgam/UI Toolkit/BuiltIn/Blur Shader"
|
||||
{
|
||||
Properties
|
||||
{
|
||||
_MainTex("Texture", 2D) = "white" {}
|
||||
_BlurOffset("Blur Offset", Vector) = (1.0, 1.0, 0)
|
||||
[KeywordEnum(Low, Medium, High)] _Samples("Sample Amount", Float) = 1
|
||||
_AdditiveColor("Additive Color", Color) = (0, 0, 0, 0)
|
||||
[MaterialToggle] _FlipVertical("flipVertical", Float) = 1
|
||||
}
|
||||
|
||||
CGINCLUDE
|
||||
|
||||
#if _SAMPLES_LOW
|
||||
|
||||
#define SAMPLES 10
|
||||
|
||||
#elif _SAMPLES_MEDIUM
|
||||
|
||||
#define SAMPLES 30
|
||||
|
||||
#else
|
||||
|
||||
#define SAMPLES 100
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#pragma multi_compile _SAMPLES_LOW _SAMPLES_MEDIUM _SAMPLES_HIGH
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
|
||||
#include "UnityCG.cginc"
|
||||
|
||||
struct appdata
|
||||
{
|
||||
float4 vertex : POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
struct v2f
|
||||
{
|
||||
float2 uv : TEXCOORD0;
|
||||
float4 vertex : SV_POSITION;
|
||||
};
|
||||
|
||||
sampler2D _MainTex;
|
||||
float4 _MainTex_TexelSize;
|
||||
float4 _MainTex_ST;
|
||||
float4 _AdditiveColor;
|
||||
float2 _BlurOffset;
|
||||
float _FlipVertical;
|
||||
|
||||
// Based on linear sampling on the GPU.
|
||||
// Weights from this excellent article:
|
||||
// https://www.rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/
|
||||
|
||||
static const float offset[3] = { 0.0, 1.3846153846, 3.2307692308 };
|
||||
static const float weight[3] = { 0.2270270270, 0.3162162162, 0.0702702703 };
|
||||
|
||||
float4 vert(float2 uv : TEXCOORD0) : SV_POSITION
|
||||
{
|
||||
float4 pos;
|
||||
pos.xy = uv;
|
||||
// This example is rendering with upside-down flipped projection,
|
||||
// so flip the vertical UV coordinate too
|
||||
if (_ProjectionParams.x < 0)
|
||||
pos.y = 1 - pos.y;
|
||||
pos.z = 0;
|
||||
pos.w = 1;
|
||||
return pos;
|
||||
}
|
||||
|
||||
v2f Vert(appdata v)
|
||||
{
|
||||
v2f o;
|
||||
o.vertex = UnityObjectToClipPos(v.vertex);
|
||||
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
|
||||
return o;
|
||||
}
|
||||
|
||||
fixed4 BlurHorizontal(v2f input) : SV_Target
|
||||
{
|
||||
// See: https://forum.unity.com/threads/_maintex_texelsize-whats-the-meaning.110278/
|
||||
// For a 1024 x 1024 texture this will be 1 / 1024.
|
||||
float2 uv2px = _MainTex_TexelSize.xy;
|
||||
|
||||
// star form, blur with a sample for every step
|
||||
half4 color;
|
||||
int sampleDiv = SAMPLES - 1;
|
||||
float weightSum = 0;
|
||||
for (float i = 0; i < SAMPLES; i++)
|
||||
{
|
||||
// Linear kernel weight interpolation
|
||||
float weight = 0.5 + (0.5 - abs(i / sampleDiv - 0.5));
|
||||
weightSum += weight;
|
||||
|
||||
// x
|
||||
float2 uv = input.uv + float2((i / sampleDiv - 0.5) * _BlurOffset.x, 0.0) * uv2px;
|
||||
|
||||
color += tex2D(_MainTex, uv) * weight;
|
||||
}
|
||||
color /= weightSum;
|
||||
color.a = 1;
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
fixed4 BlurVertical(v2f input) : SV_Target
|
||||
{
|
||||
// See: https://forum.unity.com/threads/_maintex_texelsize-whats-the-meaning.110278/
|
||||
// For a 1024 x 1024 texture this will be 1 / 1024.
|
||||
float2 uv2px = _MainTex_TexelSize.xy;
|
||||
|
||||
// star form, blur with a sample for every step
|
||||
half4 color;
|
||||
int sampleDiv = SAMPLES - 1;
|
||||
float weightSum = 0;
|
||||
for (float i = 0; i < SAMPLES; i++)
|
||||
{
|
||||
// Linear kernel weight interpolation
|
||||
float weight = 0.5 + (0.5 - abs(i / sampleDiv - 0.5));
|
||||
weightSum += weight;
|
||||
|
||||
// y
|
||||
float2 uv = input.uv + float2(0.0, (i / sampleDiv - 0.5) * _BlurOffset.y) * uv2px;
|
||||
|
||||
// Flip UVs if necessary, see
|
||||
// https://docs.unity3d.com/Manual/SL-PlatformDifferences.html
|
||||
// https://forum.unity.com/threads/how-does-unity-handle-the-uv-coordinate-inconsistency-across-different-api.979794/#post-6366516
|
||||
// https://forum.unity.com/threads/command-buffer-blit-render-texture-result-is-upside-down.1463063/
|
||||
if (_FlipVertical && _ProjectionParams.x < 0)
|
||||
{
|
||||
uv.y = 1 - uv.y;
|
||||
}
|
||||
|
||||
color += tex2D(_MainTex, uv) * weight;
|
||||
}
|
||||
color /= weightSum;
|
||||
color.a = tex2D(_MainTex, input.uv).a;
|
||||
|
||||
color += _AdditiveColor;
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
ENDCG
|
||||
|
||||
SubShader
|
||||
{
|
||||
Tags { "RenderType"="Opaque" }
|
||||
LOD 100
|
||||
|
||||
ZWrite Off
|
||||
ZTest Always
|
||||
Blend Off
|
||||
Cull Off
|
||||
|
||||
Pass
|
||||
{
|
||||
Name "Blur Horizontal"
|
||||
|
||||
CGPROGRAM
|
||||
|
||||
v2f vert(appdata v)
|
||||
{
|
||||
return Vert(v);
|
||||
}
|
||||
|
||||
fixed4 frag(v2f input) : SV_Target
|
||||
{
|
||||
return BlurHorizontal(input);
|
||||
}
|
||||
|
||||
ENDCG
|
||||
}
|
||||
|
||||
Pass
|
||||
{
|
||||
Name "Blur Vertical"
|
||||
|
||||
CGPROGRAM
|
||||
|
||||
v2f vert(appdata v)
|
||||
{
|
||||
return Vert(v);
|
||||
}
|
||||
|
||||
fixed4 frag(v2f input) : SV_Target
|
||||
{
|
||||
return BlurVertical(input);
|
||||
}
|
||||
|
||||
ENDCG
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 02c585ad185879e4cb48d6e2683f5448
|
||||
ShaderImporter:
|
||||
externalObjects: {}
|
||||
defaultTextures: []
|
||||
nonModifiableTextures: []
|
||||
preprocessorOverride: 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/Shaders/BuiltIn/UITKBlurShader.shader
|
||||
uploadId: 644498
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7f097bcc1d3713849b7927fa6b5fa498
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,241 @@
|
||||
Shader "Kamgam/UI Toolkit/URP/Blur Shader"
|
||||
{
|
||||
Properties
|
||||
{
|
||||
// MainTex is only used in ShaderGraph < 15
|
||||
_MainTex("Texture", 2D) = "white" {}
|
||||
_BlurOffset("Blur Offset", Vector) = (1.0, 1.0, 0)
|
||||
[KeywordEnum(Low, Medium, High)] _Samples("Sample Amount", Float) = 1
|
||||
}
|
||||
|
||||
HLSLINCLUDE
|
||||
|
||||
#if _SAMPLES_LOW
|
||||
|
||||
#define SAMPLES 10
|
||||
|
||||
#elif _SAMPLES_MEDIUM
|
||||
|
||||
#define SAMPLES 30
|
||||
|
||||
#else
|
||||
|
||||
#define SAMPLES 100
|
||||
|
||||
#endif
|
||||
|
||||
// TODO: Actually the only difference is the default source name _MainTex > _BlitTexture, so we could just extract that and use it in both.
|
||||
|
||||
#if UNITY_VERSION >= 202220
|
||||
// Version compare, see: https://forum.unity.com/threads/urp-version-defines.1218915/#post-9021178
|
||||
|
||||
// Shader Graph >= 14.x
|
||||
|
||||
// Based on the default Hidden/Universal Render Pipleline/Blit shader found under
|
||||
// packages/com.unity.render-pipelines.universal@x.y.z/Shaders/Utils/Blit.shader
|
||||
|
||||
#pragma multi_compile_fragment _ _LINEAR_TO_SRGB_CONVERSION
|
||||
#pragma multi_compile _SAMPLES_LOW _SAMPLES_MEDIUM _SAMPLES_HIGH
|
||||
|
||||
// Core.hlsl for XR dependencies
|
||||
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
|
||||
#include "Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl"
|
||||
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
|
||||
|
||||
SAMPLER(sampler_BlitTexture);
|
||||
#if UNITY_VERSION <= 202320
|
||||
float4 _BlitTexture_TexelSize;
|
||||
#endif
|
||||
float4 _BlurOffset;
|
||||
|
||||
half4 BlurHorizontal(Varyings input) : SV_Target
|
||||
{
|
||||
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
|
||||
float2 uv = input.texcoord;
|
||||
|
||||
// See: https://forum.unity.com/threads/_maintex_texelsize-whats-the-meaning.110278/
|
||||
// For a 1024 x 1024 texture this will be 1 / 1024.
|
||||
float2 uv2px = _BlitTexture_TexelSize.xy;
|
||||
|
||||
// star form, blur with a sample for every step
|
||||
half4 color;
|
||||
int sampleDiv = SAMPLES - 1;
|
||||
float weightSum = 0;
|
||||
for (float i = 0; i < SAMPLES; i++)
|
||||
{
|
||||
// Linear kernel weight interpolation
|
||||
float weight = 0.5 + (0.5 - abs(i / sampleDiv - 0.5));
|
||||
weightSum += weight;
|
||||
|
||||
// x
|
||||
color += SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_BlitTexture, uv + float2((i / sampleDiv - 0.5) * _BlurOffset.x, 0.0) * uv2px) * weight;
|
||||
}
|
||||
color /= weightSum;
|
||||
color.a = 1;
|
||||
|
||||
|
||||
#ifdef _LINEAR_TO_SRGB_CONVERSION
|
||||
color = LinearToSRGB(color);
|
||||
#endif
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
half4 BlurVertical(Varyings input) : SV_Target
|
||||
{
|
||||
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
|
||||
float2 uv = input.texcoord;
|
||||
|
||||
// See: https://forum.unity.com/threads/_maintex_texelsize-whats-the-meaning.110278/
|
||||
// For a 1024 x 1024 texture this will be 1 / 1024.
|
||||
float2 uv2px = _BlitTexture_TexelSize.xy;
|
||||
|
||||
// star form, blur with a sample for every step
|
||||
half4 color;
|
||||
int sampleDiv = SAMPLES - 1;
|
||||
float weightSum = 0;
|
||||
for (float i = 0; i < SAMPLES; i++)
|
||||
{
|
||||
// Linear kernel weight interpolation
|
||||
float weight = 0.5 + (0.5 - abs(i / sampleDiv - 0.5));
|
||||
weightSum += weight;
|
||||
|
||||
// y
|
||||
color += SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_BlitTexture, uv + float2(0.0, (i / sampleDiv - 0.5) * _BlurOffset.y) * uv2px) * weight;
|
||||
}
|
||||
color /= weightSum;
|
||||
color.a = 1;
|
||||
|
||||
|
||||
#ifdef _LINEAR_TO_SRGB_CONVERSION
|
||||
color = LinearToSRGB(color);
|
||||
#endif
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// Shader Graph < 15
|
||||
|
||||
// Based on the default Hidden/Universal Render Pipleline/Blit shader found under
|
||||
// packages/com.unity.render-pipelines.universal@x.y.z/Shaders/Utils/Blit.shader
|
||||
|
||||
#pragma multi_compile_fragment _ _LINEAR_TO_SRGB_CONVERSION
|
||||
#pragma multi_compile _ _USE_DRAW_PROCEDURAL
|
||||
#pragma multi_compile _SAMPLES_LOW _SAMPLES_MEDIUM _SAMPLES_HIGH
|
||||
|
||||
#include "Packages/com.unity.render-pipelines.universal/Shaders/Utils/Fullscreen.hlsl"
|
||||
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
|
||||
|
||||
TEXTURE2D_X(_MainTex);
|
||||
SAMPLER(sampler_MainTex);
|
||||
float4 _MainTex_TexelSize;
|
||||
float4 _BlurOffset;
|
||||
|
||||
half4 BlurHorizontal(Varyings input) : SV_Target
|
||||
{
|
||||
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
|
||||
float2 uv = input.uv;
|
||||
|
||||
// See: https://forum.unity.com/threads/_maintex_texelsize-whats-the-meaning.110278/
|
||||
// For a 1024 x 1024 texture this will be 1 / 1024.
|
||||
float2 uv2px = _MainTex_TexelSize.xy;
|
||||
|
||||
// star form, blur with a sample for every step
|
||||
half4 color;
|
||||
int sampleDiv = SAMPLES - 1;
|
||||
float weightSum = 0;
|
||||
for (float i = 0; i < SAMPLES; i++)
|
||||
{
|
||||
// Linear kernel weight interpolation
|
||||
float weight = 0.5 + (0.5 - abs(i / sampleDiv - 0.5));
|
||||
weightSum += weight;
|
||||
|
||||
// x
|
||||
color += SAMPLE_TEXTURE2D_X(_MainTex, sampler_MainTex, uv + float2((i / sampleDiv - 0.5) * _BlurOffset.x, 0.0) * uv2px) * weight;
|
||||
}
|
||||
color /= weightSum;
|
||||
color.a = 1;
|
||||
|
||||
#ifdef _LINEAR_TO_SRGB_CONVERSION
|
||||
color = LinearToSRGB(color);
|
||||
#endif
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
half4 BlurVertical(Varyings input) : SV_Target
|
||||
{
|
||||
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
|
||||
float2 uv = input.uv;
|
||||
|
||||
// See: https://forum.unity.com/threads/_maintex_texelsize-whats-the-meaning.110278/
|
||||
// For a 1024 x 1024 texture this will be 1 / 1024.
|
||||
float2 uv2px = _MainTex_TexelSize.xy;
|
||||
|
||||
// star form, blur with a sample for every step
|
||||
half4 color;
|
||||
int sampleDiv = SAMPLES - 1;
|
||||
float weightSum = 0;
|
||||
for (float i = 0; i < SAMPLES; i++)
|
||||
{
|
||||
// Linear kernel weight interpolation
|
||||
float weight = 0.5 + (0.5 - abs(i / sampleDiv - 0.5));
|
||||
weightSum += weight;
|
||||
|
||||
// y
|
||||
color += SAMPLE_TEXTURE2D_X(_MainTex, sampler_MainTex, uv + float2(0.0, (i / sampleDiv - 0.5) * _BlurOffset.y) * uv2px) * weight;
|
||||
}
|
||||
color /= weightSum;
|
||||
color.a = 1;
|
||||
|
||||
|
||||
#ifdef _LINEAR_TO_SRGB_CONVERSION
|
||||
color = LinearToSRGB(color);
|
||||
#endif
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
ENDHLSL
|
||||
|
||||
|
||||
|
||||
|
||||
SubShader
|
||||
{
|
||||
Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }
|
||||
LOD 100
|
||||
ZTest Always
|
||||
ZWrite Off
|
||||
Cull Off
|
||||
|
||||
Pass
|
||||
{
|
||||
Name "Blur Horizontal"
|
||||
|
||||
HLSLPROGRAM
|
||||
|
||||
#pragma vertex Vert
|
||||
#pragma fragment BlurHorizontal
|
||||
|
||||
ENDHLSL
|
||||
}
|
||||
|
||||
Pass
|
||||
{
|
||||
Name "Blur Vertical"
|
||||
|
||||
HLSLPROGRAM
|
||||
|
||||
#pragma vertex Vert
|
||||
#pragma fragment BlurVertical
|
||||
|
||||
ENDHLSL
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 97c8e4e6da971644f8c6737a0516a6e9
|
||||
ShaderImporter:
|
||||
externalObjects: {}
|
||||
defaultTextures: []
|
||||
nonModifiableTextures: []
|
||||
preprocessorOverride: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "Kamgam.UIToolkitBlurredBackground",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"Unity.RenderPipelines.Core.Runtime",
|
||||
"Unity.RenderPipelines.HighDefinition.Runtime",
|
||||
"Unity.RenderPipelines.Universal.Runtime",
|
||||
"UnityEngine.UIElementsModule",
|
||||
"UnityEngine.UIElementsInputSystemModule",
|
||||
"Unity.InputSystem"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [
|
||||
{
|
||||
"name": "com.unity.render-pipelines.high-definition",
|
||||
"expression": "0.0.1",
|
||||
"define": "KAMGAM_RENDER_PIPELINE_HDRP"
|
||||
},
|
||||
{
|
||||
"name": "com.unity.render-pipelines.high-definition",
|
||||
"expression": "13.0.0",
|
||||
"define": "KAMGAM_RENDER_PIPELINE_HDRP_13"
|
||||
},
|
||||
{
|
||||
"name": "com.unity.render-pipelines.universal",
|
||||
"expression": "0.0.1",
|
||||
"define": "KAMGAM_RENDER_PIPELINE_URP"
|
||||
},
|
||||
{
|
||||
"name": "com.unity.render-pipelines.universal",
|
||||
"expression": "13.0.0",
|
||||
"define": "KAMGAM_RENDER_PIPELINE_URP_13"
|
||||
}
|
||||
],
|
||||
"noEngineReferences": false
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1c3e60dfaf0052b4485f371308036c41
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Binary file not shown.
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ac86f35bbf69a724cb67c8f58b2ea69f
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user