Seven is the number.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

110 lines
3.2 KiB

using UnityEngine;
using static UnityEngine.Mathf;
namespace LeTai.Effects
{
public class ScalableBlurConfig : BlurConfig
{
[SerializeField] float radius = 4;
[SerializeField] int iteration = 4;
[SerializeField] int maxDepth = 6;
[SerializeField] [Range(0, 256)] float strength;
/// <summary>
/// Distance between the base texel and the texel to be sampled.
/// </summary>
public float Radius
{
get { return radius; }
set { radius = Max(0, value); }
}
/// <summary>
/// Half the number of time to process the image. It is half because the real number of iteration must alway be even. Using half also make calculation simpler
/// </summary>
/// <value>
/// Must be non-negative
/// </value>
public int Iteration
{
get { return iteration; }
set { iteration = Max(0, value); }
}
/// <summary>
/// Clamp the minimum size of the intermediate texture. Reduce flickering and blur
/// </summary>
/// <value>
/// Must larger than 0
/// </value>
public int MaxDepth
{
get { return maxDepth; }
set { maxDepth = Max(1, value); }
}
/// <summary>
/// User friendly property to control the amount of blur
/// </summary>
///<value>
/// Must be non-negative
/// </value>
public float Strength
{
get { return strength = radius * (3 * (1 << iteration) - 2) / UNIT_VARIANCE; }
set
{
strength = Max(0, value);
SetAdvancedFieldFromSimple();
}
}
// With the "correct" unit variance, the edge of the shadow at higher stddev go below 8bit fixed point resolution
// We "wastes" processing power on these.
// TODO: optimize that:
// The maximum distance that will show up is:
// e^(-D^2 / 2R^2) < .5/256
// => D < 3*sqrt(2*log(2)) * R ~ 3.53223*R
// Can probably stop sooner than that
static readonly float UNIT_VARIANCE = 1f + Sqrt(2f) / 2f;
/// <summary>
/// Calculate size and iteration from strength
/// </summary>
protected virtual void SetAdvancedFieldFromSimple()
{
if (strength < 1e-2)
{
iteration = 0;
radius = 0;
return;
}
var variance = strength * UNIT_VARIANCE;
// Each level of the pyramid have double the effective radius of the last, so total effective radius would be:
// S = (2^0 + 2^1 +...+ 2^iteration +...+ 2^1 + 2^0) * R
// https://en.wikipedia.org/wiki/1_%2B_2_%2B_4_%2B_8_%2B_%E2%8B%AF
// S = (3 * 2^I - 2) * R
// so:
// I = log((s + 2r)/ (3r))/log(2)
// and:
// R = S / (3 * 2^I - 2)
//
// Experimental result show that best result are obtained with R <= 2^I - 1, so:
// I >= log(1/6 * (sqrt(12S + 1) + 5))/log(2)
//
// There still some artifact at the lower end, not sure how to handle that yet
// TODO: use a different algorithm for low Strength.
iteration = CeilToInt(Log(1 / 6f * (Sqrt(12 * variance + 1) + 5)) / Log(2));
radius = variance / (3 * (1 << iteration) - 2);
}
void OnValidate()
{
SetAdvancedFieldFromSimple();
}
}
}