Net.Like.Xue.Tokyo/Assets/Plugins/Kamgam/UIToolkitBlurredBackground/Runtime/Scripts/Rendering/BlurredBackgroundBufferBuil...

435 lines
15 KiB
C#

#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