Skip to content

Commit

Permalink
feat: add blur effect
Browse files Browse the repository at this point in the history
  • Loading branch information
mob-sakai committed Oct 26, 2023
1 parent acc0803 commit 8d5df19
Show file tree
Hide file tree
Showing 6 changed files with 373 additions and 0 deletions.
184 changes: 184 additions & 0 deletions Packages/src/Runtime/Effects/CompositeCanvasBlur.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
using UnityEngine;
using UnityEngine.Profiling;
using UnityEngine.Rendering;

namespace CompositeCanvas
{
public class CompositeCanvasBlur : CompositeCanvasEffect
{
[Header("Blur Settings")]
[Range(0, 1)]
[SerializeField]
private float m_Blur = 0.5f;

[Range(1, 10)]
[SerializeField]
private int m_Iteration = 3;

[Range(0.05f, 3f)]
[SerializeField]
private float m_Power = 1f;

[Range(0.05f, 3f)]
[SerializeField]
private float m_Multiplier = 1;

[Range(0, 1)]
[SerializeField]
private float m_Limit = 1;

private Material _material;

public float blur
{
get => m_Blur;
set
{
value = Mathf.Clamp01(value);
if (Mathf.Approximately(m_Blur, value)) return;

m_Blur = value;
SetRendererDirty();
}
}

public int iteration
{
get => m_Iteration;
set
{
value = Mathf.Clamp(value, 1, 10);
if (m_Iteration == value) return;

m_Iteration = value;
SetRendererDirty();
}
}

public float power
{
get => m_Power;
set
{
value = Mathf.Clamp(value, 0.05f, 3f);
if (Mathf.Approximately(m_Power, value)) return;

m_Power = value;
SetRendererDirty();
}
}

public float multiplier
{
get => m_Multiplier;
set
{
value = Mathf.Clamp(value, 0.05f, 3f);
if (Mathf.Approximately(m_Multiplier, value)) return;

m_Multiplier = value;
SetRendererDirty();
}
}

public virtual float innerCutoff
{
get => 1f;
set { }
}

public float limit
{
get => m_Limit;
set
{
value = Mathf.Clamp01(value);
if (Mathf.Approximately(m_Limit, value)) return;

m_Limit = value;
SetRendererDirty();
}
}

private bool useCutoffPass => !Mathf.Approximately(innerCutoff, 1)
|| !Mathf.Approximately(power, 1)
|| !Mathf.Approximately(multiplier, 1)
|| !Mathf.Approximately(limit, 1);

protected override void OnDisable()
{
MaterialRegistry.Release(ref _material);
base.OnDisable();
}

public override void ApplyBakedEffect(CommandBuffer cb)
{
// Baked buffer
Profiler.BeginSample("(CCR)[CompositeCanvasBlur] ApplyBakedEffect > Init");
var rt = compositeCanvasRenderer.mainTexture;
var w = rt.width;
var h = rt.height;
var result = new RenderTargetIdentifier(rt);
Profiler.EndSample();

// Get blur material
Profiler.BeginSample("(CCR)[CompositeCanvasBlur] ApplyBakedEffect > Get blur material (lambda)");
var hash = new Hash128(ShaderPropertyIds.compositeCanvasBlur, 0, 0, 0);
MaterialRegistry.Get(hash, ref _material,
() => new Material(Shader.Find("Hidden/UI/CompositeCanvasRenderer/Blur"))
{
hideFlags = HideFlags.DontSave | HideFlags.NotEditable
}, CompositeCanvasRendererProjectSettings.cacheRendererMaterial);
Profiler.EndSample();

var scale = w / compositeCanvasRenderer.renderingSize.x;
var blurValue = blur * scale;
if (0 < blurValue)
{
Profiler.BeginSample("(CCR)[CompositeCanvasBlur] ApplyBakedEffect > Construct blur effect for cb");
cb.GetTemporaryRT(ShaderPropertyIds.tmpRt, w, h, 0, FilterMode.Bilinear);
for (var i = 0; i < m_Iteration; i++)
{
// Horizontal blur
cb.SetGlobalVector(ShaderPropertyIds.blur, new Vector4(blurValue, 0));
cb.Blit(result, ShaderPropertyIds.tmpRt, _material, 0);

// Vertical blur
cb.SetGlobalVector(ShaderPropertyIds.blur, new Vector4(0, blurValue));
if (i == iteration - 1 && useCutoffPass)
{
// blur and cutoff
cb.SetGlobalFloat(ShaderPropertyIds.innerCutoff, innerCutoff);
cb.SetGlobalFloat(ShaderPropertyIds.power, power);
cb.SetGlobalFloat(ShaderPropertyIds.multiplier, multiplier);
cb.SetGlobalFloat(ShaderPropertyIds.limit, limit);
cb.Blit(ShaderPropertyIds.tmpRt, result, _material);
}
else
{
cb.Blit(ShaderPropertyIds.tmpRt, result, _material, 0);
}
}

cb.ReleaseTemporaryRT(ShaderPropertyIds.tmpRt);
Profiler.EndSample();
}
else if (useCutoffPass)
{
Profiler.BeginSample("(CCR)[CompositeCanvasBlur] ApplyBakedEffect > Construct cutoff effect for cb");
cb.GetTemporaryRT(ShaderPropertyIds.tmpRt, w, h, 0, FilterMode.Bilinear);

// cutoff
cb.SetGlobalFloat(ShaderPropertyIds.innerCutoff, innerCutoff);
cb.SetGlobalFloat(ShaderPropertyIds.power, power);
cb.SetGlobalFloat(ShaderPropertyIds.multiplier, multiplier);
cb.SetGlobalFloat(ShaderPropertyIds.limit, limit);
cb.Blit(result, ShaderPropertyIds.tmpRt, _material, 1);
cb.CopyTexture(ShaderPropertyIds.tmpRt, result);

cb.ReleaseTemporaryRT(ShaderPropertyIds.tmpRt);
Profiler.EndSample();
}
}
}
}
11 changes: 11 additions & 0 deletions Packages/src/Runtime/Effects/CompositeCanvasBlur.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

60 changes: 60 additions & 0 deletions Packages/src/Runtime/Effects/CompositeCanvasEffect.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using UnityEngine;
using UnityEngine.Rendering;

namespace CompositeCanvas
{
[ExecuteAlways]
[RequireComponent(typeof(CompositeCanvasRenderer))]
[DisallowMultipleComponent]
public abstract class CompositeCanvasEffect : MonoBehaviour
{
private CompositeCanvasRenderer _compositeCanvasRenderer;

protected CompositeCanvasRenderer compositeCanvasRenderer =>
_compositeCanvasRenderer
? _compositeCanvasRenderer
: _compositeCanvasRenderer = GetComponent<CompositeCanvasRenderer>();

public virtual void Reset()
{
}

protected virtual void OnEnable()
{
SetRendererDirty();
}

protected virtual void OnDisable()
{
SetRendererDirty();
}

protected virtual void OnValidate()
{
SetRendererDirty();
}

public void SetRendererDirty()
{
if (!compositeCanvasRenderer) return;

compositeCanvasRenderer.SetDirty();
}

public void SetRendererVerticesDirty()
{
if (!compositeCanvasRenderer) return;

compositeCanvasRenderer.SetVerticesDirty();
}

public void SetRendererMaterialDirty()
{
if (!compositeCanvasRenderer) return;

compositeCanvasRenderer.SetMaterialDirty();
}

public abstract void ApplyBakedEffect(CommandBuffer cb);
}
}
11 changes: 11 additions & 0 deletions Packages/src/Runtime/Effects/CompositeCanvasEffect.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

98 changes: 98 additions & 0 deletions Packages/src/Shaders/Hidden-UI-CompositeCanvasRenderer-Blur.shader
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
Shader "Hidden/UI/CompositeCanvasRenderer/Blur"
{
Properties
{
[PerRendererData] _MainTex("Main Texture", 2D) = "white" {}
}

SubShader
{
ZTest Always
Cull Off
ZWrite Off
Blend One Zero
Fog
{
Mode Off
}

Pass
{
Name "Blur"

CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag_blur
#pragma target 2.0

#include "UnityCG.cginc"

sampler2D _MainTex;
float4 _MainTex_TexelSize;
half4 _Blur;

fixed4 tex2DBlurring1D(sampler2D tex, half2 uv, half2 blur)
{
const int KERNEL_SIZE = 9;
float4 o = 0;
float sum = 0;
for (int i = -KERNEL_SIZE / 2; i <= KERNEL_SIZE / 2; i++)
{
const half2 sample_uv = half2(uv + blur * i);
const float weight = 1.0 / (abs(i) + 2);
o += tex2D(tex, sample_uv) * weight;
sum += weight;
}
return o / sum;
}

float invLerp(const float from, const float to, const float value)
{
return saturate(max(0, value - from) / max(0.000000001, to - from));
}

fixed4 frag_blur(v2f_img IN) : SV_Target
{
const half2 blur_factor = _Blur.xy;
return tex2DBlurring1D(_MainTex, IN.uv, blur_factor * _MainTex_TexelSize.xy);
}
ENDCG
}

Pass
{
Name "Cutoff"

CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#pragma target 2.0

#include "UnityCG.cginc"

sampler2D _MainTex;
float _InnerCutoff;
float _Multiplier;
float _Power;
float _Limit;

float invLerp(const float from, const float to, const float value)
{
return (max(0, value - from) / max(0.000000001, to - from));
}

fixed4 frag(v2f_img IN) : SV_Target
{
half4 color = tex2D(_MainTex, IN.uv);
color = pow(color, _Power);

const half inner = invLerp(_InnerCutoff, 1, color.a);
color *= lerp(_Multiplier, 0, inner);

color = min(color, _Limit);
return color;
}
ENDCG
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 8d5df19

Please sign in to comment.